From f13650544bbf078d247148ced2ca24fae8792f0a Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Feb 2015 12:03:41 +1100 Subject: [PATCH 1/7] Transaction: add internal flag to disable size assertion --- src/transaction.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 31c8657..6202fbe 100644 --- a/src/transaction.js +++ b/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 @@ -73,7 +73,10 @@ Transaction.fromBuffer = function(buffer) { } 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 } From 49345748bbcf3c87c048c5e50e218005384a24bf Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Feb 2015 12:04:37 +1100 Subject: [PATCH 2/7] Transaction: add isCoinbaseHash static method --- src/transaction.js | 6 ++++++ src/transaction_builder.js | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 6202fbe..7d45e09 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -85,6 +85,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. * diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 40a5546..a45b06b 100644 --- a/src/transaction_builder.js +++ b/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 From dc253bb77ccb544d9cb344ba20abf4f9d493c88e Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Feb 2015 12:11:37 +1100 Subject: [PATCH 3/7] Transaction: extract Script size calculations --- src/transaction.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index 7d45e09..eae644b 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -68,7 +68,7 @@ Transaction.fromBuffer = function(buffer, __disableAssert) { for (i = 0; i < voutLen; ++i) { tx.outs.push({ value: readUInt64(), - script: readScript(), + script: readScript() }) } @@ -252,20 +252,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 From db17e35013883b25632237bf4352a17c8c63e180 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Feb 2015 12:07:12 +1100 Subject: [PATCH 4/7] tests: transaction.add* better tested by raw functions --- test/transaction.js | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/test/transaction.js b/test/transaction.js index 56e5ab5..eb99847 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -108,28 +108,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 +159,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() { From 445eb260c0ec30f4e2e199eee91b7eaa54e612ea Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Feb 2015 12:04:17 +1100 Subject: [PATCH 5/7] tests: add failing Coinbase transaction fixture --- test/fixtures/transaction.json | 24 ++++++++++++++++++++++++ test/transaction.js | 9 ++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/test/fixtures/transaction.json b/test/fixtures/transaction.json index cb65597..043dfed 100644 --- a/test/fixtures/transaction.json +++ b/test/fixtures/transaction.json @@ -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": { diff --git a/test/transaction.js b/test/transaction.js index eb99847..c7fd4f8 100644 --- a/test/transaction.js +++ b/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) }) From 23bae5748e56b2d025e40eeac37d5c7f516cfcdd Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Feb 2015 12:30:23 +1100 Subject: [PATCH 6/7] Transaction: fix coinbase script handling --- src/transaction.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index eae644b..ad6179d 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -51,17 +51,33 @@ Transaction.fromBuffer = function(buffer, __disableAssert) { 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() From 50dab3b6bfa199bac0b8156ffdadece3b857df83 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 19 Feb 2015 18:59:56 +1100 Subject: [PATCH 7/7] block: use Transaction.fromBuffer --- src/block.js | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/src/block.js b/src/block.js index 8b786cb..3bef165 100644 --- a/src/block.js +++ b/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 }