diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 8c9988b7..8162ccae 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -180,7 +180,13 @@ script.subscript = function subscript(s, lastSep) { }; script.verify = function verify(hash, sig, pub) { - var k = bcoin.ecdsa.keyFromPublic(pub); + var k; + + try { + k = bcoin.ecdsa.keyFromPublic(pub); + } catch (e) { + return false; + } // Points at Infinity make verify() throw. // This specifically throws on wallet-test.js @@ -240,7 +246,7 @@ script.execute = function execute(s, stack, tx, index, recurse) { var n, n1, n2, n3; var res; var pub, sig, type, subscript, hash; - var keys, i, key, m; + var keys, i, j, key, m; var succ; var lock, threshold; var evalScript; @@ -720,7 +726,7 @@ script.execute = function execute(s, stack, tx, index, recurse) { // Get signatures succ = 0; - for (i = 0; i < m; i++) { + for (i = 0, j = 0; i < m && j < n; i++) { sig = stack.pop(); type = sig[sig.length - 1]; if (!constants.rhashType[type & 0x1f]) @@ -731,8 +737,10 @@ script.execute = function execute(s, stack, tx, index, recurse) { hash = tx.subscriptHash(index, subscript, type); - // Strict order: - res = script.verify(hash, sig.slice(0, -1), keys.pop()); + res = false; + for (; !res && j < n; j++) + res = script.verify(hash, sig.slice(0, -1), keys[j]); + if (res) succ++; } @@ -740,6 +748,10 @@ script.execute = function execute(s, stack, tx, index, recurse) { // Extra value stack.pop(); + // Too many signatures on stack + // if (stack.length > 0) + // return false; + res = succ >= m; if (o === 'checkmultisigverify') { if (!res) @@ -826,8 +838,7 @@ script.exec = function(input, output, tx, i, recurse) { res = script.execute(output, stack, tx, i, recurse); - // if (!res || stack.length === 0 || new bn(stack.pop()).cmp(0) !== 0) - if (!res || stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ])) + if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) return false; return true; @@ -1204,7 +1215,11 @@ script.format = function(input, output) { var scripts = []; var prev, redeem; - if (input) { + if (Array.isArray(input)) { + scripts.push(input); + } else if (Array.isArray(output)) { + scripts.push(output); + } else if (input) { scripts.push(input.script); if (input.out.tx && input.out.tx.outputs[input.out.index]) { prev = input.out.tx.outputs[input.out.index].script; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index f96afbab..5d9996b3 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -177,8 +177,13 @@ TX.prototype.prevOut = function prevOut(i, def) { return input.out.tx.outputs[input.out.index] || def; }; -TX.prototype.signatureHash = function signatureHash(i, type) { - var input, s, hash; +TX.prototype.signatureHash = function signatureHash(i, s, type) { + var input, hash; + + if (s != null && !Array.isArray(s)) { + type = s; + s = null; + } if (typeof i === 'object') i = this.inputs.indexOf(i); @@ -193,7 +198,7 @@ TX.prototype.signatureHash = function signatureHash(i, type) { input = this.inputs[i]; // Get the previous output's subscript - s = input.out.tx.getSubscript(input.out.index); + s = s || input.out.tx.getSubscript(input.out.index); // Get the hash of the current tx, minus the other inputs, plus the sighash. hash = this.subscriptHash(i, s, type); @@ -201,9 +206,14 @@ TX.prototype.signatureHash = function signatureHash(i, type) { return hash; }; -TX.prototype.signature = function signature(i, key, type) { +TX.prototype.signature = function signature(i, key, s, type) { var hash, signature; + if (s != null && !Array.isArray(s)) { + type = s; + s = null; + } + if (typeof i === 'object') i = this.inputs.indexOf(i); @@ -214,7 +224,7 @@ TX.prototype.signature = function signature(i, key, type) { type = constants.hashType[type]; // Get the hash of the current tx, minus the other inputs, plus the sighash. - hash = this.sigHash(i, type); + hash = this.signatureHash(i, s, type); // Sign the transaction with our one input signature = bcoin.ecdsa.sign(hash, key.priv).toDER(); @@ -300,8 +310,15 @@ TX.prototype.signInput = function signInput(input, key, type) { // Get the previous output's subscript s = input.out.tx.getSubscript(input.out.index); + if (bcoin.script.isScripthash(s)) { + // We need to grab the redeem script when signing p2sh transactions. + redeem = bcoin.script.decode(input.script[input.script.length - 1]); + } else { + redeem = s; + } + // Get the hash of the current tx, minus the other inputs, plus the sighash. - hash = this.subscriptHash(this.inputs.indexOf(input), s, type); + hash = this.subscriptHash(this.inputs.indexOf(input), redeem, type); // Sign the transaction with our one input signature = bcoin.ecdsa.sign(hash, key.priv).toDER(); @@ -327,12 +344,8 @@ TX.prototype.signInput = function signInput(input, key, type) { if (bcoin.script.isMultisig(s) || bcoin.script.isScripthash(s)) { len = input.script.length; - if (bcoin.script.isScripthash(s)) { + if (bcoin.script.isScripthash(s)) len--; - redeem = bcoin.script.decode(input.script[input.script.length - 1]); - } else { - redeem = s; - } m = redeem[0]; // If using pushdata instead of OP_1-16: @@ -468,7 +481,7 @@ TX.prototype.scriptOutput = function scriptOutput(output, options) { n = options.n || keys.length; assert(m >= 1 && m <= n); - if (options.hash) + if (options.scripthash) assert(n >= 1 && n <= 15); else assert(n >= 1 && n <= 3); @@ -613,12 +626,8 @@ TX.prototype.verify = function verify(index, force) { bcoin.script.execute(input.script, stack, this, i); res = bcoin.script.execute(prev, stack, this, i); - if (!res) - return false; - // Might be necessary for arithmetic: - // if (stack.length === 0 || new bn(stack.pop()).cmp(0) !== 0) - if (stack.length === 0 || utils.isEqual(stack.pop(), [ 0 ])) + if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) return false; if (bcoin.script.isScripthash(prev)) { @@ -627,7 +636,7 @@ TX.prototype.verify = function verify(index, force) { return false; redeem = bcoin.script.decode(redeem); res = bcoin.script.execute(redeem, stack, this, i); - if (!res) + if (!res || stack.length === 0 || new bn(stack.pop()).cmpn(0) === 0) return false; } @@ -728,15 +737,6 @@ TX.prototype.maxSize = function maxSize() { return size; }; -// Building a TX: -// 1. Add outputs: -// - this.output({ address: ..., value: ... }); -// - this.output({ address: ..., value: ... }); -// 2. Add inputs with utxos and change output: -// - this.fillUnspent(unspentItems, [changeAddr]); -// 3. Fill input scripts (for each input): -// - this.scriptInput(input, pub) -// - this.signInput(input, key, [sigHashType]) TX.prototype.utxos = function utxos(unspent) { // NOTE: tx should be prefilled with all outputs var cost = this.funds('out'); @@ -826,8 +826,6 @@ TX.prototype.fillUnspent = function fillUnspent(unspent, changeAddress) { if (result.change.cmpn(TX.dust) < 0) { // Do nothing. Change is added to fee. assert(this.getFee().cmp(result.fee.add(result.change)) === 0); - // Adding change to outputs. - // this.outputs[this.outputs.length - 1].value.iadd(result.change); this.changeOutput = null; } else { this.output({ @@ -895,16 +893,15 @@ TX.getInputData = function getInputData(input) { if (!input || !input.script) return; var script = input.script; - var scriptSig, pub, hash, addr, redeem, data; - var output; + var sig, pub, hash, addr, redeem, data, output; if (bcoin.script.isPubkeyhashInput(script)) { - scriptSig = utils.toHex(script[0]); + sig = utils.toHex(script[0]); pub = script[1]; hash = utils.ripesha(pub); addr = bcoin.wallet.hash2addr(hash); return { - sig: scriptSig, + sig: sig, pub: pub, hash: hash, addr: addr @@ -946,7 +943,7 @@ TX.getOutputData = function getOutputData(output) { if (!output || !output.script) return; var script = output.script; - var pub, hash, addr, pubs; + var pub, hash, addr, pubs, ret; if (bcoin.script.isPubkey(script)) { pub = script[0]; @@ -1014,8 +1011,10 @@ TX.getOutputData = function getOutputData(output) { } if (bcoin.script.isColored(script)) { + ret = bcoin.script.colored(script); return { - data: bcoin.script.colored(script) + data: ret, + text: utils.array2ascii(ret) }; } };