Skip to content

Commit 3057d1d

Browse files
AuHauachingbrain
authored andcommitted
feat: integration of js-ipfs-repo-migrations
Integration of js-ipfs-repo-migrations brings automatic repo migrations to ipfs-repo (both in-browser and fs). It is possible to control the automatic migration using either config's setting 'repoDisableAutoMigration' or IPFSRepo's option 'disableAutoMigration'. 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 4692a47 commit 3057d1d

9 files changed

+80
-83
lines changed

README.md

+30-6
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ This is the implementation of the [IPFS repo spec](https://github.com/ipfs/specs
4141
- [Blocks](#blocks)
4242
- [`Promise<Block> repo.blocks.put(block:Block)`](#promiseblock-repoblocksputblockblock)
4343
- [`AsyncIterator<Block> repo.blocks.putMany(source)`](#asynciteratorblock-repoblocksputmanysource)
44-
- [`Promise<Buffer> repo.blocks.get(cid)`](#promisebuffer-repoblocksgetcid)
45-
- [`AsyncIterable<Buffer> repo.blocks.getMany(source)`](#asynciterablebuffer-repoblocksgetmanysource)
44+
- [`Promise<Block> repo.blocks.get(cid)`](#promiseblock-repoblocksgetcid)
45+
- [`AsyncIterable<Block> repo.blocks.getMany(source)`](#asynciterableblock-repoblocksgetmanysource)
46+
- [`Promise<boolean> repo.blocks.has (obj)`](#promiseboolean-repoblockshas-obj)
47+
- [`Promise<boolean> repo.blocks.delete (obj)`](#promiseboolean-repoblocksdelete-obj)
48+
- [`Promise<Array<Object>> repo.blocks.query (query, reconstructsCids)`](#promisearrayobject-repoblocksquery-query-reconstructscids)
4649
- [`Promise<CID> repo.blocks.delete(cid:CID)`](#promisecid-repoblocksdeletecidcid)
4750
- [`AsyncIterator<CID> repo.blocks.deleteMany(source)`](#asynciteratorcid-repoblocksdeletemanysource)
4851
- [Datastore](#datastore)
@@ -235,18 +238,39 @@ Put many blocks.
235238

236239
* `source` should be an AsyncIterable that yields entries of type [Block][]
237240

238-
#### `Promise<Buffer> repo.blocks.get(cid)`
241+
#### `Promise<Block> repo.blocks.get(cid)`
239242

240243
Get block.
241244

242245
* `cid` is the content id of type [CID][]
243246

244-
#### `AsyncIterable<Buffer> repo.blocks.getMany(source)`
247+
#### `AsyncIterable<Block> repo.blocks.getMany(source)`
245248

246-
Get block.
249+
Get many blocks
247250

248251
* `source` should be an AsyncIterable that yields entries of type [CID][]
249252

253+
#### `Promise<boolean> repo.blocks.has (obj)`
254+
255+
Indicate if block is present
256+
257+
* `obj` is either the content id of [type CID](https://github.com/ipld/js-cid#readme) or [multihash](https://github.com/multiformats/js-multihashing).
258+
259+
#### `Promise<boolean> repo.blocks.delete (obj)`
260+
261+
Deletes
262+
263+
* `obj` is either the content id of [type CID](https://github.com/ipld/js-cid#readme) or [multihash](https://github.com/multiformats/js-multihashing).
264+
265+
#### `Promise<Array<Object>> repo.blocks.query (query, reconstructsCids)`
266+
267+
Query what blocks are available in blockstore.
268+
269+
* `query` is a object as specified in [interface-datastore](https://github.com/ipfs/interface-datastore#query).
270+
* `reconstructsCids` a flag defining if the block's key is a reconstructed CID (eq. CIDv1 with RAW IPLD codec) or multihash
271+
272+
Datastore:
273+
250274
#### `Promise<CID> repo.blocks.delete(cid:CID)`
251275

252276
* `cid` should be of the type [CID][]
@@ -379,7 +403,7 @@ Returned promise resolves to a `boolean` indicating the existence of the lock.
379403

380404
### Migrations
381405

382-
When there is a new repo migration and the version of repo is increased, don't
406+
When there is a new repo migration and the version of the repo is increased, don't
383407
forget to propagate the changes into the test repo (`test/test-repo`).
384408

385409
**For tools that run mainly in the browser environment, be aware that disabling automatic

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"sinon": "^9.0.2"
6060
},
6161
"dependencies": {
62+
"base32.js": "^0.1.0",
6263
"bignumber.js": "^9.0.0",
6364
"buffer": "^5.6.0",
6465
"bytes": "^3.1.0",
@@ -69,7 +70,7 @@
6970
"debug": "^4.1.0",
7071
"err-code": "^2.0.0",
7172
"interface-datastore": "^1.0.2",
72-
"ipfs-repo-migrations": "^0.2.1",
73+
"ipfs-repo-migrations": "github:ipfs/js-ipfs-repo-migrations#migration/8-multihash_and_keys",
7374
"ipfs-utils": "^2.2.0",
7475
"ipld-block": "^0.9.1",
7576
"it-map": "^1.0.2",

src/blockstore-utils.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
const { Key } = require('interface-datastore')
44
const CID = require('cids')
5-
const multibase = require('multibase')
6-
const errcode = require('err-code')
5+
const errCode = require('err-code')
6+
const base32 = require('base32.js')
77

88
/**
99
* Transform a cid to the appropriate datastore key.
@@ -12,19 +12,25 @@ const errcode = require('err-code')
1212
* @returns {Key}
1313
*/
1414
exports.cidToKey = cid => {
15-
if (!CID.isCID(cid)) {
16-
throw errcode(new Error('Not a valid cid'), 'ERR_INVALID_CID')
15+
if (!cid || !cid.multihash) {
16+
throw errCode(new Error('Invalid Multihash'), 'ERR_INVALID_CID')
1717
}
1818

19-
return new Key('/' + multibase.encode('base32', cid.buffer).toString().slice(1).toUpperCase(), false)
19+
const enc = new base32.Encoder()
20+
return new Key('/' + enc.write(cid.multihash).finalize(), false)
2021
}
2122

2223
/**
2324
* Transform a datastore Key instance to a CID
25+
* As Key is a multihash of the CID, it is reconstructed using IPLD's RAW codec.
26+
* Hence it is highly probable that stored CID will differ from a CID retrieved from blockstore.
2427
*
2528
* @param {Key} key
2629
* @returns {CID}
2730
*/
2831
exports.keyToCid = key => {
29-
return new CID(multibase.decode('b' + key.toString().slice(1).toLowerCase()))
32+
// Block key is of the form /<base32 encoded string>
33+
const decoder = new base32.Decoder()
34+
const buff = decoder.write(key.toString().slice(1)).finalize()
35+
return new CID(1, 'raw', Buffer.from(buff))
3036
}

src/blockstore.js

+24-63
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
const core = require('datastore-core')
44
const ShardingStore = core.ShardingDatastore
55
const Block = require('ipld-block')
6-
const { cidToKey, keyToCid } = require('./blockstore-utils')
7-
const map = require('it-map')
8-
const pipe = require('it-pipe')
6+
const { cidToKey } = require('./blockstore-utils')
97

108
module.exports = async (filestore, options) => {
119
const store = await maybeWithSharding(filestore, options)
@@ -25,7 +23,7 @@ function createBaseStore (store) {
2523
/**
2624
* Query the store.
2725
*
28-
* @param {Object} query
26+
* @param {object} query
2927
* @param {Object} options
3028
* @returns {AsyncIterator<Block>}
3129
*/
@@ -41,26 +39,9 @@ function createBaseStore (store) {
4139
*/
4240
async get (cid, options) {
4341
const key = cidToKey(cid)
44-
let blockData
45-
try {
46-
blockData = await store.get(key, options)
47-
return new Block(blockData, cid)
48-
} catch (err) {
49-
if (err.code === 'ERR_NOT_FOUND') {
50-
const otherCid = cidToOtherVersion(cid)
42+
const blockData = await store.get(key, options)
5143

52-
if (!otherCid) {
53-
throw err
54-
}
55-
56-
const otherKey = cidToKey(otherCid)
57-
const blockData = await store.get(otherKey, options)
58-
await store.put(key, blockData)
59-
return new Block(blockData, cid)
60-
}
61-
62-
throw err
63-
}
44+
return new Block(blockData, cid)
6445
},
6546
/**
6647
* Like get, but for more.
@@ -86,15 +67,14 @@ function createBaseStore (store) {
8667
throw new Error('invalid block')
8768
}
8869

89-
const exists = await this.has(block.cid)
70+
const key = cidToKey(block.cid)
71+
const exists = await store.has(key, options)
9072

9173
if (exists) {
92-
return this.get(block.cid, options)
74+
return
9375
}
9476

95-
await store.put(cidToKey(block.cid), block.data, options)
96-
97-
return block
77+
return store.put(key, block.data, options)
9878
},
9979

10080
/**
@@ -104,43 +84,32 @@ function createBaseStore (store) {
10484
* @param {Object} options
10585
* @returns {AsyncIterable<Block>}
10686
*/
107-
async * putMany (blocks, options) { // eslint-disable-line require-await
108-
yield * pipe(
109-
blocks,
110-
(source) => {
111-
// turn them into a key/value pair
112-
return map(source, (block) => {
113-
return { key: cidToKey(block.cid), value: block.data }
114-
})
115-
},
116-
(source) => {
117-
// put them into the datastore
118-
return store.putMany(source, options)
119-
},
120-
(source) => {
121-
// map the returned key/value back into a block
122-
return map(source, ({ key, value }) => {
123-
return new Block(value, keyToCid(key))
124-
})
87+
async * putMany (blocks, options) {
88+
for await (const block of blocks) {
89+
const key = cidToKey(block.cid)
90+
91+
if (await store.has(key)) {
92+
continue
12593
}
126-
)
94+
95+
await store.put(key, block.data, options)
96+
97+
yield block
98+
}
12799
},
100+
128101
/**
129-
* Does the store contain block with this cid?
102+
* Does the store contain block with this CID?
130103
*
131104
* @param {CID} cid
132105
* @param {Object} options
133106
* @returns {Promise<bool>}
134107
*/
135-
async has (cid, options) {
136-
const exists = await store.has(cidToKey(cid), options)
137-
if (exists) return exists
138-
const otherCid = cidToOtherVersion(cid)
139-
if (!otherCid) return false
140-
return store.has(cidToKey(otherCid), options)
108+
async has (cid, options) { // eslint-disable-line require-await
109+
return store.has(cidToKey(cid), options)
141110
},
142111
/**
143-
* Delete a block from the store
112+
* Delete a CID or multihash from the store
144113
*
145114
* @param {CID} cid
146115
* @param {Object} options
@@ -173,11 +142,3 @@ function createBaseStore (store) {
173142
}
174143
}
175144
}
176-
177-
function cidToOtherVersion (cid) {
178-
try {
179-
return cid.version === 0 ? cid.toV1() : cid.toV0()
180-
} catch (err) {
181-
return null
182-
}
183-
}

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
@@ -350,11 +350,11 @@ class IpfsRepo {
350350
let count = new Big(0)
351351
let size = new Big(0)
352352

353-
for await (const block of this.blocks.query({})) {
353+
for await (const block of this.blocks.query({}, false)) {
354354
count = count.plus(1)
355355
size = size
356356
.plus(block.value.byteLength)
357-
.plus(block.key._buf.byteLength)
357+
.plus(block.key.toBuffer().byteLength)
358358
}
359359

360360
return { count, size }
@@ -365,7 +365,7 @@ async function getSize (queryFn) {
365365
const sum = new Big(0)
366366
for await (const block of queryFn.query({})) {
367367
sum.plus(block.value.byteLength)
368-
.plus(block.key._buf.byteLength)
368+
.plus(block.key.toBuffer().byteLength)
369369
}
370370
return sum
371371
}

test/blockstore-test.js

+4
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ module.exports = (repo) => {
157157
throw err
158158
}
159159
}
160+
161+
has () {
162+
return true
163+
}
160164
}
161165
},
162166
storageBackendOptions: {

test/blockstore-utils-test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const Repo = require('../src')
99
module.exports = () => {
1010
describe('blockstore utils', () => {
1111
it('converts a CID to a datastore Key and back', () => {
12-
const originalCid = new CID('Qme6KJdKcp85TYbLxuLV7oQzMiLremD7HMoXLZEmgo6Rnh')
12+
// CIDv1 in base32 with IPLD raw codec
13+
const originalCid = new CID('bafkreihkb3vrxxex5zvzkr3s3a6noe223r7jka4ofjy2nkzu27kueg76ii')
1314
const key = Repo.utils.blockstore.cidToKey(originalCid)
1415
expect(key instanceof Key).to.be.true()
1516
const cid = Repo.utils.blockstore.keyToCid(key)

test/repo-test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ module.exports = (repo) => {
5656

5757
describe('version', () => {
5858
afterEach(async () => {
59-
await repo.version.set(7)
59+
await repo.version.set(8)
6060
})
6161

6262
it('get version', async () => {
6363
const version = await repo.version.get()
64-
expect(version).to.equal(7)
64+
expect(version).to.equal(8)
6565
})
6666

6767
it('set version', async () => {

0 commit comments

Comments
 (0)