diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 743d2eff..ac520449 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -743,11 +743,11 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { if (!script.isKey(key)) return false; - if (!script.isSig(sig)) + if (!script.isSignature(sig)) return false; if (flags.strictder !== false) { - if (!script.isValidSig(sig)) + if (!script.isValidSignature(sig)) return false; } @@ -815,11 +815,11 @@ script.execute = function execute(s, stack, tx, index, flags, recurse) { for (i = 0, j = 0; i < m && j < n; i++) { sig = stack.pop(); - if (!script.isSig(sig)) + if (!script.isSignature(sig)) return false; if (flags.strictder !== false) { - if (!script.isValidSig(sig)) + if (!script.isValidSignature(sig)) return false; } @@ -1193,7 +1193,7 @@ script.isPubkeyInput = function isPubkeyInput(s, key, tx, i) { if (s.length !== 1 || !Array.isArray(s[0])) return false; - if (!script.isSig(s[0])) + if (!script.isSignature(s[0])) return false; // Execute the script against our key's @@ -1211,7 +1211,7 @@ script.isPubkeyhashInput = function isPubkeyhashInput(s, key) { if (s.length !== 2 || !Array.isArray(s[0]) || !Array.isArray(s[1])) return false; - if (!script.isSig(s[0])) + if (!script.isSignature(s[0])) return false; if (!script.isKey(s[1])) @@ -1240,7 +1240,7 @@ script.isMultisigInput = function isMultisigInput(s, keys, tx, i) { return false; for (i = 1; i < s.length; i++) { - if (!script.isSig(s[i])) + if (!script.isSignature(s[i])) return false; } @@ -1411,7 +1411,7 @@ script.isKey = function isKey(key) { return key.length >= 33 && key.length <= 65; }; -script.isSig = function isSig(sig, allowZero) { +script.isSignature = function isSignature(sig, allowZero) { if (!utils.isBuffer(sig)) return false; @@ -1432,7 +1432,7 @@ script.isSig = function isSig(sig, allowZero) { * * This function is consensus-critical since BIP66. */ -script.isValidSig = function isValidSig(sig, allowZero) { +script.isValidSignature = function isValidSignature(sig, allowZero) { var lenR, lenS; if (!utils.isBuffer(sig)) diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index f60d7ed7..e813bfff 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -158,7 +158,7 @@ TX.prototype._inputIndex = function _inputIndex(hash, index) { // Build the scriptSigs for inputs, excluding the signatures TX.prototype.scriptInput = function scriptInput(index, pub, redeem) { - var input, s, standard, n, i; + var input, s, n, i; if (typeof index !== 'number') index = this.inputs.indexOf(index); @@ -167,6 +167,9 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) { input = this.inputs[index]; assert(input); + // We should have previous outputs by now. + assert(input.out.tx); + // Get the previous output's subscript s = input.out.tx.getSubscript(input.out.index); @@ -194,10 +197,13 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) { // but we create n signature slots so we can order // the signatures properly. input.script = [ [] ]; + + // Grab `n` value (number of keys). n = s[s.length - 2]; - // If using pushdata instead of OP_1-16: if (Array.isArray(n)) n = n[0] || 0; + + // Fill script with `n` signature slots. for (i = 0; i < n; i++) input.script[i + 1] = []; } @@ -212,7 +218,7 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) { // Sign the now-built scriptSigs TX.prototype.signInput = function signInput(index, key, type) { var input, s, hash, signature; - var len, redeem, m, keys, pub, pubn, ki, signatures, i; + var len, redeem, m, n, keys, pub, pubn, ki, signatures, i; if (typeof index !== 'number') index = this.inputs.indexOf(index); @@ -227,6 +233,9 @@ TX.prototype.signInput = function signInput(index, key, type) { input = this.inputs[index]; assert(input); + // We should have previous outputs by now. + assert(input.out.tx); + // Get the previous output's subscript s = input.out.tx.getSubscript(input.out.index); @@ -256,19 +265,56 @@ TX.prototype.signInput = function signInput(index, key, type) { len--; } + // Add signatures. if (bcoin.script.isPubkey(s)) { // P2PK - input.script[0] = signature; + if (Array.isArray(input.script[0]) && !input.script[0].length) + input.script[0] = signature; + return true; } else if (bcoin.script.isPubkeyhash(s)) { // P2PKH - input.script[0] = signature; + if (Array.isArray(input.script[0]) && !input.script[0].length) + input.script[0] = signature; + return true; } else if (bcoin.script.isMultisig(s)) { // Multisig + + // Grab `m` value (number of sigs required). m = s[0]; - // If using pushdata instead of OP_1-16: if (Array.isArray(m)) m = m[0] || 0; + // Grab `n` value (number of keys). + n = s[s.length - 2]; + if (Array.isArray(m)) + n = n[0] || 0; + + // Something is very wrong here. Abort. + if (m < 1 || m > 15 || n < m || n > 15 || len - 1 > n) + return; + + // Count the number of current signatures. + signatures = 0; + for (i = 1; i < len; i++) { + if (Array.isArray(input.script[i]) && input.script[i].length) + signatures++; + } + + // Signatures are already finalized. + if (signatures === m && len - 1 === m) + return; + + // This can happen in a case where another + // implementation adds signatures willy-nilly + // or by `m`. Add some signature slots for + // us to use. + while (len - 1 < n) { + input.script.splice(len, 0, []); + len++; + } + + // Grab the redeem script's keys to figure + // out where our key should go. keys = s.slice(1, -2); pub = key.getPublic(true, 'array'); pubn = key.getPublic(false, 'array'); @@ -280,39 +326,57 @@ TX.prototype.signInput = function signInput(index, key, type) { break; } - // Public key is not in the prev_out script + // Our public key is not in the prev_out + // script. We tried to sign a transaction + // that is not redeemable by us. if (ki === keys.length) return; - // No signature slot available - if (ki + 1 > len - 1) - return; + // Offset key index by one to turn it into + // "sig index". Accounts for OP_0 byte at + // the start. + ki++; // Add our signature to the correct slot - // and count the total number of signatures. - signatures = 0; - for (i = 1; i < len; i++) { - if (Array.isArray(input.script[i]) && input.script[i].length) { - signatures++; - continue; - } - - if (i - 1 === ki) { - if (signatures >= m) - continue; - input.script[i] = signature; + // and increment the total number of + // signatures. + if (ki < len && signatures < m) { + if (Array.isArray(input.script[ki]) && !input.script[ki].length) { + input.script[ki] = signature; signatures++; } } - // All signatures added. Finalize by removing empty slots. + // All signatures added. Finalize. if (signatures >= m) { + // Remove empty slots left over. for (i = len - 1; i >= 1; i--) { - if (Array.isArray(input.script[i]) && !input.script[i].length) + if (Array.isArray(input.script[i]) && !input.script[i].length) { input.script.splice(i, 1); + len--; + } } + + // Remove signatures which are not required. + // If just using bcoin for signing, this + // should never happen except with dealing + // with implementations that potentially + // handle signature slots differently. + while (signatures > m) { + input.script.splice(len - 1, 1); + signatures--; + len--; + } + + // Sanity checks. + assert.equal(signatures, m); + assert.equal(len - 1, m); } + + return signatures === m; } + + return false; }; TX.prototype.scriptSig = function scriptSig(index, key, pub, redeem, type) { @@ -857,6 +921,30 @@ TX.prototype.funds = function funds(side) { return acc; }; +TX.prototype.fill = function fill(txs) { + var inputs; + + if (txs instanceof bcoin.txPool) + txs = txs._all; + else if (txs instanceof bcoin.wallet) + txs = txs.tx._all; + + if (Array.isArray(txs)) { + txs = txs.reduce(function(out, tx) { + out[tx.hash('hex')] = tx; + return out; + }, {}); + } + + inputs = this.inputs.filter(function(input) { + if (!input.out.tx && txs[input.out.hash]) + input.out.tx = txs[input.out.hash]; + return !!input.out.tx; + }, this); + + return inputs.length === this.inputs.length; +}; + // Used for postVerify/ContextualBlockCheck and miner isFinalTx call. // BIP113 will require that time-locked transactions have nLockTime set to // less than the median time of the previous block they're contained in. diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index b5921b47..6e090c3d 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -242,7 +242,7 @@ Wallet.prototype.getPrivateKey = function getPrivateKey(enc) { return priv; }; -Wallet.prototype.getScript = function getScript(enc) { +Wallet.prototype.getScript = function getScript() { if (this.redeem) return this.redeem.slice(); return bcoin.script.encode(bcoin.script.redeem(this.keys, this.m, this.n)); @@ -448,6 +448,10 @@ Wallet.prototype.fillUnspent = function fillUnspent(tx, changeAddress) { return tx.fillUnspent(this.unspent(), changeAddress); }; +Wallet.prototype.fillTX = function fillTX(tx) { + return tx.fill(this); +}; + Wallet.prototype.scriptInputs = function scriptInputs(tx) { var pub = this.getPublicKey(); var redeem = this.getScript();