UNPKG

@atproto/repo

Version:

atproto repo and MST implementation

163 lines 6.29 kB
import { TID } from '@atproto/common-web'; import { encode } from '@atproto/lex-cbor'; import { cidForCbor } from '@atproto/lex-data'; import { BlockMap } from './block-map.js'; import { CidSet } from './cid-set.js'; import { DataDiff } from './data-diff.js'; import log from './logger.js'; import { MST } from './mst/index.js'; import { ReadableRepo } from './readable-repo.js'; import { WriteOpAction, def, } from './types.js'; import * as util from './util.js'; export class Repo extends ReadableRepo { constructor(params) { super(params); this.storage = params.storage; } static async formatInitCommit(storage, did, keypair, initialWrites = [], revOverride) { const newBlocks = new BlockMap(); let data = await MST.create(storage); for (const record of initialWrites) { const cid = await newBlocks.add(record.record); const dataKey = util.formatDataKey(record.collection, record.rkey); data = await data.add(dataKey, cid); } const dataCid = await data.getPointer(); const diff = await DataDiff.of(data, null); newBlocks.addMap(diff.newMstBlocks); const rev = revOverride ?? TID.nextStr(); const commit = await util.signCommit({ did, version: 3, rev, prev: null, // added for backwards compatibility with v2 data: dataCid, }, keypair); const commitCid = await newBlocks.add(commit); return { cid: commitCid, rev, since: null, prev: null, newBlocks, relevantBlocks: newBlocks, removedCids: diff.removedCids, }; } static async createFromCommit(storage, commit) { await storage.applyCommit(commit); return Repo.load(storage, commit.cid); } static async create(storage, did, keypair, initialWrites = []) { const commit = await Repo.formatInitCommit(storage, did, keypair, initialWrites); return Repo.createFromCommit(storage, commit); } static async load(storage, cid) { const commitCid = cid || (await storage.getRoot()); if (!commitCid) { throw new Error('No cid provided and none in storage'); } const commit = await storage.readObj(commitCid, def.versionedCommit); const data = await MST.load(storage, commit.data); log.info({ did: commit.did }, 'loaded repo for'); return new Repo({ storage, data, commit: util.ensureV3Commit(commit), cid: commitCid, }); } async formatCommit(toWrite, keypair) { const writes = Array.isArray(toWrite) ? toWrite : [toWrite]; const leaves = new BlockMap(); let data = this.data; for (const write of writes) { if (write.action === WriteOpAction.Create) { const cid = await leaves.add(write.record); const dataKey = write.collection + '/' + write.rkey; data = await data.add(dataKey, cid); } else if (write.action === WriteOpAction.Update) { const cid = await leaves.add(write.record); const dataKey = write.collection + '/' + write.rkey; data = await data.update(dataKey, cid); } else if (write.action === WriteOpAction.Delete) { const dataKey = write.collection + '/' + write.rkey; data = await data.delete(dataKey); } } const dataCid = await data.getPointer(); const diff = await DataDiff.of(data, this.data); const newBlocks = diff.newMstBlocks; const removedCids = diff.removedCids; const proofs = await Promise.all(writes.map((op) => data.getCoveringProof(util.formatDataKey(op.collection, op.rkey)))); const relevantBlocks = new BlockMap(); for (const proof of proofs) relevantBlocks.addMap(proof); const addedLeaves = leaves.getMany(diff.newLeafCids.toList()); if (addedLeaves.missing.length > 0) { throw new Error(`Missing leaf blocks: ${addedLeaves.missing}`); } newBlocks.addMap(addedLeaves.blocks); relevantBlocks.addMap(addedLeaves.blocks); const rev = TID.nextStr(this.commit.rev); const commit = await util.signCommit({ did: this.did, version: 3, rev, prev: null, // added for backwards compatibility with v2 data: dataCid, }, keypair); const commitBytes = encode(commit); const commitCid = await cidForCbor(commitBytes); if (!commitCid.equals(this.cid)) { newBlocks.set(commitCid, commitBytes); relevantBlocks.set(commitCid, commitBytes); removedCids.add(this.cid); } return { cid: commitCid, rev, since: this.commit.rev, prev: this.cid, newBlocks, relevantBlocks, removedCids, }; } async applyCommit(commitData) { await this.storage.applyCommit(commitData); return Repo.load(this.storage, commitData.cid); } async applyWrites(toWrite, keypair) { const commit = await this.formatCommit(toWrite, keypair); return this.applyCommit(commit); } async formatResignCommit(rev, keypair) { const commit = await util.signCommit({ did: this.did, version: 3, rev, prev: null, // added for backwards compatibility with v2 data: this.commit.data, }, keypair); const newBlocks = new BlockMap(); const commitCid = await newBlocks.add(commit); return { cid: commitCid, rev, since: null, prev: null, newBlocks, relevantBlocks: newBlocks, removedCids: new CidSet([this.cid]), }; } async resignCommit(rev, keypair) { const formatted = await this.formatResignCommit(rev, keypair); return this.applyCommit(formatted); } } export default Repo; //# sourceMappingURL=repo.js.map