|
@ -6,6 +6,20 @@ var opcodes = require('./opcodes.json') |
|
|
var typeforce = require('typeforce') |
|
|
var typeforce = require('typeforce') |
|
|
var types = require('./types') |
|
|
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 () { |
|
|
function Transaction () { |
|
|
this.version = 1 |
|
|
this.version = 1 |
|
|
this.locktime = 0 |
|
|
this.locktime = 0 |
|
@ -18,6 +32,18 @@ Transaction.SIGHASH_ALL = 0x01 |
|
|
Transaction.SIGHASH_NONE = 0x02 |
|
|
Transaction.SIGHASH_NONE = 0x02 |
|
|
Transaction.SIGHASH_SINGLE = 0x03 |
|
|
Transaction.SIGHASH_SINGLE = 0x03 |
|
|
Transaction.SIGHASH_ANYONECANPAY = 0x80 |
|
|
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) { |
|
|
Transaction.fromBuffer = function (buffer, __noStrict) { |
|
|
var offset = 0 |
|
|
var offset = 0 |
|
@ -50,20 +76,38 @@ Transaction.fromBuffer = function (buffer, __noStrict) { |
|
|
return vi.number |
|
|
return vi.number |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function readScript () { |
|
|
function readVarSlice () { |
|
|
return readSlice(readVarInt()) |
|
|
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() |
|
|
var tx = new Transaction() |
|
|
tx.version = readInt32() |
|
|
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() |
|
|
var vinLen = readVarInt() |
|
|
for (var i = 0; i < vinLen; ++i) { |
|
|
for (var i = 0; i < vinLen; ++i) { |
|
|
tx.ins.push({ |
|
|
tx.ins.push({ |
|
|
hash: readSlice(32), |
|
|
hash: readSlice(32), |
|
|
index: readUInt32(), |
|
|
index: readUInt32(), |
|
|
script: readScript(), |
|
|
script: readVarSlice(), |
|
|
sequence: readUInt32() |
|
|
sequence: readUInt32(), |
|
|
|
|
|
witness: EMPTY_WITNESS |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -71,10 +115,16 @@ Transaction.fromBuffer = function (buffer, __noStrict) { |
|
|
for (i = 0; i < voutLen; ++i) { |
|
|
for (i = 0; i < voutLen; ++i) { |
|
|
tx.outs.push({ |
|
|
tx.outs.push({ |
|
|
value: readUInt64(), |
|
|
value: readUInt64(), |
|
|
script: readScript() |
|
|
script: readVarSlice() |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (hasWitnesses) { |
|
|
|
|
|
for (i = 0; i < vinLen; ++i) { |
|
|
|
|
|
tx.ins[i].witness = readVector() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
tx.locktime = readUInt32() |
|
|
tx.locktime = readUInt32() |
|
|
|
|
|
|
|
|
if (__noStrict) return tx |
|
|
if (__noStrict) return tx |
|
@ -99,8 +149,6 @@ Transaction.prototype.isCoinbase = function () { |
|
|
return this.ins.length === 1 && Transaction.isCoinbaseHash(this.ins[0].hash) |
|
|
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) { |
|
|
Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { |
|
|
typeforce(types.tuple( |
|
|
typeforce(types.tuple( |
|
|
types.Hash256bit, |
|
|
types.Hash256bit, |
|
@ -118,7 +166,8 @@ Transaction.prototype.addInput = function (hash, index, sequence, scriptSig) { |
|
|
hash: hash, |
|
|
hash: hash, |
|
|
index: index, |
|
|
index: index, |
|
|
script: scriptSig || EMPTY_SCRIPT, |
|
|
script: scriptSig || EMPTY_SCRIPT, |
|
|
sequence: sequence |
|
|
sequence: sequence, |
|
|
|
|
|
witness: EMPTY_WITNESS |
|
|
}) - 1) |
|
|
}) - 1) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -132,19 +181,22 @@ Transaction.prototype.addOutput = function (scriptPubKey, value) { |
|
|
}) - 1) |
|
|
}) - 1) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
Transaction.prototype.byteLength = function () { |
|
|
Transaction.prototype._hasWitnesses = function () { |
|
|
function scriptSize (someScript) { |
|
|
return this.ins.some(function (x) { |
|
|
var length = someScript.length |
|
|
return x.witness !== EMPTY_WITNESS |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return bufferutils.varIntSize(length) + length |
|
|
Transaction.prototype.byteLength = function () { |
|
|
} |
|
|
var hasWitnesses = this._hasWitnesses() |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
8 + |
|
|
(hasWitnesses ? 10 : 8) + |
|
|
bufferutils.varIntSize(this.ins.length) + |
|
|
bufferutils.varIntSize(this.ins.length) + |
|
|
bufferutils.varIntSize(this.outs.length) + |
|
|
bufferutils.varIntSize(this.outs.length) + |
|
|
this.ins.reduce(function (sum, input) { return sum + 40 + scriptSize(input.script) }, 0) + |
|
|
this.ins.reduce(function (sum, input) { return sum + 40 + varSliceSize(input.script) }, 0) + |
|
|
this.outs.reduce(function (sum, output) { return sum + 8 + scriptSize(output.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, |
|
|
hash: txIn.hash, |
|
|
index: txIn.index, |
|
|
index: txIn.index, |
|
|
script: txIn.script, |
|
|
script: txIn.script, |
|
|
sequence: txIn.sequence |
|
|
sequence: txIn.sequence, |
|
|
|
|
|
witness: txIn.witness |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
@ -172,13 +225,6 @@ Transaction.prototype.clone = function () { |
|
|
return newTx |
|
|
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. |
|
|
* Hash transaction for signing a specific input. |
|
|
* |
|
|
* |
|
@ -252,6 +298,92 @@ Transaction.prototype.hashForSignature = function (inIndex, prevOutScript, hashT |
|
|
return bcrypto.hash256(buffer) |
|
|
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 () { |
|
|
Transaction.prototype.getHash = function () { |
|
|
return bcrypto.hash256(this.toBuffer()) |
|
|
return bcrypto.hash256(this.toBuffer()) |
|
|
} |
|
|
} |
|
@ -266,19 +398,29 @@ Transaction.prototype.toBuffer = function (buffer, initialOffset) { |
|
|
|
|
|
|
|
|
var offset = initialOffset || 0 |
|
|
var offset = initialOffset || 0 |
|
|
function writeSlice (slice) { offset += slice.copy(buffer, offset) } |
|
|
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 writeUInt32 (i) { offset = buffer.writeUInt32LE(i, offset) } |
|
|
function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } |
|
|
function writeInt32 (i) { offset = buffer.writeInt32LE(i, offset) } |
|
|
function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } |
|
|
function writeUInt64 (i) { offset = bufferutils.writeUInt64LE(buffer, i, offset) } |
|
|
function writeVarInt (i) { offset += bufferutils.writeVarInt(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) |
|
|
writeInt32(this.version) |
|
|
|
|
|
|
|
|
|
|
|
var hasWitnesses = this._hasWitnesses() |
|
|
|
|
|
|
|
|
|
|
|
if (hasWitnesses) { |
|
|
|
|
|
writeUInt8(Transaction.ADVANCED_TRANSACTION_MARKER) |
|
|
|
|
|
writeUInt8(Transaction.ADVANCED_TRANSACTION_FLAG) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
writeVarInt(this.ins.length) |
|
|
writeVarInt(this.ins.length) |
|
|
|
|
|
|
|
|
this.ins.forEach(function (txIn) { |
|
|
this.ins.forEach(function (txIn) { |
|
|
writeSlice(txIn.hash) |
|
|
writeSlice(txIn.hash) |
|
|
writeUInt32(txIn.index) |
|
|
writeUInt32(txIn.index) |
|
|
writeVarInt(txIn.script.length) |
|
|
writeVarSlice(txIn.script) |
|
|
writeSlice(txIn.script) |
|
|
|
|
|
writeUInt32(txIn.sequence) |
|
|
writeUInt32(txIn.sequence) |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
@ -290,15 +432,19 @@ Transaction.prototype.toBuffer = function (buffer, initialOffset) { |
|
|
writeSlice(txOut.valueBuffer) |
|
|
writeSlice(txOut.valueBuffer) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
writeVarInt(txOut.script.length) |
|
|
writeVarSlice(txOut.script) |
|
|
writeSlice(txOut.script) |
|
|
|
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
if (hasWitnesses) { |
|
|
|
|
|
this.ins.forEach(function (input) { |
|
|
|
|
|
writeVector(input.witness) |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
writeUInt32(this.locktime) |
|
|
writeUInt32(this.locktime) |
|
|
|
|
|
|
|
|
// avoid slicing unless necessary
|
|
|
// avoid slicing unless necessary
|
|
|
if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) |
|
|
if (initialOffset !== undefined) return buffer.slice(initialOffset, offset) |
|
|
|
|
|
|
|
|
return buffer |
|
|
return buffer |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -312,4 +458,10 @@ Transaction.prototype.setInputScript = function (index, scriptSig) { |
|
|
this.ins[index].script = 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 |
|
|
module.exports = Transaction |
|
|