@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/_util.ts DELETED
@@ -1,265 +0,0 @@
1
- import fs from 'node:fs'
2
- import { TID } from '@atproto/common-web'
3
- import * as crypto from '@atproto/crypto'
4
- import { Keypair, randomBytes } from '@atproto/crypto'
5
- import * as cbor from '@atproto/lex-cbor'
6
- import { Cid, cidForCbor, parseCid } from '@atproto/lex-data'
7
- import { NsidString } from '@atproto/syntax'
8
- import {
9
- BlockMap,
10
- CollectionContents,
11
- Commit,
12
- CommitData,
13
- DataDiff,
14
- RecordPath,
15
- RecordWriteOp,
16
- RepoContents,
17
- WriteOpAction,
18
- } from '../src/index.js'
19
- import { MST } from '../src/mst/index.js'
20
- import { Repo } from '../src/repo.js'
21
- import { RepoStorage } from '../src/storage/index.js'
22
-
23
- type IdMapping = Record<string, Cid>
24
-
25
- export const randomCid = async (storage?: RepoStorage): Promise<Cid> => {
26
- const bytes = cbor.encode({ test: randomStr(50) })
27
- const cid = await cidForCbor(bytes)
28
- if (storage) {
29
- // @ts-expect-error FIXME remove this comment (and fix the TS error)
30
- await storage.putBlock(cid, bytes)
31
- }
32
- return cid
33
- }
34
-
35
- export const generateBulkDataKeys = async (
36
- count: number,
37
- blockstore?: RepoStorage,
38
- ): Promise<IdMapping> => {
39
- const obj: IdMapping = {}
40
- for (let i = 0; i < count; i++) {
41
- const key = `com.example.record/${TID.nextStr()}`
42
- obj[key] = await randomCid(blockstore)
43
- }
44
- return obj
45
- }
46
-
47
- export const keysFromMapping = (mapping: IdMapping): TID[] => {
48
- return Object.keys(mapping).map((id) => TID.fromStr(id))
49
- }
50
-
51
- export const keysFromMappings = (mappings: IdMapping[]): TID[] => {
52
- return mappings.map(keysFromMapping).flat()
53
- }
54
-
55
- export const randomStr = (len: number): string => {
56
- let result = ''
57
- const CHARS = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
58
- for (let i = 0; i < len; i++) {
59
- result += CHARS.charAt(Math.floor(Math.random() * CHARS.length))
60
- }
61
- return result
62
- }
63
-
64
- export const shuffle = <T>(arr: T[]): T[] => {
65
- const toShuffle = [...arr]
66
- const shuffled: T[] = []
67
- while (toShuffle.length > 0) {
68
- const index = Math.floor(Math.random() * toShuffle.length)
69
- shuffled.push(toShuffle[index])
70
- toShuffle.splice(index, 1)
71
- }
72
- return shuffled
73
- }
74
-
75
- export const generateObject = (): Record<string, string> => {
76
- return {
77
- name: randomStr(100),
78
- }
79
- }
80
-
81
- // Mass repo mutations & checking
82
- // -------------------------------
83
-
84
- export const testCollections: NsidString[] = [
85
- 'com.example.posts',
86
- 'com.example.likes',
87
- ]
88
-
89
- export const fillRepo = async (
90
- repo: Repo,
91
- keypair: crypto.Keypair,
92
- itemsPerCollection: number,
93
- ): Promise<{ repo: Repo; data: RepoContents }> => {
94
- const repoData: RepoContents = {}
95
- const writes: RecordWriteOp[] = []
96
- for (const collName of testCollections) {
97
- const collData: CollectionContents = {}
98
- for (let i = 0; i < itemsPerCollection; i++) {
99
- const object = generateObject()
100
- const rkey = TID.nextStr()
101
- collData[rkey] = object
102
- writes.push({
103
- action: WriteOpAction.Create,
104
- collection: collName,
105
- rkey,
106
- record: object,
107
- })
108
- }
109
- repoData[collName] = collData
110
- }
111
- const updated = await repo.applyWrites(writes, keypair)
112
- return {
113
- repo: updated,
114
- data: repoData,
115
- }
116
- }
117
-
118
- export const formatEdit = async (
119
- repo: Repo,
120
- prevData: RepoContents,
121
- keypair: crypto.Keypair,
122
- params: {
123
- adds?: number
124
- updates?: number
125
- deletes?: number
126
- },
127
- ): Promise<{ commit: CommitData; data: RepoContents }> => {
128
- const { adds = 0, updates = 0, deletes = 0 } = params
129
- const repoData: RepoContents = {}
130
- const writes: RecordWriteOp[] = []
131
- for (const collName of testCollections) {
132
- const collData = { ...(prevData[collName] ?? {}) }
133
- const shuffled = shuffle(Object.entries(collData))
134
-
135
- for (let i = 0; i < adds; i++) {
136
- const object = generateObject()
137
- const rkey = TID.nextStr()
138
- collData[rkey] = object
139
- writes.push({
140
- action: WriteOpAction.Create,
141
- collection: collName,
142
- rkey,
143
- record: object,
144
- })
145
- }
146
-
147
- const toUpdate = shuffled.slice(0, updates)
148
- for (let i = 0; i < toUpdate.length; i++) {
149
- const object = generateObject()
150
- const rkey = toUpdate[i][0]
151
- collData[rkey] = object
152
- writes.push({
153
- action: WriteOpAction.Update,
154
- collection: collName,
155
- rkey,
156
- record: object,
157
- })
158
- }
159
-
160
- const toDelete = shuffled.slice(updates, deletes)
161
- for (let i = 0; i < toDelete.length; i++) {
162
- const rkey = toDelete[i][0]
163
- delete collData[rkey]
164
- writes.push({
165
- action: WriteOpAction.Delete,
166
- collection: collName,
167
- rkey,
168
- })
169
- }
170
- repoData[collName] = collData
171
- }
172
- const commit = await repo.formatCommit(writes, keypair)
173
- return {
174
- commit,
175
- data: repoData,
176
- }
177
- }
178
-
179
- export const pathsForOps = (ops: RecordWriteOp[]): RecordPath[] =>
180
- ops.map((op) => ({ collection: op.collection, rkey: op.rkey }))
181
-
182
- export const saveMst = async (storage: RepoStorage, mst: MST): Promise<Cid> => {
183
- const diff = await mst.getUnstoredBlocks()
184
- // @ts-expect-error FIXME remove this comment (and fix the TS error)
185
- await storage.putMany(diff.blocks)
186
- return diff.root
187
- }
188
-
189
- // Creating repo
190
- // -------------------
191
- export const addBadCommit = async (
192
- repo: Repo,
193
- keypair: Keypair,
194
- ): Promise<Repo> => {
195
- const obj = generateObject()
196
- const newBlocks = new BlockMap()
197
- const cid = await newBlocks.add(obj)
198
- const updatedData = await repo.data.add(`com.example.test/${TID.next()}`, cid)
199
- const dataCid = await updatedData.getPointer()
200
- const diff = await DataDiff.of(updatedData, repo.data)
201
- newBlocks.addMap(diff.newMstBlocks)
202
- // we generate a bad sig by signing some other data
203
- const rev = TID.nextStr(repo.commit.rev)
204
- const commit: Commit = {
205
- ...repo.commit,
206
- rev,
207
- data: dataCid,
208
- sig: await keypair.sign(randomBytes(256)),
209
- }
210
- const commitCid = await newBlocks.add(commit)
211
-
212
- // @ts-expect-error FIXME remove this comment (and fix the TS error)
213
- await repo.storage.applyCommit({
214
- cid: commitCid,
215
- rev,
216
- prev: repo.cid,
217
- newBlocks,
218
- removedCids: diff.removedCids,
219
- })
220
- return await Repo.load(repo.storage, commitCid)
221
- }
222
-
223
- // Logging
224
- // ----------------
225
-
226
- export const writeMstLog = async (filename: string, tree: MST) => {
227
- let log = ''
228
- for await (const entry of tree.walk()) {
229
- if (entry.isLeaf()) continue
230
- const layer = await entry.getLayer()
231
- log += `Layer ${layer}: ${entry.pointer}\n`
232
- log += '--------------\n'
233
- const entries = await entry.getEntries()
234
- for (const e of entries) {
235
- if (e.isLeaf()) {
236
- log += `Key: ${e.key} (${e.value})\n`
237
- } else {
238
- log += `Subtree: ${e.pointer}\n`
239
- }
240
- }
241
- log += '\n\n'
242
- }
243
- fs.writeFileSync(filename, log)
244
- }
245
-
246
- export const saveMstEntries = (filename: string, entries: [string, Cid][]) => {
247
- const writable = entries.map(([key, val]) => [key, val.toString()])
248
- fs.writeFileSync(filename, JSON.stringify(writable))
249
- }
250
-
251
- export const loadMstEntries = (filename: string): [string, Cid][] => {
252
- const contents = fs.readFileSync(filename)
253
- const parsed = JSON.parse(contents.toString())
254
- return parsed.map(([key, value]) => [key, parseCid(value)])
255
- }
256
-
257
- export async function toBuffer(
258
- stream: AsyncIterable<Uint8Array> | Iterable<Uint8Array>,
259
- ): Promise<Buffer> {
260
- const chunks: Uint8Array[] = []
261
- for await (const chunk of stream) {
262
- chunks.push(chunk)
263
- }
264
- return Buffer.concat(chunks)
265
- }
@@ -1,28 +0,0 @@
1
- [
2
- {
3
- "root": "bafyreiapldaco7m23c7qzc4w42r7kxmcswm64nkindtuh4vwztrpoe7m5m",
4
- "blocks": [
5
- {
6
- "cid": "bafyreiapldaco7m23c7qzc4w42r7kxmcswm64nkindtuh4vwztrpoe7m5m",
7
- "bytes": "oWR0ZXN0ZHJvb3Q"
8
- },
9
- {
10
- "cid": "bafyreieteuyxvbvjbvuhsuo54qx6r3tnjxtp3ub6kb66rdjml3murxjcsy",
11
- "bytes": "oWR0ZXN0WQEAM32TVA//xgHS9Gtukp0vw6whQ+TnlwF9czt5A7Dxz/URSbryc9Pdw7HESX+jC2oPI/6rwKbhSml2kxJo4MaUeIg/HWI9ixxALw5gIF/I0JC3ejXVAu1Pw6bI9RWa5TgIvAnSow0pJ6jbaaWHlxCpqqHCNHUYbIC14D9k+RK3yS0h2g+O+gRUETQt8t4jOKxEhD037cYEuJCD+fWzFoLkEkrPdUWeqlFQxGt6bflCYjZFgiZKFUo72afR3XM16+jOlhOl+EtuqFcijYJ6MIB53qI8P8HMC2RVH0Mv8UYWLcWatl+CNLykEjesnMar5CvZT8j4w5EyEiS09iD4r6bljg"
12
- },
13
- {
14
- "cid": "bafyreiahvdwcywzm35id7hajal5l73p4bnoyaowgckeatd6sbxsacoo5jq",
15
- "bytes": "oWR0ZXN0WID81CSnkdKi0OBexCStL5gex6Fkehtg7R9Jaww7LFuJC/6Or9Cdb58I+6T9Kjqhf1bf5t5WLbTGt8HOlf1Ysl3wqfdxdxnRZYjyng8YjEmH2Twkc//moluskzywxfOwhRXsXI7SYt36OUkS8AKDzmhTijOG2XWSa2QvU3iSSjP8zw"
16
- },
17
- {
18
- "cid": "bafyreictawwxwjuto6csptbtktbbgwkrryqoakgyb3qovakgiqjzmvzi6a",
19
- "bytes": "oWR0ZXN0RBnEJSE"
20
- },
21
- {
22
- "cid": "bafyreierfkr6cwv2ux5hk6hp5qh5fhgzyr4yicad5m3pyu3ndaatyqucxu",
23
- "bytes": "oWR0ZXN0WQH09xLXHen1UTIPEap18egWK6OiRVQ4oLBqjfBVQGdiSsmsIn8uXDM6wp63kCrYWLvXoI0dREVW6+RUjoQIbmDhiKbEUTuLbcFiJnwD5gSlkXdUEwO4UeLfvcAsDJXel5lLaOlOeRTfA3Wf+rEEpr/ccCuwOATBPZBjPrneFVQu8UdsvTu5W144tGxp/ptBsMBqM3yLYkYYHNRrjcAI7iFiszsfKzo28GyM199Jko11mcVhNQ8KHZS72jbsWW/HyfJsL1M/dn6sDCfIaro66mTLRddSQaheQL4NBW5FEgLUBO2VDuBT9fVIl2IwQcijZAOakjLkS1sY2SyUmdsicqGRalnOrVlC5iWQcwHDXLzm9GWz/vfuoF+jzWsdpo6cjT5MIN8uXpzoZRSjH1+UXFxCzFhXkDwL/xJUq8u/0OFGp0mzDc7RLO3gC7X/ENaVPtz/wQaZ3q00EeHvDuiaxHdKIesf/+IkbqS1XjKtaHemB511MFiVf7l2OcqsUU12A5VMreqWPwbAHLgFRDPWiLS0D7yEF3KJX1IupxBmidQMT+SlmCp7FZMdJeHJ3pzpbQv+EoKSXja7it2D2uYsMcTn2DGhlVYsCcQd8PVNKZmPXuC7D82N4sh8p2XVW4LbsDfNTOrgHLX7zRX31VNa47w5Uc03Wuk"
24
- }
25
- ],
26
- "car": "OqJlcm9vdHOB2CpYJQABcRIgD1jAJ32a2L8Mi5bmo/VdgpWZ7jVIaOdD8rbM4vcT7OtndmVyc2lvbgEvAXESIA9YwCd9mti/DIuW5qP1XYKVme41SGjnQ/K2zOL3E+zroWR0ZXN0ZHJvb3StAgFxEiCTJTF6hqkNaHlR3eQv6O5tTeb90D5QfeiNLF7ZSN0ilqFkdGVzdFkBADN9k1QP/8YB0vRrbpKdL8OsIUPk55cBfXM7eQOw8c/1EUm68nPT3cOxxEl/owtqDyP+q8Cm4UppdpMSaODGlHiIPx1iPYscQC8OYCBfyNCQt3o11QLtT8OmyPUVmuU4CLwJ0qMNKSeo22mlh5cQqaqhwjR1GGyAteA/ZPkSt8ktIdoPjvoEVBE0LfLeIzisRIQ9N+3GBLiQg/n1sxaC5BJKz3VFnqpRUMRrem35QmI2RYImShVKO9mn0d1zNevozpYTpfhLbqhXIo2CejCAed6iPD/BzAtkVR9DL/FGFi3FmrZfgjS8pBI3rJzGq+Qr2U/I+MORMhIktPYg+K+m5Y6sAQFxEiAHqOwsWyzfUD+cCQL6v+38C12AOsYSiAmP0g3kATndTKFkdGVzdFiA/NQkp5HSotDgXsQkrS+YHsehZHobYO0fSWsMOyxbiQv+jq/QnW+fCPuk/So6oX9W3+beVi20xrfBzpX9WLJd8Kn3cXcZ0WWI8p4PGIxJh9k8JHP/5qJbrJM8sMXzsIUV7FyO0mLd+jlJEvACg85oU4ozhtl1kmtkL1N4kkoz/M8vAXESIFMFrXsmk3eFJ8wzVMITWVGOIOAo2A7g6oFGRBOWVyjwoWR0ZXN0RBnEJSGhBAFxEiCRKqPhWrql+nV47+wP0pzZxHmECAPrNvxTbRgBPEKCvaFkdGVzdFkB9PcS1x3p9VEyDxGqdfHoFiujokVUOKCwao3wVUBnYkrJrCJ/LlwzOsKet5Aq2Fi716CNHURFVuvkVI6ECG5g4YimxFE7i23BYiZ8A+YEpZF3VBMDuFHi373ALAyV3peZS2jpTnkU3wN1n/qxBKa/3HArsDgEwT2QYz653hVULvFHbL07uVteOLRsaf6bQbDAajN8i2JGGBzUa43ACO4hYrM7Hys6NvBsjNffSZKNdZnFYTUPCh2Uu9o27Flvx8nybC9TP3Z+rAwnyGq6Oupky0XXUkGoXkC+DQVuRRIC1ATtlQ7gU/X1SJdiMEHIo2QDmpIy5EtbGNkslJnbInKhkWpZzq1ZQuYlkHMBw1y85vRls/737qBfo81rHaaOnI0+TCDfLl6c6GUUox9flFxcQsxYV5A8C/8SVKvLv9DhRqdJsw3O0Szt4Au1/xDWlT7c/8EGmd6tNBHh7w7omsR3SiHrH//iJG6ktV4yrWh3pgeddTBYlX+5djnKrFFNdgOVTK3qlj8GwBy4BUQz1oi0tA+8hBdyiV9SLqcQZonUDE/kpZgqexWTHSXhyd6c6W0L/hKCkl42u4rdg9rmLDHE59gxoZVWLAnEHfD1TSmZj17guw/NjeLIfKdl1VuC27A3zUzq4By1+80V99VTWuO8OVHNN1rp"
27
- }
28
- ]
package/tests/car.test.ts DELETED
@@ -1,125 +0,0 @@
1
- import { wait } from '@atproto/common-web'
2
- import { encode } from '@atproto/lex-cbor'
3
- import {
4
- Cid,
5
- LexValue,
6
- cidForCbor,
7
- fromBase64,
8
- parseCid,
9
- toBase64,
10
- } from '@atproto/lex-data'
11
- import { CarBlock, readCarStream, writeCarStream } from '../src/index.js'
12
- import fixtures from './car-file-fixtures.json' with { type: 'json' }
13
-
14
- async function dataToCborBlock(data: LexValue): Promise<{
15
- cid: Cid
16
- bytes: Uint8Array
17
- }> {
18
- const bytes = encode(data)
19
- const cid = await cidForCbor(bytes)
20
- return { cid, bytes }
21
- }
22
-
23
- describe('car', () => {
24
- for (const fixture of fixtures) {
25
- it('correctly writes car files', async () => {
26
- const root = parseCid(fixture.root)
27
- async function* blockIter() {
28
- for (const block of fixture.blocks) {
29
- const cid = parseCid(block.cid)
30
- const bytes = fromBase64(block.bytes, 'base64')
31
- yield { cid, bytes }
32
- }
33
- }
34
- const carStream = writeCarStream(root, blockIter())
35
- const chunks: Uint8Array[] = []
36
- for await (const chunk of carStream) {
37
- chunks.push(chunk)
38
- }
39
- const car = Buffer.concat(chunks)
40
- // @NOTE Not using car.toString('base64') because of padding differences
41
- expect(toBase64(car)).toEqual(fixture.car)
42
- })
43
-
44
- it('correctly reads carfiles', async () => {
45
- const carStream = [fromBase64(fixture.car, 'base64')]
46
- const { roots, blocks } = await readCarStream(carStream)
47
- expect(roots.length).toBe(1)
48
- expect(roots[0].toString()).toEqual(fixture.root)
49
- const carBlocks: CarBlock[] = []
50
- for await (const block of blocks) {
51
- carBlocks.push(block)
52
- }
53
- expect(carBlocks.length).toEqual(fixture.blocks.length)
54
- for (let i = 0; i < carBlocks.length; i++) {
55
- expect(carBlocks[i].cid.toString()).toEqual(fixture.blocks[i].cid)
56
- expect(toBase64(carBlocks[i].bytes, 'base64')).toEqual(
57
- fixture.blocks[i].bytes,
58
- )
59
- }
60
- })
61
- }
62
-
63
- it('writeCar propagates errors', async () => {
64
- const iterate = async () => {
65
- async function* blockIterator() {
66
- await wait(1)
67
- const block = await dataToCborBlock({ test: 1 })
68
- yield block
69
- throw new Error('Oops!')
70
- }
71
- const iter = writeCarStream(null, blockIterator())
72
- for await (const _bytes of iter) {
73
- // no-op
74
- }
75
- }
76
- await expect(iterate).rejects.toThrow('Oops!')
77
- })
78
-
79
- it('verifies CIDs', async () => {
80
- const block0 = await dataToCborBlock({ block: 0 })
81
- const block1 = await dataToCborBlock({ block: 1 })
82
- const block2 = await dataToCborBlock({ block: 2 })
83
- const block3 = await dataToCborBlock({ block: 3 })
84
- const badBlock = await dataToCborBlock({ block: 'bad' })
85
- const blockIter = async function* () {
86
- yield block0
87
- yield block1
88
- yield block2
89
- yield { cid: block3.cid, bytes: badBlock.bytes }
90
- }
91
- const flush = async function (iter: AsyncIterable<unknown>) {
92
- for await (const _ of iter) {
93
- // no-op
94
- }
95
- }
96
- const badCar = await readCarStream(writeCarStream(block0.cid, blockIter()))
97
- await expect(flush(badCar.blocks)).rejects.toThrow(
98
- 'Not a valid CID for bytes',
99
- )
100
- })
101
-
102
- it('skips CID verification', async () => {
103
- const block0 = await dataToCborBlock({ block: 0 })
104
- const block1 = await dataToCborBlock({ block: 1 })
105
- const block2 = await dataToCborBlock({ block: 2 })
106
- const block3 = await dataToCborBlock({ block: 3 })
107
- const badBlock = await dataToCborBlock({ block: 'bad' })
108
- const blockIter = async function* () {
109
- yield block0
110
- yield block1
111
- yield block2
112
- yield { cid: block3.cid, bytes: badBlock.bytes }
113
- }
114
- const flush = async function (iter: AsyncIterable<unknown>) {
115
- for await (const _ of iter) {
116
- // no-op
117
- }
118
- }
119
- const badCar = await readCarStream(
120
- writeCarStream(block0.cid, blockIter()),
121
- { skipCidVerification: true },
122
- )
123
- await expect(flush(badCar.blocks)).resolves.toBeUndefined()
124
- })
125
- })
@@ -1,94 +0,0 @@
1
- import { Secp256k1Keypair } from '@atproto/crypto'
2
- import {
3
- Repo,
4
- WriteOpAction,
5
- blocksToCarFile,
6
- verifyProofs,
7
- } from '../src/index.js'
8
- import { MemoryBlockstore } from '../src/storage/index.js'
9
-
10
- describe('Commit data', () => {
11
- // @NOTE this test uses a fully deterministic tree structure
12
- it('includes all relevant blocks for proof in commit data', async () => {
13
- const did = 'did:example:alice'
14
- const collection = 'com.atproto.test'
15
- const record = {
16
- test: 123,
17
- }
18
-
19
- const blockstore = new MemoryBlockstore()
20
- const keypair = await Secp256k1Keypair.create()
21
- let repo = await Repo.create(blockstore, did, keypair)
22
-
23
- const keys: string[] = []
24
- for (let i = 0; i < 50; i++) {
25
- const rkey = `key-${i}`
26
- keys.push(rkey)
27
- repo = await repo.applyWrites(
28
- [
29
- {
30
- action: WriteOpAction.Create,
31
- collection,
32
- rkey,
33
- record,
34
- },
35
- ],
36
- keypair,
37
- )
38
- }
39
-
40
- // this test demonstrates the test case:
41
- // specifically in the case of deleting the first key, there is a "rearranged block" that is necessary
42
- // in the proof path but _is not_ in newBlocks (as it already existed in the repository)
43
- {
44
- const commit = await repo.formatCommit(
45
- {
46
- action: WriteOpAction.Delete,
47
- collection,
48
- rkey: keys[0],
49
- },
50
- keypair,
51
- )
52
- const car = await blocksToCarFile(commit.cid, commit.newBlocks)
53
- const proofAttempt = verifyProofs(
54
- car,
55
- [
56
- {
57
- collection,
58
- rkey: keys[0],
59
- cid: null,
60
- },
61
- ],
62
- did,
63
- keypair.did(),
64
- )
65
- await expect(proofAttempt).rejects.toThrow(/block not found/)
66
- }
67
-
68
- for (const rkey of keys) {
69
- const commit = await repo.formatCommit(
70
- {
71
- action: WriteOpAction.Delete,
72
- collection,
73
- rkey,
74
- },
75
- keypair,
76
- )
77
- const car = await blocksToCarFile(commit.cid, commit.relevantBlocks)
78
- const proofRes = await verifyProofs(
79
- car,
80
- [
81
- {
82
- collection,
83
- rkey: rkey,
84
- cid: null,
85
- },
86
- ],
87
- did,
88
- keypair.did(),
89
- )
90
- expect(proofRes.unverified.length).toBe(0)
91
- repo = await repo.applyCommit(commit)
92
- }
93
- })
94
- })
@@ -1,118 +0,0 @@
1
- [
2
- {
3
- "comment": "two deep split",
4
- "leafValue": "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454",
5
- "keys": [
6
- "A0/374913",
7
- "B1/986427",
8
- "C0/451630",
9
- "E0/670489",
10
- "F1/085263",
11
- "G0/765327"
12
- ],
13
- "adds": ["D2/269196"],
14
- "dels": [],
15
- "rootBeforeCommit": "bafyreicraprx2xwnico4tuqir3ozsxpz46qkcpox3obf5bagicqwurghpy",
16
- "rootAfterCommit": "bafyreihvay6pazw3dfa47u5d2tn3rd6pa57sr37bo5bqyvjuqc73ib65my",
17
- "blocksInProof": [
18
- "bafyreieazvzmba35p4phksumwfoklwe5o4ncmo7otud74idcyv4orrbzxi",
19
- "bafyreie4227qpa4vbtbpnsvuhp322b776vjuhxsidi5hxp2gawumr4m3de",
20
- "bafyreid44jgimksqqdratyste2moqu6zo4h6co2pknjppfoiplsqxtuxae",
21
- "bafyreiaerlvitye7fjjwodkshtbqqdsmfsdjtnlz4vs6y4trnddshsmd5a",
22
- "bafyreihvay6pazw3dfa47u5d2tn3rd6pa57sr37bo5bqyvjuqc73ib65my"
23
- ]
24
- },
25
- {
26
- "comment": "two deep leafless split",
27
- "leafValue": "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454",
28
- "keys": ["A0/374913", "B0/601692", "D0/952776", "E0/670489"],
29
- "adds": ["C2/014073"],
30
- "dels": [],
31
- "rootBeforeCommit": "bafyreialm5sgf7pijawbschsjpdevid5rss5ip3d4n4w6cc4mhu53sfl4i",
32
- "rootAfterCommit": "bafyreibxh4iztp5l2yshz3ectg2qjpeyprpw2gogao3pvceowpq3k3thya",
33
- "blocksInProof": [
34
- "bafyreih7dxytqtcjv3cfia3fi3wxofeip62teqkpynnkxisxqwfchfb4bu",
35
- "bafyreiaqbymlnvpklmogx75gozjl3y73gva43jbgwcrqu2pp5g5ejou5vm",
36
- "bafyreicfh3st5ghtnoqyyvznjv4lhfnvl7qsndempx35i4tcmoxakqbgrm",
37
- "bafyreieyjrrai6igjceyxzkajrxgxz37da2eufb33anvesb4ev6yzztauu",
38
- "bafyreibxh4iztp5l2yshz3ectg2qjpeyprpw2gogao3pvceowpq3k3thya"
39
- ]
40
- },
41
- {
42
- "comment": "add on edge with neighbor two layers down",
43
- "leafValue": "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454",
44
- "keys": ["A0/374913", "B2/827649", "C0/451630"],
45
- "adds": ["D2/269196"],
46
- "dels": [],
47
- "rootBeforeCommit": "bafyreigc6ay2qwfk7kuevvrczummpd64nknfo4yxpaooknfymzyb7u3ntq",
48
- "rootAfterCommit": "bafyreign6kxoll35r5f2ske6hjx7vg56aw3jn6r5hcopgrepzafpvohr2a",
49
- "blocksInProof": [
50
- "bafyreieazvzmba35p4phksumwfoklwe5o4ncmo7otud74idcyv4orrbzxi",
51
- "bafyreidicvcjgrpm5bmhm3ndh2ysqfhgzk4chwn3m4kuvwkenfusspb4uy",
52
- "bafyreign6kxoll35r5f2ske6hjx7vg56aw3jn6r5hcopgrepzafpvohr2a"
53
- ]
54
- },
55
- {
56
- "comment": "merge and split in multi-op commit",
57
- "leafValue": "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454",
58
- "keys": ["A0/374913", "B2/827649", "D2/269196", "E0/670489"],
59
- "adds": ["C2/014073"],
60
- "dels": ["B2/827649", "D2/269196"],
61
- "rootBeforeCommit": "bafyreiceld4icym4qjmdcn3dfgtxt7t66hdgyhvigessgmkvb56dx6amgi",
62
- "rootAfterCommit": "bafyreigkalika3taqauapfha556lo36zzcjoiifny5xeru6yis3nxw5ruq",
63
- "blocksInProof": [
64
- "bafyreid44jgimksqqdratyste2moqu6zo4h6co2pknjppfoiplsqxtuxae",
65
- "bafyreihytu6onh476trave25zuo63ziebkeong2755sc5nmf55uzdawgt4",
66
- "bafyreigkalika3taqauapfha556lo36zzcjoiifny5xeru6yis3nxw5ruq",
67
- "bafyreidnnkrdkcaswbflgtdsxm7nzs7p5f2rdous6wrlupzstuwqu5pfgm",
68
- "bafyreia2kq243hqq3volwlzkbzzphoeqauk54sc5h7vgogq4ei5fjizxvy"
69
- ]
70
- },
71
- {
72
- "comment": "complex multi-op commit",
73
- "leafValue": "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454",
74
- "keys": [
75
- "B0/601692",
76
- "C2/014073",
77
- "D0/952776",
78
- "E2/819540",
79
- "F0/697858",
80
- "H0/131238"
81
- ],
82
- "adds": ["A2/827942", "G2/611528"],
83
- "dels": ["C2/014073"],
84
- "rootBeforeCommit": "bafyreigr3plnts7dax6yokvinbhcqpyicdfgg6npvvyx6okc5jo55slfqi",
85
- "rootAfterCommit": "bafyreiftrcrbhrwmi37u4egedlg56gk3jeh3tvmqvwgowoifuklfysyx54",
86
- "blocksInProof": [
87
- "bafyreih62n3gjbzzvlicuggpfydyzrp3ssyx7hdgtltd3sct3ribm3u73e",
88
- "bafyreihrjhuoynjvgteuefin5vwnqmupyfzvmytdobpstqt3mbawgw5qhm",
89
- "bafyreibevzst4gzkxo263syohlmq3lpxdvpjhlpyqx2ay3moh43lifydca",
90
- "bafyreifsdd7dv2neal7zjhyrsvndkaocelqlpgfxwo4utoq2g77klih37e",
91
- "bafyreid2wwyroodj2lxx2obikac74q77lsn6vqkoetlqqwwnr3criwlcvy",
92
- "bafyreie55b224oljhykpsxdjq4ajn2ysksud7qm347s6kn2ei6a775faum",
93
- "bafyreiftrcrbhrwmi37u4egedlg56gk3jeh3tvmqvwgowoifuklfysyx54"
94
- ]
95
- },
96
- {
97
- "comment": "split with earlier leaves on same layer",
98
- "leafValue": "bafyreie5cvv4h45feadgeuwhbcutmh6t2ceseocckahdoe6uat64zmz454",
99
- "keys": [
100
- "app.bsky.feed.post/3lo3kqqljmfe2",
101
- "app.bsky.feed.post/3log4547dm6h2",
102
- "app.bsky.feed.post/3log45inogon2",
103
- "app.bsky.feed.post/3logaodrh74d2",
104
- "app.bsky.feed.post/3logteazog2n2",
105
- "app.bsky.feed.post/3lon5cqsbwrj2",
106
- "app.bsky.feed.repost/3l6sjhvqonco2"
107
- ],
108
- "adds": ["app.bsky.feed.post/3lon5dzeaihj2"],
109
- "dels": [],
110
- "rootBeforeCommit": "bafyreigfcsro2up7qi7l3rxdpg7n6gjtteotkmgrrqztl5oy2tf4ncl4ji",
111
- "rootAfterCommit": "bafyreig33hsjiplaixvmccy65n7rn3in5nsbtcittzx6k3w5wjfhk2sg3a",
112
- "blocksInProof": [
113
- "bafyreig33hsjiplaixvmccy65n7rn3in5nsbtcittzx6k3w5wjfhk2sg3a",
114
- "bafyreih2rhjm3apcghihwfojv2em7noqkgt5qyjcnxux7do674m464oc3m",
115
- "bafyreiajhswkduap4zvqvfhth3skdgckmk2eb5gow7vv3gvj45f4fqwmxm"
116
- ]
117
- }
118
- ]
@@ -1,63 +0,0 @@
1
- import { parseCid } from '@atproto/lex-data'
2
- import { BlockMap } from '../src/index.js'
3
- import { MST } from '../src/mst/index.js'
4
- import { MemoryBlockstore } from '../src/storage/index.js'
5
- import fixtures from './commit-proof-fixtures.json' with { type: 'json' }
6
-
7
- describe('commit proofs', () => {
8
- for (const fixture of fixtures) {
9
- it(fixture.comment, async () => {
10
- const { leafValue, keys, adds, dels } = fixture
11
- const leaf = parseCid(leafValue)
12
-
13
- const storage = new MemoryBlockstore()
14
- let mst = await MST.create(storage)
15
- for (const key of keys) {
16
- mst = await mst.add(key, leaf)
17
- }
18
-
19
- const rootBeforeCommit = await mst.getPointer()
20
- expect(rootBeforeCommit.toString()).toEqual(fixture.rootBeforeCommit)
21
-
22
- for (const key of adds) {
23
- mst = await mst.add(key, leaf)
24
- }
25
- for (const key of dels) {
26
- mst = await mst.delete(key)
27
- }
28
- const rootAfterCommit = await mst.getPointer()
29
- expect(rootAfterCommit.toString()).toEqual(fixture.rootAfterCommit)
30
- const proofs = await Promise.all(
31
- [...adds, ...dels].map((key) => mst.getCoveringProof(key)),
32
- )
33
- const proof = proofs.reduce((acc, cur) => acc.addMap(cur), new BlockMap())
34
- const blocksInProof = fixture.blocksInProof.map(parseCid)
35
- for (const cid of blocksInProof) {
36
- expect(proof.has(cid)).toBe(true)
37
- }
38
-
39
- const invertAdds = adds.map((k) => (mst: MST) => mst.delete(k))
40
- const invertDels = dels.map((k) => (mst: MST) => mst.add(k, leaf))
41
- const invertOrders = permutations([...invertAdds, ...invertDels])
42
-
43
- const proofStorage = new MemoryBlockstore(proof)
44
- for (const order of invertOrders) {
45
- let proofMst = await MST.load(proofStorage, rootAfterCommit)
46
- for (const fn of order) {
47
- proofMst = await fn(proofMst)
48
- }
49
- const rootAfterInvert = await proofMst.getPointer()
50
- expect(rootAfterInvert.toString()).toEqual(fixture.rootBeforeCommit)
51
- }
52
- })
53
- }
54
- })
55
-
56
- function permutations<T>(arr: T[]): T[][] {
57
- if (arr.length <= 1) return [arr]
58
-
59
- return arr.reduce((perms: T[][], item: T, i: number) => {
60
- const rest = [...arr.slice(0, i), ...arr.slice(i + 1)]
61
- return perms.concat(permutations(rest).map((p) => [item, ...p]))
62
- }, [])
63
- }