more script data extraction improvements.

This commit is contained in:
Christopher Jeffrey 2016-01-19 15:20:26 -08:00
parent b389dd1b5f
commit ebbb417283
2 changed files with 63 additions and 22 deletions

View File

@ -909,7 +909,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) {
val = stack.pop(); val = stack.pop();
if (flags.verifynulldummy !== false) { if (flags.verifynulldummy !== false) {
if (!Array.isArray(val) || val.length > 0) if (!script.isDummy(val))
return false; return false;
} }
@ -1170,6 +1170,15 @@ script.createMultisig = function createMultisig(keys, m, n) {
); );
}; };
script.createScripthash = function createScripthash(s) {
assert(Array.isArray(s));
return [
'hash160',
utils.ripesha(script.encode(s)),
'equal'
];
};
script.redeem = function redeem(s) { script.redeem = function redeem(s) {
if (!Array.isArray(s[s.length - 1])) if (!Array.isArray(s[s.length - 1]))
return; return;
@ -1249,13 +1258,13 @@ script.spendable = function spendable(s, lockTime) {
}; };
script.getInputData = function getData(s, prev) { script.getInputData = function getData(s, prev) {
var output; var output, type;
if (prev && !script.isScripthash(prev)) { if (prev && !script.isScripthash(prev)) {
output = script.getOutputData(prev); output = script.getOutputData(prev);
output.side = 'input'; output.side = 'input';
// We could call getInputData, but // We could call _getInputData, but
// we really only need the signatures. // we really only need the signatures.
if (output.type === 'pubkey') { if (output.type === 'pubkey') {
output.signatures = [s[0]]; output.signatures = [s[0]];
@ -1264,6 +1273,11 @@ script.getInputData = function getData(s, prev) {
output.keys = [s[1]]; output.keys = [s[1]];
} else if (output.type === 'multisig') { } else if (output.type === 'multisig') {
output.signatures = s.slice(1); output.signatures = s.slice(1);
} else if (output.type === 'scripthash') {
// Scripthash is the only case where
// we get more data from the input
// than the output.
output = script._getInputData(s, output.type);
} else { } else {
output.signatures = script.getUnknownData(s).signatures; output.signatures = script.getUnknownData(s).signatures;
} }
@ -1271,13 +1285,24 @@ script.getInputData = function getData(s, prev) {
return output; return output;
} }
return script._getInputData(s); if (script.isPubkeyInput(s))
type = 'pubkey';
else if (script.isPubkeyhashInput(s))
type = 'pubkeyhash';
else if (script.isMultisigInput(s))
type = 'multisig';
else if (script.isScripthashInput(s))
type = 'scripthash';
return script._getInputData(s, type);
}; };
script._getInputData = function _getInputData(s) { script._getInputData = function _getInputData(s, type) {
var sig, key, hash, raw, redeem, lock, hash, address, input, output; var sig, key, hash, raw, redeem, lock, hash, address, input, output;
if (script.isPubkeyInput(s)) { assert(typeof type === 'string');
if (type === 'pubkey') {
sig = s[0]; sig = s[0];
return { return {
type: 'pubkey', type: 'pubkey',
@ -1287,7 +1312,7 @@ script._getInputData = function _getInputData(s) {
}; };
} }
if (script.isPubkeyhashInput(s)) { if (type === 'pubkeyhash') {
sig = s[0]; sig = s[0];
key = s[1]; key = s[1];
hash = bcoin.wallet.key2hash(key); hash = bcoin.wallet.key2hash(key);
@ -1302,7 +1327,7 @@ script._getInputData = function _getInputData(s) {
}; };
} }
if (script.isMultisigInput(s)) { if (type === 'multisig') {
sig = s.slice(1); sig = s.slice(1);
return { return {
type: 'multisig', type: 'multisig',
@ -1313,15 +1338,15 @@ script._getInputData = function _getInputData(s) {
}; };
} }
if (script.isScripthashInput(s)) { if (type === 'scripthash') {
raw = s[s.length - 1]; raw = s[s.length - 1];
redeem = script.decode(raw); redeem = script.decode(raw);
lock = script.lockTime(redeem); lock = script.lockTime(redeem);
hash = bcoin.wallet.key2hash(raw); hash = bcoin.wallet.key2hash(raw);
address = bcoin.wallet.hash2addr(hash, 'scripthash'); address = bcoin.wallet.hash2addr(hash, 'scripthash');
input = script.getInputData(s.slice(0, -1));
delete input.none;
output = script.getOutputData(script.subscript(redeem)); output = script.getOutputData(script.subscript(redeem));
input = script._getInputData(s.slice(0, -1), output.type);
delete input.none;
return utils.merge(input, output, { return utils.merge(input, output, {
type: 'scripthash', type: 'scripthash',
side: 'input', side: 'input',
@ -1620,8 +1645,10 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) {
script.isMultisigInput = function isMultisigInput(s, keys, tx, i) { script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
var i, o; var i, o;
// We need to rule out scripthash // We need to rule out scripthash because
// because it may look like multisig // it may look like multisig. This is
// strange because it's technically a
// recursive call.
if (script.isScripthashInput(s)) if (script.isScripthashInput(s))
return false; return false;
@ -1664,7 +1691,7 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) {
}; };
}; };
script.isScripthashInput = function isScripthashInput(s, data) { script.isScripthashInput = function isScripthashInput(s, data, strict) {
var raw, redeem; var raw, redeem;
// Grab the raw redeem script. // Grab the raw redeem script.
@ -1680,6 +1707,25 @@ script.isScripthashInput = function isScripthashInput(s, data) {
if (!Array.isArray(raw)) if (!Array.isArray(raw))
return false; return false;
// If the last data element is a valid
// signature or key, it's _extremely_
// unlikely this is a scripthash.
if (script.isSignatureEncoding(raw))
return false;
if (script.isKeyEncoding(raw))
return false;
// Check data against last array in case
// a raw redeem script was passed in.
if (data && utils.isEqual(data, raw))
return raw;
// Return here if we do not want to check
// against standard transaction types.
if (!strict)
return raw;
// P2SH redeem scripts can be nonstandard: make // P2SH redeem scripts can be nonstandard: make
// it easier for other functions to parse this. // it easier for other functions to parse this.
redeem = script.subscript(script.decode(raw)); redeem = script.subscript(script.decode(raw));
@ -1694,11 +1740,6 @@ script.isScripthashInput = function isScripthashInput(s, data) {
return false; return false;
} }
// Check data against last array in case
// a raw redeem script was passed in.
if (data && utils.isEqual(data, raw))
return raw;
// Test against all other script types // Test against all other script types
if (!script.isPubkey(redeem, data) if (!script.isPubkey(redeem, data)
&& !script.isPubkeyhash(redeem, data) && !script.isPubkeyhash(redeem, data)
@ -1840,7 +1881,7 @@ script.isSignature = function isSignature(sig) {
return sig.length >= 9 && sig.length <= 73; return sig.length >= 9 && sig.length <= 73;
}; };
script.isEmpty = function isEmpty(data) { script.isDummy = function isDummy(data) {
if (!utils.isBuffer(data)) if (!utils.isBuffer(data))
return false; return false;

View File

@ -423,7 +423,7 @@ TX.prototype.signInput = function signInput(index, key, type) {
// and increment the total number of // and increment the total number of
// signatures. // signatures.
if (ki < len && signatures < m) { if (ki < len && signatures < m) {
if (bcoin.script.isEmpty(input.script[ki])) { if (bcoin.script.isDummy(input.script[ki])) {
input.script[ki] = signature; input.script[ki] = signature;
signatures++; signatures++;
} }
@ -433,7 +433,7 @@ TX.prototype.signInput = function signInput(index, key, type) {
if (signatures >= m) { if (signatures >= m) {
// Remove empty slots left over. // Remove empty slots left over.
for (i = len - 1; i >= 1; i--) { for (i = len - 1; i >= 1; i--) {
if (bcoin.script.isEmpty(input.script[i])) { if (bcoin.script.isDummy(input.script[i])) {
input.script.splice(i, 1); input.script.splice(i, 1);
len--; len--;
} }