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,