atproto/packages/repo/tests/proofs.test.ts
Daniel Holmgren b15dec2f4f
Atproto sync package (#2752)
* first pass/port

* reworking

* authenticated commit parsing

* authenticate identity evts

* some testing

* tidy & add firehose to queue

* error handling

* fix test

* refactor sync queue + some tests

* fix race in sync queue

* rm firehose from syncqueue

* add tests for queue utils

* README

* lint readme

* filter before parsing

* pr feedback

* small fix

* changesets

* fix type

* Rework dataplane subscription (#2766)

* working sync package into appview subscription

* add restart method to subscription for tests

* fix another test

* tidy subscription utils/files

* remove dupe property

* tidy after merge

* fix start cursor on subscription

* tweak process full subscription logic

* fixes
2024-09-04 20:18:16 -05:00

155 lines
4.9 KiB
TypeScript

import { TID, cidForCbor, streamToBuffer } from '@atproto/common'
import * as crypto from '@atproto/crypto'
import { RecordCidClaim, RecordPath, Repo, RepoContents } from '../src'
import { MemoryBlockstore } from '../src/storage'
import * as sync from '../src/sync'
import * as util from './_util'
describe('Repo Proofs', () => {
let storage: MemoryBlockstore
let repo: Repo
let keypair: crypto.Keypair
let repoData: RepoContents
const repoDid = 'did:example:test'
beforeAll(async () => {
storage = new MemoryBlockstore()
keypair = await crypto.Secp256k1Keypair.create()
repo = await Repo.create(storage, repoDid, keypair)
const filled = await util.fillRepo(repo, keypair, 5)
repo = filled.repo
repoData = filled.data
})
const getProofs = async (claims: RecordPath[]) => {
return streamToBuffer(sync.getRecords(storage, repo.cid, claims))
}
const doVerify = (proofs: Uint8Array, claims: RecordCidClaim[]) => {
return sync.verifyProofs(proofs, claims, repoDid, keypair.did())
}
const contentsToClaims = async (
contents: RepoContents,
): Promise<RecordCidClaim[]> => {
const claims: RecordCidClaim[] = []
for (const coll of Object.keys(contents)) {
for (const rkey of Object.keys(contents[coll])) {
claims.push({
collection: coll,
rkey: rkey,
cid: await cidForCbor(contents[coll][rkey]),
})
}
}
return claims
}
it('verifies valid records', async () => {
const claims = await contentsToClaims(repoData)
const proofs = await getProofs(claims)
const results = await doVerify(proofs, claims)
expect(results.verified.length).toBeGreaterThan(0)
expect(results.verified).toEqual(claims)
expect(results.unverified.length).toBe(0)
})
it('verifies record nonexistence', async () => {
const claims: RecordCidClaim[] = [
{
collection: util.testCollections[0],
rkey: TID.nextStr(), // does not exist
cid: null,
},
]
const proofs = await getProofs(claims)
const results = await doVerify(proofs, claims)
expect(results.verified.length).toBeGreaterThan(0)
expect(results.verified).toEqual(claims)
expect(results.unverified.length).toBe(0)
})
it('does not verify a record that doesnt exist', async () => {
const realClaims = await contentsToClaims(repoData)
const claims: RecordCidClaim[] = [
{
...realClaims[0],
rkey: TID.nextStr(),
},
]
const proofs = await getProofs(claims)
const results = await doVerify(proofs, claims)
expect(results.verified.length).toBe(0)
expect(results.unverified.length).toBeGreaterThan(0)
expect(results.unverified).toEqual(claims)
})
it('does not verify an invalid record at a real path', async () => {
const realClaims = await contentsToClaims(repoData)
const claims: RecordCidClaim[] = [
{
...realClaims[0],
cid: await util.randomCid(),
},
]
const proofs = await getProofs(claims)
const results = await doVerify(proofs, claims)
expect(results.verified.length).toBe(0)
expect(results.unverified.length).toBeGreaterThan(0)
expect(results.unverified).toEqual(claims)
})
it('does not verify a delete where the record does exist', async () => {
const realClaims = await contentsToClaims(repoData)
const claims: RecordCidClaim[] = [
{
collection: realClaims[0].collection,
rkey: realClaims[0].rkey,
cid: null,
},
]
const proofs = await getProofs(claims)
const results = await doVerify(proofs, claims)
expect(results.verified.length).toBe(0)
expect(results.unverified.length).toBeGreaterThan(0)
expect(results.unverified).toEqual(claims)
})
it('can determine record proofs from car file', async () => {
const possible = await contentsToClaims(repoData)
const claims = [
//random sampling of records
possible[0],
possible[4],
possible[5],
possible[8],
]
const proofs = await getProofs(claims)
const records = await sync.verifyRecords(proofs, repoDid, keypair.did())
for (const record of records) {
const foundClaim = claims.find(
(claim) =>
claim.collection === record.collection && claim.rkey === record.rkey,
)
if (!foundClaim) {
throw new Error('Could not find record for claim')
}
expect(foundClaim.cid).toEqual(
await cidForCbor(repoData[record.collection][record.rkey]),
)
}
})
it('verifyProofs throws on a bad signature', async () => {
const badRepo = await util.addBadCommit(repo, keypair)
const claims = await contentsToClaims(repoData)
const proofs = await streamToBuffer(
sync.getRecords(storage, badRepo.cid, claims),
)
const fn = sync.verifyProofs(proofs, claims, repoDid, keypair.did())
await expect(fn).rejects.toThrow(sync.RepoVerificationError)
})
})