From ebbb4172835626408ec61597a5517b6133773efe Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 19 Jan 2016 15:20:26 -0800 Subject: [PATCH] more script data extraction improvements. --- lib/bcoin/script.js | 81 ++++++++++++++++++++++++++++++++++----------- lib/bcoin/tx.js | 4 +-- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 8f03fc79..d6deda5e 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -909,7 +909,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) { val = stack.pop(); if (flags.verifynulldummy !== false) { - if (!Array.isArray(val) || val.length > 0) + if (!script.isDummy(val)) 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) { if (!Array.isArray(s[s.length - 1])) return; @@ -1249,13 +1258,13 @@ script.spendable = function spendable(s, lockTime) { }; script.getInputData = function getData(s, prev) { - var output; + var output, type; if (prev && !script.isScripthash(prev)) { output = script.getOutputData(prev); output.side = 'input'; - // We could call getInputData, but + // We could call _getInputData, but // we really only need the signatures. if (output.type === 'pubkey') { output.signatures = [s[0]]; @@ -1264,6 +1273,11 @@ script.getInputData = function getData(s, prev) { output.keys = [s[1]]; } else if (output.type === 'multisig') { 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 { output.signatures = script.getUnknownData(s).signatures; } @@ -1271,13 +1285,24 @@ script.getInputData = function getData(s, prev) { 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; - if (script.isPubkeyInput(s)) { + assert(typeof type === 'string'); + + if (type === 'pubkey') { sig = s[0]; return { type: 'pubkey', @@ -1287,7 +1312,7 @@ script._getInputData = function _getInputData(s) { }; } - if (script.isPubkeyhashInput(s)) { + if (type === 'pubkeyhash') { sig = s[0]; key = s[1]; 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); return { type: 'multisig', @@ -1313,15 +1338,15 @@ script._getInputData = function _getInputData(s) { }; } - if (script.isScripthashInput(s)) { + if (type === 'scripthash') { raw = s[s.length - 1]; redeem = script.decode(raw); lock = script.lockTime(redeem); hash = bcoin.wallet.key2hash(raw); address = bcoin.wallet.hash2addr(hash, 'scripthash'); - input = script.getInputData(s.slice(0, -1)); - delete input.none; output = script.getOutputData(script.subscript(redeem)); + input = script._getInputData(s.slice(0, -1), output.type); + delete input.none; return utils.merge(input, output, { type: 'scripthash', side: 'input', @@ -1620,8 +1645,10 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) { script.isMultisigInput = function isMultisigInput(s, keys, tx, i) { var i, o; - // We need to rule out scripthash - // because it may look like multisig + // We need to rule out scripthash because + // it may look like multisig. This is + // strange because it's technically a + // recursive call. if (script.isScripthashInput(s)) 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; // Grab the raw redeem script. @@ -1680,6 +1707,25 @@ script.isScripthashInput = function isScripthashInput(s, data) { if (!Array.isArray(raw)) 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 // it easier for other functions to parse this. redeem = script.subscript(script.decode(raw)); @@ -1694,11 +1740,6 @@ script.isScripthashInput = function isScripthashInput(s, data) { 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 if (!script.isPubkey(redeem, data) && !script.isPubkeyhash(redeem, data) @@ -1840,7 +1881,7 @@ script.isSignature = function isSignature(sig) { return sig.length >= 9 && sig.length <= 73; }; -script.isEmpty = function isEmpty(data) { +script.isDummy = function isDummy(data) { if (!utils.isBuffer(data)) return false; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index b2455b5a..885ada51 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -423,7 +423,7 @@ TX.prototype.signInput = function signInput(index, key, type) { // and increment the total number of // signatures. if (ki < len && signatures < m) { - if (bcoin.script.isEmpty(input.script[ki])) { + if (bcoin.script.isDummy(input.script[ki])) { input.script[ki] = signature; signatures++; } @@ -433,7 +433,7 @@ TX.prototype.signInput = function signInput(index, key, type) { if (signatures >= m) { // Remove empty slots left over. 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); len--; }