atproto user agency toolkit for individuals and groups
1/**
2 * Challenge responder: produce a response given a challenge + BlockStore.
3 *
4 * For MST challenges: generates MST proofs via generateMstProof.
5 * For block-sample challenges: checks block availability and returns prefixes.
6 */
7
8import type { BlockStore } from "../../ipfs.js";
9import type {
10 StorageChallenge,
11 StorageChallengeResponse,
12 BlockResult,
13} from "./types.js";
14import { DEFAULT_CHALLENGE_CONFIG } from "./types.js";
15import { generateMstProof, type MstProof } from "../mst-proof.js";
16
17/**
18 * Produce a challenge response.
19 *
20 * Calls generateMstProof for MST challenges, checks block availability
21 * and provides prefixes for block-sample challenges.
22 */
23export async function respondToChallenge(
24 challenge: StorageChallenge,
25 blockStore: BlockStore,
26 responderDid: string,
27 config?: { blockPrefixLength?: number },
28): Promise<StorageChallengeResponse> {
29 const prefixLength =
30 config?.blockPrefixLength ?? DEFAULT_CHALLENGE_CONFIG.blockPrefixLength;
31
32 let mstProofs: MstProof[] | undefined;
33 let blockResults: BlockResult[] | undefined;
34
35 // Handle MST proof challenges
36 if (
37 challenge.challengeType === "mst-proof" ||
38 challenge.challengeType === "combined"
39 ) {
40 mstProofs = [];
41 for (const recordPath of challenge.recordPaths) {
42 try {
43 const proof = await generateMstProof(
44 blockStore,
45 challenge.commitCid,
46 recordPath,
47 );
48 mstProofs.push(proof);
49 } catch {
50 // If we can't generate a proof (missing blocks), add an empty proof
51 mstProofs.push({
52 commitBlock: {
53 cid: challenge.commitCid,
54 bytes: new Uint8Array(0),
55 },
56 nodes: [],
57 recordCid: null,
58 found: false,
59 });
60 }
61 }
62 }
63
64 // Handle block-sample challenges
65 if (
66 challenge.challengeType === "block-sample" ||
67 challenge.challengeType === "combined"
68 ) {
69 if (challenge.blockCids) {
70 blockResults = [];
71 for (const cid of challenge.blockCids) {
72 const bytes = await blockStore.getBlock(cid);
73 if (bytes) {
74 blockResults.push({
75 cid,
76 available: true,
77 prefix: bytes.slice(0, prefixLength),
78 });
79 } else {
80 blockResults.push({
81 cid,
82 available: false,
83 });
84 }
85 }
86 }
87 }
88
89 return {
90 challengeId: challenge.id,
91 responderDid,
92 mstProofs,
93 blockResults,
94 respondedAt: new Date().toISOString(),
95 };
96}