diff --git a/lib/bcoin/keyring.js b/lib/bcoin/keyring.js index 86417085..67877908 100644 --- a/lib/bcoin/keyring.js +++ b/lib/bcoin/keyring.js @@ -601,6 +601,17 @@ KeyRing.prototype.ownOutput = function ownOutput(tx, index) { return addressMap[hash] === true; }; +KeyRing.prototype.getRedeem = function(hash) { + if (this.program && utils.equal(hash, this.programHash)) + return this.program; + + if (this.script && utils.equal(hash, this.scriptHash160)) + return this.script; + + if (this.script && utils.equal(hash, this.scriptHash256)) + return this.script; +}; + /** * Build input scripts templates for a transaction (does not * sign, only creates signature slots). Only builds scripts diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 621ad484..c4f188de 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -218,7 +218,7 @@ MTX.prototype.addOutput = function addOutput(options, value) { */ MTX.prototype.scriptInput = function scriptInput(index, ring) { - var input, prev, n, i, vector, redeemScript, witnessScript; + var input, prev, redeem; // Get the input input = this.inputs[index]; @@ -248,57 +248,82 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) { // with segwit: figuring out where the redeem script and witness // redeem scripts go. if (prev.isScripthash()) { - if (ring.program && utils.equal(prev.get(1), ring.programHash)) { - // Witness program nested in regular P2SH. - redeemScript = ring.program.toRaw(); - vector = input.witness; - if (ring.program.isWitnessScripthash()) { - // P2WSH nested within pay-to-scripthash - // (it had to be this complicated, didn't it?) - witnessScript = ring.script.toRaw(); - prev = ring.script; - } else if (ring.program.isWitnessPubkeyhash()) { - // P2WPKH nested within pay-to-scripthash. - prev = Script.fromPubkeyhash(ring.keyHash); - } else { - assert(false, 'Unknown program.'); + redeem = ring.getRedeem(prev.get(1)); + + if (!redeem) + return false; + + // Witness program nested in regular P2SH. + if (redeem.isProgram()) { + // P2WSH nested within pay-to-scripthash. + if (redeem.isWitnessScripthash()) { + prev = ring.getRedeem(redeem.get(1)); + if (!prev) + return false; + this.scriptVector(prev, input.witness, ring); + input.witness.push(prev.toRaw()); + input.script.push(redeem.toRaw()); + input.script.compile(); + return true; } - } else if (ring.script && utils.equal(prev.get(1), ring.scriptHash160)) { - // Regular P2SH. - redeemScript = ring.script.toRaw(); - vector = input.script; - prev = ring.script; - } else { + + // P2WPKH nested within pay-to-scripthash. + if (redeem.isWitnessPubkeyhash()) { + prev = Script.fromPubkeyhash(ring.keyHash); + this.scriptVector(prev, input.witness, ring); + input.script.push(redeem.toRaw()); + input.script.compile(); + return true; + } + + // Unknown witness program. return false; } - } else if (prev.isProgram()) { - // Witness program. - vector = input.witness; - if (prev.isWitnessScripthash()) { - // Bare P2WSH. - if (!ring.script || !utils.equal(prev.get(1), ring.scriptHash256)) - return false; - - witnessScript = ring.script.toRaw(); - prev = ring.script; - } else if (prev.isWitnessPubkeyhash()) { - // Bare P2WPKH. - if (!utils.equal(prev.get(1), ring.keyHash)) - return false; - - prev = Script.fromPubkeyhash(prev.get(1)); - } else { - // Bare... who knows? - return false; - } - } else { - // Wow, a normal output! Praise be to Jengus and Gord. - vector = input.script; + // Regular P2SH. + this.scriptVector(redeem, input.script, ring); + input.script.push(redeem.toRaw()); + input.script.compile(); + return true; } + // Witness program. + if (prev.isProgram()) { + // Bare P2WSH. + if (prev.isWitnessScripthash()) { + redeem = ring.getRedeem(prev.get(1)); + + if (!redeem) + return false; + + this.scriptVector(redeem, input.witness, ring); + input.witness.push(redeem.toRaw()); + input.script.compile(); + return true; + } + + // Bare P2WPKH. + if (prev.isWitnessPubkeyhash()) { + prev = Script.fromPubkeyhash(prev.get(1)); + this.scriptVector(prev, input.witness, ring); + input.script.compile(); + return true; + } + + // Bare... who knows? + return false; + } + + // Wow, a normal output! Praise be to Jengus and Gord. + this.scriptVector(prev, input.script, ring); + return true; +}; + +MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) { + var i, n; + + // P2PK if (prev.isPubkey()) { - // P2PK if (!utils.equal(prev.get(1), ring.publicKey)) return false; @@ -307,8 +332,12 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) { return true; vector.set(0, opcodes.OP_0); - } else if (prev.isPubkeyhash()) { - // P2PKH + + return; + } + + // P2PKH + if (prev.isPubkeyhash()) { if (!utils.equal(prev.get(2), ring.keyHash)) return false; @@ -318,8 +347,12 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) { vector.set(0, opcodes.OP_0); vector.set(1, ring.publicKey); - } else if (prev.isMultisig()) { - // Multisig + + return; + } + + // Multisig + if (prev.isMultisig()) { if (prev.indexOf(ring.publicKey) === -1) return false; @@ -338,41 +371,28 @@ MTX.prototype.scriptInput = function scriptInput(index, ring) { // Fill script with `n` signature slots. for (i = 0; i < n; i++) vector.set(i + 1, opcodes.OP_0); - } else { - if (prev.indexOf(ring.publicKey) === -1) - return false; - // Already has a script template (at least) - if (vector.length !== 0) - return true; - - // Likely a non-standard scripthash multisig - // input. Determine n value by counting keys. - // Also, only allow nonstandard types for - // scripthash. - vector.set(0, opcodes.OP_0); - - // Fill script with `n` signature slots. - for (i = 0; i < prev.length; i++) { - if (Script.isKey(prev.get(i))) - vector.set(i + 1, opcodes.OP_0); - } + return; } - // P2SH requires the redeem - // script after signatures. - if (redeemScript) - input.script.push(redeemScript); + if (prev.indexOf(ring.publicKey) === -1) + return false; - // P2WSH requires the witness - // script after signatures. - if (witnessScript) - input.witness.push(witnessScript); + // Already has a script template (at least) + if (vector.length !== 0) + return true; - input.script.compile(); - input.witness.compile(); + // Likely a non-standard scripthash multisig + // input. Determine n value by counting keys. + // Also, only allow nonstandard types for + // scripthash. + vector.set(0, opcodes.OP_0); - return true; + // Fill script with `n` signature slots. + for (i = 0; i < prev.length; i++) { + if (Script.isKey(prev.get(i))) + vector.set(i + 1, opcodes.OP_0); + } }; /**