@atproto/repo
Version:
atproto repo and MST implementation
185 lines • 7.38 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.RepoVerificationError = exports.verifyRecords = exports.verifyProofs = exports.verifyDiff = exports.verifyDiffCar = exports.verifyRepo = exports.verifyRepoCar = void 0;
const car_1 = require("../car");
const data_diff_1 = require("../data-diff");
const mst_1 = require("../mst");
const readable_repo_1 = require("../readable-repo");
const storage_1 = require("../storage");
const types_1 = require("../types");
const util = __importStar(require("../util"));
const verifyRepoCar = async (carBytes, did, signingKey) => {
const car = await (0, car_1.readCarWithRoot)(carBytes);
return (0, exports.verifyRepo)(car.blocks, car.root, did, signingKey);
};
exports.verifyRepoCar = verifyRepoCar;
const verifyRepo = async (blocks, head, did, signingKey, opts) => {
const diff = await (0, exports.verifyDiff)(null, blocks, head, did, signingKey, opts);
const creates = util.ensureCreates(diff.writes);
return {
creates,
commit: diff.commit,
};
};
exports.verifyRepo = verifyRepo;
const verifyDiffCar = async (repo, carBytes, did, signingKey, opts) => {
const car = await (0, car_1.readCarWithRoot)(carBytes);
return (0, exports.verifyDiff)(repo, car.blocks, car.root, did, signingKey, opts);
};
exports.verifyDiffCar = verifyDiffCar;
const verifyDiff = async (repo, updateBlocks, updateRoot, did, signingKey, opts) => {
const { ensureLeaves = true } = opts ?? {};
const stagedStorage = new storage_1.MemoryBlockstore(updateBlocks);
const updateStorage = repo
? new storage_1.SyncStorage(stagedStorage, repo.storage)
: stagedStorage;
const updated = await verifyRepoRoot(updateStorage, updateRoot, did, signingKey);
const diff = await data_diff_1.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,
},
};
};
exports.verifyDiff = verifyDiff;
// @NOTE only verifies the root, not the repo contents
const verifyRepoRoot = async (storage, head, did, signingKey) => {
const repo = await readable_repo_1.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;
};
const verifyProofs = async (proofs, claims, did, didKey) => {
const car = await (0, car_1.readCarWithRoot)(proofs);
const blockstore = new storage_1.MemoryBlockstore(car.blocks);
const commit = await blockstore.readObj(car.root, types_1.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_1.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, types_1.def.map) : null;
if (claim.cid === null) {
if (record === null) {
verified.push(claim);
}
else {
unverified.push(claim);
}
}
else {
if (claim.cid.equals(found)) {
verified.push(claim);
}
else {
unverified.push(claim);
}
}
}
return { verified, unverified };
};
exports.verifyProofs = verifyProofs;
const verifyRecords = async (proofs, did, signingKey) => {
const car = await (0, car_1.readCarWithRoot)(proofs);
const blockstore = new storage_1.MemoryBlockstore(car.blocks);
const commit = await blockstore.readObj(car.root, types_1.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_1.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;
};
exports.verifyRecords = verifyRecords;
class RepoVerificationError extends Error {
}
exports.RepoVerificationError = RepoVerificationError;
//# sourceMappingURL=consumer.js.map