diff --git a/lib/bcoin/chain.js b/lib/bcoin/chain.js index aabf97e7..3988492f 100644 --- a/lib/bcoin/chain.js +++ b/lib/bcoin/chain.js @@ -699,8 +699,10 @@ Chain.prototype._checkInputs = function _checkInputs(block, prev, flags, callbac utils.debug('Block has invalid inputs: %s (%s/%d)', block.rhash, tx.rhash, j); utils.debug(input); - utils.debug('Signature Hash: %s', - utils.toHex(tx.signatureHash(j, input.output.script, 'all'))); + utils.debug('Signature Hash v0: %s', + utils.toHex(tx.signatureHash(j, input.output.script, 'all', 0))); + utils.debug('Signature Hash v1: %s', + utils.toHex(tx.signatureHash(j, input.output.script, 'all', 1))); utils.debug('Raw Script: %s', utils.toHex(input.output.script._raw || [])); utils.debug('Reserialized Script: %s', diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 61c097ef..63321311 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -305,7 +305,7 @@ MTX.prototype.scriptInput = function scriptInput(index, addr) { return true; }; -MTX.prototype.createSignature = function createSignature(index, prev, key, type) { +MTX.prototype.createSignature = function createSignature(index, prev, key, type, version) { var prev, hash, signature; if (typeof index !== 'number') @@ -319,7 +319,7 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type) // Get the hash of the current tx, minus the other // inputs, plus the sighash type. - hash = this.signatureHash(index, prev, type); + hash = this.signatureHash(index, prev, type, version); // Sign the transaction with our one input signature = bcoin.script.sign(hash, key, type); @@ -332,7 +332,7 @@ MTX.prototype.createSignature = function createSignature(index, prev, key, type) MTX.prototype.signInput = function signInput(index, addr, type) { var input, prev, signature, ki, signatures, i; - var len, m, n, keys, vector, dummy; + var len, m, n, keys, vector, dummy, version; if (typeof index !== 'number') index = this.inputs.indexOf(index); @@ -350,6 +350,7 @@ MTX.prototype.signInput = function signInput(index, addr, type) { vector = input.script; len = vector.length; dummy = 0; + version = 0; // We need to grab the redeem script when // signing p2sh transactions. @@ -369,15 +370,17 @@ MTX.prototype.signInput = function signInput(index, addr, type) { vector = input.witness; len = vector.length - 1; dummy = new Buffer([]); + version = 1; } else if (bcoin.script.isWitnessPubkeyhash(prev)) { prev = bcoin.script.createPubkeyhash(prev[1]); vector = input.witness; len = vector.length; dummy = new Buffer([]); + version = 1; } // Create our signature. - signature = this.createSignature(index, prev, addr.key, type); + signature = this.createSignature(index, prev, addr.key, type, version); // Add signatures. if (bcoin.script.isPubkey(prev)) { diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index ce36b23e..91edec77 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -303,14 +303,14 @@ script.verify = function verify(input, witness, output, tx, i, flags) { } // Execute the input script - script.execute(input, stack, tx, i, flags); + script.execute(input, stack, tx, i, flags, 0); // Copy the stack for P2SH if (flags & constants.flags.VERIFY_P2SH) copy = stack.slice(); // Execute the previous output script - res = script.execute(output, stack, tx, i, flags); + res = script.execute(output, stack, tx, i, flags, 0); // Verify the script did not fail as well as the stack values if (!res || stack.length === 0 || !script.bool(stack.pop())) @@ -354,7 +354,7 @@ script.verify = function verify(input, witness, output, tx, i, flags) { redeem = script.decode(raw); // Execute the redeem script - res = script.execute(redeem, stack, tx, i, flags); + res = script.execute(redeem, stack, tx, i, flags, 0); // Verify the script did not fail as well as the stack values if (!res || stack.length === 0 || !script.bool(stack.pop())) @@ -465,7 +465,7 @@ script.verifyProgram = function verifyProgram(witness, output, tx, i, flags) { utils.debug(bcoin.script.format(stack)); utils.debug(bcoin.script.format(redeem)); - res = script.execute(redeem, stack, tx, i, flags); + res = script.execute(redeem, stack, tx, i, flags, 1); utils.debug(bcoin.script.format(stack)); @@ -581,7 +581,7 @@ script._next = function _next(to, s, pc) { return -1; }; -script.execute = function execute(data, stack, tx, index, flags, recurse) { +script.execute = function execute(data, stack, tx, index, flags, version, recurse) { var s = data.slice(); if (flags == null) @@ -1053,7 +1053,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) { subscript = script.getSubscript(data, lastSep); script.removeData(subscript, sig); - hash = tx.signatureHash(index, subscript, type); + hash = tx.signatureHash(index, subscript, type, version); res = script.checksig(hash, sig, key, flags); if (o === 'checksigverify') { @@ -1112,7 +1112,7 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) { type = sig[sig.length - 1]; - hash = tx.signatureHash(index, subscript, type); + hash = tx.signatureHash(index, subscript, type, version); res = false; for (; !res && j < n; j++) @@ -1215,7 +1215,9 @@ script.execute = function execute(data, stack, tx, index, flags, recurse) { if (res) return false; - res = script.execute(evalScript, stack, tx, index, flags, recurse); + res = script.execute( + evalScript, stack, tx, index, flags, version, recurse); + if (!res) return false; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 00bcf874..2c6d8869 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -212,7 +212,15 @@ TX.prototype.getSubscript = function getSubscript(index) { return bcoin.script.getSubscript(script); }; -TX.prototype.signatureHash = function signatureHash(index, s, type) { +TX.prototype.signatureHash = function signatureHash(index, s, type, version) { + assert(version >= 0 && version <= 1); + if (version === 0) + return this.signatureHashV0(index, s, type); + if (version === 1) + return this.signatureHashV1(index, s, type); +}; + +TX.prototype.signatureHashV0 = function signatureHashV0(index, s, type) { var copy = this.clone(); var i, msg, hash; @@ -288,6 +296,98 @@ TX.prototype.signatureHash = function signatureHash(index, s, type) { return hash; }; +TX.prototype.signatureHashV1 = function signatureHashV1(index, s, type) { + var i, msg, hash, hashPrevouts, hashSequence, hashOutputs; + var size, outputs, output, off, prev; + + if (typeof index !== 'number') + index = this.inputs.indexOf(index); + + if (typeof type === 'string') + type = constants.hashType[type]; + + assert(index >= 0 && index < this.inputs.length) + assert(Array.isArray(s)); + + if (!(type & constants.hashType.anyonecanpay)) { + hashPrevouts = new Buffer(36 * this.inputs.length); + off = 0; + for (i = 0; i < this.inputs.length; i++) { + prev = this.inputs[i].prevout; + off += utils.copy(new Buffer(prev.hash, 'hex'), hashPrevouts, off); + off += utils.writeU32(hashPrevouts, prev.index, off); + } + hashPrevouts = utils.dsha256(hashPrevouts); + } else { + hashPrevouts = new Buffer(32); + hashPrevouts.fill(0); + } + + if (!(type & constants.hashType.anyonecanpay) + && (type & 0x1f) !== constants.hashType.single + && (type & 0x1f) !== constants.hashType.none) { + hashSequence = new Buffer(4 * this.inputs.length); + off = 0; + for (i = 0; i < this.inputs.length; i++) + off += utils.writeU32(hashSequence, this.inputs[i].sequence, off); + hashSequence = utils.dsha256(hashSequence); + } else { + hashSequence = new Buffer(32); + hashSequence.fill(0); + } + + if ((type & 0x1f) !== constants.hashType.single + && (type & 0x1f) !== constants.hashType.none) { + size = 0; + outputs = []; + for (i = 0; i < this.outputs.length; i++) { + output = bcoin.protocol.framer.output(this.outputs[i]); + size += output.length; + outputs.push(output); + } + hashOutputs = new Buffer(size); + off = 0; + for (i = 0; i < outputs.length; i++) + off += utils.copy(outputs[i], hashOutputs, off); + hashOutputs = utils.dsha256(hashOutputs); + } else if ((type & 0x1f) === constants.hashType.single && index < this.outputs.length) { + hashOutputs = bcoin.protocol.framer.output(this.outputs[index]); + hashOutputs = utils.dsha256(hashOutputs); + } else { + hashOutputs = new Buffer(32); + hashOutputs.fill(0); + } + + s = bcoin.script.encode(s); + + msg = new Buffer( + 4 + 32 + 32 + 36 + + utils.sizeIntv(s.length) + + s.length + + 8 + 4 + 32 + 4 + 4); + + off = 0; + off += utils.write32(msg, this.version, off); + off += utils.copy(hashPrevouts, msg, off); + off += utils.copy(hashSequence, msg, off); + off += utils.copy(new Buffer(this.inputs[index].prevout.hash, 'hex'), msg, off); + off += utils.writeU32(msg, this.inputs[index].prevout.index, off); + assert(off === 4 + 32 + 32 + 36); + off += utils.writeIntv(msg, s.length, off); + off += utils.copy(s, msg, off); + off += utils.write64(msg, this.inputs[index].output.value, off); + off += utils.writeU32(msg, this.inputs[index].sequence, off); + off += utils.copy(hashOutputs, msg, off); + off += utils.writeU32(msg, this.locktime, off); + assert(off === msg.length - 4); + off += utils.writeU32(msg, type, off); + assert(off === msg.length); + + hash = utils.dsha256(msg); + + return hash; +}; + TX.prototype.normalizedHash = function normalizedHash(enc, force) { var copy = this.clone(); var i;