Skip to content

Commit 0f89dc3

Browse files
committed
feat: store blocks under multihash key
BREAKING CHANGE: repo.blocks.query() now returns multihashes as a key instead of CID. If you want to have CID returned call it as query({}, true), which will constructs CIDv1 using IPLD's RAW codec. This means that this constructed CID might not equal to the one that the block was originally saved. Related to ipfs/js-ipfs#2415
1 parent 3eb9216 commit 0f89dc3

7 files changed

+102
-63
lines changed

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,25 @@ Get block.
199199

200200
* `cid` is the content id of [type CID](https://github.com/ipld/js-cid#readme).
201201

202+
#### `Promise<boolean> repo.blocks.has (obj)`
203+
204+
Indicate if block is present
205+
206+
* `obj` is either the content id of [type CID](https://github.com/ipld/js-cid#readme) or [multihash](https://github.com/multiformats/js-multihashing).
207+
208+
#### `Promise<boolean> repo.blocks.delete (obj)`
209+
210+
Deletes
211+
212+
* `obj` is either the content id of [type CID](https://github.com/ipld/js-cid#readme) or [multihash](https://github.com/multiformats/js-multihashing).
213+
214+
#### `Promise<Array<Object>> repo.blocks.query (query, reconstructsCids)`
215+
216+
Query what blocks are available in blockstore.
217+
218+
* `query` is a object as specified in [interface-datastore](https://github.com/ipfs/interface-datastore#query).
219+
* `reconstructsCids` a flag defining if the block's key is a reconstructed CID (eq. CIDv1 with RAW IPLD codec) or multihash
220+
202221
Datastore:
203222

204223
#### `repo.datastore`

src/blockstore-utils.js

+40-5
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,58 @@ const CID = require('cids')
77
/**
88
* Transform a cid to the appropriate datastore key.
99
*
10-
* @param {CID} cid
10+
* @param {Buffer} multihash
1111
* @returns {Key}
1212
*/
13-
exports.cidToKey = cid => {
13+
exports.multihashToKey = multihash => {
1414
const enc = new base32.Encoder()
15-
return new Key('/' + enc.write(cid.buffer).finalize(), false)
15+
return new Key('/' + enc.write(multihash).finalize(), false)
1616
}
1717

1818
/**
1919
* Transform a datastore Key instance to a CID
20+
* As Key is a multihash of the CID, it is reconstructed using IPLD's RAW codec.
21+
* Hence it is highly probable that stored CID will differ from a CID retrieved from blockstore.
2022
*
2123
* @param {Key} key
2224
* @returns {CID}
2325
*/
24-
exports.keyToCid = key => {
26+
27+
function keyToCid (key) {
28+
// Block key is of the form /<base32 encoded string>
29+
const decoder = new base32.Decoder()
30+
const buff = decoder.write(key.toString().slice(1)).finalize()
31+
return new CID(1, 'raw', Buffer.from(buff))
32+
}
33+
34+
exports.keyToCid = keyToCid
35+
36+
/**
37+
* Transform a datastore Key instance to a multihash instance.
38+
*
39+
* @param {Key} key
40+
* @returns {Buffer}
41+
*/
42+
43+
function keyToMultihash (key) {
2544
// Block key is of the form /<base32 encoded string>
2645
const decoder = new base32.Decoder()
2746
const buff = decoder.write(key.toString().slice(1)).finalize()
28-
return new CID(Buffer.from(buff))
47+
return Buffer.from(buff)
48+
}
49+
50+
exports.keyToMultihash = keyToMultihash
51+
52+
/**
53+
* Transforms a datastore Key containing multihash to a Key that contains reconstructed CID
54+
*
55+
* @param {Key} key
56+
* @returns {CID}
57+
*/
58+
function keyToCidKey (key) {
59+
const cid = keyToCid(key)
60+
const enc = new base32.Encoder()
61+
return new Key('/' + enc.write(cid.buffer).finalize(), false)
2962
}
63+
64+
exports.keyToCidKey = keyToCidKey

src/blockstore.js

+34-50
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const ShardingStore = core.ShardingDatastore
55
const Block = require('ipfs-block')
66
const CID = require('cids')
77
const errcode = require('err-code')
8-
const { cidToKey } = require('./blockstore-utils')
8+
const { multihashToKey, keyToCidKey } = require('./blockstore-utils')
99

1010
module.exports = async (filestore, options) => {
1111
const store = await maybeWithSharding(filestore, options)
@@ -26,10 +26,15 @@ function createBaseStore (store) {
2626
* Query the store.
2727
*
2828
* @param {object} query
29+
* @param {boolean} reconstructsCids - Defines if Keys are converted to a reconstructed CID using IPLD_RAW codec
2930
* @return {Iterable}
3031
*/
31-
async * query (query) {
32+
async * query (query, reconstructsCids = false) {
3233
for await (const block of store.query(query)) {
34+
if (reconstructsCids) {
35+
block.key = keyToCidKey(block.key)
36+
}
37+
3338
yield block
3439
}
3540
},
@@ -43,27 +48,9 @@ function createBaseStore (store) {
4348
if (!CID.isCID(cid)) {
4449
throw errcode(new Error('Not a valid cid'), 'ERR_INVALID_CID')
4550
}
46-
const key = cidToKey(cid)
47-
let blockData
48-
try {
49-
blockData = await store.get(key)
50-
return new Block(blockData, cid)
51-
} catch (err) {
52-
if (err.code === 'ERR_NOT_FOUND') {
53-
const otherCid = cidToOtherVersion(cid)
54-
55-
if (!otherCid) {
56-
throw err
57-
}
58-
59-
const otherKey = cidToKey(otherCid)
60-
const blockData = await store.get(otherKey)
61-
await store.put(key, blockData)
62-
return new Block(blockData, cid)
63-
}
64-
65-
throw err
66-
}
51+
const key = multihashToKey(cid.multihash)
52+
const blockData = await store.get(key)
53+
return new Block(blockData, cid)
6754
},
6855
/**
6956
* Write a single block to the store.
@@ -76,7 +63,7 @@ function createBaseStore (store) {
7663
throw new Error('invalid block')
7764
}
7865

79-
const k = cidToKey(block.cid)
66+
const k = multihashToKey(block.cid.multihash)
8067
const exists = await store.has(k)
8168
if (exists) return
8269
return store.put(k, block.data)
@@ -92,7 +79,7 @@ function createBaseStore (store) {
9279
const batch = store.batch()
9380

9481
for await (const block of blocks) {
95-
const key = cidToKey(block.cid)
82+
const key = multihashToKey(block.cid.multihash)
9683

9784
if (await store.has(key)) {
9885
continue
@@ -104,33 +91,38 @@ function createBaseStore (store) {
10491
return batch.commit()
10592
},
10693
/**
107-
* Does the store contain block with this cid?
94+
* Does the store contain block with this multihash or CID?
10895
*
109-
* @param {CID} cid
110-
* @returns {Promise<bool>}
96+
* @param {CID|Buffer} obj
97+
* @returns {Promise<boolean>}
11198
*/
112-
async has (cid) {
113-
if (!CID.isCID(cid)) {
114-
throw errcode(new Error('Not a valid cid'), 'ERR_INVALID_CID')
99+
has (obj) {
100+
if (CID.isCID(obj)) {
101+
obj = obj.multihash
115102
}
116103

117-
const exists = await store.has(cidToKey(cid))
118-
if (exists) return exists
119-
const otherCid = cidToOtherVersion(cid)
120-
if (!otherCid) return false
121-
return store.has(cidToKey(otherCid))
104+
if (!Buffer.isBuffer(obj)) {
105+
throw errcode(new Error('Not a valid key'), 'ERR_INVALID_KEY')
106+
}
107+
108+
return store.has(multihashToKey(obj))
122109
},
123110
/**
124-
* Delete a block from the store
111+
* Delete a CID or multihash from the store
125112
*
126-
* @param {CID} cid
113+
* @param {CID|Buffer} obj
127114
* @returns {Promise<void>}
128115
*/
129-
async delete (cid) { // eslint-disable-line require-await
130-
if (!CID.isCID(cid)) {
131-
throw errcode(new Error('Not a valid cid'), 'ERR_INVALID_CID')
116+
async delete (obj) { // eslint-disable-line require-await
117+
if (CID.isCID(obj)) {
118+
obj = obj.multihash
119+
}
120+
121+
if (!Buffer.isBuffer(obj)) {
122+
throw errcode(new Error('Not a valid key'), 'ERR_INVALID_KEY')
132123
}
133-
return store.delete(cidToKey(cid))
124+
125+
return store.delete(multihashToKey(obj))
134126
},
135127
/**
136128
* Close the store
@@ -142,11 +134,3 @@ function createBaseStore (store) {
142134
}
143135
}
144136
}
145-
146-
function cidToOtherVersion (cid) {
147-
try {
148-
return cid.version === 0 ? cid.toV1() : cid.toV0()
149-
} catch (err) {
150-
return null
151-
}
152-
}

src/constants.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict'
22

33
module.exports = {
4-
repoVersion: 7
4+
repoVersion: 8
55
}

src/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,11 @@ class IpfsRepo {
318318
let count = new Big(0)
319319
let size = new Big(0)
320320

321-
for await (const block of this.blocks.query({})) {
321+
for await (const block of this.blocks.query({}, false)) {
322322
count = count.plus(1)
323323
size = size
324324
.plus(block.value.byteLength)
325-
.plus(block.key._buf.byteLength)
325+
.plus(block.key.toBuffer().byteLength)
326326
}
327327

328328
return { count, size }
@@ -333,7 +333,7 @@ async function getSize (queryFn) {
333333
const sum = new Big(0)
334334
for await (const block of queryFn.query({})) {
335335
sum.plus(block.value.byteLength)
336-
.plus(block.key._buf.byteLength)
336+
.plus(block.key.toBuffer().byteLength)
337337
}
338338
return sum
339339
}

test/blockstore-test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ module.exports = (repo) => {
281281
await repo.blocks.has('foo')
282282
throw new Error('Should have thrown')
283283
} catch (err) {
284-
expect(err.code).to.equal('ERR_INVALID_CID')
284+
expect(err.code).to.equal('ERR_INVALID_KEY')
285285
}
286286
})
287287

@@ -307,7 +307,7 @@ module.exports = (repo) => {
307307
await repo.blocks.delete('foo')
308308
throw new Error('Should have thrown')
309309
} catch (err) {
310-
expect(err.code).to.equal('ERR_INVALID_CID')
310+
expect(err.code).to.equal('ERR_INVALID_KEY')
311311
}
312312
})
313313
})

test/blockstore-utils-test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ const Repo = require('../src')
1111
module.exports = () => {
1212
describe('blockstore utils', () => {
1313
it('converts a CID to a datastore Key and back', () => {
14-
const originalCid = new CID('Qme6KJdKcp85TYbLxuLV7oQzMiLremD7HMoXLZEmgo6Rnh')
15-
const key = Repo.utils.blockstore.cidToKey(originalCid)
14+
// CIDv1 in base32 with IPLD raw codec
15+
const originalCid = new CID('bafkreihkb3vrxxex5zvzkr3s3a6noe223r7jka4ofjy2nkzu27kueg76ii')
16+
const key = Repo.utils.blockstore.multihashToKey(originalCid.multihash)
1617
expect(key instanceof Key).to.be.true()
1718
const cid = Repo.utils.blockstore.keyToCid(key)
1819
expect(cid instanceof CID).to.be.true()

0 commit comments

Comments
 (0)