From ef0535960f0bc641aeff900ff77e2338b751880a Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Tue, 17 Jul 2018 16:55:45 +0200 Subject: [PATCH] fix: use block headers only The current code was wrong. You could pass in a full block (with transactions) and only the header was used. The correct behaviour is, to use the header only. This means that this library does support block headers only, there is no support for transactions. --- src/util.js | 20 +++++++++++--------- test/helpers.js | 12 ++++++++++++ test/resolver.spec.js | 33 +++++++++++++++++---------------- test/util.spec.js | 42 +++++++++++++++--------------------------- 4 files changed, 55 insertions(+), 52 deletions(-) create mode 100644 test/helpers.js diff --git a/src/util.js b/src/util.js index 60ad068..aac31ef 100644 --- a/src/util.js +++ b/src/util.js @@ -6,6 +6,8 @@ const multihashes = require('multihashes') const multihashing = require('multihashing-async') const waterfall = require('async/waterfall') +const BITCOIN_BLOCK_HEADER_SIZE = 80 + /** * @callback SerializeCallback * @param {?Error} error - Error if serialization failed @@ -24,7 +26,7 @@ const serialize = (dagNode, callback) => { let err = null let binaryBlob try { - binaryBlob = dagNode.toBuffer() + binaryBlob = dagNode.toBuffer(true) } catch (serializeError) { err = serializeError } finally { @@ -47,15 +49,14 @@ const serialize = (dagNode, callback) => { * @returns {void} */ const deserialize = (binaryBlob, callback) => { - let err = null - let dagNode - try { - dagNode = BitcoinjsBlock.fromBuffer(binaryBlob) - } catch (deserializeError) { - err = deserializeError - } finally { - callback(err, dagNode) + if (binaryBlob.length !== BITCOIN_BLOCK_HEADER_SIZE) { + const err = new Error( + `Bitcoin block header needs to be ${BITCOIN_BLOCK_HEADER_SIZE} bytes`) + return callback(err) } + + const dagNode = BitcoinjsBlock.fromBuffer(binaryBlob) + callback(null, dagNode) } /** @@ -106,6 +107,7 @@ const hashToCid = (hash) => { module.exports = { hashToCid: hashToCid, + BITCOIN_BLOCK_HEADER_SIZE: BITCOIN_BLOCK_HEADER_SIZE, // Public API cid: cid, diff --git a/test/helpers.js b/test/helpers.js new file mode 100644 index 0000000..c7182a1 --- /dev/null +++ b/test/helpers.js @@ -0,0 +1,12 @@ +'use strict' + +const BITCOIN_BLOCK_HEADER_SIZE = require('../src/index') + .util.BITCOIN_BLOCK_HEADER_SIZE + +const headerFromHexBlock = (hex) => { + return Buffer.from(hex.toString(), 'hex').slice(0, BITCOIN_BLOCK_HEADER_SIZE) +} + +module.exports = { + headerFromHexBlock +} diff --git a/test/resolver.spec.js b/test/resolver.spec.js index 75ac5b0..7825682 100644 --- a/test/resolver.spec.js +++ b/test/resolver.spec.js @@ -8,14 +8,15 @@ const expect = chai.expect chai.use(dirtyChai) const CID = require('cids') const IpldBitcoin = require('../src/index') +const helpers = require('./helpers') const fixtureBlockHex = loadFixture('test/fixtures/block.hex') -const fixtureBlock = Buffer.from(fixtureBlockHex.toString(), 'hex') +const fixtureBlockHeader = helpers.headerFromHexBlock(fixtureBlockHex) const invalidBlock = Buffer.from('abcdef', 'hex') -describe('IPLD format resolver API resolve()', () => { +describe('IPLD format resolve API resolve()', () => { it('should return the deserialized node if no path is given', (done) => { - IpldBitcoin.resolver.resolve(fixtureBlock, (err, value) => { + IpldBitcoin.resolver.resolve(fixtureBlockHeader, (err, value) => { expect(err).to.not.exist() expect(value.remainderPath).is.empty() expect(value.value).is.not.empty() @@ -24,7 +25,7 @@ describe('IPLD format resolver API resolve()', () => { }) it('should return the deserialized node if path is empty', (done) => { - IpldBitcoin.resolver.resolve(fixtureBlock, '', (err, value) => { + IpldBitcoin.resolver.resolve(fixtureBlockHeader, '', (err, value) => { expect(err).to.not.exist() expect(value.remainderPath).is.empty() expect(value.value).is.not.empty() @@ -33,35 +34,35 @@ describe('IPLD format resolver API resolve()', () => { }) it('should return the version', (done) => { - verifyPath(fixtureBlock, 'version', 2, done) + verifyPath(fixtureBlockHeader, 'version', 2, done) }) it('should return the timestamp', (done) => { - verifyPath(fixtureBlock, 'timestamp', 1386981279, done) + verifyPath(fixtureBlockHeader, 'timestamp', 1386981279, done) }) it('should return the difficulty', (done) => { - verifyPath(fixtureBlock, 'difficulty', 419740270, done) + verifyPath(fixtureBlockHeader, 'difficulty', 419740270, done) }) it('should return the nonce', (done) => { - verifyPath(fixtureBlock, 'nonce', 3159344128, done) + verifyPath(fixtureBlockHeader, 'nonce', 3159344128, done) }) it('should error on non-existent path', (done) => { - verifyError(fixtureBlock, 'something/random', done) + verifyError(fixtureBlockHeader, 'something/random', done) }) it('should error on path starting with a slash', (done) => { - verifyError(fixtureBlock, '/version', done) + verifyError(fixtureBlockHeader, '/version', done) }) it('should error on partially matching path that isn\'t a link', (done) => { - verifyError(fixtureBlock, 'version/but/additional/things', done) + verifyError(fixtureBlockHeader, 'version/but/additional/things', done) }) it('should return a link when parent is requested', (done) => { - IpldBitcoin.resolver.resolve(fixtureBlock, 'parent', (err, value) => { + IpldBitcoin.resolver.resolve(fixtureBlockHeader, 'parent', (err, value) => { expect(err).to.not.exist() expect(value.remainderPath).is.empty() expect(value.value).to.deep.equal({ @@ -72,7 +73,7 @@ describe('IPLD format resolver API resolve()', () => { it('should return a link and remaining path when parent is requested', (done) => { - IpldBitcoin.resolver.resolve(fixtureBlock, 'parent/timestamp', + IpldBitcoin.resolver.resolve(fixtureBlockHeader, 'parent/timestamp', (err, value) => { expect(err).to.not.exist() expect(value.remainderPath).to.equal('timestamp') @@ -84,7 +85,7 @@ describe('IPLD format resolver API resolve()', () => { }) it('should return a link when transactions are requested', (done) => { - IpldBitcoin.resolver.resolve(fixtureBlock, 'tx/some/remainder', + IpldBitcoin.resolver.resolve(fixtureBlockHeader, 'tx/some/remainder', (err, value) => { expect(err).to.not.exist() expect(value.remainderPath).to.equal('some/remainder') @@ -101,7 +102,7 @@ describe('IPLD format resolver API resolve()', () => { describe('IPLD format resolver API tree()', () => { it('should return only paths by default', (done) => { - IpldBitcoin.resolver.tree(fixtureBlock, (err, value) => { + IpldBitcoin.resolver.tree(fixtureBlockHeader, (err, value) => { expect(err).to.not.exist() expect(value).to.deep.equal(['version', 'timestamp', 'difficulty', 'nonce', 'parent', 'tx']) @@ -110,7 +111,7 @@ describe('IPLD format resolver API tree()', () => { }) it('should be able to return paths and values', (done) => { - IpldBitcoin.resolver.tree(fixtureBlock, {values: true}, (err, value) => { + IpldBitcoin.resolver.tree(fixtureBlockHeader, {values: true}, (err, value) => { expect(err).to.not.exist() expect(value).to.deep.equal({ version: 2, diff --git a/test/util.spec.js b/test/util.spec.js index 71a5a92..787bb7d 100644 --- a/test/util.spec.js +++ b/test/util.spec.js @@ -7,14 +7,15 @@ const dirtyChai = require('dirty-chai') const expect = chai.expect chai.use(dirtyChai) const IpldBitcoin = require('../src/index') +const helpers = require('./helpers') const fixtureBlockHex = loadFixture('test/fixtures/block.hex') -const fixtureBlock = Buffer.from(fixtureBlockHex.toString(), 'hex') +const fixtureBlockHeader = helpers.headerFromHexBlock(fixtureBlockHex) const invalidDagNode = {invalid: 'dagNode'} describe('IPLD format util API deserialize()', () => { it('should work correctly', (done) => { - IpldBitcoin.util.deserialize(fixtureBlock, (err, dagNode) => { + IpldBitcoin.util.deserialize(fixtureBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyBlock(dagNode, { version: 2, @@ -30,8 +31,8 @@ describe('IPLD format util API deserialize()', () => { it('should deserialize Segwit correctly (a)', (done) => { const segwitBlockHex = loadFixture('test/fixtures/segwit.hex') - const segwitBlock = Buffer.from(segwitBlockHex.toString(), 'hex') - IpldBitcoin.util.deserialize(segwitBlock, (err, dagNode) => { + const segwitBlockHeader = helpers.headerFromHexBlock(segwitBlockHex) + IpldBitcoin.util.deserialize(segwitBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyBlock(dagNode, { version: 536870914, @@ -50,8 +51,8 @@ describe('IPLD format util API deserialize()', () => { it('should deserialize Segwit correctly (b)', (done) => { const segwitBlockHex = loadFixture('test/fixtures/segwit2.hex') - const segwitBlock = Buffer.from(segwitBlockHex.toString(), 'hex') - IpldBitcoin.util.deserialize(segwitBlock, (err, dagNode) => { + const segwitBlockHeader = helpers.headerFromHexBlock(segwitBlockHex) + IpldBitcoin.util.deserialize(segwitBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyBlock(dagNode, { version: 536870914, @@ -70,8 +71,8 @@ describe('IPLD format util API deserialize()', () => { it('should deserialize Segwit correctly (c)', (done) => { const segwitBlockHex = loadFixture('test/fixtures/segwit3.hex') - const segwitBlock = Buffer.from(segwitBlockHex.toString(), 'hex') - IpldBitcoin.util.deserialize(segwitBlock, (err, dagNode) => { + const segwitBlockHeader = helpers.headerFromHexBlock(segwitBlockHex) + IpldBitcoin.util.deserialize(segwitBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyBlock(dagNode, { version: 536870912, @@ -88,19 +89,6 @@ describe('IPLD format util API deserialize()', () => { }) }) - it('should deserialize a block without transactions', (done) => { - const hexData = '01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d1398' - const block = Buffer.from(hexData.toString(), 'hex') - IpldBitcoin.util.deserialize(block, (err, dagNode) => { - expect(err).to.not.exist() - expect(dagNode.transactions).to.be.empty() - verifyCid( - dagNode, - '56200cf2049d7ce53bebaa4e8105606ee4663ca6d8e73a84fa40717133137cfc32b8', - done) - }) - }) - it('should error on an invalid block', (done) => { const invalidBlock = Buffer.from('abcdef', 'hex') IpldBitcoin.util.deserialize(invalidBlock, (err, dagNode) => { @@ -113,11 +101,11 @@ describe('IPLD format util API deserialize()', () => { describe('IPLD format util API serialize()', () => { it('should round-trip (de)serialization correctly', (done) => { - IpldBitcoin.util.deserialize(fixtureBlock, (err, dagNode) => { + IpldBitcoin.util.deserialize(fixtureBlockHeader, (err, dagNode) => { expect(err).to.not.exist() IpldBitcoin.util.serialize(dagNode, (err, binaryBlob) => { expect(err).to.not.exist() - expect(binaryBlob).to.deep.equal(fixtureBlock) + expect(binaryBlob).to.deep.equal(fixtureBlockHeader) done() }) }) @@ -134,7 +122,7 @@ describe('IPLD format util API serialize()', () => { describe('IPLD format util API cid()', () => { it('should encode the CID correctly', (done) => { - IpldBitcoin.util.deserialize(fixtureBlock, (err, dagNode) => { + IpldBitcoin.util.deserialize(fixtureBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyCid( dagNode, @@ -152,7 +140,7 @@ describe('IPLD format util API cid()', () => { }) it('should encode the CID correctly with options', (done) => { - IpldBitcoin.util.deserialize(fixtureBlock, (err, dagNode) => { + IpldBitcoin.util.deserialize(fixtureBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyCid1( dagNode, @@ -163,7 +151,7 @@ describe('IPLD format util API cid()', () => { }) it('should encode the CID correctly with undefined options', (done) => { - IpldBitcoin.util.deserialize(fixtureBlock, (err, dagNode) => { + IpldBitcoin.util.deserialize(fixtureBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyCid1( dagNode, @@ -174,7 +162,7 @@ describe('IPLD format util API cid()', () => { }) it('should encode the CID correctly with default options specified', (done) => { - IpldBitcoin.util.deserialize(fixtureBlock, (err, dagNode) => { + IpldBitcoin.util.deserialize(fixtureBlockHeader, (err, dagNode) => { expect(err).to.not.exist() verifyCid1( dagNode,