Skip to content
This repository has been archived by the owner on Aug 11, 2021. It is now read-only.

Commit

Permalink
feat: Add resolve() method
Browse files Browse the repository at this point in the history
  • Loading branch information
vmx authored and daviddias committed Jan 16, 2018
1 parent 60f134a commit 25cef82
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'use strict'

exports.resolver = require('./resolver.js')
exports.util = require('./util.js')
79 changes: 79 additions & 0 deletions src/resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
'use strict'

const util = require('./util')

const resolve = (binaryBlob, path, callback) => {
if (typeof path === 'function') {
callback = path
path = undefined
}

util.deserialize(binaryBlob, (err, dagNode) => {
if (err) {
return callback(err)
}

// Requesting the root node returns the deserialized block
if (!path || path === '/') {
return callback(null, {
value: dagNode,
remainderPath: ''
})
}

const pathArray = path.split('/')
// `/` is the first element
pathArray.shift()

// The values that can directly be resolved
let value
// Links to other blocks
let link
switch (pathArray[0]) {
case 'version':
value = dagNode.version
break
case 'timestamp':
value = dagNode.timestamp
break
case 'difficulty':
value = dagNode.bits
break
case 'nonce':
value = dagNode.nonce
break
case 'parent':
link = dagNode.prevHash
break
case 'tx':
link = dagNode.merkleRoot
break
default:
return callback(new Error('No such path'), null)
}

let remainderPath = pathArray.slice(1).join('/')
// Bitcoin has only top-level fields, every deeper nesting needs to
// be a link
if (value !== undefined) {
if (remainderPath.length > 0) {
return callback(new Error('No such path'), null)
} else {
return callback(null, {
value: value,
remainderPath: ''
})
}
} else {
// It's a link
return callback(null, {
value: {'/': util.hashToCid(link)},
remainderPath: remainderPath
})
}
})
}

module.exports = {
resolve: resolve
}
14 changes: 11 additions & 3 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,22 @@ const cid = (dagNode, callback) => {
const firstHash = sha256().update(dagNode.toBuffer(true)).digest()
const headerHash = sha256().update(Buffer.from(firstHash)).digest()

const multihash = multihashes.encode(Buffer.from(headerHash), 'dbl-sha2-256')
const cidVersion = 1
const cid = new CID(cidVersion, 'bitcoin-block', multihash)
const cid = hashToCid(Buffer.from(headerHash))
const err = null
callback(err, cid)
}

const hashToCid = (hash) => {
const multihash = multihashes.encode(hash, 'dbl-sha2-256')
const cidVersion = 1
const cid = new CID(cidVersion, 'bitcoin-block', multihash)
return cid
}

module.exports = {
hashToCid: hashToCid,

// Public API
cid: cid,
deserialize: deserialize,
serialize: serialize
Expand Down
108 changes: 108 additions & 0 deletions test/resolver.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/* eslint-env mocha */
'use strict'

const loadFixture = require('aegir/fixtures')
const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)
const CID = require('cids')
const IpldBitcoin = require('../src/index')

const fixtureBlockHex = loadFixture(__dirname, 'fixtures/block.hex')
const fixtureBlock = Buffer.from(fixtureBlockHex.toString(), 'hex')

describe('IPLD format resolver API resolve()', () => {
it('should return the deserialized node if no path is given', (done) => {
IpldBitcoin.resolver.resolve(fixtureBlock, (err, value) => {
expect(err).to.not.exist()
expect(value.remainderPath).is.empty()
expect(value.value).is.not.empty()
done()
})
})

it('should return the deserialized node if the root is requested', (done) => {
IpldBitcoin.resolver.resolve(fixtureBlock, '/', (err, value) => {
expect(err).to.not.exist()
expect(value.remainderPath).is.empty()
expect(value.value).is.not.empty()
done()
})
})

it('should return the version', (done) => {
verifyPath(fixtureBlock, '/version', 2, done)
})

it('should return the timestamp', (done) => {
verifyPath(fixtureBlock, '/timestamp', 1386981279, done)
})

it('should return the difficulty', (done) => {
verifyPath(fixtureBlock, '/difficulty', 419740270, done)
})

it('should return the nonce', (done) => {
verifyPath(fixtureBlock, '/nonce', 3159344128, done)
})

it('should error on non-existent path', (done) => {
verifyError(fixtureBlock, '/something/random', done)
})

it('should error on partially matching path that isn\'t a link', (done) => {
verifyError(fixtureBlock, '/version/but/additional/things', done)
})

it('should return a link when parent is requested', (done) => {
IpldBitcoin.resolver.resolve(fixtureBlock, '/parent', (err, value) => {
expect(err).to.not.exist()
expect(value.remainderPath).is.empty()
expect(value.value).to.deep.equal({
'/': new CID('z4HFzdHLxSgJvCMJrsDtV7MgqiGALZdbbxgcTLVUUXQGBkGYjLb')})
done()
})
})

it('should return a link and remaining path when parent is requested',
(done) => {
IpldBitcoin.resolver.resolve(fixtureBlock, '/parent/timestamp',
(err, value) => {
expect(err).to.not.exist()
expect(value.remainderPath).to.equal('timestamp')
expect(value.value).to.deep.equal({
'/':
new CID('z4HFzdHLxSgJvCMJrsDtV7MgqiGALZdbbxgcTLVUUXQGBkGYjLb')})
done()
})
})

it('should return a link when transactions are requested', (done) => {
IpldBitcoin.resolver.resolve(fixtureBlock, '/tx/some/remainder',
(err, value) => {
expect(err).to.not.exist()
expect(value.remainderPath).to.equal('some/remainder')
expect(value.value).to.deep.equal({
'/': new CID('z4HFzdHD15kVvtmVzeD7z9sisZ7acSC88wXS3KJGwGrnr2DwcVQ')})
done()
})
})
})

const verifyPath = (block, path, expected, done) => {
IpldBitcoin.resolver.resolve(block, path, (err, value) => {
expect(err).to.not.exist()
expect(value.remainderPath).is.empty()
expect(value.value).is.equal(expected)
done()
})
}

const verifyError = (block, path, done) => {
IpldBitcoin.resolver.resolve(block, path, (err, value) => {
expect(value).to.not.exist()
expect(err).to.be.an('error')
done()
})
}

0 comments on commit 25cef82

Please sign in to comment.