From c0bbd76ad0de27458dffc2f2f797c7a7092b92aa Mon Sep 17 00:00:00 2001 From: Thomas Kerin Date: Sat, 12 Nov 2016 15:26:53 +0100 Subject: [PATCH] example for solveOutput, also checks every sigHash test vector --- src/transaction.js | 2 +- test/transaction.js | 74 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index b19fe8a..2e9f028 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -188,7 +188,7 @@ Transaction.prototype.addOutput = function (scriptPubKey, value) { Transaction.prototype._hasWitnesses = function () { return this.ins.some(function (x) { - return x.witness !== EMPTY_WITNESS + return x.witness.length > 0 }) } diff --git a/test/transaction.js b/test/transaction.js index 30b3d74..6a5cab1 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -2,7 +2,8 @@ var assert = require('assert') var bscript = require('../src/script') - +var bcrypto = require('../src/crypto') +var bufferReverse = require('buffer-reverse') var Transaction = require('../src/transaction') var fixtures = require('./fixtures/transaction') @@ -223,4 +224,75 @@ describe('Transaction', function () { }) }) }) + + describe('signature hashing', function () { + function unsignedTransactionFromRaw (raw) { + var unsigned = new Transaction() + unsigned.version = raw.version + unsigned.ins = raw.ins.map(function (input) { + return { + hash: bufferReverse(new Buffer(input.hash, 'hex')), + index: input.index, + script: new Buffer(0), // Empty for now + witness: [], // Empty for now + sequence: 4294967295 + } + }) + unsigned.outs = raw.outs.map(function (output) { + return { + value: output.value, + script: new Buffer(output.scriptHex, 'hex') + } + }) + unsigned.locktime = 0 + return unsigned + } + + function checkInputsMatchSigHash (f) { + var unsigned = unsignedTransactionFromRaw(f.raw) + + f.raw.ins.forEach(function (input) { + it('determines the correct sighash for all inputs', function () { + var prevOutScript = new Buffer(input.scriptPubKey, 'hex') + var valueOut = input.value ? input.value : 0 + var redeemScript = input.redeemScript ? new Buffer(input.redeemScript, 'hex') : undefined + var witnessScript = input.witnessScript ? new Buffer(input.witnessScript, 'hex') : undefined + var expectedSigHash = new Buffer(input.sigHash, 'hex') + var tx = unsigned + + // This is example usage of solveOutput + + var sigVersion = 0 + var solution = bscript.solveOutput(prevOutScript) + if (solution.type === bscript.types.P2SH) { + // This line is useless given the example, but illustrates usage + if (solution.solvedBy.equals(bcrypto.hash160(redeemScript))) { + solution = bscript.solveOutput(redeemScript) + } + } + + // We again use solvedBy (result of the script.*.output.decode() function) + // But notice, here we don't know if it's witness due to (i) scriptPubKey or (ii) redeemScript + // so it's nice solveOutput also returns solvedBy + if (solution.type === bscript.types.P2WPKH) { + sigVersion = 1 + solution = bscript.solveOutput(bscript.pubKeyHash.output.encode(solution.solvedBy)) + } else if (solution.type === bscript.types.P2WSH) { + sigVersion = 1 + solution = bscript.solveOutput(witnessScript) + } + + assert([bscript.types.P2PKH, bscript.types.P2PK, bscript.types.MULTISIG].indexOf(solution.type) !== -1, 'should have found a signable type') + + var hash = sigVersion === 1 + ? tx.hashForWitnessV0(0, solution.script, valueOut, Transaction.SIGHASH_ALL) + : tx.hashForSignature(0, solution.script, Transaction.SIGHASH_ALL) + + assert.equal(hash.toString('hex'), expectedSigHash.toString('hex')) + }) + }) + } + + fixtures.witness.forEach(checkInputsMatchSigHash) + }) })