UNPKG

@atproto/repo

Version:

atproto repo and MST implementation

142 lines 5.39 kB
import { readCarWithRoot } from '../car.js'; import { DataDiff } from '../data-diff.js'; import { MST } from '../mst/index.js'; import { ReadableRepo } from '../readable-repo.js'; import { MemoryBlockstore, SyncStorage, } from '../storage/index.js'; import { def, } from '../types.js'; import * as util from '../util.js'; export const verifyRepoCar = async (carBytes, did, signingKey) => { const car = await readCarWithRoot(carBytes); return verifyRepo(car.blocks, car.root, did, signingKey); }; export const verifyRepo = async (blocks, head, did, signingKey, opts) => { const diff = await verifyDiff(null, blocks, head, did, signingKey, opts); const creates = util.ensureCreates(diff.writes); return { creates, commit: diff.commit, }; }; export const verifyDiffCar = async (repo, carBytes, did, signingKey, opts) => { const car = await readCarWithRoot(carBytes); return verifyDiff(repo, car.blocks, car.root, did, signingKey, opts); }; export const verifyDiff = async (repo, updateBlocks, updateRoot, did, signingKey, opts) => { const { ensureLeaves = true } = opts ?? {}; const stagedStorage = new MemoryBlockstore(updateBlocks); const updateStorage = repo ? new SyncStorage(stagedStorage, repo.storage) : stagedStorage; const updated = await verifyRepoRoot(updateStorage, updateRoot, did, signingKey); const diff = await DataDiff.of(updated.data, repo?.data ?? null); const writes = await util.diffToWriteDescripts(diff); const newBlocks = diff.newMstBlocks; const leaves = updateBlocks.getMany(diff.newLeafCids.toList()); if (leaves.missing.length > 0 && ensureLeaves) { throw new Error(`missing leaf blocks: ${leaves.missing}`); } newBlocks.addMap(leaves.blocks); const removedCids = diff.removedCids; const commitCid = await newBlocks.add(updated.commit); // ensure the commit cid actually changed if (repo) { if (commitCid.equals(repo.cid)) { newBlocks.delete(commitCid); } else { removedCids.add(repo.cid); } } return { writes, commit: { cid: updated.cid, rev: updated.commit.rev, prev: repo?.cid ?? null, since: repo?.commit.rev ?? null, newBlocks, relevantBlocks: newBlocks, removedCids, }, }; }; // @NOTE only verifies the root, not the repo contents const verifyRepoRoot = async (storage, head, did, signingKey) => { const repo = await ReadableRepo.load(storage, head); if (did !== undefined && repo.did !== did) { throw new RepoVerificationError(`Invalid repo did: ${repo.did}`); } if (signingKey !== undefined) { const validSig = await util.verifyCommitSig(repo.commit, signingKey); if (!validSig) { throw new RepoVerificationError(`Invalid signature on commit: ${repo.cid.toString()}`); } } return repo; }; export const verifyProofs = async (proofs, claims, did, didKey) => { const car = await readCarWithRoot(proofs); const blockstore = new MemoryBlockstore(car.blocks); const commit = await blockstore.readObj(car.root, def.commit); if (commit.did !== did) { throw new RepoVerificationError(`Invalid repo did: ${commit.did}`); } const validSig = await util.verifyCommitSig(commit, didKey); if (!validSig) { throw new RepoVerificationError(`Invalid signature on commit: ${car.root.toString()}`); } const mst = MST.load(blockstore, commit.data); const verified = []; const unverified = []; for (const claim of claims) { const found = await mst.get(util.formatDataKey(claim.collection, claim.rkey)); const record = found ? await blockstore.readObj(found, def.map) : null; if (claim.cid === null) { if (record === null) { verified.push(claim); } else { unverified.push(claim); } } else { if (found?.equals(claim.cid)) { verified.push(claim); } else { unverified.push(claim); } } } return { verified, unverified }; }; export const verifyRecords = async (proofs, did, signingKey) => { const car = await readCarWithRoot(proofs); const blockstore = new MemoryBlockstore(car.blocks); const commit = await blockstore.readObj(car.root, def.commit); if (commit.did !== did) { throw new RepoVerificationError(`Invalid repo did: ${commit.did}`); } const validSig = await util.verifyCommitSig(commit, signingKey); if (!validSig) { throw new RepoVerificationError(`Invalid signature on commit: ${car.root.toString()}`); } const mst = MST.load(blockstore, commit.data); const records = []; const leaves = await mst.reachableLeaves(); for (const leaf of leaves) { const { collection, rkey } = util.parseDataKey(leaf.key); const record = await blockstore.attemptReadRecord(leaf.value); if (record) { records.push({ collection, rkey, record, }); } } return records; }; export class RepoVerificationError extends Error { } //# sourceMappingURL=consumer.js.map