Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 3 additions & 32 deletions src/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
57 changes: 40 additions & 17 deletions src/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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.
*
Expand Down Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions src/transaction_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions test/fixtures/transaction.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,30 @@
]
},
"hex": "010000000ee7b73e229790c1e79a02f0c871813b3cf26a4156c5b8d942e88b38fe8d3f43a0000000008c493046022100fd3d8fef44fb0962ba3f07bee1d4cafb84e60e38e6c7d9274504b3638a8d2f520221009fce009044e615b6883d4bf62e04c48f9fe236e19d644b082b2f0ae5c98e045c014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff7bfc005f3880a606027c7cd7dd02a0f6a6572eeb84a91aa158311be13695a7ea010000008b483045022100e2e61c40f26e2510b76dc72ea2f568ec514fce185c719e18bca9caaef2b20e9e02207f1100fc79eb0584e970c7f18fb226f178951d481767b4092d50d13c50ccba8b014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff0e0f8e6bf951fbb84d7d8ef833a1cbf5bb046ea7251973ac6e7661c755386ee3010000008a473044022048f1611e403710f248f7caf479965a6a5f63cdfbd9a714fef4ec1b68331ade1d022074919e79376c363d4575b2fc21513d5949471703efebd4c5ca2885e810eb1fa4014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffe6f17f35bf9f0aa7a4242ab3e29edbdb74c5274bf263e53043dddb8045cb585b000000008b483045022100886c07cad489dfcf4b364af561835d5cf985f07adf8bd1d5bd6ddea82b0ce6b2022045bdcbcc2b5fc55191bb997039cf59ff70e8515c56b62f293a9add770ba26738014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffe6f17f35bf9f0aa7a4242ab3e29edbdb74c5274bf263e53043dddb8045cb585b010000008a4730440220535d49b819fdf294d27d82aff2865ed4e18580f0ca9796d793f611cb43a44f47022019584d5e300c415f642e37ba2a814a1e1106b4a9b91dc2a30fb57ceafe041181014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffd3051677216ea53baa2e6d7f6a75434ac338438c59f314801c8496d1e6d1bf6d010000008b483045022100bf612b0fa46f49e70ab318ca3458d1ed5f59727aa782f7fac5503f54d9b43a590220358d7ed0e3cee63a5a7e972d9fad41f825d95de2fd0c5560382468610848d489014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff1e751ccc4e7d973201e9174ec78ece050ef2fadd6a108f40f76a9fa314979c31010000008b483045022006e263d5f73e05c48a603e3bd236e8314e5420721d5e9020114b93e8c9220e1102210099d3dead22f4a792123347a238c87e67b55b28a94a0bb7793144cc7ad94a0168014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff25c4cf2c61743b3f4252d921d937cca942cf32e4f3fa4a544d0b26f014337084010000008a47304402207d6e87588be47bf2d97eaf427bdd992e9d6b306255711328aee38533366a88b50220623099595ae442cb77eaddb3f91753a4fc9df56fde69cfec584c7f97e05533c8014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffecd93c87eb43c48481e6694904305349bdea94b01104579fa9f02bff66c89663010000008a473044022020f59498aee0cf82cb113768ef3cb721000346d381ff439adb4d405f791252510220448de723aa59412266fabbc689ec25dc94b1688c27a614982047513a80173514014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffa1fdc0a79ff98d5b6154176e321c22f4f8450dbd950bd013ad31135f5604411e010000008b48304502210088167867f87327f9c0db0444267ff0b6a026eedd629d8f16fe44a34c18e706bf0220675c8baebf89930e2d6e4463adefc50922653af99375242e38f5ee677418738a014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffb89e8249c3573b58bf1ec7433185452dd57ab8e1daab01c3cc6ddc8b66ad3de8000000008b4830450220073d50ac5ec8388d5b3906921f9368c31ad078c8e1fb72f26d36b533f35ee327022100c398b23e6692e11dca8a1b64aae2ff70c6a781ed5ee99181b56a2f583a967cd4014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff45ee07e182084454dacfad1e61b04ffdf9c7b01003060a6c841a01f4fff8a5a0010000008b483045022100991d1bf60c41358f08b20e53718a24e05ac0608915df4f6305a5b47cb61e5da7022003f14fc1cc5b737e2c3279a4f9be1852b49dbb3d9d6cc4c8af6a666f600dced8014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff4cba12549f1d70f8e60aea8b546c8357f7c099e7c7d9d8691d6ee16e7dfa3170010000008c493046022100f14e2b0ef8a8e206db350413d204bc0a5cd779e556b1191c2d30b5ec023cde6f022100b90b2d2bf256c98a88f7c3a653b93cec7d25bb6a517db9087d11dbd189e8851c014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffffa4b3aed39eb2a1dc6eae4609d9909724e211c153927c230d02bd33add3026959010000008b483045022100a8cebb4f1c58f5ba1af91cb8bd4a2ed4e684e9605f5a9dc8b432ed00922d289d0220251145d2d56f06d936fd0c51fa884b4a6a5fafd0c3318f72fb05a5c9aa372195014104aa592c859fd00ed2a02609aad3a1bf72e0b42de67713e632c70a33cc488c15598a0fb419370a54d1c275b44380e8777fc01b6dc3cd43a416c6bab0e30dc1e19fffffffff0240d52303000000001976a914167c3e1f10cc3b691c73afbdb211e156e3e3f25c88ac15462e00000000001976a914290f7d617b75993e770e5606335fa0999a28d71388ac00000000"
},
{
"description": "Coinbase transaction",
"id": "8e070d4eb85eb02e02dd938d6552316b9d723330707870c518064b7a0d232da3",
"hash": "a32d230d7a4b0618c57078703033729d6b3152658d93dd022eb05eb84e0d078e",
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff29032832051c4d696e656420627920416e74506f6f6c20626a343a45ef0454c5de8d5e5300004e2c0000ffffffff01414f1995000000001976a914b05793fe86a9f51a5f5ae3a6f07fd31932128a3f88ac00000000",
"raw": {
"version": 1,
"ins": [
{
"hash": "0000000000000000000000000000000000000000000000000000000000000000",
"index": 4294967295,
"data": "032832051c4d696e656420627920416e74506f6f6c20626a343a45ef0454c5de8d5e5300004e2c0000",
"sequence": 4294967295
}
],
"outs": [
{
"script": "OP_DUP OP_HASH160 b05793fe86a9f51a5f5ae3a6f07fd31932128a3f OP_EQUALVERIFY OP_CHECKSIG",
"value": 2501463873
}
],
"locktime": 0
}
}
],
"invalid": {
Expand Down
46 changes: 8 additions & 38 deletions test/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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() {
Expand Down