@atproto/repo
Version:
atproto repo and MST implementation
142 lines • 5.39 kB
JavaScript
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