@forestrie/receipt-verify 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,9 +15,13 @@ export type FreshenReceiptInput = {
15
15
  * value `verify` recomputes from the entry (caller derives it). */
16
16
  leafValue: Uint8Array;
17
17
  /** Consistency-proof chain covering [0 or the trusted base] → the latest
18
- * sealed size, in ascending contiguous order (the raw per-checkpoint
19
- * proofs, with `paths`). The chain's last link must end at the
20
- * checkpoint's sealed size. */
18
+ * sealed size, in ascending contiguous order: one entry per checkpoint,
19
+ * each holding the proofs that checkpoint relays (`proofs`, one or more —
20
+ * ADR-0066 D2). Every link's `signedTreeSize2` must equal its
21
+ * `treeSize2` — the size its LAST relayed proof reaches — as
22
+ * `checkpointConsistencyProof` requires of the checkpoint it decoded each
23
+ * link from. The chain's last link must end at the checkpoint's sealed
24
+ * size. */
21
25
  consistencyProofs: readonly CheckpointConsistencyProof[];
22
26
  /** Trusted base for a suffix chain — the size the caller already trusts
23
27
  * and that size's accumulator; omit for a chain from base 0 (size 0, an
@@ -40,9 +44,11 @@ export type FreshenReceiptResult = {
40
44
  sealedSize: bigint;
41
45
  };
42
46
  /**
43
- * Freshen a stale receipt to the latest sealed state. Throws if the chain is
44
- * not a contiguous cover from the trusted base to the checkpoint's sealed size,
45
- * if a climb node is missing from the supplied proofs, or if the recomputed
47
+ * Freshen a stale receipt to the latest sealed state. Throws if a link's
48
+ * signed `tree-size-2` disagrees with its declared one
49
+ * ({@link CheckpointSignedSizeMismatchError}), if the chain is not a
50
+ * contiguous cover from the trusted base to the checkpoint's sealed size, if
51
+ * a climb node is missing from the supplied proofs, or if the recomputed
46
52
  * peak does not match the folded latest accumulator.
47
53
  */
48
54
  export declare function freshenReceipt(input: FreshenReceiptInput): Promise<FreshenReceiptResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"freshen-receipt.d.ts","sourceRoot":"","sources":["../src/freshen-receipt.ts"],"names":[],"mappings":"AA2DA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAI/B;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,iEAAiE;IACjE,eAAe,EAAE,UAAU,CAAC;IAC5B;uEACmE;IACnE,SAAS,EAAE,UAAU,CAAC;IACtB;;;mCAG+B;IAC/B,iBAAiB,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACzD;;;;;kDAK8C;IAC9C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAC1D;wDACoD;IACpD,qBAAqB,EAAE,UAAU,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,wEAAwE;IACxE,OAAO,EAAE,UAAU,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AASF;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAwK/B"}
1
+ {"version":3,"file":"freshen-receipt.d.ts","sourceRoot":"","sources":["../src/freshen-receipt.ts"],"names":[],"mappings":"AA2DA,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,uBAAuB,CAAC;AAI/B;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,iEAAiE;IACjE,eAAe,EAAE,UAAU,CAAC;IAC5B;uEACmE;IACnE,SAAS,EAAE,UAAU,CAAC;IACtB;;;;;;;eAOW;IACX,iBAAiB,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACzD;;;;;kDAK8C;IAC9C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAC1D;wDACoD;IACpD,qBAAqB,EAAE,UAAU,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,wEAAwE;IACxE,OAAO,EAAE,UAAU,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AASF;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CA4L/B"}
@@ -47,7 +47,7 @@
47
47
  */
48
48
  import { calculateRoot, inclusionProofPath, mmrSizeForLeafCount, peakIndexForLeafProof, peakMMRIndexes, peaksBitmap, } from "@forestrie/merklelog";
49
49
  import { assembleReceiptFromProof, parseCheckpoint, } from "./build-receipt-offline.js";
50
- import { computeCheckpointAccumulator, } from "./checkpoint-chain.js";
50
+ import { CheckpointSignedSizeMismatchError, computeCheckpointAccumulator, } from "./checkpoint-chain.js";
51
51
  import { parseReceipt } from "./parse-receipt.js";
52
52
  import { SubtleHasher } from "./subtle-hasher.js";
53
53
  function bytesEqual(a, b) {
@@ -59,9 +59,11 @@ function bytesEqual(a, b) {
59
59
  return x === 0;
60
60
  }
61
61
  /**
62
- * Freshen a stale receipt to the latest sealed state. Throws if the chain is
63
- * not a contiguous cover from the trusted base to the checkpoint's sealed size,
64
- * if a climb node is missing from the supplied proofs, or if the recomputed
62
+ * Freshen a stale receipt to the latest sealed state. Throws if a link's
63
+ * signed `tree-size-2` disagrees with its declared one
64
+ * ({@link CheckpointSignedSizeMismatchError}), if the chain is not a
65
+ * contiguous cover from the trusted base to the checkpoint's sealed size, if
66
+ * a climb node is missing from the supplied proofs, or if the recomputed
65
67
  * peak does not match the folded latest accumulator.
66
68
  */
67
69
  export async function freshenReceipt(input) {
@@ -106,12 +108,26 @@ export async function freshenReceipt(input) {
106
108
  if (baseAccumulator.length !== basePeaks) {
107
109
  throw new Error(`base accumulator has ${baseAccumulator.length} peaks; trusted base size ${baseSize} has ${basePeaks}`);
108
110
  }
111
+ // Every link's SIGNED tree-size-2 must equal its declared one — the same
112
+ // equality `checkpointConsistencyProof` enforces when it decodes a
113
+ // checkpoint (ADR-0066 D1 as amended, D5.5). These links arrive already
114
+ // decoded, so nothing here had re-read the field the type says was pinned
115
+ // to the signature, and a link carrying `signedTreeSize2: 999n` beside
116
+ // `treeSize2: 7n` freshened (review finding I5).
117
+ for (let i = 0; i < links.length; i++) {
118
+ const link = links[i];
119
+ if (link.signedTreeSize2 !== link.treeSize2) {
120
+ throw new CheckpointSignedSizeMismatchError(`consistency proof ${i}: signed tree-size-2 (-65933) ${link.signedTreeSize2} != declared consistency-proof tree-size-2 ${link.treeSize2}`);
121
+ }
122
+ }
109
123
  // The first link must continue from that size, not from a size it names
110
124
  // itself (ADR-0066 D5.4).
111
125
  if (firstLink.treeSize1 !== baseSize) {
112
126
  throw new Error(`first consistency proof declares tree-size-1 ${firstLink.treeSize1}; the trusted base size is ${baseSize}`);
113
127
  }
114
- // Contiguity: each link continues where the previous one sealed.
128
+ // Contiguity BETWEEN checkpoints: each link continues where the previous
129
+ // one sealed. Contiguity WITHIN a link — between the proofs one
130
+ // checkpoint relays — is `computeCheckpointAccumulator`'s, below.
115
131
  for (let i = 1; i < links.length; i++) {
116
132
  if (links[i].treeSize1 !== links[i - 1].treeSize2) {
117
133
  throw new Error(`consistency chain is not contiguous at link ${i}: base ${links[i].treeSize1} != previous sealed size ${links[i - 1].treeSize2}`);
@@ -122,10 +138,11 @@ export async function freshenReceipt(input) {
122
138
  if (lastLink.treeSize2 !== sealedSize) {
123
139
  throw new Error(`consistency chain ends at size ${lastLink.treeSize2} but the checkpoint sealed size ${sealedSize}`);
124
140
  }
125
- // Fold the chain to the latest accumulator (self-check target). Each step
141
+ // Fold the chain to the latest accumulator (self-check target). Each link
126
142
  // runs against a size the caller trusts, never one read off the link being
127
143
  // folded: the trusted base for the first link, and the size the previous
128
- // link was just folded TO for every link after it.
144
+ // link was just folded TO for every link after it. A link relaying several
145
+ // proofs folds them all, in order, and ends at its last one.
129
146
  let accumulator = baseAccumulator;
130
147
  let sizeFrom = baseSize;
131
148
  for (const p of links) {
@@ -159,20 +176,23 @@ export async function freshenReceipt(input) {
159
176
  for (let k = 0; k < oldPath.length; k++) {
160
177
  store.set(fullIndices[k], oldPath[k]);
161
178
  }
162
- for (const link of links) {
163
- // A base-0 link (treeSize1 === 0) has no from-peaks to climb — a 0→N
179
+ // Every relayed proof contributes, not just one per checkpoint: a link
180
+ // that relays several sealed steps (ADR-0066 D2) carries one set of paths
181
+ // per step, each addressed by that step's own two sizes.
182
+ for (const step of links.flatMap((link) => link.proofs)) {
183
+ // A base-0 step (treeSize1 === 0) has no from-peaks to climb — a 0→N
164
184
  // consistency proof carries `paths: []` (the whole accumulator is its
165
185
  // right-peaks). Skip it: it contributes no store nodes, and calling
166
186
  // `peakMMRIndexes(-1n)` would throw (`posHeight(0)`, FOR-414). A genesis-
167
- // rooted `.sth` chain always starts with such a link.
168
- if (link.treeSize1 === 0n)
187
+ // rooted `.sth` chain always starts with such a step.
188
+ if (step.treeSize1 === 0n)
169
189
  continue;
170
- const fromPeaks = peakMMRIndexes(link.treeSize1 - 1n);
190
+ const fromPeaks = peakMMRIndexes(step.treeSize1 - 1n);
171
191
  fromPeaks.forEach((peakIndex, j) => {
172
- const climb = link.paths[j];
192
+ const climb = step.paths[j];
173
193
  if (climb === undefined)
174
194
  return;
175
- const climbIndices = inclusionProofPath(link.treeSize2 - 1n, peakIndex);
195
+ const climbIndices = inclusionProofPath(step.treeSize2 - 1n, peakIndex);
176
196
  climbIndices.forEach((ix, e) => {
177
197
  const v = climb[e];
178
198
  if (v !== undefined)
package/dist/index.d.ts CHANGED
@@ -76,7 +76,10 @@ export { findGrantLeafInMassif } from "./find-grant-leaf.js";
76
76
  export type { LocatedLeaf } from "./find-grant-leaf.js";
77
77
  /** Re-exported so callers of findGrantLeafInMassif can catch it (FOR-344). */
78
78
  export { MissingIndexError } from "@forestrie/merklelog";
79
- export { accumulatorPayload, checkpointConsistencyProof, computeCheckpointAccumulator, verifyCheckpointChain, CheckpointHighSSignatureError, CheckpointSignedSizeMismatchError, type CheckpointChainLink, type CheckpointChainResult, type CheckpointConsistencyProof, } from "./checkpoint-chain.js";
79
+ export { accumulatorPayload, checkpointConsistencyProof, computeCheckpointAccumulator, verifyCheckpointChain, CheckpointHighSSignatureError, CheckpointProtectedHeaderAlgError, CheckpointSignedSizeMismatchError, ConsistencyChainNotContiguousError, EmptyConsistencyProofsError, type CheckpointChainLink, type CheckpointChainResult, type CheckpointConsistencyProof, } from "./checkpoint-chain.js";
80
+ /** One relayed consistency proof; `CheckpointConsistencyProof.proofs` holds
81
+ * the chain of them a checkpoint carries (ADR-0066 D2). */
82
+ export type { DecodedConsistencyProof } from "./decode-checkpoint-consistency-proof.js";
80
83
  /**
81
84
  * Univocity leaf commitment hash. Was CLI-private (forestrie-cli's own
82
85
  * mirror, "hoist to the library when the FOR-297 multi-hop resolver lands");
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -61,7 +61,7 @@ export { grantCommitmentHashFromGrant } from "./grant-commitment.js";
61
61
  export { findGrantLeafInMassif } from "./find-grant-leaf.js";
62
62
  /** Re-exported so callers of findGrantLeafInMassif can catch it (FOR-344). */
63
63
  export { MissingIndexError } from "@forestrie/merklelog";
64
- export { accumulatorPayload, checkpointConsistencyProof, computeCheckpointAccumulator, verifyCheckpointChain, CheckpointHighSSignatureError, CheckpointSignedSizeMismatchError, } from "./checkpoint-chain.js";
64
+ export { accumulatorPayload, checkpointConsistencyProof, computeCheckpointAccumulator, verifyCheckpointChain, CheckpointHighSSignatureError, CheckpointProtectedHeaderAlgError, CheckpointSignedSizeMismatchError, ConsistencyChainNotContiguousError, EmptyConsistencyProofsError, } from "./checkpoint-chain.js";
65
65
  /**
66
66
  * Univocity leaf commitment hash. Was CLI-private (forestrie-cli's own
67
67
  * mirror, "hoist to the library when the FOR-297 multi-hop resolver lands");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestrie/receipt-verify",
3
- "version": "2.1.0",
3
+ "version": "3.0.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Offline SCITT grant receipt verification (ADR-0045)",
@@ -28,9 +28,9 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@noble/hashes": "^1.7.1",
31
- "@forestrie/chain-rpc": "0.3.0",
32
- "@forestrie/merklelog": "0.4.0",
33
- "@forestrie/encoding": "0.8.0"
31
+ "@forestrie/encoding": "0.9.1",
32
+ "@forestrie/merklelog": "0.5.0",
33
+ "@forestrie/chain-rpc": "0.3.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "esbuild": "^0.24.0",
@@ -37,7 +37,7 @@ import {
37
37
  unwrapCoseSign1Tag,
38
38
  type CoseSign1,
39
39
  } from "./parse-receipt.js";
40
- import { decodeConsistencyProofFromUnprotected } from "./decode-checkpoint-consistency-proof.js";
40
+ import { decodeConsistencyProofsFromUnprotected } from "./decode-checkpoint-consistency-proof.js";
41
41
  import { SubtleHasher } from "./subtle-hasher.js";
42
42
 
43
43
  /** Not in scope for the shared cose-labels module (FOR-568 §4.1). */
@@ -265,15 +265,19 @@ function cborBytes(value: unknown): Uint8Array {
265
265
  * (ADR-0066 D1 as amended, label -65933) from the PROTECTED header — not the
266
266
  * unprotected consistency proof's declared value, which a checkpoint without
267
267
  * a signing key could freely restate (ADR-0066's "keyless first checkpoint"
268
- * case). A consistency proof must still be present (an unsigned checkpoint
269
- * with no proof is not sealed at all); its declared sizes are not otherwise
270
- * used here — {@link checkpointConsistencyProof} in `checkpoint-chain.ts`
271
- * is the validating decode that requires the two to agree.
268
+ * case). At least one consistency proof must still be present (an unsigned
269
+ * checkpoint with no proof is not sealed at all) — one or more, since a
270
+ * checkpoint may relay a chain of sealed steps under one signature
271
+ * (ADR-0066 D2). Their declared sizes are not otherwise used here;
272
+ * {@link checkpointConsistencyProof} in `checkpoint-chain.ts` is the
273
+ * validating decode that requires the signed size and the last relayed
274
+ * proof's to agree.
272
275
  *
273
- * Lenient by design: an absent proof, a malformed proof, or an absent or
274
- * malformed signed tree-size-2 all yield `null` rather than a throw, so a
275
- * caller of {@link parseCheckpoint} sees exactly the same "not verifiable"
276
- * outcome either way.
276
+ * Lenient by design: an absent proof, an empty or malformed
277
+ * consistency-proofs array, or an absent or malformed signed tree-size-2
278
+ * all yield `null` rather than a throw, so a caller of
279
+ * {@link parseCheckpoint} sees exactly the same "not verifiable" outcome
280
+ * either way.
277
281
  */
278
282
  function sealedSizeFromCheckpoint(
279
283
  coseSign1: CoseSign1,
@@ -281,7 +285,7 @@ function sealedSizeFromCheckpoint(
281
285
  ): bigint | null {
282
286
  let declared;
283
287
  try {
284
- declared = decodeConsistencyProofFromUnprotected(unprotected);
288
+ declared = decodeConsistencyProofsFromUnprotected(unprotected);
285
289
  } catch {
286
290
  return null;
287
291
  }