@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
@@ -1,76 +0,0 @@
1
- import { Cid } from '@atproto/lex-data'
2
- import { BlockMap } from '../block-map.js'
3
- import { CommitData } from '../types.js'
4
- import { ReadableBlockstore } from './readable-blockstore.js'
5
- import { RepoStorage } from './types.js'
6
-
7
- export class MemoryBlockstore
8
- extends ReadableBlockstore
9
- implements RepoStorage
10
- {
11
- blocks: BlockMap
12
- root: Cid | null = null
13
- rev: string | null = null
14
-
15
- constructor(blocks?: BlockMap) {
16
- super()
17
- this.blocks = new BlockMap()
18
- if (blocks) {
19
- this.blocks.addMap(blocks)
20
- }
21
- }
22
-
23
- async getRoot(): Promise<Cid | null> {
24
- return this.root
25
- }
26
-
27
- async getBytes(cid: Cid): Promise<Uint8Array | null> {
28
- return this.blocks.get(cid) || null
29
- }
30
-
31
- async has(cid: Cid): Promise<boolean> {
32
- return this.blocks.has(cid)
33
- }
34
-
35
- async getBlocks(cids: Cid[]): Promise<{ blocks: BlockMap; missing: Cid[] }> {
36
- return this.blocks.getMany(cids)
37
- }
38
-
39
- async putBlock(cid: Cid, block: Uint8Array): Promise<void> {
40
- this.blocks.set(cid, block)
41
- }
42
-
43
- async putMany(blocks: BlockMap): Promise<void> {
44
- this.blocks.addMap(blocks)
45
- }
46
-
47
- async updateRoot(cid: Cid, rev: string): Promise<void> {
48
- this.root = cid
49
- this.rev = rev
50
- }
51
-
52
- async applyCommit(commit: CommitData): Promise<void> {
53
- this.root = commit.cid
54
- const rmCids = commit.removedCids.toList()
55
- for (const cid of rmCids) {
56
- this.blocks.delete(cid)
57
- }
58
- commit.newBlocks.forEach((bytes, cid) => {
59
- this.blocks.set(cid, bytes)
60
- })
61
- }
62
-
63
- async sizeInBytes(): Promise<number> {
64
- let total = 0
65
- this.blocks.forEach((bytes) => {
66
- total += bytes.byteLength
67
- })
68
- return total
69
- }
70
-
71
- async destroy(): Promise<void> {
72
- this.blocks.clear()
73
- }
74
- }
75
-
76
- export default MemoryBlockstore
@@ -1,55 +0,0 @@
1
- import { check } from '@atproto/common-web'
2
- import { Cid, LexMap } from '@atproto/lex-data'
3
- import { BlockMap } from '../block-map.js'
4
- import { MissingBlockError } from '../error.js'
5
- import { parseObjByDef } from '../parse.js'
6
- import { cborToLexRecord } from '../util.js'
7
-
8
- export abstract class ReadableBlockstore {
9
- abstract getBytes(cid: Cid): Promise<Uint8Array | null>
10
- abstract has(cid: Cid): Promise<boolean>
11
- abstract getBlocks(cids: Cid[]): Promise<{ blocks: BlockMap; missing: Cid[] }>
12
-
13
- async attemptRead<T>(
14
- cid: Cid,
15
- def: check.Def<T>,
16
- ): Promise<{ obj: T; bytes: Uint8Array } | null> {
17
- const bytes = await this.getBytes(cid)
18
- if (!bytes) return null
19
- return parseObjByDef(bytes, cid, def)
20
- }
21
-
22
- async readObjAndBytes<T>(
23
- cid: Cid,
24
- def: check.Def<T>,
25
- ): Promise<{ obj: T; bytes: Uint8Array }> {
26
- const read = await this.attemptRead(cid, def)
27
- if (!read) {
28
- throw new MissingBlockError(cid, def.name)
29
- }
30
- return read
31
- }
32
-
33
- async readObj<T>(cid: Cid, def: check.Def<T>): Promise<T> {
34
- const obj = await this.readObjAndBytes(cid, def)
35
- return obj.obj
36
- }
37
-
38
- async attemptReadRecord(cid: Cid): Promise<LexMap | null> {
39
- try {
40
- return await this.readRecord(cid)
41
- } catch {
42
- return null
43
- }
44
- }
45
-
46
- async readRecord(cid: Cid): Promise<LexMap> {
47
- const bytes = await this.getBytes(cid)
48
- if (!bytes) {
49
- throw new MissingBlockError(cid)
50
- }
51
- return cborToLexRecord(bytes)
52
- }
53
- }
54
-
55
- export default ReadableBlockstore
@@ -1,35 +0,0 @@
1
- import { Cid } from '@atproto/lex-data'
2
- import { BlockMap } from '../block-map.js'
3
- import { ReadableBlockstore } from './readable-blockstore.js'
4
-
5
- export class SyncStorage extends ReadableBlockstore {
6
- constructor(
7
- public staged: ReadableBlockstore,
8
- public saved: ReadableBlockstore,
9
- ) {
10
- super()
11
- }
12
-
13
- async getBytes(cid: Cid): Promise<Uint8Array | null> {
14
- const got = await this.staged.getBytes(cid)
15
- if (got) return got
16
- return this.saved.getBytes(cid)
17
- }
18
-
19
- async getBlocks(cids: Cid[]): Promise<{ blocks: BlockMap; missing: Cid[] }> {
20
- const fromStaged = await this.staged.getBlocks(cids)
21
- const fromSaved = await this.saved.getBlocks(fromStaged.missing)
22
- const blocks = fromStaged.blocks
23
- blocks.addMap(fromSaved.blocks)
24
- return {
25
- blocks,
26
- missing: fromSaved.missing,
27
- }
28
- }
29
-
30
- async has(cid: Cid): Promise<boolean> {
31
- return (await this.staged.has(cid)) || (await this.saved.has(cid))
32
- }
33
- }
34
-
35
- export default SyncStorage
@@ -1,47 +0,0 @@
1
- import type { Readable } from 'node:stream'
2
- import { check } from '@atproto/common-web'
3
- import { Cid, LexMap } from '@atproto/lex-data'
4
- import { BlockMap } from '../block-map.js'
5
- import { CommitData } from '../types.js'
6
-
7
- export interface RepoStorage {
8
- // Writable
9
- getRoot(): Promise<Cid | null>
10
- putBlock(cid: Cid, block: Uint8Array, rev: string): Promise<void>
11
- putMany(blocks: BlockMap, rev: string): Promise<void>
12
- updateRoot(cid: Cid, rev: string): Promise<void>
13
- applyCommit(commit: CommitData)
14
-
15
- // Readable
16
- getBytes(cid: Cid): Promise<Uint8Array | null>
17
- has(cid: Cid): Promise<boolean>
18
- getBlocks(cids: Cid[]): Promise<{ blocks: BlockMap; missing: Cid[] }>
19
- attemptRead<T>(
20
- cid: Cid,
21
- def: check.Def<T>,
22
- ): Promise<{ obj: T; bytes: Uint8Array } | null>
23
- readObjAndBytes<T>(
24
- cid: Cid,
25
- def: check.Def<T>,
26
- ): Promise<{ obj: T; bytes: Uint8Array }>
27
- readObj<T>(cid: Cid, def: check.Def<T>): Promise<T>
28
- attemptReadRecord(cid: Cid): Promise<LexMap | null>
29
- readRecord(cid: Cid): Promise<LexMap>
30
- }
31
-
32
- // @TODO make this less node-js specific by using AsyncIterable<Uint8Array> instead of Readable
33
- export interface BlobStore {
34
- putTemp(bytes: Uint8Array | Readable): Promise<string>
35
- makePermanent(key: string, cid: Cid): Promise<void>
36
- putPermanent(cid: Cid, bytes: Uint8Array | Readable): Promise<void>
37
- quarantine(cid: Cid): Promise<void>
38
- unquarantine(cid: Cid): Promise<void>
39
- getBytes(cid: Cid): Promise<Uint8Array>
40
- getStream(cid: Cid): Promise<Readable>
41
- hasTemp(key: string): Promise<boolean>
42
- hasStored(cid: Cid): Promise<boolean>
43
- delete(cid: Cid): Promise<void>
44
- deleteMany(cid: Cid[]): Promise<void>
45
- }
46
-
47
- export class BlobNotFoundError extends Error {}
@@ -1,207 +0,0 @@
1
- import { Cid } from '@atproto/lex-data'
2
- import { BlockMap } from '../block-map.js'
3
- import { readCarWithRoot } from '../car.js'
4
- import { DataDiff } from '../data-diff.js'
5
- import { MST } from '../mst/index.js'
6
- import { ReadableRepo } from '../readable-repo.js'
7
- import {
8
- MemoryBlockstore,
9
- ReadableBlockstore,
10
- SyncStorage,
11
- } from '../storage/index.js'
12
- import {
13
- RecordCidClaim,
14
- RecordClaim,
15
- VerifiedDiff,
16
- VerifiedRepo,
17
- def,
18
- } from '../types.js'
19
- import * as util from '../util.js'
20
-
21
- export const verifyRepoCar = async (
22
- carBytes: Uint8Array,
23
- did?: string,
24
- signingKey?: string,
25
- ): Promise<VerifiedRepo> => {
26
- const car = await readCarWithRoot(carBytes)
27
- return verifyRepo(car.blocks, car.root, did, signingKey)
28
- }
29
-
30
- export const verifyRepo = async (
31
- blocks: BlockMap,
32
- head: Cid,
33
- did?: string,
34
- signingKey?: string,
35
- opts?: { ensureLeaves?: boolean },
36
- ): Promise<VerifiedRepo> => {
37
- const diff = await verifyDiff(null, blocks, head, did, signingKey, opts)
38
- const creates = util.ensureCreates(diff.writes)
39
- return {
40
- creates,
41
- commit: diff.commit,
42
- }
43
- }
44
-
45
- export const verifyDiffCar = async (
46
- repo: ReadableRepo | null,
47
- carBytes: Uint8Array,
48
- did?: string,
49
- signingKey?: string,
50
- opts?: { ensureLeaves?: boolean },
51
- ): Promise<VerifiedDiff> => {
52
- const car = await readCarWithRoot(carBytes)
53
- return verifyDiff(repo, car.blocks, car.root, did, signingKey, opts)
54
- }
55
-
56
- export const verifyDiff = async (
57
- repo: ReadableRepo | null,
58
- updateBlocks: BlockMap,
59
- updateRoot: Cid,
60
- did?: string,
61
- signingKey?: string,
62
- opts?: { ensureLeaves?: boolean },
63
- ): Promise<VerifiedDiff> => {
64
- const { ensureLeaves = true } = opts ?? {}
65
- const stagedStorage = new MemoryBlockstore(updateBlocks)
66
- const updateStorage = repo
67
- ? new SyncStorage(stagedStorage, repo.storage)
68
- : stagedStorage
69
- const updated = await verifyRepoRoot(
70
- updateStorage,
71
- updateRoot,
72
- did,
73
- signingKey,
74
- )
75
- const diff = await DataDiff.of(updated.data, repo?.data ?? null)
76
- const writes = await util.diffToWriteDescripts(diff)
77
- const newBlocks = diff.newMstBlocks
78
- const leaves = updateBlocks.getMany(diff.newLeafCids.toList())
79
- if (leaves.missing.length > 0 && ensureLeaves) {
80
- throw new Error(`missing leaf blocks: ${leaves.missing}`)
81
- }
82
- newBlocks.addMap(leaves.blocks)
83
- const removedCids = diff.removedCids
84
- const commitCid = await newBlocks.add(updated.commit)
85
- // ensure the commit cid actually changed
86
- if (repo) {
87
- if (commitCid.equals(repo.cid)) {
88
- newBlocks.delete(commitCid)
89
- } else {
90
- removedCids.add(repo.cid)
91
- }
92
- }
93
- return {
94
- writes,
95
- commit: {
96
- cid: updated.cid,
97
- rev: updated.commit.rev,
98
- prev: repo?.cid ?? null,
99
- since: repo?.commit.rev ?? null,
100
- newBlocks,
101
- relevantBlocks: newBlocks,
102
- removedCids,
103
- },
104
- }
105
- }
106
-
107
- // @NOTE only verifies the root, not the repo contents
108
- const verifyRepoRoot = async (
109
- storage: ReadableBlockstore,
110
- head: Cid,
111
- did?: string,
112
- signingKey?: string,
113
- ): Promise<ReadableRepo> => {
114
- const repo = await ReadableRepo.load(storage, head)
115
- if (did !== undefined && repo.did !== did) {
116
- throw new RepoVerificationError(`Invalid repo did: ${repo.did}`)
117
- }
118
- if (signingKey !== undefined) {
119
- const validSig = await util.verifyCommitSig(repo.commit, signingKey)
120
- if (!validSig) {
121
- throw new RepoVerificationError(
122
- `Invalid signature on commit: ${repo.cid.toString()}`,
123
- )
124
- }
125
- }
126
- return repo
127
- }
128
-
129
- export const verifyProofs = async (
130
- proofs: Uint8Array,
131
- claims: RecordCidClaim[],
132
- did: string,
133
- didKey: string,
134
- ): Promise<{ verified: RecordCidClaim[]; unverified: RecordCidClaim[] }> => {
135
- const car = await readCarWithRoot(proofs)
136
- const blockstore = new MemoryBlockstore(car.blocks)
137
- const commit = await blockstore.readObj(car.root, def.commit)
138
- if (commit.did !== did) {
139
- throw new RepoVerificationError(`Invalid repo did: ${commit.did}`)
140
- }
141
- const validSig = await util.verifyCommitSig(commit, didKey)
142
- if (!validSig) {
143
- throw new RepoVerificationError(
144
- `Invalid signature on commit: ${car.root.toString()}`,
145
- )
146
- }
147
- const mst = MST.load(blockstore, commit.data)
148
- const verified: RecordCidClaim[] = []
149
- const unverified: RecordCidClaim[] = []
150
- for (const claim of claims) {
151
- const found = await mst.get(
152
- util.formatDataKey(claim.collection, claim.rkey),
153
- )
154
- const record = found ? await blockstore.readObj(found, def.map) : null
155
- if (claim.cid === null) {
156
- if (record === null) {
157
- verified.push(claim)
158
- } else {
159
- unverified.push(claim)
160
- }
161
- } else {
162
- if (found?.equals(claim.cid)) {
163
- verified.push(claim)
164
- } else {
165
- unverified.push(claim)
166
- }
167
- }
168
- }
169
- return { verified, unverified }
170
- }
171
-
172
- export const verifyRecords = async (
173
- proofs: Uint8Array,
174
- did: string,
175
- signingKey: string,
176
- ): Promise<RecordClaim[]> => {
177
- const car = await readCarWithRoot(proofs)
178
- const blockstore = new MemoryBlockstore(car.blocks)
179
- const commit = await blockstore.readObj(car.root, def.commit)
180
- if (commit.did !== did) {
181
- throw new RepoVerificationError(`Invalid repo did: ${commit.did}`)
182
- }
183
- const validSig = await util.verifyCommitSig(commit, signingKey)
184
- if (!validSig) {
185
- throw new RepoVerificationError(
186
- `Invalid signature on commit: ${car.root.toString()}`,
187
- )
188
- }
189
- const mst = MST.load(blockstore, commit.data)
190
-
191
- const records: RecordClaim[] = []
192
- const leaves = await mst.reachableLeaves()
193
- for (const leaf of leaves) {
194
- const { collection, rkey } = util.parseDataKey(leaf.key)
195
- const record = await blockstore.attemptReadRecord(leaf.value)
196
- if (record) {
197
- records.push({
198
- collection,
199
- rkey,
200
- record,
201
- })
202
- }
203
- }
204
- return records
205
- }
206
-
207
- export class RepoVerificationError extends Error {}
package/src/sync/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './consumer.js'
2
- export * from './provider.js'
@@ -1,67 +0,0 @@
1
- import { Cid } from '@atproto/lex-data'
2
- import { writeCarStream } from '../car.js'
3
- import { CidSet } from '../cid-set.js'
4
- import { MissingBlocksError } from '../error.js'
5
- import { MST } from '../mst/index.js'
6
- import { ReadableBlockstore, RepoStorage } from '../storage/index.js'
7
- import { CarBlock, RecordPath, def } from '../types.js'
8
- import * as util from '../util.js'
9
-
10
- // Full Repo
11
- // -------------
12
-
13
- export const getFullRepo = (
14
- storage: RepoStorage,
15
- commitCid: Cid,
16
- ): AsyncIterable<Uint8Array> => {
17
- return writeCarStream(commitCid, iterateFullRepo(storage, commitCid))
18
- }
19
-
20
- async function* iterateFullRepo(
21
- storage: RepoStorage,
22
- commitCid: Cid,
23
- ): AsyncGenerator<CarBlock> {
24
- const commit = await storage.readObjAndBytes(commitCid, def.commit)
25
- yield { cid: commitCid, bytes: commit.bytes }
26
- const mst = MST.load(storage, commit.obj.data)
27
- for await (const block of mst.carBlockStream()) {
28
- yield block
29
- }
30
- }
31
-
32
- // Narrow slices
33
- // -------------
34
-
35
- export const getRecords = (
36
- storage: ReadableBlockstore,
37
- commitCid: Cid,
38
- paths: RecordPath[],
39
- ): AsyncIterable<Uint8Array> => {
40
- return writeCarStream(
41
- commitCid,
42
- iterateRecordBlocks(storage, commitCid, paths),
43
- )
44
- }
45
-
46
- async function* iterateRecordBlocks(
47
- storage: ReadableBlockstore,
48
- commitCid: Cid,
49
- paths: RecordPath[],
50
- ): AsyncGenerator<CarBlock> {
51
- const commit = await storage.readObjAndBytes(commitCid, def.commit)
52
- yield { cid: commitCid, bytes: commit.bytes }
53
- const mst = MST.load(storage, commit.obj.data)
54
- const cidsForPaths = await Promise.all(
55
- paths.map((p) => mst.cidsForPath(util.formatDataKey(p.collection, p.rkey))),
56
- )
57
- const allCids = cidsForPaths.reduce((acc, cur) => {
58
- return acc.addSet(new CidSet(cur))
59
- }, new CidSet())
60
- const found = await storage.getBlocks(allCids.toList())
61
- if (found.missing.length > 0) {
62
- throw new MissingBlocksError('writeRecordsToCarStream', found.missing)
63
- }
64
- for (const block of found.blocks.entries()) {
65
- yield block
66
- }
67
- }