@mikeargento/bitgraph 1.1.0 → 1.1.1

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.
Files changed (144) hide show
  1. package/README.md +49 -40
  2. package/dist/__tests__/audit-anchors.test.d.ts +2 -0
  3. package/dist/__tests__/audit-anchors.test.d.ts.map +1 -0
  4. package/dist/__tests__/audit-anchors.test.js +152 -0
  5. package/dist/__tests__/audit-anchors.test.js.map +1 -0
  6. package/dist/__tests__/audit-anomalies.test.d.ts +2 -0
  7. package/dist/__tests__/audit-anomalies.test.d.ts.map +1 -0
  8. package/dist/__tests__/audit-anomalies.test.js +663 -0
  9. package/dist/__tests__/audit-anomalies.test.js.map +1 -0
  10. package/dist/__tests__/audit-attestation.test.d.ts +2 -0
  11. package/dist/__tests__/audit-attestation.test.d.ts.map +1 -0
  12. package/dist/__tests__/audit-attestation.test.js +403 -0
  13. package/dist/__tests__/audit-attestation.test.js.map +1 -0
  14. package/dist/__tests__/audit-bench-smoke.test.d.ts +2 -0
  15. package/dist/__tests__/audit-bench-smoke.test.d.ts.map +1 -0
  16. package/dist/__tests__/audit-bench-smoke.test.js +87 -0
  17. package/dist/__tests__/audit-bench-smoke.test.js.map +1 -0
  18. package/dist/__tests__/audit-boundary.test.d.ts +20 -0
  19. package/dist/__tests__/audit-boundary.test.d.ts.map +1 -0
  20. package/dist/__tests__/audit-boundary.test.js +84 -0
  21. package/dist/__tests__/audit-boundary.test.js.map +1 -0
  22. package/dist/__tests__/audit-cli.test.d.ts +2 -0
  23. package/dist/__tests__/audit-cli.test.d.ts.map +1 -0
  24. package/dist/__tests__/audit-cli.test.js +239 -0
  25. package/dist/__tests__/audit-cli.test.js.map +1 -0
  26. package/dist/__tests__/audit-dependencies.test.d.ts +2 -0
  27. package/dist/__tests__/audit-dependencies.test.d.ts.map +1 -0
  28. package/dist/__tests__/audit-dependencies.test.js +116 -0
  29. package/dist/__tests__/audit-dependencies.test.js.map +1 -0
  30. package/dist/__tests__/audit-fixtures.d.ts +265 -0
  31. package/dist/__tests__/audit-fixtures.d.ts.map +1 -0
  32. package/dist/__tests__/audit-fixtures.js +511 -0
  33. package/dist/__tests__/audit-fixtures.js.map +1 -0
  34. package/dist/__tests__/audit-ingest.test.d.ts +2 -0
  35. package/dist/__tests__/audit-ingest.test.d.ts.map +1 -0
  36. package/dist/__tests__/audit-ingest.test.js +473 -0
  37. package/dist/__tests__/audit-ingest.test.js.map +1 -0
  38. package/dist/__tests__/audit-memory-ingest.test.d.ts +12 -0
  39. package/dist/__tests__/audit-memory-ingest.test.d.ts.map +1 -0
  40. package/dist/__tests__/audit-memory-ingest.test.js +120 -0
  41. package/dist/__tests__/audit-memory-ingest.test.js.map +1 -0
  42. package/dist/__tests__/audit-property.test.d.ts +2 -0
  43. package/dist/__tests__/audit-property.test.d.ts.map +1 -0
  44. package/dist/__tests__/audit-property.test.js +771 -0
  45. package/dist/__tests__/audit-property.test.js.map +1 -0
  46. package/dist/__tests__/audit-real-fixtures.test.d.ts +2 -0
  47. package/dist/__tests__/audit-real-fixtures.test.d.ts.map +1 -0
  48. package/dist/__tests__/audit-real-fixtures.test.js +132 -0
  49. package/dist/__tests__/audit-real-fixtures.test.js.map +1 -0
  50. package/dist/__tests__/audit-real-live.test.d.ts +2 -0
  51. package/dist/__tests__/audit-real-live.test.d.ts.map +1 -0
  52. package/dist/__tests__/audit-real-live.test.js +107 -0
  53. package/dist/__tests__/audit-real-live.test.js.map +1 -0
  54. package/dist/__tests__/audit-reconstruct.test.d.ts +2 -0
  55. package/dist/__tests__/audit-reconstruct.test.d.ts.map +1 -0
  56. package/dist/__tests__/audit-reconstruct.test.js +287 -0
  57. package/dist/__tests__/audit-reconstruct.test.js.map +1 -0
  58. package/dist/__tests__/audit-report.test.d.ts +2 -0
  59. package/dist/__tests__/audit-report.test.d.ts.map +1 -0
  60. package/dist/__tests__/audit-report.test.js +186 -0
  61. package/dist/__tests__/audit-report.test.js.map +1 -0
  62. package/dist/__tests__/audit-roundtrip.test.d.ts +2 -0
  63. package/dist/__tests__/audit-roundtrip.test.d.ts.map +1 -0
  64. package/dist/__tests__/audit-roundtrip.test.js +368 -0
  65. package/dist/__tests__/audit-roundtrip.test.js.map +1 -0
  66. package/dist/__tests__/audit-temporal.test.d.ts +2 -0
  67. package/dist/__tests__/audit-temporal.test.d.ts.map +1 -0
  68. package/dist/__tests__/audit-temporal.test.js +364 -0
  69. package/dist/__tests__/audit-temporal.test.js.map +1 -0
  70. package/dist/__tests__/audit-tiers.test.d.ts +2 -0
  71. package/dist/__tests__/audit-tiers.test.d.ts.map +1 -0
  72. package/dist/__tests__/audit-tiers.test.js +282 -0
  73. package/dist/__tests__/audit-tiers.test.js.map +1 -0
  74. package/dist/__tests__/audit-witness-real.test.d.ts +19 -0
  75. package/dist/__tests__/audit-witness-real.test.d.ts.map +1 -0
  76. package/dist/__tests__/audit-witness-real.test.js +82 -0
  77. package/dist/__tests__/audit-witness-real.test.js.map +1 -0
  78. package/dist/__tests__/audit-witness.test.d.ts +2 -0
  79. package/dist/__tests__/audit-witness.test.d.ts.map +1 -0
  80. package/dist/__tests__/audit-witness.test.js +308 -0
  81. package/dist/__tests__/audit-witness.test.js.map +1 -0
  82. package/dist/__tests__/chain-hash-anchor.test.d.ts +22 -0
  83. package/dist/__tests__/chain-hash-anchor.test.d.ts.map +1 -0
  84. package/dist/__tests__/chain-hash-anchor.test.js +55 -0
  85. package/dist/__tests__/chain-hash-anchor.test.js.map +1 -0
  86. package/dist/__tests__/declared-proof-fixture.d.ts +66 -0
  87. package/dist/__tests__/declared-proof-fixture.d.ts.map +1 -0
  88. package/dist/__tests__/declared-proof-fixture.js +66 -0
  89. package/dist/__tests__/declared-proof-fixture.js.map +1 -0
  90. package/dist/__tests__/mock-proof-fixture.d.ts +15 -0
  91. package/dist/__tests__/mock-proof-fixture.d.ts.map +1 -0
  92. package/dist/__tests__/mock-proof-fixture.js +34 -0
  93. package/dist/__tests__/mock-proof-fixture.js.map +1 -0
  94. package/dist/__tests__/proof-hash-regression.test.js +4 -43
  95. package/dist/__tests__/proof-hash-regression.test.js.map +1 -1
  96. package/dist/__tests__/proof-hash.test.js +3 -21
  97. package/dist/__tests__/proof-hash.test.js.map +1 -1
  98. package/dist/__tests__/proof-integrity.test.d.ts +2 -0
  99. package/dist/__tests__/proof-integrity.test.d.ts.map +1 -0
  100. package/dist/__tests__/proof-integrity.test.js +426 -0
  101. package/dist/__tests__/proof-integrity.test.js.map +1 -0
  102. package/dist/__tests__/realistic-proof-fixture.d.ts +56 -0
  103. package/dist/__tests__/realistic-proof-fixture.d.ts.map +1 -0
  104. package/dist/__tests__/realistic-proof-fixture.js +57 -0
  105. package/dist/__tests__/realistic-proof-fixture.js.map +1 -0
  106. package/dist/__tests__/verifier.test.js +74 -0
  107. package/dist/__tests__/verifier.test.js.map +1 -1
  108. package/package.json +10 -5
  109. package/src/__tests__/audit-anchors.test.ts +188 -0
  110. package/src/__tests__/audit-anomalies.test.ts +815 -0
  111. package/src/__tests__/audit-attestation.test.ts +529 -0
  112. package/src/__tests__/audit-bench-smoke.test.ts +108 -0
  113. package/src/__tests__/audit-boundary.test.ts +96 -0
  114. package/src/__tests__/audit-cli.test.ts +288 -0
  115. package/src/__tests__/audit-dependencies.test.ts +141 -0
  116. package/src/__tests__/audit-fixtures.ts +736 -0
  117. package/src/__tests__/audit-ingest.test.ts +575 -0
  118. package/src/__tests__/audit-memory-ingest.test.ts +140 -0
  119. package/src/__tests__/audit-property.test.ts +892 -0
  120. package/src/__tests__/audit-real-fixtures.test.ts +153 -0
  121. package/src/__tests__/audit-real-live.test.ts +128 -0
  122. package/src/__tests__/audit-reconstruct.test.ts +353 -0
  123. package/src/__tests__/audit-report.test.ts +222 -0
  124. package/src/__tests__/audit-roundtrip.test.ts +464 -0
  125. package/src/__tests__/audit-temporal.test.ts +446 -0
  126. package/src/__tests__/audit-tiers.test.ts +343 -0
  127. package/src/__tests__/audit-witness-real.test.ts +92 -0
  128. package/src/__tests__/audit-witness.test.ts +345 -0
  129. package/src/__tests__/chain-hash-anchor.test.ts +67 -0
  130. package/src/__tests__/declared-proof-fixture.ts +65 -0
  131. package/src/__tests__/mock-proof-fixture.ts +36 -0
  132. package/src/__tests__/proof-hash-regression.test.ts +4 -44
  133. package/src/__tests__/proof-hash.test.ts +3 -22
  134. package/src/__tests__/proof-integrity.test.ts +541 -0
  135. package/src/__tests__/real-fixtures/anchor-after-112762.json +47 -0
  136. package/src/__tests__/real-fixtures/anchor-with-ethereum-112758.json +47 -0
  137. package/src/__tests__/real-fixtures/live-proof-0.json +42 -0
  138. package/src/__tests__/real-fixtures/live-proof-1.json +43 -0
  139. package/src/__tests__/real-fixtures/live-proof-2.json +43 -0
  140. package/src/__tests__/real-fixtures/successor-112760.json +38 -0
  141. package/src/__tests__/real-fixtures/witness-25525146.json +6 -0
  142. package/src/__tests__/real-fixtures/witness-25525147.json +6 -0
  143. package/src/__tests__/realistic-proof-fixture.ts +57 -0
  144. package/src/__tests__/verifier.test.ts +83 -0
@@ -0,0 +1,541 @@
1
+ // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
+
3
+ /**
4
+ * Tests for verifyProofIntegrity, the bytes-free proof integrity API in
5
+ * @mikeargento/bitgraph-verify.
6
+ *
7
+ * Also carries regression coverage asserting that verify() behavior is
8
+ * unchanged by the shared-pipeline refactor: same checks, same order,
9
+ * same failure strings.
10
+ */
11
+
12
+ import { describe, test, before, beforeEach } from "node:test";
13
+ import * as assert from "node:assert/strict";
14
+ import { getPublicKeyAsync, signAsync } from "@noble/ed25519";
15
+ import { sha256 } from "@noble/hashes/sha256";
16
+ import {
17
+ verify,
18
+ verifyProofIntegrity,
19
+ resetEpochLinkState,
20
+ canonicalize,
21
+ } from "@mikeargento/bitgraph-verify";
22
+ import type { BitGraphProof, ProofIntegrityResult } from "@mikeargento/bitgraph-verify";
23
+ import { Constructor } from "../constructor.js";
24
+ import type { HostCapabilities } from "../host.js";
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Test-fixture helpers
28
+ // ---------------------------------------------------------------------------
29
+
30
+ interface Fixture {
31
+ proof: BitGraphProof;
32
+ bytes: Uint8Array;
33
+ publicKeyB64: string;
34
+ measurement: string;
35
+ }
36
+
37
+ /** Constructor-built fixture, mirroring makeFixture in verifier.test.ts. */
38
+ async function makeFixture(opts?: {
39
+ epochId?: string;
40
+ withCounter?: boolean;
41
+ }): Promise<Fixture> {
42
+ const privateKey = crypto.getRandomValues(new Uint8Array(32));
43
+ const publicKeyBytes = await getPublicKeyAsync(privateKey);
44
+ const publicKeyB64 = Buffer.from(publicKeyBytes).toString("base64");
45
+ const measurement = "test-measurement-integrity";
46
+
47
+ let counter = 0;
48
+ const host: HostCapabilities = opts?.withCounter
49
+ ? {
50
+ enforcementTier: "stub" as const,
51
+ getMeasurement: async () => measurement,
52
+ getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
53
+ sign: async (data: Uint8Array) => signAsync(data, privateKey),
54
+ getPublicKey: async () => publicKeyBytes,
55
+ nextCounter: async () => String(++counter),
56
+ }
57
+ : {
58
+ enforcementTier: "stub" as const,
59
+ getMeasurement: async () => measurement,
60
+ getFreshNonce: async () => crypto.getRandomValues(new Uint8Array(16)),
61
+ sign: async (data: Uint8Array) => signAsync(data, privateKey),
62
+ getPublicKey: async () => publicKeyBytes,
63
+ };
64
+
65
+ const ctor = await Constructor.initialize(
66
+ opts?.epochId !== undefined ? { host, epochId: opts.epochId } : { host },
67
+ );
68
+
69
+ const bytes = new TextEncoder().encode("bitgraph-integrity-test-payload");
70
+ const proof = await ctor.commit({ bytes });
71
+
72
+ return { proof, bytes, publicKeyB64, measurement };
73
+ }
74
+
75
+ /** Deep-clone a proof so mutations don't affect the original. */
76
+ function clone(proof: BitGraphProof): BitGraphProof {
77
+ return JSON.parse(JSON.stringify(proof)) as BitGraphProof;
78
+ }
79
+
80
+ function b64(bytes: Uint8Array): string {
81
+ return Buffer.from(bytes).toString("base64");
82
+ }
83
+
84
+ interface ManualKey {
85
+ privateKey: Uint8Array;
86
+ publicKeyB64: string;
87
+ }
88
+
89
+ async function makeKey(): Promise<ManualKey> {
90
+ const privateKey = crypto.getRandomValues(new Uint8Array(32));
91
+ const publicKeyB64 = b64(await getPublicKeyAsync(privateKey));
92
+ return { privateKey, publicKeyB64 };
93
+ }
94
+
95
+ /**
96
+ * Manually build and sign a proof over the exact canonical SignedBody
97
+ * layout the verifier reconstructs. Used for slot-carrying and
98
+ * epochLink-carrying proofs, which the root Constructor does not produce.
99
+ */
100
+ async function signBody(
101
+ key: ManualKey,
102
+ artifact: BitGraphProof["artifact"],
103
+ commit: BitGraphProof["commit"],
104
+ measurement: string,
105
+ ): Promise<BitGraphProof> {
106
+ const signedBody = {
107
+ version: "bitgraph/1" as const,
108
+ artifact,
109
+ commit,
110
+ publicKeyB64: key.publicKeyB64,
111
+ enforcement: "stub" as const,
112
+ measurement,
113
+ };
114
+ const signatureB64 = b64(await signAsync(canonicalize(signedBody), key.privateKey));
115
+ return {
116
+ version: "bitgraph/1",
117
+ artifact,
118
+ commit,
119
+ signer: { publicKeyB64: key.publicKeyB64, signatureB64 },
120
+ environment: { enforcement: "stub", measurement },
121
+ };
122
+ }
123
+
124
+ interface SlotFixture {
125
+ proof: BitGraphProof;
126
+ bytes: Uint8Array;
127
+ measurement: string;
128
+ }
129
+
130
+ /**
131
+ * Build a slot-carrying proof: slot allocated at counter 1, commit at
132
+ * counter 2, slotHashB64 binding the signed commit to the exact slot body.
133
+ */
134
+ async function makeSlotFixture(opts?: { commitCounter?: string }): Promise<SlotFixture> {
135
+ const key = await makeKey();
136
+ const measurement = "test-measurement-slot";
137
+ const epochId = "epoch-slot-test";
138
+ const nonceB64 = b64(crypto.getRandomValues(new Uint8Array(16)));
139
+
140
+ const slotBody = {
141
+ version: "bitgraph/slot/1" as const,
142
+ nonceB64,
143
+ counter: "1",
144
+ time: 1_700_000_000_000,
145
+ epochId,
146
+ publicKeyB64: key.publicKeyB64,
147
+ };
148
+ const slotCanonical = canonicalize(slotBody);
149
+ const slotSignatureB64 = b64(await signAsync(slotCanonical, key.privateKey));
150
+ const slotHashB64 = b64(sha256(slotCanonical));
151
+
152
+ const bytes = new TextEncoder().encode("bitgraph-integrity-slot-payload");
153
+ const artifact: BitGraphProof["artifact"] = {
154
+ hashAlg: "sha256",
155
+ digestB64: b64(sha256(bytes)),
156
+ };
157
+ const commit: BitGraphProof["commit"] = {
158
+ nonceB64,
159
+ counter: opts?.commitCounter ?? "2",
160
+ epochId,
161
+ slotCounter: "1",
162
+ slotHashB64,
163
+ };
164
+
165
+ const proof = await signBody(key, artifact, commit, measurement);
166
+ proof.slotAllocation = { ...slotBody, signatureB64: slotSignatureB64 };
167
+ return { proof, bytes, measurement };
168
+ }
169
+
170
+ interface EpochLinkFixture {
171
+ proof: BitGraphProof;
172
+ bytes: Uint8Array;
173
+ }
174
+
175
+ /**
176
+ * Build an epoch-genesis proof carrying an epochLink. The link is inside
177
+ * the signed commit, so the Ed25519 signature covers it.
178
+ */
179
+ async function makeEpochLinkFixture(opts?: {
180
+ toEpochId?: string;
181
+ samePrevKey?: boolean;
182
+ }): Promise<EpochLinkFixture> {
183
+ const key = await makeKey();
184
+ const prevKey = await makeKey();
185
+ const toEpochId = opts?.toEpochId ?? "epoch-successor-a";
186
+
187
+ const epochLink = {
188
+ prevEpochId: "epoch-predecessor-a",
189
+ prevPublicKeyB64: opts?.samePrevKey === true ? key.publicKeyB64 : prevKey.publicKeyB64,
190
+ prevCounter: "42",
191
+ prevProofHashB64: b64(sha256(new TextEncoder().encode("predecessor-terminal-proof"))),
192
+ toEpochId,
193
+ toPublicKeyB64: key.publicKeyB64,
194
+ };
195
+
196
+ const bytes = new TextEncoder().encode("bitgraph-integrity-epochlink-payload");
197
+ const artifact: BitGraphProof["artifact"] = {
198
+ hashAlg: "sha256",
199
+ digestB64: b64(sha256(bytes)),
200
+ };
201
+ const commit: BitGraphProof["commit"] = {
202
+ nonceB64: b64(crypto.getRandomValues(new Uint8Array(16))),
203
+ counter: "1",
204
+ epochId: toEpochId,
205
+ epochLink,
206
+ };
207
+
208
+ const proof = await signBody(key, artifact, commit, "test-measurement-epochlink");
209
+ return { proof, bytes };
210
+ }
211
+
212
+ // ---------------------------------------------------------------------------
213
+ // Module-level fixtures
214
+ // ---------------------------------------------------------------------------
215
+
216
+ let fx!: Fixture;
217
+
218
+ before(async () => {
219
+ fx = await makeFixture();
220
+ });
221
+
222
+ // ---------------------------------------------------------------------------
223
+ // Happy path: integrity without bytes
224
+ // ---------------------------------------------------------------------------
225
+
226
+ describe("verifyProofIntegrity: valid proof, no bytes", () => {
227
+ test("passes for a freshly created proof and reports binding not checked", async () => {
228
+ const result = await verifyProofIntegrity({ proof: fx.proof });
229
+ assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
230
+ });
231
+
232
+ test("passes for a proof with counter and epochId", async () => {
233
+ const chained = await makeFixture({ withCounter: true, epochId: "epoch-integrity-xyz" });
234
+ const result = await verifyProofIntegrity({ proof: chained.proof });
235
+ assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
236
+ });
237
+
238
+ test("passes even when verify() with wrong bytes fails: binding is genuinely not checked", async () => {
239
+ const wrongBytes = new Uint8Array([1, 2, 3]);
240
+ const full = await verify({ proof: fx.proof, bytes: wrongBytes });
241
+ assert.equal(full.valid, false);
242
+ assert.match(full.reason ?? "", /digest mismatch/);
243
+
244
+ const integrity = await verifyProofIntegrity({ proof: fx.proof });
245
+ assert.deepEqual(integrity, { valid: true, artifactBinding: "not-checked" });
246
+ });
247
+ });
248
+
249
+ // ---------------------------------------------------------------------------
250
+ // Result shape
251
+ // ---------------------------------------------------------------------------
252
+
253
+ describe("verifyProofIntegrity: result shape", () => {
254
+ test("failure result also carries artifactBinding 'not-checked' plus a reason", async () => {
255
+ const tampered = clone(fx.proof);
256
+ tampered.signer.signatureB64 = Buffer.alloc(64).toString("base64");
257
+ const result: ProofIntegrityResult = await verifyProofIntegrity({ proof: tampered });
258
+ assert.equal(result.valid, false);
259
+ assert.equal(result.artifactBinding, "not-checked");
260
+ assert.equal(typeof result.reason, "string");
261
+ });
262
+ });
263
+
264
+ // ---------------------------------------------------------------------------
265
+ // Tampering
266
+ // ---------------------------------------------------------------------------
267
+
268
+ describe("verifyProofIntegrity: tampering", () => {
269
+ test("fails when signatureB64 is replaced with all-zeros", async () => {
270
+ const tampered = clone(fx.proof);
271
+ tampered.signer.signatureB64 = Buffer.alloc(64).toString("base64");
272
+ const result = await verifyProofIntegrity({ proof: tampered });
273
+ assert.equal(result.valid, false);
274
+ assert.match(result.reason ?? "", /signature/);
275
+ });
276
+
277
+ test("fails when version is tampered, with a version reason", async () => {
278
+ const tampered = clone(fx.proof);
279
+ (tampered as unknown as Record<string, unknown>)["version"] = "bitgraph/2";
280
+ const result = await verifyProofIntegrity({ proof: tampered });
281
+ assert.equal(result.valid, false);
282
+ assert.equal(result.reason, "unsupported proof version: bitgraph/2");
283
+ });
284
+
285
+ test("fails when a signed field (measurement) is tampered", async () => {
286
+ const tampered = clone(fx.proof);
287
+ tampered.environment.measurement = "evil-measurement";
288
+ const result = await verifyProofIntegrity({ proof: tampered });
289
+ assert.equal(result.valid, false);
290
+ assert.match(result.reason ?? "", /signature/);
291
+ });
292
+
293
+ test("fails when artifact.digestB64 is not valid base64 (well-formedness still checked)", async () => {
294
+ const tampered = clone(fx.proof);
295
+ tampered.artifact.digestB64 = "!!!";
296
+ const result = await verifyProofIntegrity({ proof: tampered });
297
+ assert.equal(result.valid, false);
298
+ assert.equal(result.reason, "artifact.digestB64 is not valid base64");
299
+ });
300
+
301
+ test("fails for non-object proof", async () => {
302
+ const result = await verifyProofIntegrity({
303
+ proof: "not-a-proof" as unknown as BitGraphProof,
304
+ });
305
+ assert.equal(result.valid, false);
306
+ assert.match(result.reason ?? "", /object/);
307
+ });
308
+ });
309
+
310
+ // ---------------------------------------------------------------------------
311
+ // Slot allocation (bytes-free)
312
+ // ---------------------------------------------------------------------------
313
+
314
+ describe("verifyProofIntegrity: slot allocation", () => {
315
+ test("passes for a valid slot-carrying proof", async () => {
316
+ const slotFx = await makeSlotFixture();
317
+ const result = await verifyProofIntegrity({ proof: slotFx.proof });
318
+ assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
319
+ });
320
+
321
+ test("fails when commit.slotHashB64 is tampered (signed field)", async () => {
322
+ const slotFx = await makeSlotFixture();
323
+ const tampered = clone(slotFx.proof);
324
+ tampered.commit.slotHashB64 = Buffer.alloc(32, 0xee).toString("base64");
325
+ const result = await verifyProofIntegrity({ proof: tampered });
326
+ assert.equal(result.valid, false);
327
+ assert.match(result.reason ?? "", /signature/);
328
+ });
329
+
330
+ test("fails with a slot binding reason when the embedded slot record is swapped", async () => {
331
+ const slotA = await makeSlotFixture();
332
+ const slotB = await makeSlotFixture();
333
+ const tampered = clone(slotA.proof);
334
+ // slotB's record is validly signed by its own key, but it is not the
335
+ // record that slotA's signed commit.slotHashB64 binds to.
336
+ tampered.slotAllocation = clone(slotB.proof).slotAllocation!;
337
+ const result = await verifyProofIntegrity({ proof: tampered });
338
+ assert.equal(result.valid, false);
339
+ assert.match(result.reason ?? "", /slotHashB64 does not match SHA-256 of canonical slot body/);
340
+ });
341
+
342
+ test("fails when slotAllocation.counter is tampered (slot signature breaks)", async () => {
343
+ const slotFx = await makeSlotFixture();
344
+ const tampered = clone(slotFx.proof);
345
+ tampered.slotAllocation!.counter = "999";
346
+ const result = await verifyProofIntegrity({ proof: tampered });
347
+ assert.equal(result.valid, false);
348
+ assert.match(result.reason ?? "", /slotAllocation signature verification failed/);
349
+ });
350
+
351
+ test("fails when slot counter is not less than commit counter", async () => {
352
+ // Slot at counter 1, commit also at counter 1: ordering violated even
353
+ // though every signature is genuine.
354
+ const slotFx = await makeSlotFixture({ commitCounter: "1" });
355
+ const result = await verifyProofIntegrity({ proof: slotFx.proof });
356
+ assert.equal(result.valid, false);
357
+ assert.match(result.reason ?? "", /must be less than commit\.counter/);
358
+ });
359
+ });
360
+
361
+ // ---------------------------------------------------------------------------
362
+ // Epoch link (bytes-free)
363
+ // ---------------------------------------------------------------------------
364
+
365
+ describe("verifyProofIntegrity: epochLink", () => {
366
+ beforeEach(() => {
367
+ resetEpochLinkState();
368
+ });
369
+
370
+ test("passes for a valid epoch-genesis proof with epochLink", async () => {
371
+ const linkFx = await makeEpochLinkFixture();
372
+ const result = await verifyProofIntegrity({ proof: linkFx.proof });
373
+ assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
374
+ });
375
+
376
+ test("is idempotent for the same successor epoch", async () => {
377
+ const linkFx = await makeEpochLinkFixture();
378
+ const first = await verifyProofIntegrity({ proof: linkFx.proof });
379
+ const second = await verifyProofIntegrity({ proof: linkFx.proof });
380
+ assert.equal(first.valid, true);
381
+ assert.equal(second.valid, true);
382
+ });
383
+
384
+ test("fails when predecessor and successor keys are identical", async () => {
385
+ const linkFx = await makeEpochLinkFixture({ samePrevKey: true });
386
+ const result = await verifyProofIntegrity({ proof: linkFx.proof });
387
+ assert.equal(result.valid, false);
388
+ assert.match(result.reason ?? "", /different keys/);
389
+ });
390
+
391
+ test("detects a fork: same predecessor consumed by two successor epochs", async () => {
392
+ const first = await makeEpochLinkFixture({ toEpochId: "epoch-successor-a" });
393
+ const second = await makeEpochLinkFixture({ toEpochId: "epoch-successor-b" });
394
+
395
+ const r1 = await verifyProofIntegrity({ proof: first.proof });
396
+ assert.equal(r1.valid, true);
397
+
398
+ const r2 = await verifyProofIntegrity({ proof: second.proof });
399
+ assert.equal(r2.valid, false);
400
+ assert.match(r2.reason ?? "", /FORK DETECTED/);
401
+ });
402
+ });
403
+
404
+ // ---------------------------------------------------------------------------
405
+ // Policy checks (trustAnchors)
406
+ // ---------------------------------------------------------------------------
407
+
408
+ describe("verifyProofIntegrity: policy (trustAnchors)", () => {
409
+ test("passes when measurement is in the allowlist", async () => {
410
+ const result = await verifyProofIntegrity({
411
+ proof: fx.proof,
412
+ trustAnchors: { allowedMeasurements: [fx.measurement] },
413
+ });
414
+ assert.deepEqual(result, { valid: true, artifactBinding: "not-checked" });
415
+ });
416
+
417
+ test("fails when measurement is not in the allowlist", async () => {
418
+ const result = await verifyProofIntegrity({
419
+ proof: fx.proof,
420
+ trustAnchors: { allowedMeasurements: ["expected-meas"] },
421
+ });
422
+ assert.equal(result.valid, false);
423
+ assert.match(result.reason ?? "", /measurement/);
424
+ });
425
+
426
+ test("fails when requireEnforcement does not match", async () => {
427
+ const result = await verifyProofIntegrity({
428
+ proof: fx.proof,
429
+ trustAnchors: { requireEnforcement: "measured-tee" },
430
+ });
431
+ assert.equal(result.valid, false);
432
+ assert.match(result.reason ?? "", /enforcement/);
433
+ });
434
+
435
+ test("requireSlot fails for a slotless proof and passes for a slot-carrying proof", async () => {
436
+ const slotless = await verifyProofIntegrity({
437
+ proof: fx.proof,
438
+ trustAnchors: { requireSlot: true },
439
+ });
440
+ assert.equal(slotless.valid, false);
441
+ assert.match(slotless.reason ?? "", /slotAllocation/);
442
+
443
+ const slotFx = await makeSlotFixture();
444
+ const slotted = await verifyProofIntegrity({
445
+ proof: slotFx.proof,
446
+ trustAnchors: { requireSlot: true },
447
+ });
448
+ assert.deepEqual(slotted, { valid: true, artifactBinding: "not-checked" });
449
+ });
450
+
451
+ test("counter policy applies without bytes", async () => {
452
+ const slotFx = await makeSlotFixture();
453
+ const pass = await verifyProofIntegrity({
454
+ proof: slotFx.proof,
455
+ trustAnchors: { minCounter: "1" },
456
+ });
457
+ assert.equal(pass.valid, true);
458
+
459
+ const failResult = await verifyProofIntegrity({
460
+ proof: slotFx.proof,
461
+ trustAnchors: { minCounter: "999" },
462
+ });
463
+ assert.equal(failResult.valid, false);
464
+ assert.match(failResult.reason ?? "", /counter/);
465
+ });
466
+ });
467
+
468
+ // ---------------------------------------------------------------------------
469
+ // Regression: verify() behavior unchanged by the shared-pipeline refactor
470
+ // ---------------------------------------------------------------------------
471
+
472
+ describe("regression: verify() unchanged", () => {
473
+ test("valid proof with bytes returns exactly { valid: true }", async () => {
474
+ const result = await verify({ proof: fx.proof, bytes: fx.bytes });
475
+ assert.deepEqual(result, { valid: true });
476
+ });
477
+
478
+ test("wrong bytes fail with the exact digest mismatch reason", async () => {
479
+ const result = await verify({ proof: fx.proof, bytes: new Uint8Array([9, 9, 9]) });
480
+ assert.equal(result.valid, false);
481
+ assert.equal(
482
+ result.reason,
483
+ "artifact digest mismatch: the provided bytes do not match the committed digest",
484
+ );
485
+ });
486
+
487
+ test("tampered signature fails with the exact signature reason", async () => {
488
+ const tampered = clone(fx.proof);
489
+ tampered.signer.signatureB64 = Buffer.alloc(64).toString("base64");
490
+ const result = await verify({ proof: tampered, bytes: fx.bytes });
491
+ assert.equal(result.valid, false);
492
+ assert.equal(result.reason, "signature verification failed: signature does not match");
493
+ });
494
+
495
+ test("tampered version fails with the exact version reason", async () => {
496
+ const tampered = clone(fx.proof);
497
+ (tampered as unknown as Record<string, unknown>)["version"] = "bitgraph/2";
498
+ const result = await verify({ proof: tampered, bytes: fx.bytes });
499
+ assert.equal(result.valid, false);
500
+ assert.equal(result.reason, "unsupported proof version: bitgraph/2");
501
+ });
502
+
503
+ test("verify() and verifyProofIntegrity() agree on every non-artifact failure", async () => {
504
+ const variants: Array<(p: BitGraphProof) => void> = [
505
+ (p) => {
506
+ (p as unknown as Record<string, unknown>)["version"] = "occ/1";
507
+ },
508
+ (p) => {
509
+ p.signer.signatureB64 = Buffer.alloc(64).toString("base64");
510
+ },
511
+ (p) => {
512
+ p.environment.measurement = "evil-measurement";
513
+ },
514
+ (p) => {
515
+ delete (p as unknown as Record<string, unknown>)["signer"];
516
+ },
517
+ ];
518
+
519
+ for (const mutate of variants) {
520
+ const tampered = clone(fx.proof);
521
+ mutate(tampered);
522
+ const full = await verify({ proof: tampered, bytes: fx.bytes });
523
+ const integrity = await verifyProofIntegrity({ proof: tampered });
524
+ assert.equal(full.valid, integrity.valid);
525
+ assert.equal(full.reason, integrity.reason);
526
+ }
527
+ });
528
+
529
+ test("verify() accepts the slot-carrying fixture with its bytes", async () => {
530
+ const slotFx = await makeSlotFixture();
531
+ const result = await verify({ proof: slotFx.proof, bytes: slotFx.bytes });
532
+ assert.deepEqual(result, { valid: true });
533
+ });
534
+
535
+ test("verify() accepts the epochLink fixture with its bytes", async () => {
536
+ resetEpochLinkState();
537
+ const linkFx = await makeEpochLinkFixture();
538
+ const result = await verify({ proof: linkFx.proof, bytes: linkFx.bytes });
539
+ assert.deepEqual(result, { valid: true });
540
+ });
541
+ });
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": "bitgraph/1",
3
+ "artifact": {
4
+ "hashAlg": "sha256",
5
+ "digestB64": "s0SCQLL8QR9pf1U/ep6prd3gF1qXAW6bZVf1S15KbfI="
6
+ },
7
+ "commit": {
8
+ "nonceB64": "/fDc/tHc5VXw1lN6TbdloXwJ9kViJ2Xm6DPqAV4J/m8=",
9
+ "counter": "112762",
10
+ "slotCounter": "112761",
11
+ "slotHashB64": "r2DHz1TJRWnW74PeyxhBKBAFc2yIJBE5AEeQIpEU1vs=",
12
+ "epochId": "PlvP2VDcMH9RiBA3shEOrd8xEhHuJj6Gshym/V/qKNg=",
13
+ "prevB64": "ZJs0jf97XakAdw1e7fiK3yMKpliUplgw6HgTw50n+bw=",
14
+ "chainId": "bitgraph:main"
15
+ },
16
+ "signer": {
17
+ "publicKeyB64": "yiKzaYXZyI+tHiSb7K/+OQv8XwErhez2TTk1efl6XAA=",
18
+ "signatureB64": "PhE6MG8SyxFMJIGBSkWPftfs/qZ68liHoJ30i0gv0vHRm86sSrmGyEpxYYcY2gfmYRK+p5BLyOoCXc6bhoXCCA=="
19
+ },
20
+ "environment": {
21
+ "enforcement": "measured-tee",
22
+ "measurement": "e2fccbae77ee40aac4830e84f195e05d69eb4547bbd961f4d3459feba10807140424aca42ad03810354982598c86b9cb",
23
+ "attestation": {
24
+ "format": "aws-nitro",
25
+ "reportB64": "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"
26
+ }
27
+ },
28
+ "slotAllocation": {
29
+ "version": "bitgraph/slot/1",
30
+ "nonceB64": "/fDc/tHc5VXw1lN6TbdloXwJ9kViJ2Xm6DPqAV4J/m8=",
31
+ "counter": "112761",
32
+ "epochId": "PlvP2VDcMH9RiBA3shEOrd8xEhHuJj6Gshym/V/qKNg=",
33
+ "publicKeyB64": "yiKzaYXZyI+tHiSb7K/+OQv8XwErhez2TTk1efl6XAA=",
34
+ "chainId": "bitgraph:main",
35
+ "signatureB64": "ywygI3mZcy7FR4JhTk7Sdk3MYrLdhXB3dUpv+VoTmpwdgQCBKXnTlzXCjsEv3/RXNmh0gzwnHgUAXrqF+lofDQ=="
36
+ },
37
+ "attribution": {
38
+ "name": "Ethereum Anchor",
39
+ "title": "https://etherscan.io/block/25525147",
40
+ "message": "0x04f48a70f24f21daae546d1ae49132df9e767ba4238b92ae39f0c153eb1436dc"
41
+ },
42
+ "proofHash": "H7OZf8C1Ry7cbiFMg6wxfbiqBgWNYpzJDhMePmkmda4=",
43
+ "ethereum": {
44
+ "blockNumber": 25525147,
45
+ "blockHash": "0x04f48a70f24f21daae546d1ae49132df9e767ba4238b92ae39f0c153eb1436dc"
46
+ }
47
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": "bitgraph/1",
3
+ "artifact": {
4
+ "hashAlg": "sha256",
5
+ "digestB64": "Sq/RI+tZjNeZRi9CYKZfZY8b0JubpEHNsk52P5kx6/w="
6
+ },
7
+ "commit": {
8
+ "nonceB64": "dlp1d1hPlQ8Kk8zlbX8U+E8PXC0N5I/nLWFhbCuE6SI=",
9
+ "counter": "112758",
10
+ "slotCounter": "112757",
11
+ "slotHashB64": "sOraHEPEXQkQen88WQpIWD0R/1PZH6EmE7rla1MALrw=",
12
+ "epochId": "PlvP2VDcMH9RiBA3shEOrd8xEhHuJj6Gshym/V/qKNg=",
13
+ "prevB64": "s+1wyG2WDNUQGbdVQVaawxgxpzFdrCwZaT4j6BGgQCc=",
14
+ "chainId": "bitgraph:main"
15
+ },
16
+ "signer": {
17
+ "publicKeyB64": "yiKzaYXZyI+tHiSb7K/+OQv8XwErhez2TTk1efl6XAA=",
18
+ "signatureB64": "So9nZ92UPC2qh9V9TXLBBe7R5YKSS+QsBtq2qXvj7Ppg4dY+FvE8BWrlvxYnf5EqrQi3JsWJUdvj8CKHmWV+BA=="
19
+ },
20
+ "environment": {
21
+ "enforcement": "measured-tee",
22
+ "measurement": "e2fccbae77ee40aac4830e84f195e05d69eb4547bbd961f4d3459feba10807140424aca42ad03810354982598c86b9cb",
23
+ "attestation": {
24
+ "format": "aws-nitro",
25
+ "reportB64": "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"
26
+ }
27
+ },
28
+ "slotAllocation": {
29
+ "version": "bitgraph/slot/1",
30
+ "nonceB64": "dlp1d1hPlQ8Kk8zlbX8U+E8PXC0N5I/nLWFhbCuE6SI=",
31
+ "counter": "112757",
32
+ "epochId": "PlvP2VDcMH9RiBA3shEOrd8xEhHuJj6Gshym/V/qKNg=",
33
+ "publicKeyB64": "yiKzaYXZyI+tHiSb7K/+OQv8XwErhez2TTk1efl6XAA=",
34
+ "chainId": "bitgraph:main",
35
+ "signatureB64": "2HAntaoLZ3xIe73STycRwvUnWfXsb/+j8UkfO01j35QtuXesC80nACuuinAV4ZuWkAexS2tkesPrgBtgjRJ9Dg=="
36
+ },
37
+ "attribution": {
38
+ "name": "Ethereum Anchor",
39
+ "title": "https://etherscan.io/block/25525146",
40
+ "message": "0xde7eada919e2a80b194b40143d923df8925d72bd99cb68470a3c33b0dca33a3d"
41
+ },
42
+ "proofHash": "sGePFJcZMW4FPAmmLxP0E3cphwIsv0e+Gt0dqkexoCA=",
43
+ "ethereum": {
44
+ "blockNumber": 25525146,
45
+ "blockHash": "0xde7eada919e2a80b194b40143d923df8925d72bd99cb68470a3c33b0dca33a3d"
46
+ }
47
+ }