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

Commit

Permalink
fix: use the cleaned up IPLD Format API
Browse files Browse the repository at this point in the history
BREAKING CHANGE: All formats now return data according to the [IPLD Data Model]

The most important change is that now *all* formats return links as [CID instances]
and no longer as the JSON representation (`{"/": "base-encoded-cid"}`.

[IPLD Data Model]: https://github.com/ipld/specs/blob/master/IPLD-Data-Model-v1.md
[CID instances]: https://github.com/multiformats/js-cid/
  • Loading branch information
vmx committed May 8, 2019
1 parent c26bcbe commit 108aef0
Show file tree
Hide file tree
Showing 12 changed files with 306 additions and 561 deletions.
20 changes: 9 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,30 @@
"license": "MIT",
"devDependencies": {
"aegir": "^18.2.1",
"async": "^2.6.1",
"bitcoinjs-lib": "^4.0.3",
"bitcoinjs-lib": "^5.0.3",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"dirty-chai": "^2.0.1",
"ethereumjs-block": "^2.2.0",
"fs-extra": "^7.0.1",
"ipfs-block-service": "~0.15.2",
"ipfs-repo": "~0.26.1",
"ipld-bitcoin": "~0.1.9",
"ipld-ethereum": "^2.0.3",
"ipld-git": "~0.2.3",
"ipld-bitcoin": "~0.2.0",
"ipld-ethereum": "^3.0.0",
"ipld-git": "~0.4.0",
"ipld-in-memory": "^2.0.0",
"ipld-zcash": "~0.1.6",
"ipld-zcash": "~0.2.0",
"merkle-patricia-tree": "^3.0.0",
"multihashes": "~0.4.14",
"ncp": "^2.0.0",
"rimraf": "^2.6.3",
"rlp": "^2.2.2",
"zcash-bitcore-lib": "~0.13.20-rc3"
},
"dependencies": {
"cids": "~0.5.7",
"ipfs-block": "~0.8.0",
"ipld-dag-cbor": "~0.13.1",
"ipld-dag-pb": "~0.15.2",
"ipld-raw": "^2.0.1",
"ipld-dag-cbor": "~0.14.0",
"ipld-dag-pb": "~0.16.0",
"ipld-raw": "^3.0.0",
"merge-options": "^1.0.1",
"multicodec": "~0.5.0",
"promisify-es6": "^1.0.3",
Expand Down
68 changes: 13 additions & 55 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,13 @@ class IPLDResolver {
* @returns {this}
*/
addFormat (format) {
// IPLD Formats are using strings instead of constants for the multicodec
const codecBuffer = multicodec.getCodeVarint(format.resolver.multicodec)
const codec = multicodec.getCode(codecBuffer)
if (this.resolvers[codec]) {
const codec = format.codec
if (this.resolvers[format.format]) {
const codecName = multicodec.print[codec]
throw new Error(`Resolver already exists for codec "${codecName}"`)
}

this.resolvers[codec] = {
resolver: format.resolver,
util: format.util
}
this.resolvers[codec] = format

return this
}
Expand Down Expand Up @@ -101,7 +96,7 @@ class IPLDResolver {
// use local resolver
// update path value
const block = await promisify(this.bs.get.bind(this.bs))(cid)
const result = await promisify(format.resolver.resolve)(block.data, path)
const result = format.resolver.resolve(block.data, path)

// Prepare for the next iteration if there is a `remainderPath`
path = result.remainderPath
Expand Down Expand Up @@ -136,7 +131,7 @@ class IPLDResolver {
async get (cid) {
const block = await promisify(this.bs.get.bind(this.bs))(cid)
const format = await this._getFormat(block.cid.codec)
const node = await promisify(format.util.deserialize)(block.data)
const node = format.util.deserialize(block.data)

return node
}
Expand Down Expand Up @@ -194,10 +189,12 @@ class IPLDResolver {
hashAlg: options.hashAlg,
onlyHash: options.onlyHash
}
const cid = await promisify(formatImpl.util.cid)(node, cidOptions)
const serialized = formatImpl.util.serialize(node)
const cid = await formatImpl.util.cid(serialized, cidOptions)

if (!options.onlyHash) {
await this._store(cid, node)
const block = new Block(serialized, cid)
await promisify(this.bs.put.bind(this.bs))(block)
}

return cid
Expand Down Expand Up @@ -310,12 +307,10 @@ class IPLDResolver {
const maybeRecurse = async (block, treePath) => {
// A treepath we might want to follow recursively
const format = await this._getFormat(block.cid.codec)
const link = await promisify(
format.resolver.isLink)(block.data, treePath)
const result = format.resolver.resolve(block.data, treePath)
// Something to follow recusively, hence push it into the queue
if (link) {
const cid = IPLDResolver._maybeCID(link)
return cid
if (CID.isCID(result.value)) {
return result.value
} else {
return null
}
Expand Down Expand Up @@ -343,7 +338,7 @@ class IPLDResolver {
const format = await this._getFormat(cid.codec)
block = await promisify(this.bs.get.bind(this.bs))(cid)

const paths = await promisify(format.resolver.tree)(block.data)
const paths = format.resolver.tree(block.data)
treePaths.push(...paths)
}

Expand Down Expand Up @@ -394,43 +389,6 @@ class IPLDResolver {
this.addFormat(format)
return format
}

async _store (cid, node) {
const format = await this._getFormat(cid.codec)
const serialized = await promisify(format.util.serialize)(node)
const block = new Block(serialized, cid)
await promisify(this.bs.put.bind(this.bs))(block)
}

/**
* Deserialize a given block
*
* @param {Object} block - The block to deserialize
* @return {Object} = Returns the deserialized node
*/
async _deserialize (block) {
const format = await this._getFormat(block.cid.codec)
return promisify(format.util.deserialize)(block.data)
}

/**
* Return a CID instance if it is a link.
*
* If something is a link `{"/": "baseencodedcid"}` or a CID, then return
* a CID object, else return `null`.
*
* @param {*} link - The object to check
* @returns {?CID} - A CID instance
*/
static _maybeCID (link) {
if (CID.isCID(link)) {
return link
}
if (link && link['/'] !== undefined) {
return new CID(link['/'])
}
return null
}
}

/**
Expand Down
27 changes: 12 additions & 15 deletions test/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

'use strict'

const series = require('async/series')
const IPFSRepo = require('ipfs-repo')
const promisify = require('promisify-es6')

const basePath = 'ipfs' + Math.random()

Expand All @@ -19,22 +19,19 @@ idb.deleteDatabase(basePath + '/blocks')
describe('Browser', () => {
const repo = new IPFSRepo(basePath)

before((done) => {
series([
(cb) => repo.init({}, cb),
(cb) => repo.open(cb)
], done)
const repoInit = promisify(repo.init.bind(repo))
const repoOpen = promisify(repo.open.bind(repo))
const repoClose = promisify(repo.close.bind(repo))

before(async () => {
await repoInit({})
await repoOpen()
})

after((done) => {
series([
(cb) => repo.close(cb),
(cb) => {
idb.deleteDatabase(basePath)
idb.deleteDatabase(basePath + '/blocks')
cb()
}
], done)
after(async () => {
await repoClose()
idb.deleteDatabase(basePath)
idb.deleteDatabase(basePath + '/blocks')
})

require('./basics')(repo)
Expand Down
95 changes: 31 additions & 64 deletions test/ipld-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ const expect = chai.expect
chai.use(chaiAsProised)
chai.use(dirtyChai)
const dagPB = require('ipld-dag-pb')
const dagCBOR = require('ipld-dag-cbor')
const each = require('async/each')
const waterfall = require('async/waterfall')
const CID = require('cids')
const inMemory = require('ipld-in-memory')
const multicodec = require('multicodec')
const promisify = require('promisify-es6')

const IPLDResolver = require('../src')

Expand All @@ -31,92 +29,61 @@ describe('IPLD Resolver for dag-cbor + dag-pb', () => {
let cidCbor
let cidPb

before((done) => {
waterfall([
(cb) => inMemory(IPLDResolver, cb),
(res, cb) => {
resolver = res
dagPB.DAGNode.create(Buffer.from('I am inside a Protobuf'), cb)
},
(node, cb) => {
nodePb = node
dagPB.util.cid(nodePb, cb)
},
(cid, cb) => {
cidPb = cid
nodeCbor = {
someData: 'I am inside a Cbor object',
pb: cidPb
}

dagCBOR.util.cid(nodeCbor, cb)
},
(cid, cb) => {
cidCbor = cid

each([
{ node: nodePb, format: multicodec.DAG_PB, cidVersion: 0 },
{ node: nodeCbor, format: multicodec.DAG_CBOR, cidVersion: 1 }
], (nac, cb) => {
resolver.put(nac.node, nac.format, {
cidVersion: nac.cidVersion
}).then(
() => cb(null),
(error) => cb(error)
)
}, cb)
}
], done)
before(async () => {
resolver = await new Promise((resolve, reject) => {
inMemory(IPLDResolver, (error, res) => {
if (error) reject(error)
else resolve(res)
})
})

nodePb = dagPB.DAGNode.create(Buffer.from('I am inside a Protobuf'))
cidPb = await resolver.put(nodePb, multicodec.DAG_PB, { cidVersion: 0 })

nodeCbor = {
someData: 'I am inside a Cbor object',
pb: cidPb
}

cidCbor = await resolver.put(nodeCbor, multicodec.DAG_CBOR)
})

it('resolve through different formats', async () => {
const result = resolver.resolve(cidCbor, 'pb/Data')
const [node1, node2] = await result.all()

expect(node1.remainderPath).to.eql('Data')
expect(node1.value).to.eql(cidPb)

expect(node1.value.equals(cidPb)).to.be.true()
expect(node2.remainderPath).to.eql('')
expect(node2.value).to.eql(Buffer.from('I am inside a Protobuf'))
})

it('does not store nodes when onlyHash is passed', (done) => {
waterfall([
(cb) => dagPB.DAGNode.create(Buffer.from('Some data here'), cb),
(node, cb) => {
resolver.put(node, multicodec.DAG_PB, {
onlyHash: true,
cidVersion: 1,
hashAlg: multicodec.SHA2_256
}).then(
(cid) => cb(null, cid),
(error) => cb(error)
)
},
(cid, cb) => resolver.bs._repo.blocks.has(cid, cb)
], (error, result) => {
if (error) {
return done(error)
}

expect(result).to.be.false()
done()
it('does not store nodes when onlyHash is passed', async () => {
const node = dagPB.DAGNode.create(Buffer.from('Some data here'))
const cid = await resolver.put(node, multicodec.DAG_PB, {
onlyHash: true,
cidVersion: 1,
hashAlg: multicodec.SHA2_256
})
const result = await promisify(resolver.bs._repo.blocks.has)(cid)
expect(result).to.be.false()
})

describe('get', () => {
it('should return nodes correctly', async () => {
const result = resolver.getMany([cidCbor, cidPb])
const [node1, node2] = await result.all()
expect(node1).to.eql(nodeCbor)
expect(node1.someData).to.eql(nodeCbor.someData)
expect(node1.pb.equals(nodeCbor.pb)).to.be.true()
expect(node2).to.eql(nodePb)
})

it('should return nodes in input order', async () => {
const result = resolver.getMany([cidPb, cidCbor])
const [node1, node2] = await result.all()
expect(node1).to.eql(nodePb)
expect(node2).to.eql(nodeCbor)
expect(node2.someData).to.eql(nodeCbor.someData)
expect(node2.pb.equals(nodeCbor.pb)).to.be.true()
})

it('should return error on invalid CID', async () => {
Expand Down
Loading

0 comments on commit 108aef0

Please sign in to comment.