Browse Source

Merge pull request #363 from bitcoinjs/coinbase

Coinbase Transaction parsing
hk-custom-address
Daniel Cousens 10 years ago
parent
commit
1da8297f3c
  1. 35
      src/block.js
  2. 57
      src/transaction.js
  3. 8
      src/transaction_builder.js
  4. 24
      test/fixtures/transaction.json
  5. 46
      test/transaction.js

35
src/block.js

@ -39,46 +39,17 @@ Block.fromBuffer = function(buffer) {
if (buffer.length === 80) return block
function readUInt64() {
var i = bufferutils.readUInt64LE(buffer, offset)
offset += 8
return i
}
function readVarInt() {
var vi = bufferutils.readVarInt(buffer, offset)
offset += vi.size
return vi.number
}
function readScript() {
return Script.fromBuffer(readSlice(readVarInt()))
}
// FIXME: poor performance
function readTransaction() {
var tx = new Transaction()
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()
var tx = Transaction.fromBuffer(buffer.slice(offset), true)
offset += tx.toBuffer().length
return tx
}

57
src/transaction.js

@ -22,7 +22,7 @@ Transaction.SIGHASH_NONE = 0x02
Transaction.SIGHASH_SINGLE = 0x03
Transaction.SIGHASH_ANYONECANPAY = 0x80
Transaction.fromBuffer = function(buffer) {
Transaction.fromBuffer = function(buffer, __disableAssert) {
var offset = 0
function readSlice(n) {
offset += n
@ -51,29 +51,48 @@ Transaction.fromBuffer = function(buffer) {
return Script.fromBuffer(readSlice(readVarInt()))
}
function readGenerationScript() {
return new Script(readSlice(readVarInt()), [])
}
var tx = new Transaction()
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 hash = readSlice(32)
if (Transaction.isCoinbaseHash(hash)) {
tx.ins.push({
hash: hash,
index: readUInt32(),
script: readGenerationScript(),
sequence: readUInt32()
})
} else {
tx.ins.push({
hash: hash,
index: readUInt32(),
script: readScript(),
sequence: readUInt32()
})
}
}
var voutLen = readVarInt()
for (i = 0; i < voutLen; ++i) {
tx.outs.push({
value: readUInt64(),
script: readScript(),
script: readScript()
})
}
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
}
@ -82,6 +101,12 @@ Transaction.fromHex = function(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.
*
@ -243,20 +268,18 @@ Transaction.prototype.getId = function () {
}
Transaction.prototype.toBuffer = function () {
var txInSize = this.ins.reduce(function(a, x) {
return a + (40 + bufferutils.varIntSize(x.script.buffer.length) + x.script.buffer.length)
}, 0)
function scriptSize(script) {
var length = script.buffer.length
var txOutSize = this.outs.reduce(function(a, x) {
return a + (8 + bufferutils.varIntSize(x.script.buffer.length) + x.script.buffer.length)
}, 0)
return bufferutils.varIntSize(length) + length
}
var buffer = new Buffer(
8 +
bufferutils.varIntSize(this.ins.length) +
bufferutils.varIntSize(this.outs.length) +
txInSize +
txOutSize
this.ins.reduce(function(sum, input) { return sum + 40 + scriptSize(input.script) }, 0) +
this.outs.reduce(function(sum, output) { return sum + 8 + scriptSize(output.script) }, 0)
)
var offset = 0

8
src/transaction_builder.js

@ -7,12 +7,6 @@ var ECSignature = require('./ecsignature')
var Script = require('./script')
var Transaction = require('./transaction')
function isCoinbase(txHash) {
return Array.prototype.every.call(txHash, function(x) {
return x === 0
})
}
function extractInput(txIn) {
var redeemScript
var scriptSig = txIn.script
@ -116,7 +110,7 @@ TransactionBuilder.fromTransaction = function(transaction) {
// Extract/add signatures
txb.inputs = transaction.ins.map(function(txIn) {
// 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
if (txIn.script.buffer.length === 0) return

24
test/fixtures/transaction.json

File diff suppressed because one or more lines are too long

46
test/transaction.js

@ -16,7 +16,14 @@ describe('Transaction', function() {
raw.ins.forEach(function(txIn) {
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)
})
@ -108,28 +115,6 @@ describe('Transaction', function() {
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) {
it('throws on ' + f.exception, function() {
var tx = new Transaction()
@ -181,21 +166,6 @@ describe('Transaction', function() {
assert.equal(tx.addOutput(destScript, 40000), 0)
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() {

Loading…
Cancel
Save