Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
feat: store blocks by multihash instead of CID (#3124)
Browse files Browse the repository at this point in the history
Updates the `ipfs-repo` dep to a version that stores blocks by multihash instead of CID to support CIDv1 and CIDv0 access to the same block.

New features:

- Adds a `--multihash` argument to the cli command `ipfs refs local` which prints the base32 encoded multihash of each block

BREAKING CHANGES:

- `ipfs.refs.local` now returns a v1 CID with the raw codec for every block and not the original CID by which it was added to the blockstore

Co-authored-by: Hugo Dias <hugomrdias@gmail.com>
  • Loading branch information
achingbrain and hugomrdias committed Jul 2, 2020
1 parent 0b64c3e commit 03b17f5
Show file tree
Hide file tree
Showing 105 changed files with 216 additions and 184 deletions.
2 changes: 2 additions & 0 deletions docs/core-api/REFS.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ for await (const ref of ipfs.refs(ipfsPath, { recursive: true })) {

> Output all local references (CIDs of all blocks in the blockstore)
Blocks in the blockstore are stored by multihash and not CID so yielded CIDs are v1 CIDs with the 'raw' codec. These may not match the CID originally used to store a given block, though the multihash contained within the CID will.

### Parameters

None
Expand Down
2 changes: 1 addition & 1 deletion examples/custom-ipfs-repo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"dependencies": {
"datastore-fs": "^1.1.0",
"ipfs": "^0.47.0",
"ipfs-repo": "^3.0.0",
"ipfs-repo": "^4.0.0",
"it-all": "^1.0.1"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions packages/interface-ipfs-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@
"it-last": "^1.0.1",
"it-pushable": "^1.3.1",
"multiaddr": "^7.4.3",
"multibase": "^0.7.0",
"multibase": "^1.0.1",
"multihashing-async": "^1.0.0",
"nanoid": "^3.0.2",
"peer-id": "^0.13.12",
"readable-stream": "^3.4.0",
"temp-write": "^4.0.0"
},
"devDependencies": {
"aegir": "^22.1.0",
"aegir": "^23.0.0",
"ipfsd-ctl": "^4.1.1"
},
"contributors": [
Expand Down
4 changes: 2 additions & 2 deletions packages/interface-ipfs-core/src/block/rm.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module.exports = (common, options) => {
// block should be present in the local store
const localRefs = await all(ipfs.refs.local())
expect(localRefs).to.have.property('length').that.is.greaterThan(0)
expect(localRefs.find(ref => ref.ref === cid.toString())).to.be.ok()
expect(localRefs.find(ref => ref.ref === new CID(1, 'raw', cid.multihash).toString())).to.be.ok()

const result = await all(ipfs.block.rm(cid))
expect(result).to.be.an('array').and.to.have.lengthOf(1)
Expand All @@ -49,7 +49,7 @@ module.exports = (common, options) => {

// did we actually remove the block?
const localRefsAfterRemove = await all(ipfs.refs.local())
expect(localRefsAfterRemove.find(ref => ref.ref === cid.toString())).to.not.be.ok()
expect(localRefsAfterRemove.find(ref => ref.ref === new CID(1, 'raw', cid.multihash).toString())).to.not.be.ok()
})

it('should remove by CID in string', async () => {
Expand Down
19 changes: 17 additions & 2 deletions packages/interface-ipfs-core/src/refs-local.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const all = require('it-all')
const importer = require('ipfs-unixfs-importer')
const drain = require('it-drain')
const testTimeout = require('./utils/test-timeout')
const CID = require('cids')

/** @typedef { import("ipfsd-ctl/src/factory") } Factory */
/**
Expand Down Expand Up @@ -54,8 +55,22 @@ module.exports = (common, options) => {

const refs = await all(ipfs.refs.local())
const cids = refs.map(r => r.ref)
expect(cids).to.include('QmVwdDCY4SPGVFnNCiZnX5CtzwWDn6kAM98JXzKxE3kCmn')
expect(cids).to.include('QmR4nFjTu18TyANgC65ArNWp5Yaab1gPzQ4D8zp7Kx3vhr')

expect(
cids.find(cid => {
const multihash = new CID(cid).multihash

return imported[0].cid.multihash.equals(multihash)
})
).to.be.ok()

expect(
cids.find(cid => {
const multihash = new CID(cid).multihash

return imported[1].cid.multihash.equals(multihash)
})
).to.be.ok()
})
})
}
42 changes: 18 additions & 24 deletions packages/interface-ipfs-core/src/repo/gc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const { getDescribe, getIt, expect } = require('../utils/mocha')
const { DAGNode } = require('ipld-dag-pb')
const all = require('it-all')
const testTimeout = require('../utils/test-timeout')
const CID = require('cids')

/** @typedef { import("ipfsd-ctl/src/factory") } Factory */
/**
Expand Down Expand Up @@ -58,15 +59,15 @@ module.exports = (common, options) => {
// the initial list and contain hash
const refsAfterAdd = await all(ipfs.refs.local())
expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length)
expect(refsAfterAdd.map(r => r.ref)).includes(cid.toString())
expect(refsAfterAdd.map(r => new CID(r.ref).multihash)).deep.includes(cid.multihash)

// Run garbage collection
await all(ipfs.repo.gc())

// Get the list of local blocks after GC, should still contain the hash,
// because the file is still pinned
const refsAfterGc = await all(ipfs.refs.local())
expect(refsAfterGc.map(r => r.ref)).includes(cid.toString())
expect(refsAfterGc.map(r => new CID(r.ref).multihash)).deep.includes(cid.multihash)

// Unpin the data
await ipfs.pin.rm(cid)
Expand All @@ -76,7 +77,7 @@ module.exports = (common, options) => {

// The list of local blocks should no longer contain the hash
const refsAfterUnpinAndGc = await all(ipfs.refs.local())
expect(refsAfterUnpinAndGc.map(r => r.ref)).not.includes(cid.toString())
expect(refsAfterUnpinAndGc.map(r => new CID(r.ref).multihash)).not.deep.includes(cid.multihash)
})

it('should clean up removed MFS files', async () => {
Expand All @@ -87,21 +88,20 @@ module.exports = (common, options) => {
await ipfs.files.write('/test', Buffer.from('oranges'), { create: true })
const stats = await ipfs.files.stat('/test')
expect(stats.type).to.equal('file')
const hash = stats.cid.toString()

// Get the list of local blocks after the add, should be bigger than
// the initial list and contain hash
const refsAfterAdd = await all(ipfs.refs.local())
expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length)
expect(refsAfterAdd.map(r => r.ref)).includes(hash)
expect(refsAfterAdd.map(r => new CID(r.ref).multihash)).deep.includes(stats.cid.multihash)

// Run garbage collection
await all(ipfs.repo.gc())

// Get the list of local blocks after GC, should still contain the hash,
// because the file is in MFS
const refsAfterGc = await all(ipfs.refs.local())
expect(refsAfterGc.map(r => r.ref)).includes(hash)
expect(refsAfterGc.map(r => new CID(r.ref).multihash)).deep.includes(stats.cid.multihash)

// Remove the file
await ipfs.files.rm('/test')
Expand All @@ -111,7 +111,7 @@ module.exports = (common, options) => {

// The list of local blocks should no longer contain the hash
const refsAfterUnpinAndGc = await all(ipfs.refs.local())
expect(refsAfterUnpinAndGc.map(r => r.ref)).not.includes(hash)
expect(refsAfterUnpinAndGc.map(r => new CID(r.ref).multihash)).not.deep.includes(stats.cid.multihash)
})

it('should clean up block only after unpinned and removed from MFS', async () => {
Expand All @@ -135,17 +135,15 @@ module.exports = (common, options) => {
// the initial list and contain the data hash
const refsAfterAdd = await all(ipfs.refs.local())
expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length)
const hashesAfterAdd = refsAfterAdd.map(r => r.ref)
expect(hashesAfterAdd).includes(dataCid.toString())
expect(refsAfterAdd.map(r => new CID(r.ref).multihash)).deep.includes(dataCid.multihash)

// Run garbage collection
await all(ipfs.repo.gc())

// Get the list of local blocks after GC, should still contain the hash,
// because the file is pinned and in MFS
const refsAfterGc = await all(ipfs.refs.local())
const hashesAfterGc = refsAfterGc.map(r => r.ref)
expect(hashesAfterGc).includes(dataCid.toString())
expect(refsAfterGc.map(r => new CID(r.ref).multihash)).deep.includes(dataCid.multihash)

// Remove the file
await ipfs.files.rm('/test')
Expand All @@ -156,9 +154,8 @@ module.exports = (common, options) => {
// Get the list of local blocks after GC, should still contain the hash,
// because the file is still pinned
const refsAfterRmAndGc = await all(ipfs.refs.local())
const hashesAfterRmAndGc = refsAfterRmAndGc.map(r => r.ref)
expect(hashesAfterRmAndGc).not.includes(mfsFileCid.toString())
expect(hashesAfterRmAndGc).includes(dataCid.toString())
expect(refsAfterRmAndGc.map(r => new CID(r.ref).multihash)).not.deep.includes(mfsFileCid.multihash)
expect(refsAfterRmAndGc.map(r => new CID(r.ref).multihash)).deep.includes(dataCid.multihash)

// Unpin the data
await ipfs.pin.rm(dataCid)
Expand All @@ -168,9 +165,8 @@ module.exports = (common, options) => {

// The list of local blocks should no longer contain the hashes
const refsAfterUnpinAndGc = await all(ipfs.refs.local())
const hashesAfterUnpinAndGc = refsAfterUnpinAndGc.map(r => r.ref)
expect(hashesAfterUnpinAndGc).not.includes(mfsFileCid.toString())
expect(hashesAfterUnpinAndGc).not.includes(dataCid.toString())
expect(refsAfterUnpinAndGc.map(r => new CID(r.ref).multihash)).not.deep.includes(mfsFileCid.multihash)
expect(refsAfterUnpinAndGc.map(r => new CID(r.ref).multihash)).not.deep.includes(dataCid.multihash)
})

it('should clean up indirectly pinned data after recursive pin removal', async () => {
Expand Down Expand Up @@ -201,9 +197,8 @@ module.exports = (common, options) => {
// the initial list and contain data and object hash
const refsAfterAdd = await all(ipfs.refs.local())
expect(refsAfterAdd.length).to.be.gt(refsBeforeAdd.length)
const hashesAfterAdd = refsAfterAdd.map(r => r.ref)
expect(hashesAfterAdd).includes(objCid.toString())
expect(hashesAfterAdd).includes(dataCid.toString())
expect(refsAfterAdd.map(r => new CID(r.ref).multihash)).deep.includes(objCid.multihash)
expect(refsAfterAdd.map(r => new CID(r.ref).multihash)).deep.includes(dataCid.multihash)

// Recursively pin the object
await ipfs.pin.add(objCid, { recursive: true })
Expand All @@ -218,7 +213,7 @@ module.exports = (common, options) => {
// Get the list of local blocks after GC, should still contain the data
// hash, because the data is still (indirectly) pinned
const refsAfterGc = await all(ipfs.refs.local())
expect(refsAfterGc.map(r => r.ref)).includes(dataCid.toString())
expect(refsAfterGc.map(r => new CID(r.ref).multihash)).deep.includes(dataCid.multihash)

// Recursively unpin the object
await ipfs.pin.rm(objCid.toString())
Expand All @@ -228,9 +223,8 @@ module.exports = (common, options) => {

// The list of local blocks should no longer contain the hashes
const refsAfterUnpinAndGc = await all(ipfs.refs.local())
const hashesAfterUnpinAndGc = refsAfterUnpinAndGc.map(r => r.ref)
expect(hashesAfterUnpinAndGc).not.includes(objCid.toString())
expect(hashesAfterUnpinAndGc).not.includes(dataCid.toString())
expect(refsAfterUnpinAndGc.map(r => new CID(r.ref).multihash)).not.deep.includes(objCid.multihash)
expect(refsAfterUnpinAndGc.map(r => new CID(r.ref).multihash)).not.deep.includes(dataCid.multihash)
})
})
}
2 changes: 1 addition & 1 deletion packages/ipfs-core-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"ipfs-utils": "^2.2.2"
},
"devDependencies": {
"aegir": "^22.1.0",
"aegir": "^23.0.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"delay": "^4.3.0",
Expand Down
6 changes: 3 additions & 3 deletions packages/ipfs-http-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@
"merge-options": "^2.0.0",
"multiaddr": "^7.4.3",
"multiaddr-to-uri": "^5.1.0",
"multibase": "^0.7.0",
"multibase": "^1.0.1",
"multicodec": "^1.0.0",
"multihashes": "^1.0.1",
"nanoid": "^3.0.2",
"node-fetch": "^2.6.0",
"parse-duration": "^0.1.2",
"parse-duration": "^0.4.4",
"stream-to-it": "^0.2.0"
},
"devDependencies": {
"aegir": "^22.1.0",
"aegir": "^23.0.0",
"cross-env": "^7.0.0",
"go-ipfs-dep": "^0.5.1",
"interface-ipfs-core": "^0.137.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/ipfs-http-client/src/lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const Multiaddr = require('multiaddr')
const toUri = require('multiaddr-to-uri')
const { isBrowser, isWebWorker } = require('ipfs-utils/src/env')
const { URL } = require('iso-url')
const parseDuration = require('parse-duration')
const parseDuration = require('parse-duration').default
const log = require('debug')('ipfs-http-client:lib:error-handler')
const HTTP = require('ipfs-utils/src/http')
const merge = require('merge-options')
Expand Down
14 changes: 7 additions & 7 deletions packages/ipfs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"ipfs-core-utils": "^0.2.4",
"ipfs-http-client": "^44.3.0",
"ipfs-http-response": "^0.5.0",
"ipfs-repo": "^3.0.0",
"ipfs-repo": "^4.0.0",
"ipfs-unixfs": "^1.0.3",
"ipfs-unixfs-exporter": "^2.0.2",
"ipfs-unixfs-importer": "^2.0.2",
Expand All @@ -117,7 +117,7 @@
"it-all": "^1.0.1",
"it-concat": "^1.0.0",
"it-drain": "^1.0.1",
"it-glob": "0.0.7",
"it-glob": "0.0.8",
"it-last": "^1.0.1",
"it-map": "^1.0.0",
"it-multipart": "^1.0.1",
Expand Down Expand Up @@ -148,19 +148,19 @@
"mortice": "^2.0.0",
"multiaddr": "^7.4.3",
"multiaddr-to-uri": "^5.1.0",
"multibase": "^0.7.0",
"multibase": "^1.0.1",
"multicodec": "^1.0.0",
"multihashing-async": "^1.0.0",
"p-defer": "^3.0.0",
"p-queue": "^6.1.0",
"parse-duration": "^0.1.2",
"parse-duration": "^0.4.4",
"peer-id": "^0.13.12",
"pretty-bytes": "^5.3.0",
"progress": "^2.0.1",
"protons": "^1.2.0",
"semver": "^7.3.2",
"stream-to-it": "^0.2.0",
"streaming-iterables": "^4.1.1",
"streaming-iterables": "^5.0.0",
"temp": "^0.9.0",
"timeout-abort-controller": "^1.1.0",
"update-notifier": "^4.0.0",
Expand All @@ -170,7 +170,7 @@
"yargs-promise": "^1.1.0"
},
"devDependencies": {
"aegir": "^22.1.0",
"aegir": "^23.0.0",
"base64url": "^3.0.1",
"clear-module": "^4.0.0",
"cross-env": "^7.0.0",
Expand All @@ -191,7 +191,7 @@
"qs": "^6.9.3",
"rimraf": "^3.0.0",
"sinon": "^9.0.1",
"stream-to-promise": "^2.2.0",
"stream-to-promise": "^3.0.0",
"string-argv": "^0.3.1",
"temp-write": "^4.0.0",
"wrtc": "^0.4.4"
Expand Down
4 changes: 2 additions & 2 deletions packages/ipfs/src/cli/commands/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const {
} = require('../utils')
const { cidToString } = require('../../utils/cid')
const globSource = require('ipfs-utils/src/files/glob-source')
const parseDuration = require('parse-duration')
const parseDuration = require('parse-duration').default

async function getTotalBytes (paths) {
const sizes = await Promise.all(paths.map(p => getFolderSize(p)))
Expand Down Expand Up @@ -90,7 +90,7 @@ module.exports = {
'cid-base': {
describe: 'Number base to display CIDs in.',
type: 'string',
choices: multibase.names
choices: Object.keys(multibase.names)
},
hash: {
type: 'string',
Expand Down
4 changes: 2 additions & 2 deletions packages/ipfs/src/cli/commands/bitswap/stat.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const multibase = require('multibase')
const { cidToString } = require('../../../utils/cid')
const prettyBytes = require('pretty-bytes')
const parseDuration = require('parse-duration')
const parseDuration = require('parse-duration').default

module.exports = {
command: 'stat',
Expand All @@ -14,7 +14,7 @@ module.exports = {
'cid-base': {
describe: 'Number base to display CIDs in. Note: specifying a CID base for v0 CIDs will have no effect.',
type: 'string',
choices: multibase.names
choices: Object.keys(multibase.names)
},
human: {
type: 'boolean',
Expand Down
4 changes: 2 additions & 2 deletions packages/ipfs/src/cli/commands/bitswap/unwant.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const multibase = require('multibase')
const { cidToString } = require('../../../utils/cid')
const parseDuration = require('parse-duration')
const parseDuration = require('parse-duration').default

module.exports = {
command: 'unwant <key>',
Expand All @@ -18,7 +18,7 @@ module.exports = {
'cid-base': {
describe: 'Number base to display CIDs in. Note: specifying a CID base for v0 CIDs will have no effect.',
type: 'string',
choices: multibase.names
choices: Object.keys(multibase.names)
},
timeout: {
type: 'string',
Expand Down
Loading

0 comments on commit 03b17f5

Please sign in to comment.