From f9d3c7eeb0940a8c0b9e664fd8b9a1069fcd1af3 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Tue, 4 Aug 2020 17:38:51 +0100 Subject: [PATCH] fix: replace node buffers with uint8array (#65) Accepts and emits `Uint8Array`s instead of node `Buffer`s but converts them internally because `bitcoinjs-lib` only understands `Buffer`s. BREAKING CHANGE: - `util.serialize` returns `Uint8Array`s - `util.cid` returns `CID`s with a breaking API change - see https://github.com/multiformats/js-cid/pull/117 for changes --- package.json | 13 ++++++------- src/resolver.js | 7 +++---- src/util.js | 11 ++++++++--- test/helpers.js | 4 ++-- test/mod.spec.js | 5 +---- test/resolver.spec.js | 9 +++------ test/util.spec.js | 29 +++++++++++++++++++---------- 7 files changed, 42 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 9cc2e1b..46f9181 100644 --- a/package.json +++ b/package.json @@ -35,15 +35,14 @@ "dependencies": { "bitcoinjs-lib": "^5.0.0", "buffer": "^5.6.0", - "cids": "^0.8.3", - "multicodec": "^1.0.0", - "multihashes": "^1.0.1", - "multihashing-async": "^1.0.0" + "cids": "^1.0.0", + "multicodec": "^2.0.0", + "multihashes": "^3.0.0", + "multihashing-async": "^2.0.0", + "uint8arrays": "^1.0.0" }, "devDependencies": { - "aegir": "^25.0.0", - "chai": "^4.2.0", - "dirty-chai": "^2.0.1" + "aegir": "^25.0.0" }, "contributors": [ "Volker Mische ", diff --git a/src/resolver.js b/src/resolver.js index a60daf7..737484e 100644 --- a/src/resolver.js +++ b/src/resolver.js @@ -1,7 +1,6 @@ 'use strict' const CID = require('cids') -const { Buffer } = require('buffer') const util = require('./util') @@ -11,7 +10,7 @@ const util = require('./util') * Returns the value or a link and the partial mising path. This way the * IPLD Resolver can fetch the link and continue to resolve. * - * @param {Buffer} binaryBlob - Binary representation of a Bitcoin block + * @param {Uint8Array} binaryBlob - Binary representation of a Bitcoin block * @param {string} [path='/'] - Path that should be resolved * @returns {Object} result - Result of the path it it was resolved successfully * @returns {*} result.value - Value the path resolves to @@ -46,7 +45,7 @@ exports.resolve = (binaryBlob, path) => { const traverse = function * (node, path) { // Traverse only objects and arrays - if (Buffer.isBuffer(node) || CID.isCID(node) || typeof node === 'string' || + if (node instanceof Uint8Array || CID.isCID(node) || typeof node === 'string' || node === null) { return } @@ -61,7 +60,7 @@ const traverse = function * (node, path) { * Return all available paths of a block. * * @generator - * @param {Buffer} binaryBlob - Binary representation of a Bitcoin block + * @param {Uint8Array} binaryBlob - Binary representation of a Bitcoin block * @yields {string} - A single path */ exports.tree = function * (binaryBlob) { diff --git a/src/util.js b/src/util.js index efde4b4..9db5ebc 100644 --- a/src/util.js +++ b/src/util.js @@ -5,6 +5,7 @@ const CID = require('cids') const multicodec = require('multicodec') const multihashes = require('multihashes') const multihashing = require('multihashing-async') +const { Buffer } = require('buffer') const BITCOIN_BLOCK_HEADER_SIZE = 80 const CODEC = multicodec.BITCOIN_BLOCK @@ -14,7 +15,7 @@ const DEFAULT_HASH_ALG = multicodec.DBL_SHA2_256 * Serialize internal representation into a binary Bitcoin block. * * @param {BitcoinBlock} dagNode - Internal representation of a Bitcoin block - * @returns {Buffer} + * @returns {Uint8Array} */ const serialize = (dagNode) => { return dagNode.toBuffer(true) @@ -23,7 +24,7 @@ const serialize = (dagNode) => { /** * Deserialize Bitcoin block into the internal representation. * - * @param {Buffer} binaryBlob - Binary representation of a Bitcoin block + * @param {Uint8Array} binaryBlob - Binary representation of a Bitcoin block * @returns {BitcoinBlock} */ const deserialize = (binaryBlob) => { @@ -32,6 +33,10 @@ const deserialize = (binaryBlob) => { `Bitcoin block header needs to be ${BITCOIN_BLOCK_HEADER_SIZE} bytes`) } + if (!Buffer.isBuffer(binaryBlob)) { + binaryBlob = Buffer.from(binaryBlob, binaryBlob.byteOffset, binaryBlob.byteLength) + } + const deserialized = BitcoinjsBlock.fromBuffer(binaryBlob) const getters = { @@ -88,7 +93,7 @@ const cid = async (binaryBlob, userOptions) => { return cid } -// Convert a Bitcoin hash (as Buffer) to a CID +// Convert a Bitcoin hash (as Uint8Array) to a CID const hashToCid = (hash) => { const multihash = multihashes.encode(hash, DEFAULT_HASH_ALG) const cidVersion = 1 diff --git a/test/helpers.js b/test/helpers.js index 0ce3704..640133c 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -1,12 +1,12 @@ 'use strict' -const { Buffer } = require('buffer') +const uint8ArrayFromString = require('uint8arrays/from-string') 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) + return uint8ArrayFromString(hex.toString(), 'base16').slice(0, BITCOIN_BLOCK_HEADER_SIZE) } module.exports = { diff --git a/test/mod.spec.js b/test/mod.spec.js index d0e15de..abcbdf6 100644 --- a/test/mod.spec.js +++ b/test/mod.spec.js @@ -1,10 +1,7 @@ /* eslint-env mocha */ 'use strict' -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) +const { expect } = require('aegir/utils/chai') const multicodec = require('multicodec') const mod = require('../src') diff --git a/test/resolver.spec.js b/test/resolver.spec.js index 812bf10..3f65844 100644 --- a/test/resolver.spec.js +++ b/test/resolver.spec.js @@ -2,18 +2,15 @@ 'use strict' const loadFixture = require('aegir/fixtures') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) +const { expect } = require('aegir/utils/chai') const CID = require('cids') -const { Buffer } = require('buffer') +const uint8ArrayFromString = require('uint8arrays/from-string') const IpldBitcoin = require('../src/index') const helpers = require('./helpers') const fixtureBlockHex = loadFixture('test/fixtures/block.hex') const fixtureBlockHeader = helpers.headerFromHexBlock(fixtureBlockHex) -const invalidBlock = Buffer.from('abcdef', 'hex') +const invalidBlock = uint8ArrayFromString('abcdef', 'base16') describe('IPLD format resolve API resolve()', () => { it('should return the deserialized node if path is empty', () => { diff --git a/test/util.spec.js b/test/util.spec.js index d7532e2..4319a31 100644 --- a/test/util.spec.js +++ b/test/util.spec.js @@ -2,13 +2,10 @@ 'use strict' const loadFixture = require('aegir/fixtures') -const chai = require('chai') -const dirtyChai = require('dirty-chai') -const expect = chai.expect -chai.use(dirtyChai) +const { expect } = require('aegir/utils/chai') const CID = require('cids') const multicodec = require('multicodec') -const { Buffer } = require('buffer') +const uint8ArrayFromString = require('uint8arrays/from-string') const IpldBitcoin = require('../src/index') const helpers = require('./helpers') @@ -29,6 +26,18 @@ describe('IPLD format util API deserialize()', () => { }) }) + it('should work correctly with Uint8Arrays', () => { + const dagNode = IpldBitcoin.util.deserialize(Uint8Array.from(fixtureBlockHeader)) + verifyBlock(dagNode, { + version: 2, + prevHash: '87d6242b27d248a9e145fe764a0bcef03a403883a2e4c8590200000000000000', + merkleRoot: '11a5b9a70acebedbbf71ef8ca341e8a98cf279c49eee8f92e10a2227743b6aeb', + timestamp: 1386981279, + bits: 419740270, + nonce: 3159344128 + }) + }) + it('should deserialize Segwit correctly (a)', () => { const segwitBlockHex = loadFixture('test/fixtures/segwit.hex') const segwitBlockHeader = helpers.headerFromHexBlock(segwitBlockHex) @@ -72,7 +81,7 @@ describe('IPLD format util API deserialize()', () => { }) it('should error on an invalid block', () => { - const invalidBlock = Buffer.from('abcdef', 'hex') + const invalidBlock = uint8ArrayFromString('abcdef', 'base16') expect(() => { IpldBitcoin.util.deserialize(invalidBlock) }).to.throw('Bitcoin block header needs to be 80 bytes') @@ -94,9 +103,9 @@ describe('IPLD format util API serialize()', () => { }) describe('IPLD format util API cid()', () => { - const expectedCid = new CID(1, 'bitcoin-block', Buffer.from( + const expectedCid = new CID(1, 'bitcoin-block', uint8ArrayFromString( '56203ec2c691d447b2fd0d6a94742345af1f351037dab1ab9e900200000000000000', - 'hex')) + 'base16')) it('should encode the CID correctly', async () => { const cid = await IpldBitcoin.util.cid(fixtureBlockHeader) @@ -107,9 +116,9 @@ describe('IPLD format util API cid()', () => { const cid = await IpldBitcoin.util.cid(fixtureBlockHeader, { hashAlg: multicodec.SHA3_256 }) - expect(cid.equals(new CID(1, 'bitcoin-block', Buffer.from( + expect(cid.equals(new CID(1, 'bitcoin-block', uint8ArrayFromString( '16208fd2802e0304c79c08a1ff2afb706ce64b78f3b94fd1c9142946c2e715589cfb', - 'hex' + 'base16' )))).to.be.true() })