@atproto/repo 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +22 -17
  3. package/jest.config.cjs +0 -24
  4. package/src/block-map.ts +0 -131
  5. package/src/car.ts +0 -357
  6. package/src/cid-set.ts +0 -55
  7. package/src/data-diff.ts +0 -117
  8. package/src/error.ts +0 -43
  9. package/src/index.ts +0 -11
  10. package/src/logger.ts +0 -7
  11. package/src/mst/diff.ts +0 -114
  12. package/src/mst/index.ts +0 -4
  13. package/src/mst/mst.ts +0 -892
  14. package/src/mst/util.ts +0 -160
  15. package/src/mst/walker.ts +0 -118
  16. package/src/parse.ts +0 -44
  17. package/src/readable-repo.ts +0 -86
  18. package/src/repo.ts +0 -236
  19. package/src/storage/index.ts +0 -4
  20. package/src/storage/memory-blockstore.ts +0 -76
  21. package/src/storage/readable-blockstore.ts +0 -55
  22. package/src/storage/sync-storage.ts +0 -35
  23. package/src/storage/types.ts +0 -47
  24. package/src/sync/consumer.ts +0 -207
  25. package/src/sync/index.ts +0 -2
  26. package/src/sync/provider.ts +0 -67
  27. package/src/types.ts +0 -227
  28. package/src/util.ts +0 -146
  29. package/tests/_keys.ts +0 -156
  30. package/tests/_util.ts +0 -265
  31. package/tests/car-file-fixtures.json +0 -28
  32. package/tests/car.test.ts +0 -125
  33. package/tests/commit-data.test.ts +0 -94
  34. package/tests/commit-proof-fixtures.json +0 -118
  35. package/tests/commit-proofs.test.ts +0 -63
  36. package/tests/covering-proofs.test.ts +0 -256
  37. package/tests/mst.test.ts +0 -450
  38. package/tests/proofs.test.ts +0 -155
  39. package/tests/repo.test.ts +0 -106
  40. package/tests/sync.test.ts +0 -95
  41. package/tsconfig.build.json +0 -8
  42. package/tsconfig.build.tsbuildinfo +0 -1
  43. package/tsconfig.json +0 -7
  44. package/tsconfig.tests.json +0 -7
package/tests/mst.test.ts DELETED
@@ -1,450 +0,0 @@
1
- import assert from 'node:assert'
2
- import { Cid, parseCid } from '@atproto/lex-data'
3
- import { DataAdd, DataDelete, DataDiff, DataUpdate } from '../src/data-diff.js'
4
- import { MST } from '../src/mst/index.js'
5
- import { InvalidMstKeyError, countPrefixLen } from '../src/mst/util.js'
6
- import { MemoryBlockstore } from '../src/storage/index.js'
7
- import * as util from './_util.js'
8
-
9
- describe('Merkle Search Tree', () => {
10
- let blockstore: MemoryBlockstore
11
- let mst: MST
12
- let mapping: Record<string, Cid>
13
- let shuffled: [string, Cid][]
14
-
15
- beforeAll(async () => {
16
- blockstore = new MemoryBlockstore()
17
- mst = await MST.create(blockstore)
18
- mapping = await util.generateBulkDataKeys(1000, blockstore)
19
- shuffled = util.shuffle(Object.entries(mapping))
20
- })
21
-
22
- it('adds records', async () => {
23
- for (const entry of shuffled) {
24
- mst = await mst.add(entry[0], entry[1])
25
- }
26
- for (const entry of shuffled) {
27
- const got = await mst.get(entry[0])
28
- assert(got !== null)
29
- expect(entry[1].equals(got)).toBe(true)
30
- }
31
-
32
- const totalSize = await mst.leafCount()
33
- expect(totalSize).toBe(1000)
34
- })
35
-
36
- it('edits records', async () => {
37
- let editedMst = mst
38
- const toEdit = shuffled.slice(0, 100)
39
-
40
- const edited: [string, Cid][] = []
41
- for (const entry of toEdit) {
42
- const newCid = await util.randomCid()
43
- editedMst = await editedMst.update(entry[0], newCid)
44
- edited.push([entry[0], newCid])
45
- }
46
-
47
- for (const entry of edited) {
48
- const got = await editedMst.get(entry[0])
49
- assert(got !== null)
50
- expect(entry[1].equals(got)).toBe(true)
51
- }
52
-
53
- const totalSize = await editedMst.leafCount()
54
- expect(totalSize).toBe(1000)
55
- })
56
-
57
- it('deletes records', async () => {
58
- let deletedMst = mst
59
- const toDelete = shuffled.slice(0, 100)
60
- const theRest = shuffled.slice(100)
61
- for (const entry of toDelete) {
62
- deletedMst = await deletedMst.delete(entry[0])
63
- }
64
-
65
- const totalSize = await deletedMst.leafCount()
66
- expect(totalSize).toBe(900)
67
-
68
- for (const entry of toDelete) {
69
- const got = await deletedMst.get(entry[0])
70
- expect(got).toBe(null)
71
- }
72
- for (const entry of theRest) {
73
- const got = await deletedMst.get(entry[0])
74
- assert(got !== null)
75
- expect(entry[1].equals(got)).toBe(true)
76
- }
77
- })
78
-
79
- it('is order independent', async () => {
80
- const allNodes = await mst.allNodes()
81
-
82
- let recreated = await MST.create(blockstore)
83
- const reshuffled = util.shuffle(Object.entries(mapping))
84
- for (const entry of reshuffled) {
85
- recreated = await recreated.add(entry[0], entry[1])
86
- }
87
- const allReshuffled = await recreated.allNodes()
88
-
89
- expect(allNodes.length).toBe(allReshuffled.length)
90
- for (let i = 0; i < allNodes.length; i++) {
91
- expect(await allNodes[i].equals(allReshuffled[i])).toBeTruthy()
92
- }
93
- })
94
-
95
- it('saves and loads from blockstore', async () => {
96
- const root = await util.saveMst(blockstore, mst)
97
- const loaded = await MST.load(blockstore, root)
98
- const origNodes = await mst.allNodes()
99
- const loadedNodes = await loaded.allNodes()
100
- expect(origNodes.length).toBe(loadedNodes.length)
101
- for (let i = 0; i < origNodes.length; i++) {
102
- expect(await origNodes[i].equals(loadedNodes[i])).toBeTruthy()
103
- }
104
- })
105
-
106
- it('diffs', async () => {
107
- let toDiff = mst
108
-
109
- const toAdd = Object.entries(
110
- await util.generateBulkDataKeys(100, blockstore),
111
- )
112
- const toEdit = shuffled.slice(500, 600)
113
- const toDel = shuffled.slice(400, 500)
114
-
115
- const expectedAdds: Record<string, DataAdd> = {}
116
- const expectedUpdates: Record<string, DataUpdate> = {}
117
- const expectedDels: Record<string, DataDelete> = {}
118
-
119
- for (const entry of toAdd) {
120
- toDiff = await toDiff.add(entry[0], entry[1])
121
- expectedAdds[entry[0]] = { key: entry[0], cid: entry[1] }
122
- }
123
- for (const entry of toEdit) {
124
- const updated = await util.randomCid()
125
- toDiff = await toDiff.update(entry[0], updated)
126
- expectedUpdates[entry[0]] = {
127
- key: entry[0],
128
- prev: entry[1],
129
- cid: updated,
130
- }
131
- }
132
- for (const entry of toDel) {
133
- toDiff = await toDiff.delete(entry[0])
134
- expectedDels[entry[0]] = { key: entry[0], cid: entry[1] }
135
- }
136
-
137
- const diff = await DataDiff.of(toDiff, mst)
138
-
139
- expect(diff.addList().length).toBe(100)
140
- expect(diff.updateList().length).toBe(100)
141
- expect(diff.deleteList().length).toBe(100)
142
-
143
- expect(diff.adds).toEqual(expectedAdds)
144
- expect(diff.updates).toEqual(expectedUpdates)
145
- expect(diff.deletes).toEqual(expectedDels)
146
-
147
- // ensure we correctly report all added CIDs
148
- for await (const entry of toDiff.walk()) {
149
- const cid = entry.isTree() ? await entry.getPointer() : entry.value
150
- const found =
151
- (await blockstore.has(cid)) ||
152
- diff.newMstBlocks.has(cid) ||
153
- diff.newLeafCids.has(cid)
154
- expect(found).toBeTruthy()
155
- }
156
- })
157
-
158
- describe('utils', () => {
159
- it('counts prefix length', () => {
160
- expect(countPrefixLen('abc', 'abc')).toBe(3)
161
- expect(countPrefixLen('', 'abc')).toBe(0)
162
- expect(countPrefixLen('abc', '')).toBe(0)
163
- expect(countPrefixLen('ab', 'abc')).toBe(2)
164
- expect(countPrefixLen('abc', 'ab')).toBe(2)
165
- expect(countPrefixLen('abcde', 'abc')).toBe(3)
166
- expect(countPrefixLen('abc', 'abcde')).toBe(3)
167
- expect(countPrefixLen('abcde', 'abc1')).toBe(3)
168
- expect(countPrefixLen('abcde', 'abb')).toBe(2)
169
- expect(countPrefixLen('abcde', 'qbb')).toBe(0)
170
- expect(countPrefixLen('', 'asdf')).toBe(0)
171
- expect(countPrefixLen('abc', 'abc\x00')).toBe(3)
172
- expect(countPrefixLen('abc\x00', 'abc')).toBe(3)
173
- })
174
- })
175
-
176
- describe('MST Interop Allowable Keys', () => {
177
- let blockstore: MemoryBlockstore
178
- let mst: MST
179
- let cid1: Cid
180
-
181
- beforeAll(async () => {
182
- blockstore = new MemoryBlockstore()
183
- mst = await MST.create(blockstore)
184
- cid1 = parseCid(
185
- 'bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454',
186
- )
187
- })
188
-
189
- const expectReject = async (key: string) => {
190
- const promise = mst.add(key, cid1)
191
- await expect(promise).rejects.toThrow(InvalidMstKeyError)
192
- }
193
-
194
- const expectAllow = async (key: string) => {
195
- await mst.add(key, cid1)
196
- }
197
-
198
- it('rejects the empty key', async () => {
199
- await expectReject('')
200
- })
201
-
202
- it('rejects a key with no collection', async () => {
203
- await expectReject('asdf')
204
- })
205
-
206
- it('rejects a key with a nested collection', async () => {
207
- await expectReject('nested/collection/asdf')
208
- })
209
-
210
- it('rejects on empty coll or rkey', async () => {
211
- await expectReject('coll/')
212
- await expectReject('/rkey')
213
- })
214
-
215
- it('rejects non-ascii chars', async () => {
216
- await expectReject('coll/jalapeñoA')
217
- await expectReject('coll/coöperative')
218
- await expectReject('coll/abc💩')
219
- })
220
-
221
- it('rejects ascii that we dont support', async () => {
222
- await expectReject('coll/key$')
223
- await expectReject('coll/key%')
224
- await expectReject('coll/key(')
225
- await expectReject('coll/key)')
226
- await expectReject('coll/key+')
227
- await expectReject('coll/key=')
228
- await expectReject('coll/@handle')
229
- await expectReject('coll/any space')
230
- await expectReject('coll/#extra')
231
- await expectReject('coll/any+space')
232
- await expectReject('coll/number[3]')
233
- await expectReject('coll/number(3)')
234
- await expectReject('coll/dHJ1ZQ==')
235
- await expectReject('coll/"quote"')
236
- })
237
-
238
- it('rejects keys over 1024 chars', async () => {
239
- await expectReject(
240
- 'coll/asdofiupoiwqeurfpaosidfuapsodirupasoirupasoeiruaspeoriuaspeoriu2p3o4iu1pqw3oiuaspdfoiuaspdfoiuasdfpoiasdufpwoieruapsdofiuaspdfoiuasdpfoiausdfpoasidfupasodifuaspdofiuasdpfoiasudfpoasidfuapsodfiuasdpfoiausdfpoasidufpasodifuapsdofiuasdpofiuasdfpoaisdufpaoasdofiupoiwqeurfpaosidfuapsodirupasoirupasoeiruaspeoriuaspeoriu2p3o4iu1pqw3oiuaspdfoiuaspdfoiuasdfpoiasdufpwoieruapsdofiuaspdfoiuasdpfoiausdfpoasidfupasodifuaspdofiuasdpfoiasudfpoasidfuapsodfiuasdpfoiausdfpoasidufpasodifuapsdofiuasdpofiuasdfpoaisdufpaoasdofiupoiwqeurfpaosidfuapsodirupasoirupasoeiruaspeoriuaspeoriu2p3o4iu1pqw3oiuaspdfoiuaspdfoiuasdfpoiasdufpwoieruapsdofiuaspdfoiuasdpfoiausdfpoasidfupasodifuaspdofiuasdpfoiasudfpoasidfuapsodfiuasdpfoiausdfpoasidufpasodifuapsdofiuasdpofiuasdfpoaisdufpaoasdofiupoiwqeurfpaosidfuapsodirupasoirupasoeiruaspeoriuaspeoriu2p3o4iu1pqw3oiuaspdfoiuaspdfoiuasdfpoiasdufpwoieruapsdofiuaspdfoiuasdpfoiausdfpoasidfupasodifuaspdofiuasdpfoiasudfpoasidfuapsodfiuasdpfoiausdfpoasidufpasodifuapsdofiuasdpofiuasdfpoaisdufpaoasdofiupoiwqeurfpaosidfuapsodirupasoirupasoeiruaspeoriuaspeoriu2p3o4iu1pqw3oiuaspdfoiuaspdfoiuasdfpoiasdufpwoieruapsdofiuaspdfoiuasdpfoiausdfpoasidfupasodifuaspdofiuasdpfoiasudfpoasidfuapsodfiuasdpfoiausdfpoasidufpasodifuapsdofiuasdpofiuasdfpoaisdufpao',
241
- )
242
- })
243
-
244
- it('allows valid keys', async () => {
245
- await expectAllow('coll/3jui7kd54zh2y')
246
- await expectAllow('coll/self')
247
- await expectAllow('coll/example.com')
248
- await expectAllow('com.example/rkey')
249
- await expectAllow('coll/~1.2-3_')
250
- await expectAllow('coll/dHJ1ZQ')
251
- await expectAllow('coll/pre:fix')
252
- await expectAllow('coll/_')
253
- })
254
- })
255
-
256
- describe('MST Interop Known Maps', () => {
257
- let blockstore: MemoryBlockstore
258
- let mst: MST
259
- let cid1: Cid
260
-
261
- beforeAll(async () => {
262
- blockstore = new MemoryBlockstore()
263
- cid1 = parseCid(
264
- 'bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454',
265
- )
266
- })
267
-
268
- beforeEach(async () => {
269
- mst = await MST.create(blockstore)
270
- })
271
-
272
- it('computes "empty" tree root CID', async () => {
273
- expect(await mst.leafCount()).toBe(0)
274
- expect((await mst.getPointer()).toString()).toBe(
275
- 'bafyreie5737gdxlw5i64vzichcalba3z2v5n6icifvx5xytvske7mr3hpm',
276
- )
277
- })
278
-
279
- it('computes "trivial" tree root CID', async () => {
280
- mst = await mst.add('com.example.record/3jqfcqzm3fo2j', cid1)
281
- expect(await mst.leafCount()).toBe(1)
282
- expect((await mst.getPointer()).toString()).toBe(
283
- 'bafyreibj4lsc3aqnrvphp5xmrnfoorvru4wynt6lwidqbm2623a6tatzdu',
284
- )
285
- })
286
-
287
- it('computes "singlelayer2" tree root CID', async () => {
288
- mst = await mst.add('com.example.record/3jqfcqzm3fx2j', cid1)
289
- expect(await mst.leafCount()).toBe(1)
290
- expect(await mst.layer).toBe(2)
291
- expect((await mst.getPointer()).toString()).toBe(
292
- 'bafyreih7wfei65pxzhauoibu3ls7jgmkju4bspy4t2ha2qdjnzqvoy33ai',
293
- )
294
- })
295
-
296
- it('computes "simple" tree root CID', async () => {
297
- mst = await mst.add('com.example.record/3jqfcqzm3fp2j', cid1) // level 0
298
- mst = await mst.add('com.example.record/3jqfcqzm3fr2j', cid1) // level 0
299
- mst = await mst.add('com.example.record/3jqfcqzm3fs2j', cid1) // level 1
300
- mst = await mst.add('com.example.record/3jqfcqzm3ft2j', cid1) // level 0
301
- mst = await mst.add('com.example.record/3jqfcqzm4fc2j', cid1) // level 0
302
- expect(await mst.leafCount()).toBe(5)
303
- expect((await mst.getPointer()).toString()).toBe(
304
- 'bafyreicmahysq4n6wfuxo522m6dpiy7z7qzym3dzs756t5n7nfdgccwq7m',
305
- )
306
- })
307
- })
308
-
309
- describe('MST Interop Edge Cases', () => {
310
- let blockstore: MemoryBlockstore
311
- let mst: MST
312
- let cid1: Cid
313
-
314
- beforeAll(async () => {
315
- blockstore = new MemoryBlockstore()
316
- cid1 = parseCid(
317
- 'bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454',
318
- )
319
- })
320
-
321
- beforeEach(async () => {
322
- mst = await MST.create(blockstore)
323
- })
324
-
325
- it('trims top of tree on delete', async () => {
326
- const l1root =
327
- 'bafyreifnqrwbk6ffmyaz5qtujqrzf5qmxf7cbxvgzktl4e3gabuxbtatv4'
328
- const l0root =
329
- 'bafyreie4kjuxbwkhzg2i5dljaswcroeih4dgiqq6pazcmunwt2byd725vi'
330
-
331
- mst = await mst.add('com.example.record/3jqfcqzm3fn2j', cid1) // level 0
332
- mst = await mst.add('com.example.record/3jqfcqzm3fo2j', cid1) // level 0
333
- mst = await mst.add('com.example.record/3jqfcqzm3fp2j', cid1) // level 0
334
- mst = await mst.add('com.example.record/3jqfcqzm3fs2j', cid1) // level 1
335
- mst = await mst.add('com.example.record/3jqfcqzm3ft2j', cid1) // level 0
336
- mst = await mst.add('com.example.record/3jqfcqzm3fu2j', cid1) // level 0
337
-
338
- expect(await mst.leafCount()).toBe(6)
339
- expect(await mst.layer).toBe(1)
340
- expect((await mst.getPointer()).toString()).toBe(l1root)
341
-
342
- mst = await mst.delete('com.example.record/3jqfcqzm3fs2j') // level 1
343
- expect(await mst.leafCount()).toBe(5)
344
- expect(await mst.layer).toBe(0)
345
- expect((await mst.getPointer()).toString()).toBe(l0root)
346
- })
347
-
348
- /**
349
- *
350
- * * *
351
- * _________|________ ____|_____
352
- * | | | | | | | |
353
- * * d * i * -> * f *
354
- * __|__ __|__ __|__ __|__ __|___
355
- * | | | | | | | | | | | | | | |
356
- * a b c e g h j k l * d * * i *
357
- * __|__ | _|_ __|__
358
- * | | | | | | | | |
359
- * a b c e g h j k l
360
- *
361
- */
362
- it('handles insertion that splits two layers down', async () => {
363
- const l1root =
364
- 'bafyreiettyludka6fpgp33stwxfuwhkzlur6chs4d2v4nkmq2j3ogpdjem'
365
- const l2root =
366
- 'bafyreid2x5eqs4w4qxvc5jiwda4cien3gw2q6cshofxwnvv7iucrmfohpm'
367
-
368
- mst = await mst.add('com.example.record/3jqfcqzm3fo2j', cid1) // A; level 0
369
- mst = await mst.add('com.example.record/3jqfcqzm3fp2j', cid1) // B; level 0
370
- mst = await mst.add('com.example.record/3jqfcqzm3fr2j', cid1) // C; level 0
371
- mst = await mst.add('com.example.record/3jqfcqzm3fs2j', cid1) // D; level 1
372
- mst = await mst.add('com.example.record/3jqfcqzm3ft2j', cid1) // E; level 0
373
- // GAP for F
374
- mst = await mst.add('com.example.record/3jqfcqzm3fz2j', cid1) // G; level 0
375
- mst = await mst.add('com.example.record/3jqfcqzm4fc2j', cid1) // H; level 0
376
- mst = await mst.add('com.example.record/3jqfcqzm4fd2j', cid1) // I; level 1
377
- mst = await mst.add('com.example.record/3jqfcqzm4ff2j', cid1) // J; level 0
378
- mst = await mst.add('com.example.record/3jqfcqzm4fg2j', cid1) // K; level 0
379
- mst = await mst.add('com.example.record/3jqfcqzm4fh2j', cid1) // L; level 0
380
-
381
- expect(await mst.leafCount()).toBe(11)
382
- expect(await mst.layer).toBe(1)
383
- expect((await mst.getPointer()).toString()).toBe(l1root)
384
-
385
- // insert F, which will push E out of the node with G+H to a new node under D
386
- mst = await mst.add('com.example.record/3jqfcqzm3fx2j', cid1) // F; level 2
387
- expect(await mst.leafCount()).toBe(12)
388
- expect(await mst.layer).toBe(2)
389
- expect((await mst.getPointer()).toString()).toBe(l2root)
390
-
391
- // remove F, which should push E back over with G+H
392
- mst = await mst.delete('com.example.record/3jqfcqzm3fx2j') // F; level 2
393
- expect(await mst.leafCount()).toBe(11)
394
- expect(await mst.layer).toBe(1)
395
- expect((await mst.getPointer()).toString()).toBe(l1root)
396
- })
397
-
398
- /**
399
- *
400
- * * -> *
401
- * __|__ __|__
402
- * | | | | |
403
- * a c * b *
404
- * | |
405
- * * *
406
- * | |
407
- * a c
408
- *
409
- */
410
- it('handles new layers that are two higher than existing', async () => {
411
- const l0root =
412
- 'bafyreidfcktqnfmykz2ps3dbul35pepleq7kvv526g47xahuz3rqtptmky'
413
- const l2root =
414
- 'bafyreiavxaxdz7o7rbvr3zg2liox2yww46t7g6hkehx4i4h3lwudly7dhy'
415
- const l2root2 =
416
- 'bafyreig4jv3vuajbsybhyvb7gggvpwh2zszwfyttjrj6qwvcsp24h6popu'
417
-
418
- mst = await mst.add('com.example.record/3jqfcqzm3ft2j', cid1) // A; level 0
419
- mst = await mst.add('com.example.record/3jqfcqzm3fz2j', cid1) // C; level 0
420
- expect(await mst.leafCount()).toBe(2)
421
- expect(await mst.layer).toBe(0)
422
- expect((await mst.getPointer()).toString()).toBe(l0root)
423
-
424
- // insert B, which is two levels above
425
- mst = await mst.add('com.example.record/3jqfcqzm3fx2j', cid1) // B; level 2
426
- expect(await mst.leafCount()).toBe(3)
427
- expect(await mst.layer).toBe(2)
428
- expect((await mst.getPointer()).toString()).toBe(l2root)
429
-
430
- // remove B
431
- mst = await mst.delete('com.example.record/3jqfcqzm3fx2j') // B; level 2
432
- expect(await mst.leafCount()).toBe(2)
433
- expect(await mst.layer).toBe(0)
434
- expect((await mst.getPointer()).toString()).toBe(l0root)
435
-
436
- // insert B (level=2) and D (level=1)
437
- mst = await mst.add('com.example.record/3jqfcqzm3fx2j', cid1) // B; level 2
438
- mst = await mst.add('com.example.record/3jqfcqzm4fd2j', cid1) // D; level 1
439
- expect(await mst.leafCount()).toBe(4)
440
- expect(await mst.layer).toBe(2)
441
- expect((await mst.getPointer()).toString()).toBe(l2root2)
442
-
443
- // remove D
444
- mst = await mst.delete('com.example.record/3jqfcqzm4fd2j') // D; level 1
445
- expect(await mst.leafCount()).toBe(3)
446
- expect(await mst.layer).toBe(2)
447
- expect((await mst.getPointer()).toString()).toBe(l2root)
448
- })
449
- })
450
- })
@@ -1,155 +0,0 @@
1
- import { TID } from '@atproto/common-web'
2
- import * as crypto from '@atproto/crypto'
3
- import { cidForLex } from '@atproto/lex-cbor'
4
- import { NsidString } from '@atproto/syntax'
5
- import { RecordCidClaim, RecordPath, Repo, RepoContents } from '../src/index.js'
6
- import { MemoryBlockstore } from '../src/storage/index.js'
7
- import * as sync from '../src/sync/index.js'
8
- import * as util from './_util.js'
9
-
10
- describe('Repo Proofs', () => {
11
- let storage: MemoryBlockstore
12
- let repo: Repo
13
- let keypair: crypto.Keypair
14
- let repoData: RepoContents
15
-
16
- const repoDid = 'did:example:test'
17
-
18
- beforeAll(async () => {
19
- storage = new MemoryBlockstore()
20
- keypair = await crypto.Secp256k1Keypair.create()
21
- repo = await Repo.create(storage, repoDid, keypair)
22
- const filled = await util.fillRepo(repo, keypair, 5)
23
- repo = filled.repo
24
- repoData = filled.data
25
- })
26
-
27
- const getProofs = async (claims: RecordPath[]) => {
28
- return util.toBuffer(sync.getRecords(storage, repo.cid, claims))
29
- }
30
-
31
- const doVerify = (proofs: Uint8Array, claims: RecordCidClaim[]) => {
32
- return sync.verifyProofs(proofs, claims, repoDid, keypair.did())
33
- }
34
-
35
- const contentsToClaims = async (
36
- contents: RepoContents,
37
- ): Promise<RecordCidClaim[]> => {
38
- const claims: RecordCidClaim[] = []
39
- for (const coll of Object.keys(contents) as NsidString[]) {
40
- for (const rkey of Object.keys(contents[coll])) {
41
- claims.push({
42
- collection: coll,
43
- rkey: rkey,
44
- cid: await cidForLex(contents[coll][rkey]),
45
- })
46
- }
47
- }
48
- return claims
49
- }
50
-
51
- it('verifies valid records', async () => {
52
- const claims = await contentsToClaims(repoData)
53
- const proofs = await getProofs(claims)
54
- const results = await doVerify(proofs, claims)
55
- expect(results.verified.length).toBeGreaterThan(0)
56
- expect(results.verified).toEqual(claims)
57
- expect(results.unverified.length).toBe(0)
58
- })
59
-
60
- it('verifies record nonexistence', async () => {
61
- const claims: RecordCidClaim[] = [
62
- {
63
- collection: util.testCollections[0],
64
- rkey: TID.nextStr(), // does not exist
65
- cid: null,
66
- },
67
- ]
68
- const proofs = await getProofs(claims)
69
- const results = await doVerify(proofs, claims)
70
- expect(results.verified.length).toBeGreaterThan(0)
71
- expect(results.verified).toEqual(claims)
72
- expect(results.unverified.length).toBe(0)
73
- })
74
-
75
- it('does not verify a record that doesnt exist', async () => {
76
- const realClaims = await contentsToClaims(repoData)
77
- const claims: RecordCidClaim[] = [
78
- {
79
- ...realClaims[0],
80
- rkey: TID.nextStr(),
81
- },
82
- ]
83
- const proofs = await getProofs(claims)
84
- const results = await doVerify(proofs, claims)
85
- expect(results.verified.length).toBe(0)
86
- expect(results.unverified.length).toBeGreaterThan(0)
87
- expect(results.unverified).toEqual(claims)
88
- })
89
-
90
- it('does not verify an invalid record at a real path', async () => {
91
- const realClaims = await contentsToClaims(repoData)
92
- const claims: RecordCidClaim[] = [
93
- {
94
- ...realClaims[0],
95
- cid: await util.randomCid(),
96
- },
97
- ]
98
- const proofs = await getProofs(claims)
99
- const results = await doVerify(proofs, claims)
100
- expect(results.verified.length).toBe(0)
101
- expect(results.unverified.length).toBeGreaterThan(0)
102
- expect(results.unverified).toEqual(claims)
103
- })
104
-
105
- it('does not verify a delete where the record does exist', async () => {
106
- const realClaims = await contentsToClaims(repoData)
107
- const claims: RecordCidClaim[] = [
108
- {
109
- collection: realClaims[0].collection,
110
- rkey: realClaims[0].rkey,
111
- cid: null,
112
- },
113
- ]
114
- const proofs = await getProofs(claims)
115
- const results = await doVerify(proofs, claims)
116
- expect(results.verified.length).toBe(0)
117
- expect(results.unverified.length).toBeGreaterThan(0)
118
- expect(results.unverified).toEqual(claims)
119
- })
120
-
121
- it('can determine record proofs from car file', async () => {
122
- const possible = await contentsToClaims(repoData)
123
- const claims = [
124
- //random sampling of records
125
- possible[0],
126
- possible[4],
127
- possible[5],
128
- possible[8],
129
- ]
130
- const proofs = await getProofs(claims)
131
- const records = await sync.verifyRecords(proofs, repoDid, keypair.did())
132
- for (const record of records) {
133
- const foundClaim = claims.find(
134
- (claim) =>
135
- claim.collection === record.collection && claim.rkey === record.rkey,
136
- )
137
- if (!foundClaim) {
138
- throw new Error('Could not find record for claim')
139
- }
140
- expect(foundClaim.cid).toEqual(
141
- await cidForLex(repoData[record.collection][record.rkey]),
142
- )
143
- }
144
- })
145
-
146
- it('verifyProofs throws on a bad signature', async () => {
147
- const badRepo = await util.addBadCommit(repo, keypair)
148
- const claims = await contentsToClaims(repoData)
149
- const proofs = await util.toBuffer(
150
- sync.getRecords(storage, badRepo.cid, claims),
151
- )
152
- const fn = sync.verifyProofs(proofs, claims, repoDid, keypair.did())
153
- await expect(fn).rejects.toThrow(sync.RepoVerificationError)
154
- })
155
- })