From 122794292dd977541785e0cc281b1c980549b985 Mon Sep 17 00:00:00 2001 From: Jorge Valdeiglesias Date: Fri, 18 May 2018 14:34:32 -0700 Subject: [PATCH] Add ZCash support in bitcoinjs-lib Summary: Add ZCash support when creating transactions Merge branch 'add-zcash-support' of github.com:BitGo/bitcoinjs-lib into add-zcash-support Merge branch 'add-zcash-support' of github.com:BitGo/bitcoinjs-lib into add-zcash-support Add zcash to the network file Merge branch 'add-zcash-support' of github.com:BitGo/bitcoinjs-lib into add-zcash-support Reviewers: tyler, taylor, arik, alex Reviewed By: taylor, arik, alex Subscribers: taylor Differential Revision: https://phabricator.bitgo.com/D8644 --- .arcconfig | 3 + .arclint | 19 +++ .gitignore | 1 + package.json | 2 +- src/address.js | 23 ++- src/block.js | 2 +- src/coins.js | 28 ++++ src/ecpair.js | 2 +- src/index.js | 1 + src/networks.js | 22 +++ src/templates/nulldata.js | 11 +- src/transaction.js | 249 +++++++++++++++++++++++++++++++-- src/types.js | 8 +- test/address.js | 2 +- test/fixtures/address.json | 14 ++ test/fixtures/script.json | 4 + test/fixtures/transaction.json | 116 +++++++++++++++ test/transaction.js | 35 +++++ test/transaction_builder.js | 8 +- 19 files changed, 518 insertions(+), 32 deletions(-) create mode 100644 .arcconfig create mode 100644 .arclint create mode 100644 src/coins.js diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 00000000..388fb4bd --- /dev/null +++ b/.arcconfig @@ -0,0 +1,3 @@ +{ + "phabricator.uri" : "https://phabricator.bitgo.com" +} diff --git a/.arclint b/.arclint new file mode 100644 index 00000000..9e255f08 --- /dev/null +++ b/.arclint @@ -0,0 +1,19 @@ +{ + "linters": { + "filename": { + "type": "filename" + }, + "generated": { + "type": "generated" + }, + "merge-conflict": { + "type": "merge-conflict" + }, + "nolint": { + "type": "nolint" + }, + "spelling": { + "type": "spelling" + } + } +} diff --git a/.gitignore b/.gitignore index a6c0ab82..29e90886 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ coverage node_modules .nyc_output npm-debug.log +.idea diff --git a/package.json b/package.json index 1634b7a0..56ed4d10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bitgo-bitcoinjs-lib", - "version": "3.1.1", + "version": "3.2.0", "description": "Client-side Bitcoin JavaScript library", "main": "./src/index.js", "engines": { diff --git a/src/address.js b/src/address.js index 221f85a4..6dcc629d 100644 --- a/src/address.js +++ b/src/address.js @@ -12,10 +12,13 @@ function fromBase58Check (address) { // TODO: 4.0.0, move to "toOutputScript" if (payload.length < 21) throw new TypeError(address + ' is too short') - if (payload.length > 21) throw new TypeError(address + ' is too long') + if (payload.length > 22) throw new TypeError(address + ' is too long') - var version = payload.readUInt8(0) - var hash = payload.slice(1) + var multibyte = payload.length === 22 + var offset = multibyte ? 2 : 1 + + var version = multibyte ? payload.readUInt16BE(0) : payload[0] + var hash = payload.slice(offset) return { version: version, hash: hash } } @@ -32,11 +35,17 @@ function fromBech32 (address) { } function toBase58Check (hash, version) { - typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments) + typeforce(types.tuple(types.Hash160bit, types.UInt16), arguments) + + // Zcash adds an extra prefix resulting in a bigger (22 bytes) payload. We identify them Zcash by checking if the + // version is multibyte (2 bytes instead of 1) + var multibyte = version > 0xff + var size = multibyte ? 22 : 21 + var offset = multibyte ? 2 : 1 - var payload = Buffer.allocUnsafe(21) - payload.writeUInt8(version, 0) - hash.copy(payload, 1) + var payload = Buffer.allocUnsafe(size) + multibyte ? payload.writeUInt16BE(version, 0) : payload.writeUInt8(version, 0) + hash.copy(payload, offset) return bs58check.encode(payload) } diff --git a/src/block.js b/src/block.js index c545996b..5392c09b 100644 --- a/src/block.js +++ b/src/block.js @@ -54,7 +54,7 @@ Block.fromBuffer = function (buffer) { } function readTransaction () { - var tx = Transaction.fromBuffer(buffer.slice(offset), true) + var tx = Transaction.fromBuffer(buffer.slice(offset), null, true) offset += tx.byteLength() return tx } diff --git a/src/coins.js b/src/coins.js new file mode 100644 index 00000000..46d3b2cd --- /dev/null +++ b/src/coins.js @@ -0,0 +1,28 @@ +// Coins supported by bitgo-bitcoinjs-lib + +const typeforce = require('typeforce') + +const coins = { + BCH: 'bch', + BTC: 'btc', + BTG: 'btg', + ZEC: 'zec' +} + +coins.isBitcoin = function (value) { + return typeforce.String(value) && value === coins.BTC +} + +coins.isBitcoinCash = function (value) { + return typeforce.String(value) && value === coins.BCH +} + +coins.isBitcoinGold = function (value) { + return typeforce.String(value) && value === coins.BTG +} + +coins.isZcash = function (value) { + return typeforce.String(value) && value === coins.ZEC +} + +module.exports = coins diff --git a/src/ecpair.js b/src/ecpair.js index 960f2706..b5fbabc6 100644 --- a/src/ecpair.js +++ b/src/ecpair.js @@ -65,7 +65,7 @@ ECPair.fromWIF = function (string, network) { if (types.Array(network)) { network = network.filter(function (x) { return version === x.wif - }).pop() + }).pop() // We should not use pop since it depends on the order of the networks for the same wif if (!network) throw new Error('Unknown network version') diff --git a/src/index.js b/src/index.js index 1ad7099c..33055f21 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,7 @@ module.exports = { TransactionBuilder: require('./transaction_builder'), address: require('./address'), + coins: require('./coins'), crypto: require('./crypto'), networks: require('./networks'), opcodes: require('bitcoin-ops'), diff --git a/src/networks.js b/src/networks.js index af744803..d0ee0151 100644 --- a/src/networks.js +++ b/src/networks.js @@ -2,6 +2,28 @@ // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 module.exports = { + zcash: { + messagePrefix: '\x18ZCash Signed Message:\n', + bech32: 'bc', + bip32: { + public: 0x0488b21e, + private: 0x0488ade4 + }, + pubKeyHash: 0x1cb8, + scriptHash: 0x1cbd, + wif: 0x80 + }, + zcashTest: { + messagePrefix: '\x18ZCash Signed Message:\n', + bech32: 'tb', + bip32: { + public: 0x043587cf, + private: 0x04358394 + }, + pubKeyHash: 0x1d25, + scriptHash: 0x1cba, + wif: 0xef + }, bitcoingold: { messagePrefix: '\x18Bitcoin Gold Signed Message:\n', bip32: { diff --git a/src/templates/nulldata.js b/src/templates/nulldata.js index 107aaebb..b962a35b 100644 --- a/src/templates/nulldata.js +++ b/src/templates/nulldata.js @@ -14,15 +14,20 @@ function check (script) { check.toJSON = function () { return 'null data output' } function encode (data) { - typeforce(types.Buffer, data) + // Allow arrays types since decompile returns an array too + typeforce(typeforce.oneOf(types.Buffer, types.Array), data) - return bscript.compile([OPS.OP_RETURN, data]) + return bscript.compile([OPS.OP_RETURN].concat(data)) } function decode (buffer) { typeforce(check, buffer) - return buffer.slice(2) + var chunks = bscript.decompile(buffer) + + chunks.shift() + + return chunks.length === 1 ? chunks[0] : chunks } module.exports = { diff --git a/src/transaction.js b/src/transaction.js index 74c4277b..4726bc11 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -2,6 +2,7 @@ var Buffer = require('safe-buffer').Buffer var bcrypto = require('./crypto') var bscript = require('./script') var bufferutils = require('./bufferutils') +var coins = require('./coins') var opcodes = require('bitcoin-ops') var typeforce = require('typeforce') var types = require('./types') @@ -26,6 +27,15 @@ function Transaction () { this.locktime = 0 this.ins = [] this.outs = [] + this.coin = coins.BTC // By default, assume is a bitcoin transaction + // ZCash version >= 2 + this.joinsplits = [] + this.joinsplitPubkey = [] + this.joinsplitSig = [] + // ZCash version >= 3 + this.overwintered = 0 + this.versionGroupId = 0 // 0x03C48270 for overwinter + this.expiryHeight = 0 } Transaction.DEFAULT_SEQUENCE = 0xffffffff @@ -49,13 +59,28 @@ var BLANK_OUTPUT = { valueBuffer: VALUE_UINT64_MAX } -Transaction.fromBuffer = function (buffer, __noStrict) { +Transaction.ZCASH_OVERWINTER_VERSION = 3 +Transaction.ZCASH_JOINSPLITS_SUPPORT_VERSION = 2 +Transaction.ZCASH_NUM_JOINSPLITS_INPUTS = 2 +Transaction.ZCASH_NUM_JOINSPLITS_OUTPUTS = 2 +Transaction.ZCASH_NOTECIPHERTEXT_SIZE = 1 + 8 + 32 + 32 + 512 + 16 + +Transaction.ZCASH_G1_PREFIX_MASK = 0x02 +Transaction.ZCASH_G2_PREFIX_MASK = 0x0a + +Transaction.fromBuffer = function (buffer, coin, __noStrict) { var offset = 0 function readSlice (n) { offset += n return buffer.slice(offset - n, offset) } + function readUInt8 () { + var i = buffer.readUInt8(offset) + offset += 1 + return i + } + function readUInt32 () { var i = buffer.readUInt32LE(offset) offset += 4 @@ -91,19 +116,48 @@ Transaction.fromBuffer = function (buffer, __noStrict) { return vector } + function readCompressedG1 () { + var yLsb = readUInt8() & 1 + var x = readSlice(32) + return { + x: x, + yLsb: yLsb + } + } + + function readCompressedG2 () { + var yLsb = readUInt8() & 1 + var x = readSlice(64) + return { + x: x, + yLsb: yLsb + } + } + var tx = new Transaction() tx.version = readInt32() + if (coins.isZcash(coin)) { + // Split the header into fOverwintered and nVersion + tx.overwintered = tx.version >>> 31 // Must be 1 for version 3 and up + tx.version = tx.version & 0x07FFFFFFF // 3 for overwinter + } + 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) { + flag === Transaction.ADVANCED_TRANSACTION_FLAG && + !coins.isZcash(coin)) { offset += 2 hasWitnesses = true } + if (coins.isZcash(coin) && tx.version >= Transaction.ZCASH_OVERWINTER_VERSION) { + tx.versionGroupId = readUInt32() + } + var vinLen = readVarInt() for (var i = 0; i < vinLen; ++i) { tx.ins.push({ @@ -134,14 +188,75 @@ Transaction.fromBuffer = function (buffer, __noStrict) { tx.locktime = readUInt32() + if (coins.isZcash(coin) && tx.version >= Transaction.ZCASH_OVERWINTER_VERSION) { + tx.expiryHeight = readUInt32() + } + + if (coins.isZcash(coin) && tx.version >= Transaction.ZCASH_JOINSPLITS_SUPPORT_VERSION) { + var joinSplitsLen = readVarInt() + for (i = 0; i < joinSplitsLen; ++i) { + var vpubOld = readUInt64() + var vpubNew = readUInt64() + var anchor = readSlice(32) + var nullifiers = [] + for (var j = 0; j < Transaction.ZCASH_NUM_JOINSPLITS_INPUTS; j++) { + nullifiers.push(readSlice(32)) + } + var commitments = [] + for (j = 0; j < Transaction.ZCASH_NUM_JOINSPLITS_OUTPUTS; j++) { + commitments.push(readSlice(32)) + } + var ephemeralKey = readSlice(32) + var randomSeed = readSlice(32) + var macs = [] + for (j = 0; j < Transaction.ZCASH_NUM_JOINSPLITS_INPUTS; j++) { + macs.push(readSlice(32)) + } + // TODO what are those exactly? Can it be expressed by BigNum? + var zproof = { + gA: readCompressedG1(), + gAPrime: readCompressedG1(), + gB: readCompressedG2(), + gBPrime: readCompressedG1(), + gC: readCompressedG1(), + gCPrime: readCompressedG1(), + gK: readCompressedG1(), + gH: readCompressedG1() + } + var ciphertexts = [] + for (j = 0; j < Transaction.ZCASH_NUM_JOINSPLITS_OUTPUTS; j++) { + ciphertexts.push(readSlice(Transaction.ZCASH_NOTECIPHERTEXT_SIZE)) + } + + tx.joinsplits.push({ + vpubOld: vpubOld, + vpubNew: vpubNew, + anchor: anchor, + nullifiers: nullifiers, + commitments: commitments, + ephemeralKey: ephemeralKey, + randomSeed: randomSeed, + macs: macs, + zproof: zproof, + ciphertexts: ciphertexts + }) + } + if (joinSplitsLen > 0) { + tx.joinsplitPubkey = readSlice(32) + tx.joinsplitSig = readSlice(64) + } + } + + tx.coin = coin + if (__noStrict) return tx if (offset !== buffer.length) throw new Error('Transaction has unexpected data') return tx } -Transaction.fromHex = function (hex) { - return Transaction.fromBuffer(Buffer.from(hex, 'hex')) +Transaction.fromHex = function (hex, coin) { + return Transaction.fromBuffer(Buffer.from(hex, 'hex'), coin) } Transaction.isCoinbaseHash = function (buffer) { @@ -208,16 +323,46 @@ Transaction.prototype.byteLength = function () { return this.__byteLength(true) } +Transaction.prototype.joinsplitByteLength = function () { + if (!this.fOverwintered && this.version < 2) { + return 0 + } + + if (!coins.isZcash(this.coin)) { + return 0 + } + + var pubkeySigLength = (this.joinsplits.length > 0) ? (32 + 64) : 0 + return ( + bufferutils.varIntSize(this.joinsplits.length) + + this.joinsplits.reduce(function (sum, joinsplit) { + return ( + sum + + 8 + 8 + 32 + + joinsplit.nullifiers.length * 32 + + joinsplit.commitments.length * 32 + + 32 + 32 + + joinsplit.macs.length * 32 + + 65 + 33 * 7 + + joinsplit.ciphertexts.length * Transaction.ZCASH_NOTECIPHERTEXT_SIZE + ) + }, 0) + + pubkeySigLength + ) +} + Transaction.prototype.__byteLength = function (__allowWitness) { var hasWitnesses = __allowWitness && this.hasWitnesses() return ( + (coins.isZcash(this.coin) && this.version >= Transaction.ZCASH_OVERWINTER_VERSION ? 8 : 0) + (hasWitnesses ? 10 : 8) + varuint.encodingLength(this.ins.length) + varuint.encodingLength(this.outs.length) + 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) + (hasWitnesses ? this.ins.reduce(function (sum, input) { return sum + vectorSize(input.witness) }, 0) : 0) + + this.joinsplitByteLength() ) } @@ -225,6 +370,13 @@ Transaction.prototype.clone = function () { var newTx = new Transaction() newTx.version = this.version newTx.locktime = this.locktime + newTx.coin = this.coin + + if (coins.isZcash(this.coin) && this.version >= Transaction.ZCASH_OVERWINTER_VERSION) { + newTx.overwintered = this.overwintered + newTx.versionGroupId = this.versionGroupId + newTx.expiryHeight = this.expiryHeight + } newTx.ins = this.ins.map(function (txIn) { return { @@ -243,6 +395,26 @@ Transaction.prototype.clone = function () { } }) + if (coins.isZcash(this.coin) && this.version >= Transaction.ZCASH_JOINSPLITS_SUPPORT_VERSION) { + newTx.joinsplits = this.joinsplits.map(function (txJoinsplit) { + return { + vpubOld: txJoinsplit.vpubOld, + vpubNew: txJoinsplit.vpubNew, + anchor: txJoinsplit.anchor, + nullifiers: txJoinsplit.nullifiers, + commitments: txJoinsplit.commitments, + ephemeralKey: txJoinsplit.ephemeralKey, + randomSeed: txJoinsplit.randomSeed, + macs: txJoinsplit.macs, + zproof: txJoinsplit.zproof, + ciphertexts: txJoinsplit.ciphertexts + } + }) + + newTx.joinsplitPubkey = this.joinsplitPubkey + newTx.joinsplitSig = this.joinsplitSig + } + return newTx } @@ -278,7 +450,7 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT input.sequence = 0 }) - // SIGHASH_SINGLE: ignore all outputs, except at the same index? + // SIGHASH_SINGLE: ignore all outputs, except at the same index? } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) { // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60 if (inIndex >= this.outs.length) return ONE @@ -304,7 +476,7 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT txTmp.ins = [txTmp.ins[inIndex]] txTmp.ins[0].script = ourScript - // SIGHASH_ALL: only ignore input scripts + // SIGHASH_ALL: only ignore input scripts } else { // "blank" others input scripts txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) @@ -349,8 +521,8 @@ Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value } if (!(hashType & Transaction.SIGHASH_ANYONECANPAY) && - (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + (hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { tbuffer = Buffer.allocUnsafe(4 * this.ins.length) toffset = 0 @@ -362,7 +534,7 @@ Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value } if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && - (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { var txOutsSize = this.outs.reduce(function (sum, output) { return sum + 8 + varSliceSize(output.script) }, 0) @@ -486,7 +658,23 @@ Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitne function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) } - writeInt32(this.version) + function writeCompressedG1 (i) { + writeUInt8(Transaction.ZCASH_G1_PREFIX_MASK | i.yLsb) + writeSlice(i.x) + } + + function writeCompressedG2 (i) { + writeUInt8(Transaction.ZCASH_G2_PREFIX_MASK | i.yLsb) + writeSlice(i.x) + } + + if (coins.isZcash(this.coin) && this.version >= Transaction.ZCASH_OVERWINTER_VERSION) { + const mask = (this.overwintered ? 1 : 0) + writeInt32(this.version | (mask << 31)) // Set overwinter bit + writeUInt32(this.versionGroupId) + } else { + writeInt32(this.version) + } var hasWitnesses = __allowWitness && this.hasWitnesses() @@ -523,6 +711,45 @@ Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitne writeUInt32(this.locktime) + if (coins.isZcash(this.coin) && this.version >= Transaction.ZCASH_OVERWINTER_VERSION) { + writeUInt32(this.expiryHeight) + } + + if (coins.isZcash(this.coin) && this.version >= Transaction.ZCASH_JOINSPLITS_SUPPORT_VERSION) { + writeVarInt(this.joinsplits.length) + this.joinsplits.forEach(function (joinsplit) { + writeUInt64(joinsplit.vpubOld) + writeUInt64(joinsplit.vpubNew) + writeSlice(joinsplit.anchor) + joinsplit.nullifiers.forEach(function (nullifier) { + writeSlice(nullifier) + }) + joinsplit.commitments.forEach(function (nullifier) { + writeSlice(nullifier) + }) + writeSlice(joinsplit.ephemeralKey) + writeSlice(joinsplit.randomSeed) + joinsplit.macs.forEach(function (nullifier) { + writeSlice(nullifier) + }) + writeCompressedG1(joinsplit.zproof.gA) + writeCompressedG1(joinsplit.zproof.gAPrime) + writeCompressedG2(joinsplit.zproof.gB) + writeCompressedG1(joinsplit.zproof.gBPrime) + writeCompressedG1(joinsplit.zproof.gC) + writeCompressedG1(joinsplit.zproof.gCPrime) + writeCompressedG1(joinsplit.zproof.gK) + writeCompressedG1(joinsplit.zproof.gH) + joinsplit.ciphertexts.forEach(function (ciphertext) { + writeSlice(ciphertext) + }) + }) + if (this.joinsplits.length > 0) { + writeSlice(this.joinsplitPubkey) + writeSlice(this.joinsplitSig) + } + } + // avoid slicing unless necessary if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) return buffer diff --git a/src/types.js b/src/types.js index c1e5fb16..92967b8d 100644 --- a/src/types.js +++ b/src/types.js @@ -21,14 +21,15 @@ var ECPoint = typeforce.quacksLike('Point') // exposed, external API var ECSignature = typeforce.compile({ r: BigInt, s: BigInt }) +var networkVersion = typeforce.oneOf(typeforce.UInt8, typeforce.UInt16) var Network = typeforce.compile({ messagePrefix: typeforce.oneOf(typeforce.Buffer, typeforce.String), bip32: { public: typeforce.UInt32, private: typeforce.UInt32 }, - pubKeyHash: typeforce.UInt8, - scriptHash: typeforce.UInt8, + pubKeyHash: networkVersion, + scriptHash: networkVersion, wif: typeforce.UInt8 }) @@ -43,7 +44,8 @@ var types = { Hash256bit: typeforce.BufferN(32), Network: Network, Satoshi: Satoshi, - UInt31: UInt31 + UInt31: UInt31, + NetworkVersion: networkVersion } for (var typeName in typeforce) { diff --git a/test/address.js b/test/address.js index 78376ee5..e9c8a80a 100644 --- a/test/address.js +++ b/test/address.js @@ -66,7 +66,7 @@ describe('address', function () { assert.throws(function () { baddress.fromOutputScript(script) - }, new RegExp(f.script + ' ' + f.exception)) + }, new RegExp(f.exception)) }) }) }) diff --git a/test/fixtures/address.json b/test/fixtures/address.json index 2b7e5fa4..226ca904 100644 --- a/test/fixtures/address.json +++ b/test/fixtures/address.json @@ -63,6 +63,20 @@ "bech32": "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "data": "000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", "script": "OP_0 000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433" + }, + { + "network": "zcash", + "version": 7352, + "hash": "751e76e8199196d454941c45d1b3a323f1433bd6", + "base58check": "t1UYsZVJkLPeMjxEtACvSxfWuNmddpWfxzs", + "script": "OP_DUP OP_HASH160 751e76e8199196d454941c45d1b3a323f1433bd6 OP_EQUALVERIFY OP_CHECKSIG" + }, + { + "network": "zcashTest", + "version": 7461, + "hash": "751e76e8199196d454941c45d1b3a323f1433bd6", + "base58check": "tmLPctKo9j49rtCSKpwEBpLBeykiTGomGQs", + "script": "OP_DUP OP_HASH160 751e76e8199196d454941c45d1b3a323f1433bd6 OP_EQUALVERIFY OP_CHECKSIG" } ], "bech32": [ diff --git a/test/fixtures/script.json b/test/fixtures/script.json index 3ba8bc3c..50467577 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -95,6 +95,10 @@ "asm": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef", "script": "6a14deadffffffffffffffffffffffffffffffffbeef" }, + { + "asm": "OP_RETURN deadffffffffffffffffffffffffffffffffbeef deadffffffffffffffffffffffffffffffffbeef", + "script": "6a14deadffffffffffffffffffffffffffffffffbeef14deadffffffffffffffffffffffffffffffffbeef" + }, { "asm": "OP_0 OP_0 3044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901 3045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", "script": "0000473044022001ab168e80b863fdec694350b587339bb72a37108ac3c989849251444d13ebba02201811272023e3c1038478eb972a82d3ad431bfc2408e88e4da990f1a7ecbb263901483045022100aaeb7204c17eee2f2c4ff1c9f8b39b79e75e7fbf33e92cc67ac51be8f15b75f90220659eee314a4943a6384d2b154fa5821ef7a084814d7ee2c6f9f7f0ffb53be34b01", diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json index 6bf60906..a5c98bab 100644 --- a/test/fixtures/transaction.json +++ b/test/fixtures/transaction.json @@ -831,5 +831,121 @@ "hex": "0100000000010113ae35a2063ba413c3a1bb9b3820c76291e40e83bd3f23c8ff83333f0c64d623000000004a00483045022100e332e8367d5fee22c205ce1bf4e01e39f1a8decb3ba20d1336770cf38b8ee72d022076b5f83b3ee15390133b7ebf526ec189eb73cc6ee0a726f70b939bc51fa18d8001ffffffff0180969800000000001976a914b1ae3ceac136e4bdb733663e7a1e2f0961198a1788ac0000000000" } ] + }, + "zcash": { + "valid": [ + { + "description": "Version 1 transaction (1:1)", + "hex": "01000000013f5cdd55fac42f620796ac33f3acbce8417e75fde7457662fdc612c8759d400a01000000fdfd00004730440220785552c1ce40d5eba426b9a3b0e4e58cb243334f35290902d58746755c13531a02200801731fbda2a334f76b21732a5213b344edc2b966e149301ea688fc0df0abf901483045022100fa5027215031352af57ad343b6680aa47bbae174abec3450aaaf1811c9942a6d02202ce3d68ecc468381e0c4610493ff043ba61c8875e74d7d65b46f6e17f0583752014c695221037acffd52bb7c39a4ac3d4c01af33ce0367afec45347e332edca63a38d1fb2e472102658831a87322b3583515ca8725841335505755ada53ee133c70a6b4b8d3978702102641ee6557561c9038242cafa7f538070d7646a969bcf6169f9950abfcfefd6b853aeffffffff01601ce0110000000017a914040c4ab99a665c767adaa50fb28dce2ae514363b8700000000", + "version": 1, + "overwintered": 0, + "locktime": 0, + "expiryHeight": 0, + "versionGroupId": 0, + "insLength": 1, + "outsLength": 1, + "joinsplitsLength": 0, + "joinsplitPubkeyLength": 0, + "joinsplitSigLength": 0 + }, + { + "description": "Version 1 transaction (1:3)", + "hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0403058a00ffffffff03000d2834000000001976a9149c93193a41a0dcc93c8c6beefaab996084f2f83a88ac80b2e60e0000000017a914ab13d4675630d69f9c9000c701a981938b0d585d8700bd7207000000001976a914d45f8fdbd5624b89a720cd9c170d50927eac5bdc88ac00000000", + "version": 1, + "overwintered": 0, + "locktime": 0, + "expiryHeight": 0, + "versionGroupId": 0, + "insLength": 1, + "outsLength": 3, + "joinsplitsLength": 0, + "joinsplitPubkeyLength": 0, + "joinsplitSigLength": 0 + }, + { + "description": "Version 2 transaction (3:1)", + "hex": "0200000003e9bf44b27f0784cfcb33f8c159f3b69b9726bc2214366ad834a678094f8d5de4010000006b4830450221009ef714b94f700554711ae904a065de00e182fced0e82ac8dc109ba234c0bc5b102206bf2deae0168a69511821f7d754fcecf8d05a7d19fe77c5ebe2fb6b6c206c4df01210201d494a45f36f545443bafd1a9050b02f448dd236bb4ce2602f83978980b98f2ffffffff4761e1441eb007b7f339f57fd6f276965e1a53d5e5d585a7dbbc72ee2d44b62f000000006a473044022047fce86902b769a329e2010bd3eaca8f6d3818b7adfe03982651fe2165d23b42022048a2246efbb7934aa06d11b621b51e70bb27ca66a57115b002d36158b9c0cc3f01210201d494a45f36f545443bafd1a9050b02f448dd236bb4ce2602f83978980b98f2ffffffffd28cb1a3bd695b43bfdc59eaef5d047740955f057c36137e42ad92fcc55c7f17020000006b483045022100a06516dd99e2c156755ab37ef776cc773d8404587ff7ae56d2ce716046cca7460220087543467d271c9af7bc998342349862833e7429ee0eebdd73b34129a918e92a01210201d494a45f36f545443bafd1a9050b02f448dd236bb4ce2602f83978980b98f2ffffffff0196c46407000000001976a914d6733a8dfde223f481332d354847639d0fa9e20788ac00000000013097402500000000000000000000000075d8accc9c558e6e3535081f24fe79dee3e6b25e0bb68e07419ba16bb1be9d8b3ca0418663fe179e6faed4b507917107bb5024da77d4fde616a53e2bb7815e16612cadcf85f574f7a88644b040412091ab63d30ff0ec82ff7af711753d76de91d4dd4f569ef7201bce0e0a5edc88ec1e98f86d2604f721f12e10e63b9093e4f065edb26a8e5e303b1ae6b67320ba25e97578d76cae6cf91e224d678010e49b17427db24e38a8cf1a7246ddd22b3e0254f4fda70d1391da4b0966f3e857b7e655b15a2d3bfb4b8e824ff9f5e0ed75112a671d5d44559ebf738b3a14f45d4342e84add4bbd5795f7c103d5175b7b633dc50063048bec71c18397f66673118ec7436378639f56c122e4454edbec93db27fa5fc34586596112079a43889d8ee7d5ed032bb412a7ea62fa71b648ca499c5420a2657863a882c0e059b9ea18d542ec704a03233b8a39e63824a83b2c3de172859c1d99bb40c6e87a9fb9da5b85f245176c8d0a02af17f894dd758f27ba00c6a5468ee0a2e8c2828866ef4a43f503603a26be98272b958ea77693cec47f274520034be49c8836bbe88fde847d566c69b97b3cb6030710a6b22983e4675371f4fb302732114ac0bfca7e50d4b0790fb8c496315c3c03011cf767bd6a6ea10b4cde14e5bdf995b63c72ab43c9bf1d6b680c1135b15ca70320a77cf95eea1147c81a353a0d7e0973805eab3a37324892157395b767a627630206dd9ea41df4fbe93203c3abcb28336cf8031519ef35f1d68cf00060e5e1385503060ccf460b70b23d03b9faabc4e564df87f66ee61abc376de156548d9d934351ede7e8809aead84fda544f5dc91371d4669949b4366d249f591aa842d5dc93d2b04adff389dbc442c67a86965b6391e71900b6b0bbf0eb8d8976e1d5a17807898e97e96b71ec97e69d179566b0a52937dae0aa945dc4fb9f65217518514b5c6c8fc706bb17823c6d7b924f99630a9636d4b04dcc687d5e1c539ad4a876638ac5eceaa1becface1846fbef96153cd7333cf1bab8f6d50605e21583a5a61230151edde9b3b9a2ea22484cdcd4e788bb4ee12b093db6ead549d75ceedaf33a3b94c7d93df4dde593aee0040408434c63057b945812c798e30b31724d7d2ba554545768cc6ad9f118632981c9092d15ae78ca927fbaf0ac5255f30179e5df952599c0f6225e6608a675bcc67fb424840ee0b043d18a68b4e660ec105d3c8e098ff8a30c3275428ba51e82947ae5918acad110bc60331b2130b60361581d073a0fa4648e485bdf632e4f6730987144b7852cfca4a854b09b18c174c4a9069d07774c0ff5444a994322d86a40d838cd7668b74c89abb5115a0d785def8f8c517b89433738148d3958f6ce5c2f6dea8ea05ba0011b9aa5344f42531279f9981bb6d73bc7dc9af76371645ae18b7c382ffe841f48469aa69291c191acbc5a14a3a6d80bad137b07b8c8f19f24c47c94e309e27591949add68d9c2267a8301fa35f9c385febb858b861857bc0c6c5ac7d5e5fb1a8d1cf11f48136406193f778a9b30ccb7685553671b2bb98e670c5c2a63a42427b8216e1d9444963a551fab436d5393bf54c6869ebe45243b6b015464143a2b80a53b63e978e862fc22f6b8359feba0d10ef4bf7178e69d6262acf6b22b9c93d0a2355df1695706b5c46477012a1bac72f67c60ec8bd869d54890c8dad256c23e3d0a325723c11b3112f5bd66df40e42c2ee19a6314d5c7dfd9a6cabb9ce41854d17ed68b6f934538330f30454cb0ea39ab7e2430d20ca5aa151afc13f1bb1a17da29d31d008863eb624f8d4bfec4b1ae15bcb7487c0d1b3c0db4bbfffa3f39c6fe0e4e57d223671198f798ac788521888caa9438b8dafed6b7eea6d703ea703459ecc2dfd770ea49de8546032ba426758c9e6f31044f01b6d788dc626a52f047e552c7bf60716b116ac8122dddba2d461896eebf3d8a8796947a3d301ace3cd0d4bc5ed8999835b54b5d0709878a0c522fa1d7835fe8fad03cbfde00ad8451041893a29d007e1a877cb60b547330935d7be2a56f93b54769bf23407edc488efc606e757186ea6eef2d251439aeac37a184be6b2d35776550358313d19ad5efc3547603eef8cedfbb516214a81e5db96b928d310600ada52f1e5144f1311299001a20b9a6b3e77b54e9475841c8ca50f7888bd3781bd736489c288137459b35ed809df719ce2730c91703442866ae9121396db1d48b9915a3b4a81a62e7d49b819cadc93299f31ca7b336e91528db788c0342d755b8aebe83f7ea978a6fd673b0b8c25771cda462f500bef6eb063b71134f61740acab1fc0d01e68b24204cbea6809ef84b1b290ce46fc809cf2a551550f61e0ec5d173e1ebead0869bff0b880aff71776286b2113fe3e0d82f2d362acf36077b939092ce941198bb203ce67036abd8860c4f786e32b3abbb302af9a24336d4ec56e36f35be5f1affefd271bd4fa0980b1106357fd7255d039211665317f4b957975adf75258935fe51e410806f2f3b9ea0283e51a8f481b27e85783a465ad6d10cf52554995a86a8174793e685de4401269145f79bf3d7d603903a07f03b7b2827c0b8e4e273df0fa8c0b95954715d68df17281ca9626e102385ea90b493764e982d76ed41887b6fcfff8ed3d17aa0d", + "version": 2, + "overwintered": 0, + "locktime": 0, + "expiryHeight": 0, + "versionGroupId": 0, + "insLength": 3, + "outsLength": 1, + "joinsplitsLength": 1, + "joinsplitPubkeyLength": 32, + "joinsplitSigLength": 64 + }, + { + "description": "Version 2 transaction (4:0) with JoinSplit", + "hex": "0200000004388550971589e75e5bb1028a36dea5e02553108d3a7fd6ff6b0f6ebba50a494d000000006a4730440220118a49bf67eef4a98dc154c8f5422645fd979e4122205addd0a22dd60656b06c0220209da845f7bf3af9e7e7b9732e6fb3d2f09c94b401ee69fa17f987c77049673e012103e9083677f121003ab9abc4610194440e5605576cf67ed1e40b520aeeb8a04ddeffffffff5ce50e125eb8a70603debcd38ad2cfdaf1fbe9fffc87f2697f4fcd6eff6a1490000000006a47304402205abb286d4757088d8259ab07e9f4aaf4d1a549f9fbce8308293095d264aae262022026ae2d5d255caa446da86ecd08ffc98dd221a115b3274bc4758efd381ad1abb8012103e9083677f121003ab9abc4610194440e5605576cf67ed1e40b520aeeb8a04ddeffffffffc4af66507f7d97a3f74e88b0ab9fd39bc4bd3ce8c5374c366bb6cad22c1ef359000000006a47304402205a4b852596ff9829098803d8eba6dda630bd6074c88ba16a3aa4c0a9f5b080b402200dbd820b579cd6f4d1efdf979e995d4dca1f5c61a812a15bcdc46981e2ddef7b012103e9083677f121003ab9abc4610194440e5605576cf67ed1e40b520aeeb8a04ddeffffffff0a2c229118bea9c5a415ec02b49c0eb6ac8a8f34047ce6df7d4fa67ad7e6bfbc000000006a47304402204d212b232be4199f55210742d5d077187eabd1a1ed1354c3ffa2674fbf86a4900220365a049537a28d23c15d9c8283e95cc71a60a036ecb35a4d14eb3431a95bef04012103e9083677f121003ab9abc4610194440e5605576cf67ed1e40b520aeeb8a04ddeffffffff000000000001e21596d6000000000000000000000000c814f402d7b755c029a0102cd0d73aee65e1a34ce3969b5b21d96ae5e8dc437672544ce06a576640da172673a2b9e4f8fe55f15c9f07b0514b3e8030d908c699d5d49fc9f402f01ec360b4e434c7529e2882fac8d604a73856efb8e4650b19e4a0280815ab101021714f3981c546136cf1d76a83eeb0cf7a6055350da26ce44ae6d3e666d4481f72f97d6639546b9dfcb3afb69f0782804ca411ac57fc398447be70bd23757a44d0929cb2e84d362f6c65487a08020a74a9c8a4d30e2e83ce468ed842fff5232eab69b8fc32123ea8e7a098c1dd16265a2ffbcbdd817dfdcd1988b63fe90b5a345f5311a47e0bfcf473c7312922fc367c6f3faa1b31dac5e9cc52d41692da2ec399875f47dde6f761f8469d0ff63f597468447ca41858a2ced303243dd89e7f8ddbfc8cf2583be37dbe04830e784b620c0bf96a966a08f531e7380313141cba3cd89542c8fb4b3f80c1835f3f0e76c5c54b66ed94bedfe60510dd5b0a042436a60863132a05a84fba9cc60e3842b2523c705be71db4f799588c861dd625fdeb90e2f53714cbf3fc99d6843d6712444f76961053e4fb2be6e5e7254862030f60af26875175db6c513a45adac70ac2cf342f40d75376f13da9530eb807c3a0322851b648c2eb95d9493e96747edf86906d2217b2238eb843dd971071ead174b0309ffbfc5181d5ecc13242267a859c8547267bba4b1c229137b79f117cf0ec9ac022b93bb5b01ebc61e8a9a7d49dc51ad78a3f1328290dfd0ed98acf41954457fcf022576b61816a35b130c2f982edc1f8eeaa5168d4985b5c2de1a840eddcc23bbabfc3f2f002ac9ad683041b33db78a85a1bb9a9a306906805157f5b0d91491e43aa8f91f7e45b4fff9bf53fc8f43c7d19bd87142f3ec4dbceed2e4191fd0d749c6de3332a707710930799f83eb12495883a4aa8f75bccac3a3919df798a534ecd03c0605ec0b2c13a2ec54efb50c68df9e5522bdc2a6fd35944c86551ba318399b55d9efcc985229fc14a7d2c7aee241e5009ecc42d0364a591835d66083469dcd30da56e43712b302d16bfaed1b4ca19f41ceaa68faeef0114d912f012b4957590d56af4d9b08554195aa391a5e998a9a5056ac40bf6dde4732f7ce186bc6c6078c791fab4b527303aa77c822a19103a3795d4f43690259b1fd5f55b3c937ad0dece3d14796b8dd2c373ddfee2df04cafe567f8b47fb831dbbbde086395c7c57e86886d19136c52baca371a297ad45148d724b6a4caf59cf9bcf4b967be6d5d383334e82218ec251768a556be137b78bc266982a8905bb7841f502766d12715b5011d00c5be6d50aaecf1a0d04254e42d142ae21220d7cf2b4170e5e3f752feff5a0b44fd5001b618dac780467a6b8837fe5669a6073852c6e630cfd31943c175e029a6b6b83db4adf2b88c2df2b3a68d6bbb25f691adf0257d280915e1aef79f8703f08db6c0bd016a3c28a2c43de99f2caea8fc89586b928a552a56c207d1d1a9b313bb942d2e3efd6eb94fce7ba363c3d8032a959ed3a50eed4015c3979860c6c3b30d756e441a6556d9e9b0292ff2dfb8ccf7f0081fb8b61be63e6170f61ed7748e1e320bfb480ebc62f9e4385d00dfb17ca64687313ea97f278bd7b89ab84b1cf9d1b87647311fee91e81b17fdabf3d2784b8899f4428548c9a901e91e7abfc7660aeed9f1ba990bd4512d46b968cd5c7254c56edbed795098b820c7c0b13cb7a6a247ab0a4c73419e4afeadb0a627e3a00c35069649694f0eadecefaac1e959b5e21f0d965f67265e7fb0daf6b4c0a3d82f6b177ce90b02462ecb5512fba3592dd2a7636ea3553ea82a1c8053f9413d02d7c83d4518e70ed5ee430ff2056764b5482b44dff780571b09edbdc5198b7a16113504d846a274f5aab4782e6e4a390fb4c9c7da62296e6c9416c462439a2d17501e072d17980461d6d9cb5bed7a399108c34366bb99cbf7264852eb8797f4f76e7a5c097055f560616a4dc2085dde104408d6275f750ea0f11a262294ff4f806803a341e82bccef14abc4b85726d8513e8468199ead834dac88223320765131c610316ddea200d607b49f4864a20a200295ec14b0874b1e8630188f88fadffe41e692cebf136c64d703c69aa3c09881b8525a99a7cf2442c8a3a1ae05428de52e068c989d63fede662abe9621da6faa8190f804ed6f4ec7950db576380be04477c4d838997f485124c2eaec0a83c3fef8a99a3e7424f6058725377f17880b9c7fd8ef9f1c367e3663d3321d29982031cf7e6dd83eae6647070cb18e2ea780393261349bd9ad1eeeeebfb3da272bba30f9d4924ccb22a70e3138686b42bcc684412c31ec89891546d2fe7ff542fa85ef461acde236ac611f0224a1f7969142b77f3126c6b7043c3b8e633401f7dc948ce75d395f0770028af1939ccf3863890d5723b869a47c5da17f16f885b4c24ca38b1fab5dce5b96977bafcb55ec9e560fc58c8e2b9c6915fa1e276aa350f14cd781e9298dc56a1c4732faf677f1fb0754e081c71c347f1779b4ba000f4abe6201d050b549e2a790105f9051027cf93e1c0ccfb2301b885290b37f7b0f739a6ae31849547caa880e33d2ed23f35b24873603349938840a9722d12e1931576e0405907e9b1c89b3f802977ebde7edbd03", + "version": 2, + "overwintered": 0, + "locktime": 0, + "expiryHeight": 0, + "versionGroupId": 0, + "insLength": 4, + "outsLength": 0, + "joinsplitsLength": 1, + "joinsplitPubkeyLength": 32, + "joinsplitSigLength": 64 + }, + { + "description": "Version 2 transaction (0:1) with JoinSplit and bits that look like marker and witness", + "hex": "020000000001f0f69d51000000001976a914a99534ac90feba29f0a227f3fc3fae435a9f142488ac00000000010000000000000000001e9e5100000000b0a9e141f46fd8c083e699d2d2c27aa989ff1d2a23fcc616cd8fb8cec7d86426cd26aa6934cd890f3fc488753c5090e78700afb75c46284fd39a4629eaac3adcdaa763aa275958bc40c1e171a40f365c68cef3322a62cce2052ffe17a16c900cd2260587bd224fc0a4fd01870f9bab696c371b282209a74ff1b532e09647927f2bd07730ddf47a6b6026bb7723e353c2c067efea4799d6d8ec74387da1795cae8539f6ad35d4f7d0c6103ce19fee91cb7bbb1172387d72d2f1ccdefde76c0375fc80c7155baa47e952d79014595bdb9ce77c127bc8edc7216467a57d8532cdad3b8c6f0922a12458d9547c13cf8a41b4957612add8fe97518a91e969f38123d4244e89a642282df5d0101f5e15d82fd2f05c895e846259daa0e4ee0bcd6e6d3e031ea45ebe26b4961dfe7cc54a5dfb6e4d72cf4473dc8c7e41d28a3429bdce7695032f36f48006c520933928e1aa19a621c120b031f9f1e46f5655e5b306f7fb135c0a0257085cfd5645b17f463651a587740f444d709445883a7b65a6fc850dab844309b574e4c771e28ddfb835d30d5c9b1ba7185eda06d3e0299412a9065ce2eeee02045b1e5ee0927b2af75da5054a2a58c8fb8db9edd0f3a21021279590cd0c54750318323a7ef8de99be95daf23b27a7ea50665f0f8b70ed2021f1feab8c88f94077020ff3852848142a232ee4006f00834501ebfdd245896dcc4a6d41b91bb93048c603018b5b47e588cf667117fba0abab8983878bb2a20853ff4e172e3a86475b8da1032ffba7ebbb94c574f9b51888aecf5c41574fac458905d00cef322a0e6b9d93a3de0663601ee99c83ad9ec909fb56ee501cb1b4331826561392dde9bdbeffa194c9806580d0d4de0337f1714785dcfa4fcd5e634a105856ecaf3846a78fc10f38821db07b23506b88eb48793fab2c62e78a12e15ae368f35a6c0f72b113a3aac00d76cd2c9e3c42d99c959ac4da7b9688ddf99eeafe965d9d1b6746add646ecffe48d3803d368898f4f340e26f4320b64018876bfdb73093c1b673c38342831e74a9bcefefa29ade557c81b799ad388133085c1f39f4fc94fa29d30f8f20e545e1be53d91f44cb5a74c0e68c8f0897fe3d7d3225d8eb650f42e652539ab7a8f313664f0713fa9a9b9525da19e91988c87e20f20b5447456659f7b1df24f3672985fcfe4c3a01faf687660f29c4d6ee13add9191e8901cf46ae1720ce8fec6b904ed7975fc4faf602bc7d248af151f25f7400094bc60b3893192e0cf8936edcbc641ae642e31103bcdf06699838c1425f45dad1c7262d5beeb16d54e5a1f2e0ba5813e37d11e2d5b7d68958d7fc69849f62118fba5e992396bd7f4c6a456f74f4bcf86042f72daab6463680fa5be23f7a1fd67c2ffc0274dc6d67e7faf51728f71ce63541d587542877f0c5ebb006b156679d4c7f43867e1fca70e8f9496edb9e28c4512882b38e3bab6d7fed1d6f83fb2ecadee5b50a70f1275f909828a75440cc43a7d78ef181d68e27310b13d0eca338486056fb43b69d61694c8444df849a1e42f4ac8758f8b3b2507c19ad1628f491e70ba240e653dd8c0ffb7d321d7da3f0242819b524f54d08dbf9030653ff12545709ecc5935086279d783ac676891726cb382e09f35504a05d7f3978ba5fa20ac1fe3dcca1c3cbf08e1a7786a6ec09486198d1e246036a1c35ddac7e1ffe03092cd3e20223faa3a194abe52f0c723309e247bccc67382ed23ffa86f4515b8f8864d6e5a1c187e20c0d3f8c47c7ce139ae4bd41a34c3372bffb32cc463eab2d72e7f85fab611c512886025966dd0a849ae28bc05d53d80449993c37147bc188eab6bb521207cda3be45799928aa2ef3010cc98447476226ebef1ff26ce79157cc5aea6b2585225d3cec5ce808f4a69c5f2f9b8d4399c201a4a53370183c1840c753957d577944e811ea4110b65e68bf92d87fdee6272e2bb2242d03721ff7d54813655ab1c4872c7bd5907803fa4b84d68459bc13d67ead82783d93ec14575f1fd893d9dbdbce1091826b45dbdf82b56d5aaaaef6c8e1140140b7760ebf596b6870db150e6dac356275eeb7ed70739e5dc4b9163389532e340d8299f4caa7bd10657342e48e4ec35b11df43e13bd08e698cd42bbed02e796c692bea3e56a160f23a7352db9d15ac5536833e8e77fb29803fb43fead1ef8b3bdf0c325c61b9ac5b0d771e36a6b25ec3b574ae0f028c7456ab6c667146342ffca67158e6ba40032d38b842454a5f15a68b85646e118e2e024f9e72b7c5434c820ad6dac948b3369d57037a6396b398adbcdb7fd177c260f72f515b4f6e9e9a9621dccde3e87876e7a5e790a356a8f9f0502db6d9fba9979209aa6db77fc399e73d1401951875873b4e420515bf636576bc16aa58213d8b76b030beb7cdae220790e855dd1ed09a9739a4cb37994a724cf48b89c96542815c0a4e666fb652ec0d73390612fb35992bdad5624b53a27e5cfb26ddfd4cced292760ebccff669e9b882f1726b59a5fb6161bb313c77fd77f7c38b973814ea67679558857db23fd32069a864e2b71175587cb01ba586af3cb6697dbe98ea4294790dfdaa6494b40e2a926f195bb7e719dc15c55c88f8b569472774c333b20ca42e34806bac4126cb9cd5aa0fcb2c7f0b6320c", + "version": 2, + "overwintered": 0, + "locktime": 0, + "expiryHeight": 0, + "versionGroupId": 0, + "insLength": 0, + "outsLength": 1, + "joinsplitsLength": 1, + "joinsplitPubkeyLength": 32, + "joinsplitSigLength": 64 + }, + { + "description": "Version 3 transaction (4:2)", + "hex": "030000807082c403047e6070481b33c176e887a3d7662d708891b6e643b6ae038a29b4b57e0da5a107000000006a473044022029d58b11f67a83e472f36695486046d670e666a52f367d78d7fddfba3308b5a902206290bc7a822027158cc39e7ab02d2acf2d2ae54cac5ef5931ceb948159d4c0790121031db18bdce8d9fa4f0054c5be45163917ea1f3faba8defdbb2b3975d4bba055c5feffffffeae9560561ce09393263ca98bb01b15d16c88b02dc504d8c9ed1b0efe81222a0010000006b483045022100f97edbcda6287a156803ef614080c3b264cc446f949c6596dddca8af2dd2fa500220267ce4b50dbae8c1ef66cff2ef802583c75fefdfd87be401a595e4f4399ebd2d0121024eb7c7c1244f5e318c3def22a4a61257acc82862b2352bd442bd2d691b5eabecfeffffff1a578230b8c382713319771f2c904399297087b51530965b3589c27f9139d6e8000000006a473044022037f5aa4a8e1ce24dfa10919f37e718c4066bd72e1cd258439be24cb69a534b5302207604c1b3faec20b95ed4bcb530d6ccfe823f2c6002e5d5bef2e61c48b59187c20121028170001108bb31f159092b97cceab2522f9266435efd3d72357d8070bf846ebdfeffffffef8f47a709c437898a3bab2570362cc0325015bde19544ca0576ec07f9364428000000006a47304402206c4b7e1efd8b7493878e39912b20864f1b450ac845214236973d47a0bd447274022042689b896acfa16c4dd8eac6d10e3a55ab41fc04457de7f2885966c267f1fb9e012102b2ed048db551bb3dfa9a57585eaa2437b086c28c47bd4c13bdecf8f89c0c109dfeffffff0200a3e111000000001976a9141b4ca322da3708bf4cc7a265ebf7ad011f35c5de88acff384700000000001976a914034c9da5a8cdca724275e96fea3d4d58d7c28c7988ac9f920300be92030000", + "version": 3, + "overwintered": 1, + "locktime": 234143, + "expiryHeight": 234174, + "versionGroupId": 63210096, + "insLength": 4, + "outsLength": 2, + "joinsplitsLength": 0, + "joinsplitPubkeyLength": 0, + "joinsplitSigLength": 0 + }, + { + "description": "Version 3 transaction (2:2)", + "hex": "030000807082c40302ca6461f8faee6b6ab6bdec3ea72b8efa02d9ea6fa47631f7ca48be972141d3c1010000006a473044022078120f45e89d805675c9252fc84851d36a7b1bad9d3bdf29850d086f50eb7842022065b32e392d7fee460ba77ed6bf66c9088ae9e6f70735710d5ca9af22dfd572c5012102e941bb1e2d2d13aa901efca2e62cb3883a71e5db0cc741b4837e9a8e5164639bfeffffff5685689a1a158df93ee1979b6322843ef06365b2f62204feccbcb076cdaf9490010000006a47304402207244a75fdfdfcbc65a80d2796ed24cde8758968304a983dec055c002f05b843d02206fa8613df5482310820ef4b92016b2dc01bb8fe5affebc229ef82b6764fd87ef012103a802561f573d200dddd381adda1c7043445140b6f8ff352985517e866504f082feffffff0218210000000000001976a91483867e664ff6dd7ef1bec58a766062704cfdf23988ac10270000000000001976a9142e6e46800c3e1d48a1ff298a483de421f30850d688ac86760300a576030000", + "version": 3, + "overwintered": 1, + "locktime": 226950, + "expiryHeight": 226981, + "versionGroupId": 63210096, + "insLength": 2, + "outsLength": 2, + "joinsplitsLength": 0, + "joinsplitPubkeyLength": 0, + "joinsplitSigLength": 0 + }, + { + "description": "Version 3 transaction (1:2) zero lock_time", + "hex": "030000807082c403010000000000000000000000000000000000000000000000000000000000000000ffffffff06033e5a03011affffffff0200ca9a3b000000001976a914b142bd45409b058dc076d25d9f257b98578188d788ac80b2e60e0000000017a9144c5ade3c465399986b230b7cddc877c72877a02e87000000000000000000", + "version": 3, + "overwintered": 1, + "locktime": 0, + "expiryHeight": 0, + "versionGroupId": 63210096, + "insLength": 1, + "outsLength": 2, + "joinsplitsLength": 0, + "joinsplitPubkeyLength": 0, + "joinsplitSigLength": 0 + } + ] } } diff --git a/test/transaction.js b/test/transaction.js index 62c24d01..941ac709 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -2,6 +2,7 @@ var assert = require('assert') var bscript = require('../src/script') +var coins = require('../src/coins') var fixtures = require('./fixtures/transaction') var Transaction = require('../src/transaction') @@ -87,6 +88,40 @@ describe('Transaction', function () { }) }) + describe('fromBuffer/fromHex for Zcash', function () { + fixtures.zcash.valid.forEach(function (testData) { + it('imports ' + testData.description, function () { + const tx = Transaction.fromHex(testData.hex, coins.ZEC) + assert.equal(tx.version, testData.version) + assert.equal(tx.versionGroupId, testData.versionGroupId) + assert.equal(tx.overwintered, testData.overwintered) + assert.equal(tx.locktime, testData.locktime) + assert.equal(tx.expiryHeight, testData.expiryHeight) + assert.equal(tx.ins.length, testData.insLength) + assert.equal(tx.outs.length, testData.outsLength) + assert.equal(tx.joinsplits.length, testData.joinsplitsLength) + assert.equal(tx.joinsplitPubkey.length, testData.joinsplitPubkeyLength) + assert.equal(tx.joinsplitSig.length, testData.joinsplitSigLength) + }) + }) + + fixtures.zcash.valid.forEach(function (testData) { + it('exports ' + testData.description, function () { + const tx = Transaction.fromHex(testData.hex, coins.ZEC) + const hexTx = tx.toHex() + assert.equal(testData.hex, hexTx) + }) + }) + + fixtures.zcash.valid.forEach(function (testData) { + it('clone ' + testData.description, function () { + const tx = Transaction.fromHex(testData.hex, coins.ZEC) + const clonedTx = tx.clone() + assert.equal(clonedTx.toHex(), testData.hex) + }) + }) + }) + describe('toBuffer/toHex', function () { fixtures.valid.forEach(function (f) { it('exports ' + f.description + ' (' + f.id + ')', function () { diff --git a/test/transaction_builder.js b/test/transaction_builder.js index bcc331ec..3d19a933 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -500,10 +500,10 @@ describe('TransactionBuilder', function () { '194a565cd6aa4cc38b8eaffa343402201c5b4b61d73fa38e49c1ee68cc0e6dfd2f5dae453dd86eb142e87a' + '0bafb1bc8401210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000' var txb = TransactionBuilder.fromTransaction(Transaction.fromHex(rawtx)) - txb.inputs[0].value = 81530 - txb.inputs[1].value = 81530 - txb.inputs[2].value = 88920 - txb.inputs[3].value = 88920 + txb.inputs[0].value = 241530 + txb.inputs[1].value = 241530 + txb.inputs[2].value = 241530 + txb.inputs[3].value = 241530 assert.throws(function () { txb.build()