diff --git a/src/transaction.js b/src/transaction.js index 8302713..da73337 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -6,6 +6,20 @@ var opcodes = require('./opcodes.json') var typeforce = require('typeforce') var types = require('./types') +function varSliceSize (someScript) { + var length = someScript.length + + return bufferutils.varIntSize(length) + length +} + +function vectorSize (someVector) { + var length = someVector.length + + return bufferutils.varIntSize(length) + someVector.reduce(function (sum, witness) { + return sum + varSliceSize(witness) + }, 0) +} + function Transaction () { this.version = 1 this.locktime = 0 @@ -18,6 +32,18 @@ Transaction.SIGHASH_ALL = 0x01 Transaction.SIGHASH_NONE = 0x02 Transaction.SIGHASH_SINGLE = 0x03 Transaction.SIGHASH_ANYONECANPAY = 0x80 +Transaction.ADVANCED_TRANSACTION_MARKER = 0x00 +Transaction.ADVANCED_TRANSACTION_FLAG = 0x01 + +var EMPTY_SCRIPT = new Buffer(0) +var EMPTY_WITNESS = [] +var ZERO = new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex') +var ONE = new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex') +var VALUE_UINT64_MAX = new Buffer('ffffffffffffffff', 'hex') +var BLANK_OUTPUT = { + script: EMPTY_SCRIPT, + valueBuffer: VALUE_UINT64_MAX +} Transaction.fromBuffer = function (buffer, __noStrict) { var offset = 0 @@ -50,20 +76,38 @@ Transaction.fromBuffer = function (buffer, __noStrict) { return vi.number } - function readScript () { + function readVarSlice () { return readSlice(readVarInt()) } + function readVector () { + var count = readVarInt() + var vector = [] + for (var i = 0; i < count; i++) vector.push(readVarSlice()) + return vector + } + var tx = new Transaction() tx.version = readInt32() + var marker = buffer.readUInt8(offset) + var flag = buffer.readUInt8(offset + 1) + + var hasWitnesses = false + if (marker === Transaction.ADVANCED_TRANSACTION_MARKER && + flag === Transaction.ADVANCED_TRANSACTION_FLAG) { + offset += 2 + hasWitnesses = true + } + var vinLen = readVarInt() for (var i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), - script: readScript(), - sequence: readUInt32() + script: readVarSlice(), + sequence: readUInt32(), + witness: EMPTY_WITNESS }) } @@ -71,10 +115,16 @@ Transaction.fromBuffer = function (buffer, __noStrict) { for (i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), - script: readScript() + script: readVarSlice() }) } + if (hasWitnesses) { + for (i = 0; i < vinLen; ++i) { + tx.ins[i].witness = readVector() + } + } + tx.locktime = readUInt32() if (__noStrict) return tx @@ -99,8 +149,6 @@ Transaction.prototype.isCoinbase = function () { return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) } -var EMPTY_SCRIPT = new Buffer(0) - Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { typeforce(types.tuple( types.Hash256bit, @@ -118,7 +166,8 @@ Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { hash: hash, index: index, script: scriptSig || EMPTY_SCRIPT, - sequence: sequence + sequence: sequence, + witness: EMPTY_WITNESS }) - 1) } @@ -132,19 +181,22 @@ Transaction.prototype.addOutput = function (scriptPubKey, value) { }) - 1) } -Transaction.prototype.byteLength = function () { - function scriptSize (someScript) { - var length = someScript.length +Transaction.prototype._hasWitnesses = function () { + return this.ins.some(function (x) { + return x.witness !== EMPTY_WITNESS + }) +} - return bufferutils.varIntSize(length) + length - } +Transaction.prototype.byteLength = function () { + var hasWitnesses = this._hasWitnesses() return ( - 8 + + (hasWitnesses ? 10 : 8) + bufferutils.varIntSize(this.ins.length) + bufferutils.varIntSize(this.outs.length) + - this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) + - this.outs.reduce(function (sum, output) { return sum + 8 + scriptSize(output.script) }, 0) + this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) + + this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) + + (hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0) ) } @@ -158,7 +210,8 @@ Transaction.prototype.clone = function () { hash: txIn.hash, index: txIn.index, script: txIn.script, - sequence: txIn.sequence + sequence: txIn.sequence, + witness: txIn.witness } }) @@ -172,13 +225,6 @@ Transaction.prototype.clone = function () { return newTx } -var ONE = new Buffer('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -var VALUE_UINT64_MAX = new Buffer('ffffffffffffffff', 'hex') -var BLANK_OUTPUT = { - script: EMPTY_SCRIPT, - valueBuffer: VALUE_UINT64_MAX -} - /** * Hash transaction for signing a specific input. * @@ -252,6 +298,92 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT return bcrypto.hash256(buffer) } +Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, amount, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) + + var hashOutputs, hashPrevouts, hashSequence + + var tbuffer, toffset + function writeSlice (slice) { toffset += slice.copy(tbuffer, toffset) } + function writeUInt32 (i) { toffset = tbuffer.writeUInt32LE(i, toffset) } + function writeUInt64 (i) { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } + function writeVarInt (i) { toffset += bufferutils.writeVarInt(tbuffer, i, toffset) } + function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { + tbuffer = new Buffer(36 * this.ins.length) + toffset = 0 + + this.ins.forEach(function (txIn) { + writeSlice(txIn.hash) + writeUInt32(txIn.index) + }) + + hashPrevouts = bcrypto.hash256(tbuffer) + } + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + tbuffer = new Buffer(4 * this.ins.length) + toffset = 0 + + this.ins.forEach(function (txIn) { + writeUInt32(txIn.sequence) + }) + + hashSequence = bcrypto.hash256(tbuffer) + } + + if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + var txOutsSize = this.outs.reduce(function (sum, output) { + return sum + 8 + varSliceSize(output.script) + }, 0) + + tbuffer = new Buffer(txOutsSize) + toffset = 0 + + this.outs.forEach(function (out) { + writeUInt64(out.value) + writeVarSlice(out.script) + }) + + hashOutputs = bcrypto.hash256(tbuffer) + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { + var output = this.outs[inIndex] + + tbuffer = new Buffer(8 + varSliceSize(output.script)) + toffset = 0 + writeUInt64(output.value) + writeVarSlice(output.script) + + hashOutputs = bcrypto.hash256(tbuffer) + } + + hashPrevouts = hashPrevouts || ZERO + hashSequence = hashSequence || ZERO + hashOutputs = hashOutputs || ZERO + + tbuffer = new Buffer(156 + varSliceSize(prevOutScript)) + toffset = 0 + + var input = this.ins[inIndex] + writeUInt32(this.version) + writeSlice(hashPrevouts) + writeSlice(hashSequence) + writeSlice(input.hash) + writeUInt32(input.index) + writeVarSlice(prevOutScript) + writeUInt64(amount) + writeUInt32(input.sequence) + writeSlice(hashOutputs) + writeUInt32(this.locktime) + writeUInt32(hashType) + + return bcrypto.hash256(tbuffer) +} + Transaction.prototype.getHash = function () { return bcrypto.hash256(this.toBuffer()) } @@ -266,19 +398,29 @@ Transaction.prototype.toBuffer = function (buffer, initialOffset) { var offset = initialOffset || 0 function writeSlice (slice) { offset += slice.copy(buffer, offset) } + function writeUInt8 (i) { offset = buffer.writeUInt8(i, offset) } function writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } function writeVarInt (i) { offset += bufferutils.writeVarInt(buffer, i, offset) } + function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } + function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) } writeInt32(this.version) + + var hasWitnesses = this._hasWitnesses() + + if (hasWitnesses) { + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) + } + writeVarInt(this.ins.length) this.ins.forEach(function (txIn) { writeSlice(txIn.hash) writeUInt32(txIn.index) - writeVarInt(txIn.script.length) - writeSlice(txIn.script) + writeVarSlice(txIn.script) writeUInt32(txIn.sequence) }) @@ -290,15 +432,19 @@ Transaction.prototype.toBuffer = function (buffer, initialOffset) { writeSlice(txOut.valueBuffer) } - writeVarInt(txOut.script.length) - writeSlice(txOut.script) + writeVarSlice(txOut.script) }) + if (hasWitnesses) { + this.ins.forEach(function (input) { + writeVector(input.witness) + }) + } + writeUInt32(this.locktime) // avoid slicing unless necessary if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) - return buffer } @@ -312,4 +458,10 @@ Transaction.prototype.setInputScript = function (index, scriptSig) { this.ins[index].script = scriptSig } +Transaction.prototype.setWitness = function (index, witness) { + typeforce(types.tuple(types.Number, types.Buffer), arguments) + + this.ins[index].witness = witness +} + module.exports = Transaction diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json index 9def479..3226340 100644 --- a/test/fixtures/transaction.json +++ b/test/fixtures/transaction.json @@ -289,5 +289,177 @@ "hex": "0100000002f1fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe000000006b483045022100e661badd8d2cf1af27eb3b82e61b5d3f5d5512084591796ae31487f5b82df948022006df3c2a2cac79f68e4b179f4bbb8185a0bb3c4a2486d4405c59b2ba07a74c2101210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798fffffffff2fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe0100000083483045022100be54a46a44fb7e6bf4ebf348061d0dace7ddcbb92d4147ce181cf4789c7061f0022068ccab2a89a47fc29bb5074bca99ae846ab446eecf3c3aaeb238a13838783c78012102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee517a9147ccb85f0ab2d599bc17246c98babd5a20b1cdc7687ffffffff0250c30000000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88acf04902000000000017a9147ccb85f0ab2d599bc17246c98babd5a20b1cdc768700000000ffffffff" } ] - } -} + }, + "hashForWitnessV0": [ + { + "description": "Native P2WPKH with SIGHASH_ALL", + "txHex": "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000", + "inIndex": 1, + "script": "OP_DUP OP_HASH160 1d0f172a0ecb48aee1be1f2687d2963ae33f71a1 OP_EQUALVERIFY OP_CHECKSIG", + "type": 1, + "hash": "c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670", + "value": 600000000 + }, + { + "description": "P2SH(P2WPKH) with SIGHASH_ALL", + "txHex": "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000", + "inIndex": 0, + "script": "OP_DUP OP_HASH160 79091972186c449eb1ded22b78e40d009bdf0089 OP_EQUALVERIFY OP_CHECKSIG", + "type": 1, + "hash": "64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6", + "value": 1000000000 + }, + { + "description": "P2SH(P2WSH) with SIGHASH_ALL", + "txHex": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "inIndex": 0, + "script": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", + "type": 1, + "hash": "185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c", + "value": 987654321 + }, + { + "description": "P2SH(P2WSH) with SIGHASH_NONE", + "txHex": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "inIndex": 0, + "script": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", + "type": 2, + "hash": "e9733bc60ea13c95c6527066bb975a2ff29a925e80aa14c213f686cbae5d2f36", + "value": 987654321 + }, + { + "description": "P2SH(P2WSH) with SIGHASH_SINGLE", + "txHex": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "inIndex": 0, + "script": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", + "type": 3, + "hash": "1e1f1c303dc025bd664acb72e583e933fae4cff9148bf78c157d1e8f78530aea", + "value": 987654321 + }, + { + "description": "P2SH(P2WSH) with SIGHASH_ALL | SIGHASH_ANYONECANPAY", + "txHex": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "inIndex": 0, + "script": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", + "type": 129, + "hash": "2a67f03e63a6a422125878b40b82da593be8d4efaafe88ee528af6e5a9955c6e", + "value": 987654321 + }, + { + "description": "P2SH(P2WSH) with SIGHASH_NONE | SIGHASH_ANYONECANPAY", + "txHex": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "inIndex": 0, + "script": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", + "type": 130, + "hash": "781ba15f3779d5542ce8ecb5c18716733a5ee42a6f51488ec96154934e2c890a", + "value": 987654321 + }, + { + "description": "P2SH(P2WSH) with SIGHASH_SINGLE | SIGHASH_ANYONECANPAY", + "txHex": "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000", + "inIndex": 0, + "script": "OP_6 0307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba3 03b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b 034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a 033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f4 03a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac16 02d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b OP_6 OP_CHECKMULTISIG", + "type": 131, + "hash": "511e8e52ed574121fc1b654970395502128263f62662e076dc6baf05c2e6a99b", + "value": 987654321 + } + ], + "witness": [ + { + "description": "P2SH(P2WSH) Multisig", + "id": "bd38915e2aa04b8efa307e0e29ab95582954738a7062cf88730a6276224bd917", + "hash": "17d94b2276620a7388cf62708a7354295895ab290e7e30fa8e4ba02a5e9138bd", + "hex": "0100000000010113ae35a2063ba413c3a1bb9b3820c76291e40e83bd3f23c8ff83333f0c64d623000000002322002086b2dcecbf2e0f0e4095ef11bc8834e2e148d245f844f0b8091389fef91b69ffffffffff0180969800000000001976a914b1ae3ceac136e4bdb733663e7a1e2f0961198a1788ac03004730440220121a629bb5fee3ecaf3e7a0b111101c51de816f427eaedd992b57f49b69b228e0220402ecd144a7321b4bad6ba3bfa5876b755b9c52a8c8ab17a33830d5929a76cbe0125512103b848ab6ac853cd69baaa750c70eb352ebeadb07da0ff5bbd642cb285895ee43f51ae00000000", + "raw": { + "version": "1", + "ins": [ + { + "hash": "23d6640c3f3383ffc8233fbd830ee49162c720389bbba1c313a43b06a235ae13", + "index": "0", + "script": "002086b2dcecbf2e0f0e4095ef11bc8834e2e148d245f844f0b8091389fef91b69ff" + } + ], + "outs": [ + { + "value": "10000000", + "script": "OP_DUP OP_HASH160 b1ae3ceac136e4bdb733663e7a1e2f0961198a17 OP_EQUALVERIFY OP_CHECKSIG" + } + ], + "locktime": "0" + }, + "coinbase": false + }, + { + "description": "P2SH(Multisig)", + "id": "128f7d5f2b213bf496ebae1990800475d7c448f980e29a82bd99e83e14638e85", + "hash": "858e63143ee899bd829ae280f948c4d77504809019aeeb96f43b212b5f7d8f12", + "hex": "0100000000010113ae35a2063ba413c3a1bb9b3820c76291e40e83bd3f23c8ff83333f0c64d623000000007000483045022100e332e8367d5fee22c205ce1bf4e01e39f1a8decb3ba20d1336770cf38b8ee72d022076b5f83b3ee15390133b7ebf526ec189eb73cc6ee0a726f70b939bc51fa18d800125512103b848ab6ac853cd69baaa750c70eb352ebeadb07da0ff5bbd642cb285895ee43f51aeffffffff0180969800000000001976a914b1ae3ceac136e4bdb733663e7a1e2f0961198a1788ac0000000000", + "raw": { + "version": "1", + "ins": [ + { + "hash": "23d6640c3f3383ffc8233fbd830ee49162c720389bbba1c313a43b06a235ae13", + "index": "0", + "script": " 3045022100e332e8367d5fee22c205ce1bf4e01e39f1a8decb3ba20d1336770cf38b8ee72d022076b5f83b3ee15390133b7ebf526ec189eb73cc6ee0a726f70b939bc51fa18d8001 512103b848ab6ac853cd69baaa750c70eb352ebeadb07da0ff5bbd642cb285895ee43f51ae" + } + ], + "outs": [ + { + "value": "10000000", + "script": "OP_DUP OP_HASH160 b1ae3ceac136e4bdb733663e7a1e2f0961198a17 OP_EQUALVERIFY OP_CHECKSIG" + } + ], + "locktime": "0" + }, + "coinbase": false + }, + { + "description": "Multisig", + "id": "3cd3dfe7312d34798b87351d9b72b3356e6ec109d841919cec510f3efa5d291f", + "hash": "1f295dfa3e0f51ec9c9141d809c16e6e35b3729b1d35878b79342d31e7dfd33c", + "hex": "0100000000010113ae35a2063ba413c3a1bb9b3820c76291e40e83bd3f23c8ff83333f0c64d623000000004a00483045022100e332e8367d5fee22c205ce1bf4e01e39f1a8decb3ba20d1336770cf38b8ee72d022076b5f83b3ee15390133b7ebf526ec189eb73cc6ee0a726f70b939bc51fa18d8001ffffffff0180969800000000001976a914b1ae3ceac136e4bdb733663e7a1e2f0961198a1788ac0000000000", + "raw": { + "version": "1", + "ins": [ + { + "hash": "23d6640c3f3383ffc8233fbd830ee49162c720389bbba1c313a43b06a235ae13", + "index": "0", + "script": " 3045022100e332e8367d5fee22c205ce1bf4e01e39f1a8decb3ba20d1336770cf38b8ee72d022076b5f83b3ee15390133b7ebf526ec189eb73cc6ee0a726f70b939bc51fa18d8001" + } + ], + "outs": [ + { + "value": "10000000", + "script": "OP_DUP OP_HASH160 b1ae3ceac136e4bdb733663e7a1e2f0961198a17 OP_EQUALVERIFY OP_CHECKSIG" + } + ], + "locktime": "0" + }, + "coinbase": false + }, + { + "description": "P2PK", + "id": "6cf3688270c95846d3ced68b7fcf90f30daddd63e951ed04791cbf1f8c731c77", + "hash": "771c738c1fbf1c7904ed51e963ddad0df390cf7f8bd6ced34658c9708268f36c", + "hex": "0100000000010113ae35a2063ba413c3a1bb9b3820c76291e40e83bd3f23c8ff83333f0c64d623000000006a47304402203f965bdf792ea80c1f96e4292d1edb52ca62f22c7511aefca967fb9f3067063402204acbfaa4e7f1d5631227d491426d89d954d5e2abfed6f0dbc300216f01916baa012103b848ab6ac853cd69baaa750c70eb352ebeadb07da0ff5bbd642cb285895ee43fffffffff0100000000000000001976a914b1ae3ceac136e4bdb733663e7a1e2f0961198a1788ac0000000000", + "raw": { + "version": "1", + "ins": [ + { + "hash": "23d6640c3f3383ffc8233fbd830ee49162c720389bbba1c313a43b06a235ae13", + "index": "0", + "script": "304402203f965bdf792ea80c1f96e4292d1edb52ca62f22c7511aefca967fb9f3067063402204acbfaa4e7f1d5631227d491426d89d954d5e2abfed6f0dbc300216f01916baa01 03b848ab6ac853cd69baaa750c70eb352ebeadb07da0ff5bbd642cb285895ee43f" + } + ], + "outs": [ + { + "value": "0", + "script": "OP_DUP OP_HASH160 b1ae3ceac136e4bdb733663e7a1e2f0961198a17 OP_EQUALVERIFY OP_CHECKSIG" + } + ], + "locktime": "0" + }, + "coinbase": false + } + ] +} \ No newline at end of file diff --git a/test/transaction.js b/test/transaction.js index 6299a93..b4423c1 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -50,6 +50,14 @@ describe('Transaction', function () { }) }) + fixtures.witness.forEach(function (f) { + it('imports ' + f.description + ' (' + f.id + ')', function () { + var actual = Transaction.fromHex(f.hex) + + assert.strictEqual(actual.toHex(), f.hex, actual.toHex()) + }) + }) + fixtures.invalid.fromBuffer.forEach(function (f) { it('throws on ' + f.exception, function () { assert.throws(function () { @@ -207,4 +215,15 @@ describe('Transaction', function () { }) }) }) + + describe('hashForWitnessV0', function () { + fixtures.hashForWitnessV0.forEach(function (f) { + it('should return ' + f.hash + ' for ' + (f.description ? ('case "' + f.description + '"') : ''), function () { + var tx = Transaction.fromHex(f.txHex) + var script = bscript.fromASM(f.script) + + assert.strictEqual(tx.hashForWitnessV0(f.inIndex, script, f.value, f.type).toString('hex'), f.hash) + }) + }) + }) })