Merge pull request #363 from bitcoinjs/coinbase
Coinbase Transaction parsing
This commit is contained in:
commit
1da8297f3c
5 changed files with 76 additions and 94 deletions
35
src/block.js
35
src/block.js
|
@ -39,46 +39,17 @@ Block.fromBuffer = function(buffer) {
|
||||||
|
|
||||||
if (buffer.length === 80) return block
|
if (buffer.length === 80) return block
|
||||||
|
|
||||||
function readUInt64() {
|
|
||||||
var i = bufferutils.readUInt64LE(buffer, offset)
|
|
||||||
offset += 8
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
function readVarInt() {
|
function readVarInt() {
|
||||||
var vi = bufferutils.readVarInt(buffer, offset)
|
var vi = bufferutils.readVarInt(buffer, offset)
|
||||||
offset += vi.size
|
offset += vi.size
|
||||||
return vi.number
|
return vi.number
|
||||||
}
|
}
|
||||||
|
|
||||||
function readScript() {
|
// FIXME: poor performance
|
||||||
return Script.fromBuffer(readSlice(readVarInt()))
|
|
||||||
}
|
|
||||||
|
|
||||||
function readTransaction() {
|
function readTransaction() {
|
||||||
var tx = new Transaction()
|
var tx = Transaction.fromBuffer(buffer.slice(offset), true)
|
||||||
tx.version = readUInt32()
|
|
||||||
|
|
||||||
var vinLen = readVarInt()
|
|
||||||
for (var i = 0; i < vinLen; ++i) {
|
|
||||||
tx.ins.push({
|
|
||||||
hash: readSlice(32),
|
|
||||||
index: readUInt32(),
|
|
||||||
script: readScript(),
|
|
||||||
sequence: readUInt32()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var voutLen = readVarInt()
|
|
||||||
for (i = 0; i < voutLen; ++i) {
|
|
||||||
tx.outs.push({
|
|
||||||
value: readUInt64(),
|
|
||||||
script: readScript(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tx.locktime = readUInt32()
|
|
||||||
|
|
||||||
|
offset += tx.toBuffer().length
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ Transaction.SIGHASH_NONE = 0x02
|
||||||
Transaction.SIGHASH_SINGLE = 0x03
|
Transaction.SIGHASH_SINGLE = 0x03
|
||||||
Transaction.SIGHASH_ANYONECANPAY = 0x80
|
Transaction.SIGHASH_ANYONECANPAY = 0x80
|
||||||
|
|
||||||
Transaction.fromBuffer = function(buffer) {
|
Transaction.fromBuffer = function(buffer, __disableAssert) {
|
||||||
var offset = 0
|
var offset = 0
|
||||||
function readSlice(n) {
|
function readSlice(n) {
|
||||||
offset += n
|
offset += n
|
||||||
|
@ -51,29 +51,48 @@ Transaction.fromBuffer = function(buffer) {
|
||||||
return Script.fromBuffer(readSlice(readVarInt()))
|
return Script.fromBuffer(readSlice(readVarInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readGenerationScript() {
|
||||||
|
return new Script(readSlice(readVarInt()), [])
|
||||||
|
}
|
||||||
|
|
||||||
var tx = new Transaction()
|
var tx = new Transaction()
|
||||||
tx.version = readUInt32()
|
tx.version = readUInt32()
|
||||||
|
|
||||||
var vinLen = readVarInt()
|
var vinLen = readVarInt()
|
||||||
for (var i = 0; i < vinLen; ++i) {
|
for (var i = 0; i < vinLen; ++i) {
|
||||||
tx.ins.push({
|
var hash = readSlice(32)
|
||||||
hash: readSlice(32),
|
|
||||||
index: readUInt32(),
|
if (Transaction.isCoinbaseHash(hash)) {
|
||||||
script: readScript(),
|
tx.ins.push({
|
||||||
sequence: readUInt32()
|
hash: hash,
|
||||||
})
|
index: readUInt32(),
|
||||||
|
script: readGenerationScript(),
|
||||||
|
sequence: readUInt32()
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tx.ins.push({
|
||||||
|
hash: hash,
|
||||||
|
index: readUInt32(),
|
||||||
|
script: readScript(),
|
||||||
|
sequence: readUInt32()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var voutLen = readVarInt()
|
var voutLen = readVarInt()
|
||||||
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: readScript()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.locktime = readUInt32()
|
tx.locktime = readUInt32()
|
||||||
assert.equal(offset, buffer.length, 'Transaction has unexpected data')
|
|
||||||
|
if (!__disableAssert) {
|
||||||
|
assert.equal(offset, buffer.length, 'Transaction has unexpected data')
|
||||||
|
}
|
||||||
|
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
@ -82,6 +101,12 @@ Transaction.fromHex = function(hex) {
|
||||||
return Transaction.fromBuffer(new Buffer(hex, 'hex'))
|
return Transaction.fromBuffer(new Buffer(hex, 'hex'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transaction.isCoinbaseHash = function(buffer) {
|
||||||
|
return Array.prototype.every.call(buffer, function(x) {
|
||||||
|
return x === 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new txIn.
|
* Create a new txIn.
|
||||||
*
|
*
|
||||||
|
@ -243,20 +268,18 @@ Transaction.prototype.getId = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction.prototype.toBuffer = function () {
|
Transaction.prototype.toBuffer = function () {
|
||||||
var txInSize = this.ins.reduce(function(a, x) {
|
function scriptSize(script) {
|
||||||
return a + (40 + bufferutils.varIntSize(x.script.buffer.length) + x.script.buffer.length)
|
var length = script.buffer.length
|
||||||
}, 0)
|
|
||||||
|
|
||||||
var txOutSize = this.outs.reduce(function(a, x) {
|
return bufferutils.varIntSize(length) + length
|
||||||
return a + (8 + bufferutils.varIntSize(x.script.buffer.length) + x.script.buffer.length)
|
}
|
||||||
}, 0)
|
|
||||||
|
|
||||||
var buffer = new Buffer(
|
var buffer = new Buffer(
|
||||||
8 +
|
8 +
|
||||||
bufferutils.varIntSize(this.ins.length) +
|
bufferutils.varIntSize(this.ins.length) +
|
||||||
bufferutils.varIntSize(this.outs.length) +
|
bufferutils.varIntSize(this.outs.length) +
|
||||||
txInSize +
|
this.ins.reduce(function(sum, input) { return sum + 40 + scriptSize(input.script) }, 0) +
|
||||||
txOutSize
|
this.outs.reduce(function(sum, output) { return sum + 8 + scriptSize(output.script) }, 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
var offset = 0
|
var offset = 0
|
||||||
|
|
|
@ -7,12 +7,6 @@ var ECSignature = require('./ecsignature')
|
||||||
var Script = require('./script')
|
var Script = require('./script')
|
||||||
var Transaction = require('./transaction')
|
var Transaction = require('./transaction')
|
||||||
|
|
||||||
function isCoinbase(txHash) {
|
|
||||||
return Array.prototype.every.call(txHash, function(x) {
|
|
||||||
return x === 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractInput(txIn) {
|
function extractInput(txIn) {
|
||||||
var redeemScript
|
var redeemScript
|
||||||
var scriptSig = txIn.script
|
var scriptSig = txIn.script
|
||||||
|
@ -116,7 +110,7 @@ TransactionBuilder.fromTransaction = function(transaction) {
|
||||||
// Extract/add signatures
|
// Extract/add signatures
|
||||||
txb.inputs = transaction.ins.map(function(txIn) {
|
txb.inputs = transaction.ins.map(function(txIn) {
|
||||||
// TODO: remove me after testcase added
|
// TODO: remove me after testcase added
|
||||||
assert(!isCoinbase(txIn.hash), 'coinbase inputs not supported')
|
assert(!Transaction.isCoinbaseHash(txIn.hash), 'coinbase inputs not supported')
|
||||||
|
|
||||||
// Ignore empty scripts
|
// Ignore empty scripts
|
||||||
if (txIn.script.buffer.length === 0) return
|
if (txIn.script.buffer.length === 0) return
|
||||||
|
|
24
test/fixtures/transaction.json
vendored
24
test/fixtures/transaction.json
vendored
File diff suppressed because one or more lines are too long
|
@ -16,7 +16,14 @@ describe('Transaction', function() {
|
||||||
|
|
||||||
raw.ins.forEach(function(txIn) {
|
raw.ins.forEach(function(txIn) {
|
||||||
var txHash = new Buffer(txIn.hash, 'hex')
|
var txHash = new Buffer(txIn.hash, 'hex')
|
||||||
var script = txIn.script ? Script.fromASM(txIn.script) : undefined
|
var script
|
||||||
|
|
||||||
|
if (txIn.data) {
|
||||||
|
script = new Script(new Buffer(txIn.data, 'hex'), [])
|
||||||
|
|
||||||
|
} else if (txIn.script) {
|
||||||
|
script = Script.fromASM(txIn.script)
|
||||||
|
}
|
||||||
|
|
||||||
tx.addInput(txHash, txIn.index, txIn.sequence, script)
|
tx.addInput(txHash, txIn.index, txIn.sequence, script)
|
||||||
})
|
})
|
||||||
|
@ -108,28 +115,6 @@ describe('Transaction', function() {
|
||||||
assert.equal(tx.ins[0].script, Script.EMPTY)
|
assert.equal(tx.ins[0].script, Script.EMPTY)
|
||||||
})
|
})
|
||||||
|
|
||||||
fixtures.valid.forEach(function(f) {
|
|
||||||
it('should add the inputs for ' + f.id + ' correctly', function() {
|
|
||||||
var tx = new Transaction()
|
|
||||||
|
|
||||||
f.raw.ins.forEach(function(txIn, i) {
|
|
||||||
var txHash = new Buffer(txIn.hash, 'hex')
|
|
||||||
var script = txIn.script ? Script.fromASM(txIn.script) : undefined
|
|
||||||
var j = tx.addInput(txHash, txIn.index, txIn.sequence, script)
|
|
||||||
var sequence = txIn.sequence
|
|
||||||
if (sequence === undefined || sequence === null ) {
|
|
||||||
sequence = Transaction.DEFAULT_SEQUENCE
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.equal(i, j)
|
|
||||||
assert.equal(tx.ins[i].hash.toString('hex'), txIn.hash)
|
|
||||||
assert.equal(tx.ins[i].index, txIn.index)
|
|
||||||
assert.equal(tx.ins[i].sequence, sequence)
|
|
||||||
assert.deepEqual(tx.ins[i].script, script || Script.EMPTY)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
fixtures.invalid.addInput.forEach(function(f) {
|
fixtures.invalid.addInput.forEach(function(f) {
|
||||||
it('throws on ' + f.exception, function() {
|
it('throws on ' + f.exception, function() {
|
||||||
var tx = new Transaction()
|
var tx = new Transaction()
|
||||||
|
@ -181,21 +166,6 @@ describe('Transaction', function() {
|
||||||
assert.equal(tx.addOutput(destScript, 40000), 0)
|
assert.equal(tx.addOutput(destScript, 40000), 0)
|
||||||
assert.equal(tx.addOutput(destScript, 40000), 1)
|
assert.equal(tx.addOutput(destScript, 40000), 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
fixtures.valid.forEach(function(f) {
|
|
||||||
it('should add the outputs for ' + f.id + ' correctly', function() {
|
|
||||||
var tx = new Transaction()
|
|
||||||
|
|
||||||
f.raw.outs.forEach(function(txOut, i) {
|
|
||||||
var scriptPubKey = Script.fromASM(txOut.script)
|
|
||||||
var j = tx.addOutput(scriptPubKey, txOut.value)
|
|
||||||
|
|
||||||
assert.equal(i, j)
|
|
||||||
assert.equal(tx.outs[i].script, scriptPubKey)
|
|
||||||
assert.equal(tx.outs[i].value, txOut.value)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('clone', function() {
|
describe('clone', function() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue