diff --git a/src/block.js b/src/block.js index c545996..33a2cb9 100644 --- a/src/block.js +++ b/src/block.js @@ -1,8 +1,8 @@ var Buffer = require('safe-buffer').Buffer var bcrypto = require('./crypto') var fastMerkleRoot = require('merkle-lib/fastRoot') -var typeforce = require('typeforce') -var types = require('./types') +// var typeforce = require('typeforce') +// var types = require('./types') var varuint = require('varuint-bitcoin') var Transaction = require('./transaction') @@ -55,7 +55,7 @@ Block.fromBuffer = function (buffer) { function readTransaction () { var tx = Transaction.fromBuffer(buffer.slice(offset), true) - offset += tx.byteLength() + offset += tx.byteLength return tx } @@ -74,7 +74,7 @@ Block.prototype.byteLength = function (headersOnly) { if (headersOnly || !this.transactions) return 80 return 80 + varuint.encodingLength(this.transactions.length) + this.transactions.reduce(function (a, x) { - return a + x.byteLength() + return a + x.byteLength }, 0) } @@ -129,7 +129,7 @@ Block.prototype.toBuffer = function (headersOnly) { offset += varuint.encode.bytes this.transactions.forEach(function (tx) { - var txSize = tx.byteLength() // TODO: extract from toBuffer? + var txSize = tx.byteLength // TODO: extract from toBuffer? tx.toBuffer(buffer, offset) offset += txSize }) @@ -150,11 +150,11 @@ Block.calculateTarget = function (bits) { } Block.calculateMerkleRoot = function (transactions) { - typeforce([{ getHash: types.Function }], transactions) + // typeforce([{ getHash: types.Function }], transactions) ToDo: What should this be? if (transactions.length === 0) throw TypeError('Cannot compute merkle root for zero transactions') var hashes = transactions.map(function (transaction) { - return transaction.getHash() + return transaction.hash }) return fastMerkleRoot(hashes, bcrypto.hash256) diff --git a/src/transaction.js b/src/transaction.js index 86cccea..c891848 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -1,108 +1,454 @@ -var Buffer = require('safe-buffer').Buffer -var bcrypto = require('./crypto') -var bscript = require('./script') -var bufferutils = require('./bufferutils') -var opcodes = require('bitcoin-ops') -var typeforce = require('typeforce') -var types = require('./types') -var varuint = require('varuint-bitcoin') +const Buffer = require('safe-buffer').Buffer +const bcrypto = require('./crypto') +const bscript = require('./script') +const bufferutils = require('./bufferutils') +const opcodes = require('bitcoin-ops') +const typeforce = require('typeforce') +const types = require('./types') +const varuint = require('varuint-bitcoin') -function varSliceSize (someScript) { - var length = someScript.length - - return varuint.encodingLength(length) + length -} - -function vectorSize (someVector) { - var length = someVector.length - - return varuint.encodingLength(length) + someVector.reduce(function (sum, witness) { - return sum + varSliceSize(witness) - }, 0) -} - -function Transaction () { - this.version = 1 - this.locktime = 0 - this.ins = [] - this.outs = [] -} - -Transaction.DEFAULT_SEQUENCE = 0xffffffff -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 = Buffer.allocUnsafe(0) -var EMPTY_WITNESS = [] -var ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') -var ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') -var VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex') -var BLANK_OUTPUT = { +const EMPTY_SCRIPT = Buffer.allocUnsafe(0) +const EMPTY_WITNESS = [] +const ZERO = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex') +const ONE = Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') +const VALUE_UINT64_MAX = Buffer.from('ffffffffffffffff', 'hex') +const BLANK_OUTPUT = { script: EMPTY_SCRIPT, valueBuffer: VALUE_UINT64_MAX } -Transaction.fromBuffer = function (buffer, __noStrict) { - var offset = 0 - function readSlice (n) { +const varSliceSize = (someScript) => { + const length = someScript.length + + return varuint.encodingLength(length) + length +} + +const vectorSize = (someVector) => { + const length = someVector.length + + return varuint.encodingLength(length) + someVector.reduce((sum, witness) => + sum + varSliceSize(witness) + , 0) +} + +class Transaction { + constructor () { + this.version = 1 + this.locktime = 0 + this.ins = [] + this.outs = [] + } + + static get DEFAULT_SEQUENCE () { return 0xffffffff } + static get SIGHASH_ALL () { return 0x01 } + static get SIGHASH_NONE () { return 0x02 } + static get SIGHASH_SINGLE () { return 0x03 } + static get SIGHASH_ANYONECANPAY () { return 0x80 } + static get ADVANCED_TRANSACTION_MARKER () { return 0x00 } + static get ADVANCED_TRANSACTION_FLAG () { return 0x01 } + + get isCoinbase () { + return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) + } + + get hasWitnesses () { + return this.ins.some((x) => + x.witness.length !== 0 + ) + } + + get weight () { + const base = this.__byteLength(false) + const total = this.__byteLength(true) + return base * 3 + total + } + + get virtualSize () { + return Math.ceil(this.weight / 4) + } + + get byteLength () { + return this.__byteLength(true) + } + + get hash () { + return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) + } + + get id () { + // transaction hash's are displayed in reverse order + return this.hash.reverse().toString('hex') + } + + get buffer () { + return this.__toBuffer(undefined, undefined, true) + } + + get hex () { + return this.buffer.toString('hex') + } + + addInput (hash, index, sequence, scriptSig) { + typeforce(types.tuple( + types.Hash256bit, + types.UInt32, + types.maybe(types.UInt32), + types.maybe(types.Buffer) + ), arguments) + + if (types.Null(sequence)) { + sequence = Transaction.DEFAULT_SEQUENCE + } + + // Add the input and return the input's index + return (this.ins.push({ + hash: hash, + index: index, + script: scriptSig || EMPTY_SCRIPT, + sequence: sequence, + witness: EMPTY_WITNESS + }) - 1) + } + + addOutput (scriptPubKey, value) { + typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) + + // Add the output and return the output's index + return (this.outs.push({ + script: scriptPubKey, + value: value + }) - 1) + } + + __byteLength (__allowWitness) { + const hasWitnesses = __allowWitness && this.hasWitnesses + + return ( + (hasWitnesses ? 10 : 8) + + varuint.encodingLength(this.ins.length) + + varuint.encodingLength(this.outs.length) + + this.ins.reduce((sum, input) => sum + 40 + varSliceSize(input.script), 0) + + this.outs.reduce((sum, output) => sum + 8 + varSliceSize(output.script), 0) + + (hasWitnesses ? this.ins.reduce((sum, input) => sum + vectorSize(input.witness), 0) : 0) + ) + } + + clone () { + const newTx = new Transaction() + newTx.version = this.version + newTx.locktime = this.locktime + + newTx.ins = this.ins.map((txIn) => + ({ + hash: txIn.hash, + index: txIn.index, + script: txIn.script, + sequence: txIn.sequence, + witness: txIn.witness + }) + ) + + newTx.outs = this.outs.map((txOut) => + ({ + script: txOut.script, + value: txOut.value + }) + ) + + return newTx + } + + /** + * Hash transaction for signing a specific input. + * + * Bitcoin uses a different hash for each signed transaction input. + * This method copies the transaction, makes the necessary changes based on the + * hashType, and then hashes the result. + * This hash can then be used to sign the provided transaction input. + */ + hashForSignature (inIndex, prevOutScript, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) + + // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 + if (inIndex >= this.ins.length) return ONE + + // ignore OP_CODESEPARATOR + const ourScript = bscript.compile(bscript.decompile(prevOutScript).filter((x) => + x !== opcodes.OP_CODESEPARATOR + )) + + const txTmp = this.clone() + + // SIGHASH_NONE: ignore all outputs? (wildcard payee) + if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { + txTmp.outs = [] + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, i) => { + if (i === inIndex) return + + input.sequence = 0 + }) + + // 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 + + // truncate outputs after + txTmp.outs.length = inIndex + 1 + + // "blank" outputs before + for (let i = 0; i < inIndex; i++) { + txTmp.outs[i] = BLANK_OUTPUT + } + + // ignore sequence numbers (except at inIndex) + txTmp.ins.forEach((input, y) => { + if (y === inIndex) return + + input.sequence = 0 + }) + } + + // SIGHASH_ANYONECANPAY: ignore inputs entirely? + if (hashType & Transaction.SIGHASH_ANYONECANPAY) { + txTmp.ins = [txTmp.ins[inIndex]] + txTmp.ins[0].script = ourScript + + // SIGHASH_ALL: only ignore input scripts + } else { + // "blank" others input scripts + txTmp.ins.forEach((input) => { input.script = EMPTY_SCRIPT }) + txTmp.ins[inIndex].script = ourScript + } + + // serialize and hash + const buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) + buffer.writeInt32LE(hashType, buffer.length - 4) + txTmp.__toBuffer(buffer, 0, false) + + return bcrypto.hash256(buffer) + } + + hashForWitnessV0 (inIndex, prevOutScript, value, hashType) { + typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) + + let tbuffer, toffset + const writeSlice = (slice) => { toffset += slice.copy(tbuffer, toffset) } + const writeUInt32 = (i) => { toffset = tbuffer.writeUInt32LE(i, toffset) } + const writeUInt64 = (i) => { toffset = bufferutils.writeUInt64LE(tbuffer, i, toffset) } + const writeVarInt = (i) => { + varuint.encode(i, tbuffer, toffset) + toffset += varuint.encode.bytes + } + const writeVarSlice = (slice) => { writeVarInt(slice.length); writeSlice(slice) } + + let hashOutputs = ZERO + let hashPrevouts = ZERO + let hashSequence = ZERO + + if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { + tbuffer = Buffer.allocUnsafe(36 * this.ins.length) + toffset = 0 + + this.ins.forEach((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 = Buffer.allocUnsafe(4 * this.ins.length) + toffset = 0 + + this.ins.forEach((txIn) => { + writeUInt32(txIn.sequence) + }) + + hashSequence = bcrypto.hash256(tbuffer) + } + + if ((hashType & 0x1f) !== Transaction.SIGHASH_SINGLE && + (hashType & 0x1f) !== Transaction.SIGHASH_NONE) { + const txOutsSize = this.outs.reduce((sum, output) => + sum + 8 + varSliceSize(output.script) + , 0) + + tbuffer = Buffer.allocUnsafe(txOutsSize) + toffset = 0 + + this.outs.forEach((out) => { + writeUInt64(out.value) + writeVarSlice(out.script) + }) + + hashOutputs = bcrypto.hash256(tbuffer) + } else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE && inIndex < this.outs.length) { + const output = this.outs[inIndex] + + tbuffer = Buffer.allocUnsafe(8 + varSliceSize(output.script)) + toffset = 0 + writeUInt64(output.value) + writeVarSlice(output.script) + + hashOutputs = bcrypto.hash256(tbuffer) + } + + tbuffer = Buffer.allocUnsafe(156 + varSliceSize(prevOutScript)) + toffset = 0 + + const input = this.ins[inIndex] + writeUInt32(this.version) + writeSlice(hashPrevouts) + writeSlice(hashSequence) + writeSlice(input.hash) + writeUInt32(input.index) + writeVarSlice(prevOutScript) + writeUInt64(value) + writeUInt32(input.sequence) + writeSlice(hashOutputs) + writeUInt32(this.locktime) + writeUInt32(hashType) + return bcrypto.hash256(tbuffer) + } + + toBuffer (buffer, initialOffset) { + return this.__toBuffer(buffer, initialOffset, true) + } + + __toBuffer (buffer, initialOffset, __allowWitness) { + if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) + + let offset = initialOffset || 0 + const writeSlice = (slice) => { offset += slice.copy(buffer, offset) } + const writeUInt8 = (i) => { offset = buffer.writeUInt8(i, offset) } + const writeUInt32 = (i) => { offset = buffer.writeUInt32LE(i, offset) } + const writeInt32 = (i) => { offset = buffer.writeInt32LE(i, offset) } + const writeUInt64 = (i) => { offset = bufferutils.writeUInt64LE(buffer, i, offset) } + const writeVarInt = (i) => { + varuint.encode(i, buffer, offset) + offset += varuint.encode.bytes + } + const writeVarSlice = (slice) => { writeVarInt(slice.length); writeSlice(slice) } + const writeVector = (vector) => { writeVarInt(vector.length); vector.forEach(writeVarSlice) } + + writeInt32(this.version) + + const hasWitnesses = __allowWitness && this.hasWitnesses + + if (hasWitnesses) { + writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) + writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) + } + + writeVarInt(this.ins.length) + + this.ins.forEach((txIn) => { + writeSlice(txIn.hash) + writeUInt32(txIn.index) + writeVarSlice(txIn.script) + writeUInt32(txIn.sequence) + }) + + writeVarInt(this.outs.length) + this.outs.forEach((txOut) => { + if (!txOut.valueBuffer) { + writeUInt64(txOut.value) + } else { + writeSlice(txOut.valueBuffer) + } + + writeVarSlice(txOut.script) + }) + + if (hasWitnesses) { + this.ins.forEach((input) => { + writeVector(input.witness) + }) + } + + writeUInt32(this.locktime) + + // avoid slicing unless necessary + if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) + return buffer + } + + setInputScript (index, scriptSig) { + typeforce(types.tuple(types.Number, types.Buffer), arguments) + + this.ins[index].script = scriptSig + } + + setWitness (index, witness) { + typeforce(types.tuple(types.Number, [types.Buffer]), arguments) + + this.ins[index].witness = witness + } +} + +Transaction.fromBuffer = (buffer, __noStrict) => { + let offset = 0 + const readSlice = (n) => { offset += n return buffer.slice(offset - n, offset) } - function readUInt32 () { - var i = buffer.readUInt32LE(offset) + const readUInt32 = () => { + const i = buffer.readUInt32LE(offset) offset += 4 return i } - function readInt32 () { - var i = buffer.readInt32LE(offset) + const readInt32 = () => { + const i = buffer.readInt32LE(offset) offset += 4 return i } - function readUInt64 () { - var i = bufferutils.readUInt64LE(buffer, offset) + const readUInt64 = () => { + const i = bufferutils.readUInt64LE(buffer, offset) offset += 8 return i } - function readVarInt () { - var vi = varuint.decode(buffer, offset) + const readVarInt = () => { + const vi = varuint.decode(buffer, offset) offset += varuint.decode.bytes return vi } - function readVarSlice () { + const readVarSlice = () => { return readSlice(readVarInt()) } - function readVector () { - var count = readVarInt() - var vector = [] - for (var i = 0; i < count; i++) vector.push(readVarSlice()) + const readVector = () => { + const count = readVarInt() + const vector = [] + for (let i = 0; i < count; i++) vector.push(readVarSlice()) return vector } - var tx = new Transaction() + const tx = new Transaction() tx.version = readInt32() - var marker = buffer.readUInt8(offset) - var flag = buffer.readUInt8(offset + 1) + const marker = buffer.readUInt8(offset) + const flag = buffer.readUInt8(offset + 1) - var hasWitnesses = false + let 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) { + const vinLen = readVarInt() + for (let i = 0; i < vinLen; ++i) { tx.ins.push({ hash: readSlice(32), index: readUInt32(), @@ -112,8 +458,8 @@ Transaction.fromBuffer = function (buffer, __noStrict) { }) } - var voutLen = readVarInt() - for (i = 0; i < voutLen; ++i) { + const voutLen = readVarInt() + for (let i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), script: readVarSlice() @@ -121,12 +467,12 @@ Transaction.fromBuffer = function (buffer, __noStrict) { } if (hasWitnesses) { - for (i = 0; i < vinLen; ++i) { + for (let i = 0; i < vinLen; ++i) { tx.ins[i].witness = readVector() } // was this pointless? - if (!tx.hasWitnesses()) throw new Error('Transaction has superfluous witness data') + if (!tx.hasWitnesses) throw new Error('Transaction has superfluous witness data') } tx.locktime = readUInt32() @@ -137,356 +483,16 @@ Transaction.fromBuffer = function (buffer, __noStrict) { return tx } -Transaction.fromHex = function (hex) { +Transaction.fromHex = (hex) => { return Transaction.fromBuffer(Buffer.from(hex, 'hex')) } -Transaction.isCoinbaseHash = function (buffer) { +Transaction.isCoinbaseHash = (buffer) => { typeforce(types.Hash256bit, buffer) - for (var i = 0; i < 32; ++i) { + for (let i = 0; i < 32; ++i) { if (buffer[i] !== 0) return false } return true } -Transaction.prototype.isCoinbase = function () { - return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) -} - -Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { - typeforce(types.tuple( - types.Hash256bit, - types.UInt32, - types.maybe(types.UInt32), - types.maybe(types.Buffer) - ), arguments) - - if (types.Null(sequence)) { - sequence = Transaction.DEFAULT_SEQUENCE - } - - // Add the input and return the input's index - return (this.ins.push({ - hash: hash, - index: index, - script: scriptSig || EMPTY_SCRIPT, - sequence: sequence, - witness: EMPTY_WITNESS - }) - 1) -} - -Transaction.prototype.addOutput = function (scriptPubKey, value) { - typeforce(types.tuple(types.Buffer, types.Satoshi), arguments) - - // Add the output and return the output's index - return (this.outs.push({ - script: scriptPubKey, - value: value - }) - 1) -} - -Transaction.prototype.hasWitnesses = function () { - return this.ins.some(function (x) { - return x.witness.length !== 0 - }) -} - -Transaction.prototype.weight = function () { - var base = this.__byteLength(false) - var total = this.__byteLength(true) - return base * 3 + total -} - -Transaction.prototype.virtualSize = function () { - return Math.ceil(this.weight() / 4) -} - -Transaction.prototype.byteLength = function () { - return this.__byteLength(true) -} - -Transaction.prototype.__byteLength = function (__allowWitness) { - var hasWitnesses = __allowWitness && this.hasWitnesses() - - return ( - (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) - ) -} - -Transaction.prototype.clone = function () { - var newTx = new Transaction() - newTx.version = this.version - newTx.locktime = this.locktime - - newTx.ins = this.ins.map(function (txIn) { - return { - hash: txIn.hash, - index: txIn.index, - script: txIn.script, - sequence: txIn.sequence, - witness: txIn.witness - } - }) - - newTx.outs = this.outs.map(function (txOut) { - return { - script: txOut.script, - value: txOut.value - } - }) - - return newTx -} - -/** - * Hash transaction for signing a specific input. - * - * Bitcoin uses a different hash for each signed transaction input. - * This method copies the transaction, makes the necessary changes based on the - * hashType, and then hashes the result. - * This hash can then be used to sign the provided transaction input. - */ -Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number), arguments) - - // https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L29 - if (inIndex >= this.ins.length) return ONE - - // ignore OP_CODESEPARATOR - var ourScript = bscript.compile(bscript.decompile(prevOutScript).filter(function (x) { - return x !== opcodes.OP_CODESEPARATOR - })) - - var txTmp = this.clone() - - // SIGHASH_NONE: ignore all outputs? (wildcard payee) - if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) { - txTmp.outs = [] - - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach(function (input, i) { - if (i === inIndex) return - - input.sequence = 0 - }) - - // 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 - - // truncate outputs after - txTmp.outs.length = inIndex + 1 - - // "blank" outputs before - for (var i = 0; i < inIndex; i++) { - txTmp.outs[i] = BLANK_OUTPUT - } - - // ignore sequence numbers (except at inIndex) - txTmp.ins.forEach(function (input, y) { - if (y === inIndex) return - - input.sequence = 0 - }) - } - - // SIGHASH_ANYONECANPAY: ignore inputs entirely? - if (hashType & Transaction.SIGHASH_ANYONECANPAY) { - txTmp.ins = [txTmp.ins[inIndex]] - txTmp.ins[0].script = ourScript - - // SIGHASH_ALL: only ignore input scripts - } else { - // "blank" others input scripts - txTmp.ins.forEach(function (input) { input.script = EMPTY_SCRIPT }) - txTmp.ins[inIndex].script = ourScript - } - - // serialize and hash - var buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4) - buffer.writeInt32LE(hashType, buffer.length - 4) - txTmp.__toBuffer(buffer, 0, false) - - return bcrypto.hash256(buffer) -} - -Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value, hashType) { - typeforce(types.tuple(types.UInt32, types.Buffer, types.Satoshi, types.UInt32), arguments) - - 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) { - varuint.encode(i, tbuffer, toffset) - toffset += varuint.encode.bytes - } - function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } - - var hashOutputs = ZERO - var hashPrevouts = ZERO - var hashSequence = ZERO - - if (!(hashType & Transaction.SIGHASH_ANYONECANPAY)) { - tbuffer = Buffer.allocUnsafe(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 = Buffer.allocUnsafe(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 = Buffer.allocUnsafe(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 = Buffer.allocUnsafe(8 + varSliceSize(output.script)) - toffset = 0 - writeUInt64(output.value) - writeVarSlice(output.script) - - hashOutputs = bcrypto.hash256(tbuffer) - } - - tbuffer = Buffer.allocUnsafe(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(value) - writeUInt32(input.sequence) - writeSlice(hashOutputs) - writeUInt32(this.locktime) - writeUInt32(hashType) - return bcrypto.hash256(tbuffer) -} - -Transaction.prototype.getHash = function () { - return bcrypto.hash256(this.__toBuffer(undefined, undefined, false)) -} - -Transaction.prototype.getId = function () { - // transaction hash's are displayed in reverse order - return this.getHash().reverse().toString('hex') -} - -Transaction.prototype.toBuffer = function (buffer, initialOffset) { - return this.__toBuffer(buffer, initialOffset, true) -} - -Transaction.prototype.__toBuffer = function (buffer, initialOffset, __allowWitness) { - if (!buffer) buffer = Buffer.allocUnsafe(this.__byteLength(__allowWitness)) - - 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) { - varuint.encode(i, buffer, offset) - offset += varuint.encode.bytes - } - function writeVarSlice (slice) { writeVarInt(slice.length); writeSlice(slice) } - function writeVector (vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice) } - - writeInt32(this.version) - - var hasWitnesses = __allowWitness && 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) - writeVarSlice(txIn.script) - writeUInt32(txIn.sequence) - }) - - writeVarInt(this.outs.length) - this.outs.forEach(function (txOut) { - if (!txOut.valueBuffer) { - writeUInt64(txOut.value) - } else { - writeSlice(txOut.valueBuffer) - } - - 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 -} - -Transaction.prototype.toHex = function () { - return this.toBuffer().toString('hex') -} - -Transaction.prototype.setInputScript = function (index, scriptSig) { - typeforce(types.tuple(types.Number, types.Buffer), arguments) - - 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/src/transaction_builder.js b/src/transaction_builder.js index 1f1f490..52a0065 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -547,7 +547,7 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu prevOutScript = txOut.script value = txOut.value - txHash = txHash.getHash() + txHash = txHash.hash } return this.__addInputUnsafe(txHash, vout, { @@ -648,7 +648,7 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) { if (!allowIncomplete) { // do not rely on this, its merely a last resort - if (this.__overMaximumFees(tx.virtualSize())) { + if (this.__overMaximumFees(tx.virtualSize)) { throw new Error('Transaction has absurd fees') } } diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index b042ae1..f82e0d2 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -184,7 +184,7 @@ describe('Bitcoin-core', function () { it('should hash ' + txHex.slice(0, 40) + '... (' + hashTypeName + ')', function () { var transaction = bitcoin.Transaction.fromHex(txHex) - assert.strictEqual(transaction.toHex(), txHex) + assert.strictEqual(transaction.hex, txHex) var script = Buffer.from(scriptHex, 'hex') var scriptChunks = bitcoin.script.decompile(script) diff --git a/test/integration/cltv.js b/test/integration/cltv.js index 547ac79..7f9d240 100644 --- a/test/integration/cltv.js +++ b/test/integration/cltv.js @@ -61,11 +61,11 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { ], redeemScript) tx.setInputScript(0, redeemScriptSig) - regtestUtils.broadcast(tx.toHex(), function (err) { + regtestUtils.broadcast(tx.hex, function (err) { if (err) return done(err) regtestUtils.verify({ - txId: tx.getId(), + txId: tx.id, address: regtestUtils.RANDOM_ADDRESS, vout: 0, value: 7e4 @@ -106,7 +106,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { tx.setInputScript(0, redeemScriptSig) // TODO: test that it failures _prior_ to expiry, unfortunately, race conditions when run concurrently -// regtestUtils.broadcast(tx.toHex(), function (err) { +// regtestUtils.broadcast(tx.hex, function (err) { // // fails before the expiry // assert.throws(function () { // if (err) throw err @@ -116,11 +116,11 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { regtestUtils.mine(51, function (err) { if (err) return done(err) - regtestUtils.broadcast(tx.toHex(), function (err) { + regtestUtils.broadcast(tx.hex, function (err) { if (err) return done(err) regtestUtils.verify({ - txId: tx.getId(), + txId: tx.id, address: regtestUtils.RANDOM_ADDRESS, vout: 0, value: 7e4 @@ -161,11 +161,11 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { ], redeemScript) tx.setInputScript(0, redeemScriptSig) - regtestUtils.broadcast(tx.toHex(), function (err) { + regtestUtils.broadcast(tx.hex, function (err) { if (err) return done(err) regtestUtils.verify({ - txId: tx.getId(), + txId: tx.id, address: regtestUtils.RANDOM_ADDRESS, vout: 0, value: 8e4 @@ -203,7 +203,7 @@ describe('bitcoinjs-lib (transactions w/ CLTV)', function () { ], redeemScript) tx.setInputScript(0, redeemScriptSig) - regtestUtils.broadcast(tx.toHex(), function (err) { + regtestUtils.broadcast(tx.hex, function (err) { assert.throws(function () { if (err) throw err }, /Error: 64: non-final/) diff --git a/test/integration/transactions.js b/test/integration/transactions.js index fb3fd03..6ff90d0 100644 --- a/test/integration/transactions.js +++ b/test/integration/transactions.js @@ -22,7 +22,7 @@ describe('bitcoinjs-lib (transactions)', function () { txb.sign(0, alice) // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below - assert.strictEqual(txb.build().toHex(), '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000') + assert.strictEqual(txb.build().hex, '01000000019d344070eac3fe6e394a16d06d7704a7d5c0a10eb2a2c16bc98842b7cc20d561000000006b48304502210088828c0bdfcdca68d8ae0caeb6ec62cd3fd5f9b2191848edae33feb533df35d302202e0beadd35e17e7f83a733f5277028a9b453d525553e3f5d2d7a7aa8010a81d60121029f50f51d63b345039a290c94bffd3180c99ed659ff6ea6b1242bca47eb93b59fffffffff01e02e0000000000001976a91406afd46bcdfd22ef94ac122aa11f241244a37ecc88ac00000000') }) it('can create a 2-to-2 Transaction', function () { @@ -41,7 +41,7 @@ describe('bitcoinjs-lib (transactions)', function () { txb.sign(0, alice) // Alice signs her input, which was the first input (0th) // prepare for broadcast to the Bitcoin network, see "can broadcast a Transaction" below - assert.strictEqual(txb.build().toHex(), '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') + assert.strictEqual(txb.build().hex, '01000000024c94e48a870b85f41228d33cf25213dfcc8dd796e7211ed6b1f9a014809dbbb5060000006a473044022041450c258ce7cac7da97316bf2ea1ce66d88967c4df94f3e91f4c2a30f5d08cb02203674d516e6bb2b0afd084c3551614bd9cec3c2945231245e891b145f2d6951f0012103e05ce435e462ec503143305feb6c00e06a3ad52fbf939e85c65f3a765bb7baacffffffff3077d9de049574c3af9bc9c09a7c9db80f2d94caaf63988c9166249b955e867d000000006b483045022100aeb5f1332c79c446d3f906e4499b2e678500580a3f90329edf1ba502eec9402e022072c8b863f8c8d6c26f4c691ac9a6610aa4200edc697306648ee844cfbc089d7a012103df7940ee7cddd2f97763f67e1fb13488da3fbdd7f9c68ec5ef0864074745a289ffffffff0220bf0200000000001976a9147dd65592d0ab2fe0d0257d571abf032cd9db93dc88ac10980200000000001976a914c42e7ef92fdb603af844d064faad95db9bcdfd3d88ac00000000') }) it('can create (and broadcast via 3PBP) a typical Transaction', function (done) { @@ -70,7 +70,7 @@ describe('bitcoinjs-lib (transactions)', function () { txb.sign(1, alice2) // build and broadcast our RegTest network - regtestUtils.broadcast(txb.build().toHex(), done) + regtestUtils.broadcast(txb.build().hex, done) // to build and broadcast to the actual Bitcoin network, see https://github.com/bitcoinjs/bitcoinjs-lib/issues/839 }) }) @@ -94,7 +94,7 @@ describe('bitcoinjs-lib (transactions)', function () { txb.sign(0, keyPair) // build and broadcast to the RegTest network - regtestUtils.broadcast(txb.build().toHex(), done) + regtestUtils.broadcast(txb.build().hex, done) }) }) @@ -125,11 +125,11 @@ describe('bitcoinjs-lib (transactions)', function () { var tx = txb.build() // build and broadcast to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { + regtestUtils.broadcast(tx.hex, function (err) { if (err) return done(err) regtestUtils.verify({ - txId: tx.getId(), + txId: tx.id, address: regtestUtils.RANDOM_ADDRESS, vout: 0, value: 1e4 @@ -161,11 +161,11 @@ describe('bitcoinjs-lib (transactions)', function () { var tx = txb.build() // build and broadcast to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { + regtestUtils.broadcast(tx.hex, function (err) { if (err) return done(err) regtestUtils.verify({ - txId: tx.getId(), + txId: tx.id, address: regtestUtils.RANDOM_ADDRESS, vout: 0, value: 2e4 @@ -203,11 +203,11 @@ describe('bitcoinjs-lib (transactions)', function () { var tx = txb.build() // build and broadcast to the Bitcoin RegTest network - regtestUtils.broadcast(tx.toHex(), function (err) { + regtestUtils.broadcast(tx.hex, function (err) { if (err) return done(err) regtestUtils.verify({ - txId: tx.getId(), + txId: tx.id, address: regtestUtils.RANDOM_ADDRESS, vout: 0, value: 3e4 diff --git a/test/transaction.js b/test/transaction.js index 62c24d0..a30d431 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -55,14 +55,14 @@ describe('Transaction', function () { it('imports ' + f.description + ' (' + id + ')', function () { var actual = Transaction.fromHex(txHex) - assert.strictEqual(actual.toHex(), txHex) + assert.strictEqual(actual.hex, txHex) }) if (f.whex) { it('imports ' + f.description + ' (' + id + ') as witness', function () { var actual = Transaction.fromHex(f.whex) - assert.strictEqual(actual.toHex(), f.whex) + assert.strictEqual(actual.hex, f.whex) }) } } @@ -91,13 +91,13 @@ describe('Transaction', function () { fixtures.valid.forEach(function (f) { it('exports ' + f.description + ' (' + f.id + ')', function () { var actual = fromRaw(f.raw, true) - assert.strictEqual(actual.toHex(), f.hex) + assert.strictEqual(actual.hex, f.hex) }) if (f.whex) { it('exports ' + f.description + ' (' + f.id + ') as witness', function () { var wactual = fromRaw(f.raw) - assert.strictEqual(wactual.toHex(), f.whex) + assert.strictEqual(wactual.hex, f.whex) }) } }) @@ -105,7 +105,7 @@ describe('Transaction', function () { it('accepts target Buffer and offset parameters', function () { var f = fixtures.valid[0] var actual = fromRaw(f.raw) - var byteLength = actual.byteLength() + var byteLength = actual.byteLength var target = Buffer.alloc(byteLength * 2) var a = actual.toBuffer(target, 0) @@ -124,7 +124,7 @@ describe('Transaction', function () { describe('hasWitnesses', function () { fixtures.valid.forEach(function (f) { it('detects if the transaction has witnesses: ' + (f.whex ? 'true' : 'false'), function () { - assert.strictEqual(Transaction.fromHex(f.whex ? f.whex : f.hex).hasWitnesses(), !!f.whex) + assert.strictEqual(Transaction.fromHex(f.whex ? f.whex : f.hex).hasWitnesses, !!f.whex) }) }) }) @@ -134,7 +134,7 @@ describe('Transaction', function () { fixtures.valid.forEach(function (f) { var transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) - assert.strictEqual(transaction.virtualSize(), f.virtualSize) + assert.strictEqual(transaction.virtualSize, f.virtualSize) }) }) @@ -142,7 +142,7 @@ describe('Transaction', function () { fixtures.valid.forEach(function (f) { var transaction = Transaction.fromHex(f.whex ? f.whex : f.hex) - assert.strictEqual(transaction.weight(), f.weight) + assert.strictEqual(transaction.weight, f.weight) }) }) }) @@ -212,8 +212,8 @@ describe('Transaction', function () { it('should return the id for ' + f.id + '(' + f.description + ')', function () { var tx = Transaction.fromHex(f.whex || f.hex) - assert.strictEqual(tx.getHash().toString('hex'), f.hash) - assert.strictEqual(tx.getId(), f.id) + assert.strictEqual(tx.hash.toString('hex'), f.hash) + assert.strictEqual(tx.id, f.id) }) } @@ -225,7 +225,7 @@ describe('Transaction', function () { it('should return ' + f.coinbase + ' for ' + f.id + '(' + f.description + ')', function () { var tx = Transaction.fromHex(f.hex) - assert.strictEqual(tx.isCoinbase(), f.coinbase) + assert.strictEqual(tx.isCoinbase, f.coinbase) }) } diff --git a/test/transaction_builder.js b/test/transaction_builder.js index cc24571..00ab620 100644 --- a/test/transaction_builder.js +++ b/test/transaction_builder.js @@ -72,7 +72,7 @@ function construct (f, dontSign) { if (sign.stage) { var tx = txb.buildIncomplete() - assert.strictEqual(tx.toHex(), stages.shift()) + assert.strictEqual(tx.hex, stages.shift()) txb = TransactionBuilder.fromTransaction(tx, network) } }) @@ -101,7 +101,7 @@ describe('TransactionBuilder', function () { var txb = TransactionBuilder.fromTransaction(tx, network) var txAfter = f.incomplete ? txb.buildIncomplete() : txb.build() - assert.strictEqual(txAfter.toHex(), f.txHex) + assert.strictEqual(txAfter.hex, f.txHex) assert.strictEqual(txb.network, network) }) }) @@ -192,7 +192,7 @@ describe('TransactionBuilder', function () { assert.strictEqual(vin, 0) var txIn = txb.__tx.ins[0] - assert.deepEqual(txIn.hash, prevTx.getHash()) + assert.deepEqual(txIn.hash, prevTx.hash) assert.strictEqual(txIn.index, 1) assert.strictEqual(txIn.sequence, 54) assert.strictEqual(txb.__inputs[0].prevOutScript, scripts[1]) @@ -306,7 +306,7 @@ describe('TransactionBuilder', function () { txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1) txb.addOutput('1111111111111111111114oLvT2', 100000) txb.sign(0, keyPair) - assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000002c0930060201000201000121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') + assert.equal(txb.build().hex, '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000002c0930060201000201000121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000') }) fixtures.invalid.sign.forEach(function (f) { @@ -346,7 +346,7 @@ describe('TransactionBuilder', function () { var txb = construct(f) var tx = f.incomplete ? txb.buildIncomplete() : txb.build() - assert.strictEqual(tx.toHex(), f.txHex) + assert.strictEqual(tx.hex, f.txHex) }) }) @@ -481,7 +481,7 @@ describe('TransactionBuilder', function () { }) tx = txb.build() - assert.strictEqual(tx.toHex(), f.txHex) + assert.strictEqual(tx.hex, f.txHex) }) }) }) @@ -529,8 +529,8 @@ describe('TransactionBuilder', function () { // 2-of-2 signed only once var tx = txb.buildIncomplete() // Only input is segwit, so txid should be accurate with the final tx - assert.equal(tx.getId(), 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') - var txHex = tx.toHex() + assert.equal(tx.id, 'f15d0a65b21b4471405b21a099f8b18e1ae4d46d55efbd0f4766cf11ad6cb821') + var txHex = tx.hex var newTxb = TransactionBuilder.fromTransaction(Transaction.fromHex(txHex)) // input should have the key 'witness' set to true assert.equal(newTxb.__inputs[0].witness, true) @@ -552,7 +552,7 @@ describe('TransactionBuilder', function () { txb.sign(0, keyPair2, redeemScript) var tx2 = txb.build() - assert.equal(tx2.getId(), 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') + assert.equal(tx2.id, 'eab59618a564e361adef6d918bd792903c3d41bcf1220137364fb847880467f9') assert.equal(bscript.toASM(tx2.ins[0].script), 'OP_0 3045022100daf0f4f3339d9fbab42b098045c1e4958ee3b308f4ae17be80b63808558d0adb02202f07e3d1f79dc8da285ae0d7f68083d769c11f5621ebd9691d6b48c0d4283d7d01 3045022100a346c61738304eac5e7702188764d19cdf68f4466196729db096d6c87ce18cdd022018c0e8ad03054b0e7e235cda6bedecf35881d7aa7d94ff425a8ace7220f38af001 52410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a4104f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e67253ae') }) @@ -561,20 +561,20 @@ describe('TransactionBuilder', function () { txb.setVersion(1) txb.addInput('aa94ab02c182214f090e99a0d57021caffd0f195a81c24602b1028b130b63e31', 0) - var incomplete = txb.buildIncomplete().toHex() + var incomplete = txb.buildIncomplete().hex var keyPair = ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy') // sign, as expected txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) txb.sign(0, keyPair) - var txId = txb.build().getId() + var txId = txb.build().id assert.equal(txId, '54f097315acbaedb92a95455da3368eb45981cdae5ffbc387a9afc872c0f29b3') // and, repeat txb = TransactionBuilder.fromTransaction(Transaction.fromHex(incomplete)) txb.addOutput('1Gokm82v6DmtwKEB8AiVhm82hyFSsEvBDK', 15000) txb.sign(0, keyPair) - var txId2 = txb.build().getId() + var txId2 = txb.build().id assert.equal(txId, txId2) }) })