@mikeargento/bitgraph 1.9.0 → 1.9.2

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 (252) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +58 -35
  3. package/dist/constructor.js +1 -1
  4. package/dist/constructor.js.map +1 -1
  5. package/dist/fuse-cli.js +1 -1
  6. package/dist/fuse-cli.js.map +1 -1
  7. package/dist/fuse.js +1 -1
  8. package/dist/fuse.js.map +1 -1
  9. package/dist/host.js +1 -1
  10. package/dist/host.js.map +1 -1
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/policy.js +1 -1
  16. package/dist/policy.js.map +1 -1
  17. package/package.json +23 -8
  18. package/src/constructor.ts +1 -1
  19. package/src/fuse-cli.ts +1 -1
  20. package/src/fuse.ts +1 -1
  21. package/src/host.ts +1 -1
  22. package/src/index.ts +3 -2
  23. package/src/policy.ts +1 -1
  24. package/dist/__tests__/audit-anchors.test.d.ts +0 -2
  25. package/dist/__tests__/audit-anchors.test.d.ts.map +0 -1
  26. package/dist/__tests__/audit-anchors.test.js +0 -152
  27. package/dist/__tests__/audit-anchors.test.js.map +0 -1
  28. package/dist/__tests__/audit-anomalies.test.d.ts +0 -2
  29. package/dist/__tests__/audit-anomalies.test.d.ts.map +0 -1
  30. package/dist/__tests__/audit-anomalies.test.js +0 -663
  31. package/dist/__tests__/audit-anomalies.test.js.map +0 -1
  32. package/dist/__tests__/audit-attestation.test.d.ts +0 -2
  33. package/dist/__tests__/audit-attestation.test.d.ts.map +0 -1
  34. package/dist/__tests__/audit-attestation.test.js +0 -403
  35. package/dist/__tests__/audit-attestation.test.js.map +0 -1
  36. package/dist/__tests__/audit-bench-smoke.test.d.ts +0 -2
  37. package/dist/__tests__/audit-bench-smoke.test.d.ts.map +0 -1
  38. package/dist/__tests__/audit-bench-smoke.test.js +0 -87
  39. package/dist/__tests__/audit-bench-smoke.test.js.map +0 -1
  40. package/dist/__tests__/audit-boundary.test.d.ts +0 -20
  41. package/dist/__tests__/audit-boundary.test.d.ts.map +0 -1
  42. package/dist/__tests__/audit-boundary.test.js +0 -84
  43. package/dist/__tests__/audit-boundary.test.js.map +0 -1
  44. package/dist/__tests__/audit-cli.test.d.ts +0 -2
  45. package/dist/__tests__/audit-cli.test.d.ts.map +0 -1
  46. package/dist/__tests__/audit-cli.test.js +0 -239
  47. package/dist/__tests__/audit-cli.test.js.map +0 -1
  48. package/dist/__tests__/audit-dependencies.test.d.ts +0 -2
  49. package/dist/__tests__/audit-dependencies.test.d.ts.map +0 -1
  50. package/dist/__tests__/audit-dependencies.test.js +0 -116
  51. package/dist/__tests__/audit-dependencies.test.js.map +0 -1
  52. package/dist/__tests__/audit-fixtures.d.ts +0 -265
  53. package/dist/__tests__/audit-fixtures.d.ts.map +0 -1
  54. package/dist/__tests__/audit-fixtures.js +0 -511
  55. package/dist/__tests__/audit-fixtures.js.map +0 -1
  56. package/dist/__tests__/audit-ingest.test.d.ts +0 -2
  57. package/dist/__tests__/audit-ingest.test.d.ts.map +0 -1
  58. package/dist/__tests__/audit-ingest.test.js +0 -473
  59. package/dist/__tests__/audit-ingest.test.js.map +0 -1
  60. package/dist/__tests__/audit-memory-ingest.test.d.ts +0 -12
  61. package/dist/__tests__/audit-memory-ingest.test.d.ts.map +0 -1
  62. package/dist/__tests__/audit-memory-ingest.test.js +0 -135
  63. package/dist/__tests__/audit-memory-ingest.test.js.map +0 -1
  64. package/dist/__tests__/audit-property.test.d.ts +0 -2
  65. package/dist/__tests__/audit-property.test.d.ts.map +0 -1
  66. package/dist/__tests__/audit-property.test.js +0 -771
  67. package/dist/__tests__/audit-property.test.js.map +0 -1
  68. package/dist/__tests__/audit-real-fixtures.test.d.ts +0 -2
  69. package/dist/__tests__/audit-real-fixtures.test.d.ts.map +0 -1
  70. package/dist/__tests__/audit-real-fixtures.test.js +0 -132
  71. package/dist/__tests__/audit-real-fixtures.test.js.map +0 -1
  72. package/dist/__tests__/audit-real-live.test.d.ts +0 -2
  73. package/dist/__tests__/audit-real-live.test.d.ts.map +0 -1
  74. package/dist/__tests__/audit-real-live.test.js +0 -107
  75. package/dist/__tests__/audit-real-live.test.js.map +0 -1
  76. package/dist/__tests__/audit-reconstruct.test.d.ts +0 -2
  77. package/dist/__tests__/audit-reconstruct.test.d.ts.map +0 -1
  78. package/dist/__tests__/audit-reconstruct.test.js +0 -287
  79. package/dist/__tests__/audit-reconstruct.test.js.map +0 -1
  80. package/dist/__tests__/audit-report.test.d.ts +0 -2
  81. package/dist/__tests__/audit-report.test.d.ts.map +0 -1
  82. package/dist/__tests__/audit-report.test.js +0 -186
  83. package/dist/__tests__/audit-report.test.js.map +0 -1
  84. package/dist/__tests__/audit-roundtrip.test.d.ts +0 -2
  85. package/dist/__tests__/audit-roundtrip.test.d.ts.map +0 -1
  86. package/dist/__tests__/audit-roundtrip.test.js +0 -368
  87. package/dist/__tests__/audit-roundtrip.test.js.map +0 -1
  88. package/dist/__tests__/audit-temporal.test.d.ts +0 -2
  89. package/dist/__tests__/audit-temporal.test.d.ts.map +0 -1
  90. package/dist/__tests__/audit-temporal.test.js +0 -364
  91. package/dist/__tests__/audit-temporal.test.js.map +0 -1
  92. package/dist/__tests__/audit-tiers.test.d.ts +0 -2
  93. package/dist/__tests__/audit-tiers.test.d.ts.map +0 -1
  94. package/dist/__tests__/audit-tiers.test.js +0 -282
  95. package/dist/__tests__/audit-tiers.test.js.map +0 -1
  96. package/dist/__tests__/audit-witness-real.test.d.ts +0 -19
  97. package/dist/__tests__/audit-witness-real.test.d.ts.map +0 -1
  98. package/dist/__tests__/audit-witness-real.test.js +0 -82
  99. package/dist/__tests__/audit-witness-real.test.js.map +0 -1
  100. package/dist/__tests__/audit-witness.test.d.ts +0 -2
  101. package/dist/__tests__/audit-witness.test.d.ts.map +0 -1
  102. package/dist/__tests__/audit-witness.test.js +0 -308
  103. package/dist/__tests__/audit-witness.test.js.map +0 -1
  104. package/dist/__tests__/canonical.test.d.ts +0 -2
  105. package/dist/__tests__/canonical.test.d.ts.map +0 -1
  106. package/dist/__tests__/canonical.test.js +0 -162
  107. package/dist/__tests__/canonical.test.js.map +0 -1
  108. package/dist/__tests__/chain-hash-anchor.test.d.ts +0 -22
  109. package/dist/__tests__/chain-hash-anchor.test.d.ts.map +0 -1
  110. package/dist/__tests__/chain-hash-anchor.test.js +0 -55
  111. package/dist/__tests__/chain-hash-anchor.test.js.map +0 -1
  112. package/dist/__tests__/commit-service.integration.test.d.ts +0 -2
  113. package/dist/__tests__/commit-service.integration.test.d.ts.map +0 -1
  114. package/dist/__tests__/commit-service.integration.test.js +0 -95
  115. package/dist/__tests__/commit-service.integration.test.js.map +0 -1
  116. package/dist/__tests__/constructor.test.d.ts +0 -2
  117. package/dist/__tests__/constructor.test.d.ts.map +0 -1
  118. package/dist/__tests__/constructor.test.js +0 -267
  119. package/dist/__tests__/constructor.test.js.map +0 -1
  120. package/dist/__tests__/declared-proof-fixture.d.ts +0 -66
  121. package/dist/__tests__/declared-proof-fixture.d.ts.map +0 -1
  122. package/dist/__tests__/declared-proof-fixture.js +0 -66
  123. package/dist/__tests__/declared-proof-fixture.js.map +0 -1
  124. package/dist/__tests__/fuse-placement.test.d.ts +0 -2
  125. package/dist/__tests__/fuse-placement.test.d.ts.map +0 -1
  126. package/dist/__tests__/fuse-placement.test.js +0 -31
  127. package/dist/__tests__/fuse-placement.test.js.map +0 -1
  128. package/dist/__tests__/fuse-roundtrip.test.d.ts +0 -2
  129. package/dist/__tests__/fuse-roundtrip.test.d.ts.map +0 -1
  130. package/dist/__tests__/fuse-roundtrip.test.js +0 -241
  131. package/dist/__tests__/fuse-roundtrip.test.js.map +0 -1
  132. package/dist/__tests__/fuse-sdk.test.d.ts +0 -2
  133. package/dist/__tests__/fuse-sdk.test.d.ts.map +0 -1
  134. package/dist/__tests__/fuse-sdk.test.js +0 -117
  135. package/dist/__tests__/fuse-sdk.test.js.map +0 -1
  136. package/dist/__tests__/fuse-set-sdk.test.d.ts +0 -2
  137. package/dist/__tests__/fuse-set-sdk.test.d.ts.map +0 -1
  138. package/dist/__tests__/fuse-set-sdk.test.js +0 -721
  139. package/dist/__tests__/fuse-set-sdk.test.js.map +0 -1
  140. package/dist/__tests__/fuse-set.test.d.ts +0 -2
  141. package/dist/__tests__/fuse-set.test.d.ts.map +0 -1
  142. package/dist/__tests__/fuse-set.test.js +0 -868
  143. package/dist/__tests__/fuse-set.test.js.map +0 -1
  144. package/dist/__tests__/fuse-set2.test.d.ts +0 -2
  145. package/dist/__tests__/fuse-set2.test.d.ts.map +0 -1
  146. package/dist/__tests__/fuse-set2.test.js +0 -294
  147. package/dist/__tests__/fuse-set2.test.js.map +0 -1
  148. package/dist/__tests__/fuse-vectors.test.d.ts +0 -2
  149. package/dist/__tests__/fuse-vectors.test.d.ts.map +0 -1
  150. package/dist/__tests__/fuse-vectors.test.js +0 -175
  151. package/dist/__tests__/fuse-vectors.test.js.map +0 -1
  152. package/dist/__tests__/fuse-verify.test.d.ts +0 -2
  153. package/dist/__tests__/fuse-verify.test.d.ts.map +0 -1
  154. package/dist/__tests__/fuse-verify.test.js +0 -250
  155. package/dist/__tests__/fuse-verify.test.js.map +0 -1
  156. package/dist/__tests__/hello-world.test.d.ts +0 -2
  157. package/dist/__tests__/hello-world.test.d.ts.map +0 -1
  158. package/dist/__tests__/hello-world.test.js +0 -9
  159. package/dist/__tests__/hello-world.test.js.map +0 -1
  160. package/dist/__tests__/mock-proof-fixture.d.ts +0 -15
  161. package/dist/__tests__/mock-proof-fixture.d.ts.map +0 -1
  162. package/dist/__tests__/mock-proof-fixture.js +0 -34
  163. package/dist/__tests__/mock-proof-fixture.js.map +0 -1
  164. package/dist/__tests__/policy-enforcement.test.d.ts +0 -2
  165. package/dist/__tests__/policy-enforcement.test.d.ts.map +0 -1
  166. package/dist/__tests__/policy-enforcement.test.js +0 -188
  167. package/dist/__tests__/policy-enforcement.test.js.map +0 -1
  168. package/dist/__tests__/proof-hash-regression.test.d.ts +0 -2
  169. package/dist/__tests__/proof-hash-regression.test.d.ts.map +0 -1
  170. package/dist/__tests__/proof-hash-regression.test.js +0 -95
  171. package/dist/__tests__/proof-hash-regression.test.js.map +0 -1
  172. package/dist/__tests__/proof-hash.test.d.ts +0 -2
  173. package/dist/__tests__/proof-hash.test.d.ts.map +0 -1
  174. package/dist/__tests__/proof-hash.test.js +0 -116
  175. package/dist/__tests__/proof-hash.test.js.map +0 -1
  176. package/dist/__tests__/proof-integrity.test.d.ts +0 -2
  177. package/dist/__tests__/proof-integrity.test.d.ts.map +0 -1
  178. package/dist/__tests__/proof-integrity.test.js +0 -426
  179. package/dist/__tests__/proof-integrity.test.js.map +0 -1
  180. package/dist/__tests__/realistic-proof-fixture.d.ts +0 -56
  181. package/dist/__tests__/realistic-proof-fixture.d.ts.map +0 -1
  182. package/dist/__tests__/realistic-proof-fixture.js +0 -57
  183. package/dist/__tests__/realistic-proof-fixture.js.map +0 -1
  184. package/dist/__tests__/verifier.test.d.ts +0 -2
  185. package/dist/__tests__/verifier.test.d.ts.map +0 -1
  186. package/dist/__tests__/verifier.test.js +0 -531
  187. package/dist/__tests__/verifier.test.js.map +0 -1
  188. package/dist/canonical.d.ts +0 -54
  189. package/dist/canonical.d.ts.map +0 -1
  190. package/dist/canonical.js +0 -119
  191. package/dist/canonical.js.map +0 -1
  192. package/dist/proof-hash.d.ts +0 -9
  193. package/dist/proof-hash.d.ts.map +0 -1
  194. package/dist/proof-hash.js +0 -75
  195. package/dist/proof-hash.js.map +0 -1
  196. package/dist/types.d.ts +0 -705
  197. package/dist/types.d.ts.map +0 -1
  198. package/dist/types.js +0 -3
  199. package/dist/types.js.map +0 -1
  200. package/dist/verifier.d.ts +0 -27
  201. package/dist/verifier.d.ts.map +0 -1
  202. package/dist/verifier.js +0 -844
  203. package/dist/verifier.js.map +0 -1
  204. package/src/__tests__/audit-anchors.test.ts +0 -188
  205. package/src/__tests__/audit-anomalies.test.ts +0 -815
  206. package/src/__tests__/audit-attestation.test.ts +0 -529
  207. package/src/__tests__/audit-bench-smoke.test.ts +0 -108
  208. package/src/__tests__/audit-boundary.test.ts +0 -96
  209. package/src/__tests__/audit-cli.test.ts +0 -288
  210. package/src/__tests__/audit-dependencies.test.ts +0 -141
  211. package/src/__tests__/audit-fixtures.ts +0 -736
  212. package/src/__tests__/audit-ingest.test.ts +0 -575
  213. package/src/__tests__/audit-memory-ingest.test.ts +0 -156
  214. package/src/__tests__/audit-property.test.ts +0 -892
  215. package/src/__tests__/audit-real-fixtures.test.ts +0 -153
  216. package/src/__tests__/audit-real-live.test.ts +0 -128
  217. package/src/__tests__/audit-reconstruct.test.ts +0 -353
  218. package/src/__tests__/audit-report.test.ts +0 -222
  219. package/src/__tests__/audit-roundtrip.test.ts +0 -464
  220. package/src/__tests__/audit-temporal.test.ts +0 -446
  221. package/src/__tests__/audit-tiers.test.ts +0 -343
  222. package/src/__tests__/audit-witness-real.test.ts +0 -92
  223. package/src/__tests__/audit-witness.test.ts +0 -345
  224. package/src/__tests__/canonical.test.ts +0 -253
  225. package/src/__tests__/chain-hash-anchor.test.ts +0 -67
  226. package/src/__tests__/commit-service.integration.test.ts +0 -117
  227. package/src/__tests__/constructor.test.ts +0 -340
  228. package/src/__tests__/declared-proof-fixture.ts +0 -65
  229. package/src/__tests__/fuse-placement.test.ts +0 -32
  230. package/src/__tests__/fuse-roundtrip.test.ts +0 -243
  231. package/src/__tests__/fuse-sdk.test.ts +0 -142
  232. package/src/__tests__/fuse-set-sdk.test.ts +0 -852
  233. package/src/__tests__/fuse-set.test.ts +0 -1005
  234. package/src/__tests__/fuse-set2.test.ts +0 -343
  235. package/src/__tests__/fuse-vectors.test.ts +0 -218
  236. package/src/__tests__/fuse-verify.test.ts +0 -278
  237. package/src/__tests__/hello-world.test.ts +0 -10
  238. package/src/__tests__/mock-proof-fixture.ts +0 -36
  239. package/src/__tests__/policy-enforcement.test.ts +0 -226
  240. package/src/__tests__/proof-hash-regression.test.ts +0 -107
  241. package/src/__tests__/proof-hash.test.ts +0 -129
  242. package/src/__tests__/proof-integrity.test.ts +0 -541
  243. package/src/__tests__/real-fixtures/anchor-after-112762.json +0 -47
  244. package/src/__tests__/real-fixtures/anchor-with-ethereum-112758.json +0 -47
  245. package/src/__tests__/real-fixtures/live-proof-0.json +0 -42
  246. package/src/__tests__/real-fixtures/live-proof-1.json +0 -43
  247. package/src/__tests__/real-fixtures/live-proof-2.json +0 -43
  248. package/src/__tests__/real-fixtures/successor-112760.json +0 -38
  249. package/src/__tests__/real-fixtures/witness-25525146.json +0 -6
  250. package/src/__tests__/real-fixtures/witness-25525147.json +0 -6
  251. package/src/__tests__/realistic-proof-fixture.ts +0 -57
  252. package/src/__tests__/verifier.test.ts +0 -624
@@ -1,243 +0,0 @@
1
- // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
-
3
- /**
4
- * BitGraph Fuse: reconstruction round trips (spec 15.3). A placement enters
5
- * the registry only with this test passing: original plus proof rebuilds
6
- * bytes whose hash equals the artifact digest, from fixed vectors.
7
- */
8
-
9
- import { describe, test } from "node:test";
10
- import * as assert from "node:assert/strict";
11
- import { readFileSync } from "node:fs";
12
- import { fileURLToPath } from "node:url";
13
- import { sha256 } from "@noble/hashes/sha256";
14
- import { PLACEMENTS, getPlacement, computeSlotCommitment, bytesToBase64, buildFrame, parseFrame, readFrameMarker, readFuseAttribution, fuseAttribution } from "@mikeargento/bitgraph-verify";
15
- import type { BitGraphProof } from "@mikeargento/bitgraph-verify";
16
-
17
- const FIX = fileURLToPath(new URL("../../src/__tests__/fuse-fixtures/", import.meta.url));
18
- const bytes = (name: string) => new Uint8Array(readFileSync(FIX + name));
19
- const proofOf = (name: string) => JSON.parse(readFileSync(FIX + name, "utf8")) as BitGraphProof;
20
-
21
- describe("registry", () => {
22
- test("fixed order and ids", () => {
23
- assert.deepEqual(PLACEMENTS.map((p) => p.id), ["trailer/1", "container/1", "container/2", "produced/1"]);
24
- assert.deepEqual(PLACEMENTS.map((p) => p.byteExact), [true, true, true, false]);
25
- assert.equal(getPlacement("xmp/9"), undefined);
26
- assert.equal(getPlacement(""), undefined);
27
- });
28
- });
29
-
30
- describe("trailer/1", () => {
31
- const p = getPlacement("trailer/1")!;
32
- test("original + proof rebuilds the committed bytes", () => {
33
- const proof = proofOf("trailer.proof.json");
34
- const rebuilt = p.build({ original: bytes("original.txt"), commitment: computeSlotCommitment(proof.slotAllocation!) });
35
- assert.deepEqual(rebuilt, bytes("fused-trailer.bin"));
36
- assert.equal(bytesToBase64(sha256(rebuilt)), proof.artifact.digestB64);
37
- });
38
- test("locate returns the commitment and the exact original prefix", () => {
39
- const proof = proofOf("trailer.proof.json");
40
- const l = p.locate(bytes("fused-trailer.bin"))!;
41
- assert.deepEqual(l.commitment, computeSlotCommitment(proof.slotAllocation!));
42
- assert.deepEqual(l.originalBytes, bytes("original.txt"));
43
- assert.equal(l.originDigest, undefined, "trailer/1 writes no origin digest");
44
- });
45
- test("locate refuses short input, wrong magic, non-zero reserved bytes", () => {
46
- assert.equal(p.locate(new Uint8Array(47)), null);
47
- const f = bytes("fused-trailer.bin");
48
- const magic = f.slice(); magic[magic.length - 48] = 0x58;
49
- assert.equal(p.locate(magic), null);
50
- const reserved = f.slice(); reserved[reserved.length - 40] = 1;
51
- assert.equal(p.locate(reserved), null);
52
- assert.equal(p.locate(bytes("original.txt")), null, "an unfused file has no trailer");
53
- });
54
- test("build refuses a bad commitment length and a missing original", () => {
55
- assert.throws(() => p.build({ commitment: new Uint8Array(32) }), /original/);
56
- assert.throws(() => p.build({ original: new Uint8Array(1), commitment: new Uint8Array(31) }), /32 bytes/);
57
- });
58
- });
59
-
60
- describe("container/1", () => {
61
- const p = getPlacement("container/1")!;
62
- test("original + proof rebuilds the committed archive byte for byte, twice", () => {
63
- const proof = proofOf("container.proof.json");
64
- const c = computeSlotCommitment(proof.slotAllocation!);
65
- const a = p.build({ original: bytes("image.png"), commitment: c });
66
- const b = p.build({ original: bytes("image.png"), commitment: c });
67
- assert.deepEqual(a, b, "deterministic");
68
- assert.deepEqual(a, bytes("fused-container.tar"));
69
- assert.equal(bytesToBase64(sha256(a)), proof.artifact.digestB64);
70
- });
71
- test("locate returns commitment, origin digest and the original bytes", () => {
72
- const proof = proofOf("container.proof.json");
73
- const l = p.locate(bytes("fused-container.tar"))!;
74
- assert.deepEqual(l.commitment, computeSlotCommitment(proof.slotAllocation!));
75
- assert.deepEqual(l.originDigest, sha256(bytes("image.png")));
76
- assert.deepEqual(l.originalBytes, bytes("image.png"));
77
- });
78
- test("locate refuses any deviation from the fixed layout", () => {
79
- const f = bytes("fused-container.tar");
80
- const mtime = f.slice(); mtime[136] = 0x31; // manifest header mtime
81
- assert.equal(p.locate(mtime), null, "non-zero mtime");
82
- const uid = f.slice(); uid[108] = 0x31;
83
- assert.equal(p.locate(uid), null, "uid");
84
- const truncated = f.subarray(0, f.length - 512);
85
- assert.equal(p.locate(truncated), null, "missing end block");
86
- const extra = new Uint8Array(f.length + 512); extra.set(f);
87
- assert.equal(p.locate(extra), null, "extra trailing block");
88
- // Swap entry order: rebuild with the original first is not this placement.
89
- const swapped = f.slice(); swapped[0] = 0x78; // corrupt the manifest name
90
- assert.equal(p.locate(swapped), null, "wrong entry name");
91
- assert.equal(p.locate(bytes("image.png")), null, "not a tar");
92
- assert.equal(p.locate(bytes("fused-trailer.bin")), null);
93
- });
94
- test("an archive with a wrong manifest origin does not locate", () => {
95
- const proof = proofOf("container.proof.json");
96
- const c = computeSlotCommitment(proof.slotAllocation!);
97
- const lying = p.build({ original: bytes("image.png"), originDigest: sha256(bytes("original.txt")), commitment: c });
98
- // build accepts a caller-supplied origin digest; locate returns what is written,
99
- // and the verifier compares it against the marker and the original's hash.
100
- const l = p.locate(lying)!;
101
- assert.deepEqual(l.originDigest, sha256(bytes("original.txt")));
102
- assert.notDeepEqual(l.originDigest, sha256(l.originalBytes!));
103
- });
104
- });
105
-
106
- describe("produced/1", () => {
107
- const p = getPlacement("produced/1")!;
108
- test("build and locate agree, with and without an origin", () => {
109
- for (const [name, proofName] of [["produced-origin.json", "produced-origin.proof.json"], ["produced-bare.json", "produced-bare.proof.json"]] as const) {
110
- const proof = proofOf(proofName);
111
- const l = p.locate(bytes(name))!;
112
- assert.deepEqual(l.commitment, computeSlotCommitment(proof.slotAllocation!));
113
- assert.equal(bytesToBase64(sha256(bytes(name))), proof.artifact.digestB64);
114
- }
115
- assert.equal(p.locate(bytes("original.txt")), null);
116
- assert.throws(() => p.build({ original: new Uint8Array(1), commitment: new Uint8Array(32) }), /no original/);
117
- });
118
- });
119
-
120
- describe("legacy files that carry a nonce line", () => {
121
- test("a file with a 'nonce:' line and no trailer, container or payload locates nothing", () => {
122
- // ~/BitGraph holds recordings whose bytes begin with a test label and a line
123
- // "nonce: <hex>" (the proto pattern, minted 2026-06-27 and 2026-08-06). That
124
- // line is content, not a commitment: every registered placement must miss.
125
- const legacy = new TextEncoder().encode("BitGraph test subject folder-02 file-02\nnonce: e317096692576a308263bc283d66b42e\nuoJjpYb8igrEhhSlczKKTKDyw6guO2iU5iHdBRF+mPELQqBfjrO2WZYrSWyHEuKK\n");
126
- for (const p of PLACEMENTS) assert.equal(p.locate(legacy), null, p.id);
127
- });
128
- });
129
-
130
- describe("attribution and Frame", () => {
131
- test("fused attribution carries placement in title and origin in message (standard base64)", () => {
132
- const origin = sha256(bytes("original.txt"));
133
- const a = fuseAttribution("trailer/1", origin);
134
- assert.deepEqual(a, { name: "bitgraph-fuse/1", title: "trailer/1", message: bytesToBase64(origin) });
135
- assert.deepEqual(fuseAttribution("produced/1"), { name: "bitgraph-fuse/1", title: "produced/1" });
136
- const proof = proofOf("trailer.proof.json");
137
- const m = readFuseAttribution(proof)!;
138
- assert.equal(m.placement, "trailer/1");
139
- assert.equal(m.placementSource, "attribution");
140
- assert.equal(m.source, "attribution");
141
- assert.deepEqual(m.originDigest, origin);
142
- assert.equal(m.originSource, "attribution");
143
- const bare = readFuseAttribution(proofOf("trailer-undeclared.proof.json"))!;
144
- assert.equal(bare.originDigest, undefined);
145
- assert.equal(bare.originSource, undefined);
146
- assert.equal(readFuseAttribution(proofOf("recorded.proof.json")), null, "an ordinary proof has no marker");
147
- });
148
- test("a Frame parses regardless of outer formatting and the nested proof is untouched", () => {
149
- const text = readFileSync(FIX + "trailer.bitgraph-fuse.json", "utf8");
150
- const a = parseFrame(text)!;
151
- const b = parseFrame(JSON.stringify(JSON.parse(text)))!;
152
- assert.deepEqual(a, b);
153
- assert.equal(a.manifest.placement, "trailer/1");
154
- assert.deepEqual(a.proof, proofOf("trailer.proof.json"));
155
- const marker = readFrameMarker(a);
156
- assert.equal(marker.source, "manifest");
157
- assert.equal(marker.placementSource, "manifest");
158
- assert.deepEqual(marker.originDigest, sha256(bytes("original.txt")));
159
- const undeclared = parseFrame({ type: "bitgraph-fuse/1", manifest: { artifact: { algorithm: "sha256", digest: "00".repeat(32) }, fusedFile: null }, proof: proofOf("trailer.proof.json") })!;
160
- assert.equal(readFrameMarker(undeclared).placement, null, "a manifest may leave the placement undeclared");
161
- assert.equal(parseFrame(proofOf("trailer.proof.json")), null, "a bare proof is not a Frame");
162
- assert.equal(parseFrame("{not json"), null);
163
- assert.equal(parseFrame({ type: "bitgraph-fuse/1", manifest: { placement: "trailer/1", artifact: { algorithm: "sha256", digest: "zz" }, fusedFile: null }, proof: {} }), null);
164
- });
165
- test("a Frame is never proof-shaped", () => {
166
- const f = JSON.parse(readFileSync(FIX + "produced-origin.bitgraph-fuse.json", "utf8")) as Record<string, unknown>;
167
- assert.equal(f.version, undefined);
168
- assert.equal(f.artifact, undefined);
169
- assert.equal(f.signer, undefined);
170
- assert.equal(f.type, "bitgraph-fuse/1");
171
- assert.ok(f.fusePayload, "Form C frames carry the parsed payload view");
172
- assert.throws(() => buildFrame({ proof: proofOf("produced-origin.proof.json"), placement: "produced/1", artifactDigest: new Uint8Array(32), fusedFile: null, fusePayload: new Uint8Array([1]) }), /canonical/);
173
- });
174
- });
175
-
176
- describe("container/2: the original first, then the manifest", () => {
177
- const p = getPlacement("container/2")!;
178
- const commitment = new Uint8Array(32).fill(0x5a);
179
- const other = new Uint8Array(32).fill(0xa5);
180
- const sizes = [0, 1, 511, 512, 513, 1023, 1024, 1025, 100_000, 1_048_579];
181
- const filled = (n: number) => { const b = new Uint8Array(n); for (let i = 0; i < n; i++) b[i] = (i * 13 + 7) & 0xff; return b; };
182
-
183
- test("build carries the original whole and first; locate reads it back and each container refuses the other's bytes", () => {
184
- for (const n of sizes) {
185
- const original = filled(n);
186
- const fused = p.build({ original, commitment });
187
- assert.equal(new TextDecoder().decode(fused.subarray(0, 22)), "bitgraph-fuse/original");
188
- const l = p.locate(fused);
189
- assert.ok(l, `locate ${n}`);
190
- assert.deepEqual(l!.commitment, commitment);
191
- assert.deepEqual(l!.originDigest, sha256(original));
192
- assert.deepEqual(l!.originalBytes, original);
193
- const c1 = getPlacement("container/1")!.build({ original, commitment });
194
- assert.equal(p.locate(c1), null, `container/2 refuses container/1 bytes (${n})`);
195
- assert.equal(getPlacement("container/1")!.locate(fused), null, `container/1 refuses container/2 bytes (${n})`);
196
- const tampered = new Uint8Array(fused); tampered[tampered.length - 600] = (tampered[tampered.length - 600] ?? 0) ^ 1;
197
- assert.equal(p.locate(tampered), null);
198
- }
199
- });
200
-
201
- test("frame: prefix, original, suffix is the build, byte for byte, for every Form A and B placement and size", () => {
202
- for (const id of ["trailer/1", "container/1", "container/2"] as const) {
203
- const q = getPlacement(id)!;
204
- for (const n of sizes) {
205
- const original = filled(n);
206
- const originDigest = sha256(original);
207
- for (const c of [commitment, other]) {
208
- const f = q.frame!({ originalSize: n, originDigest, commitment: c });
209
- const joined = new Uint8Array(f.prefix.length + n + f.suffix.length);
210
- joined.set(f.prefix, 0); joined.set(original, f.prefix.length); joined.set(f.suffix, f.prefix.length + n);
211
- assert.deepEqual(joined, q.build({ original, originDigest, commitment: c }), `${id} ${n}`);
212
- }
213
- }
214
- }
215
- });
216
-
217
- test("scanPrefix: fixed for trailer/1 (empty) and container/2 (the original's header), null for container/1 whose prefix carries the commitment", () => {
218
- for (const n of sizes) {
219
- const originDigest = sha256(filled(n));
220
- assert.deepEqual(getPlacement("trailer/1")!.scanPrefix!(n), new Uint8Array(0));
221
- const pre = p.scanPrefix!(n)!;
222
- assert.equal(pre.length, 512);
223
- for (const c of [commitment, other]) assert.deepEqual(p.frame!({ originalSize: n, originDigest, commitment: c }).prefix, pre, `the same prefix under any commitment (${n})`);
224
- assert.equal(getPlacement("container/1")!.scanPrefix!(n), null);
225
- }
226
- assert.equal(getPlacement("produced/1")!.frame, undefined);
227
- assert.equal(getPlacement("set/1")!.frame, undefined);
228
- });
229
-
230
- test("a hasher state saved after prefix and original, finished with the suffix, is the fused digest: the file is read once", () => {
231
- for (const n of [0, 1, 511, 512, 513, 100_000]) {
232
- const original = filled(n);
233
- const originDigest = sha256(original);
234
- const h = sha256.create();
235
- h.update(p.scanPrefix!(n)!);
236
- h.update(original);
237
- for (const c of [commitment, other]) {
238
- const finished = h.clone().update(p.frame!({ originalSize: n, originDigest, commitment: c }).suffix).digest();
239
- assert.deepEqual(finished, sha256(p.build({ original, originDigest, commitment: c })), `${n} bytes`);
240
- }
241
- }
242
- });
243
- });
@@ -1,142 +0,0 @@
1
- // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
-
3
- /**
4
- * fuse() against a fake transport built from real fixture proofs (minted
5
- * through the local enclave harness). Every refusal path runs.
6
- */
7
-
8
- import { describe, test } from "node:test";
9
- import * as assert from "node:assert/strict";
10
- import { readFileSync } from "node:fs";
11
- import { fileURLToPath } from "node:url";
12
- import { fuse, builderFor, FuseError } from "../fuse.js";
13
- import type { BitGraphProof, SlotAllocation } from "../fuse.js";
14
-
15
- const FIX = fileURLToPath(new URL("../../src/__tests__/fuse-fixtures/", import.meta.url));
16
- const bytes = (name: string) => new Uint8Array(readFileSync(FIX + name));
17
- const proofOf = (name: string) => JSON.parse(readFileSync(FIX + name, "utf8")) as BitGraphProof;
18
-
19
- interface Call { path: string; body: unknown }
20
-
21
- /** A transport that hands out `slot` and answers the commit with `answer`. */
22
- function fakeTransport(slot: SlotAllocation, answer: (calls: Call[], body: unknown) => { status: number; json: unknown }, lookup?: (calls: Call[]) => { status: number; json: unknown }) {
23
- const calls: Call[] = [];
24
- const f: typeof fetch = async (input, init) => {
25
- const url = String(input);
26
- const path = url.replace(/^https?:\/\/[^/]+/, "");
27
- const body = init?.body ? JSON.parse(String(init.body)) : undefined;
28
- calls.push({ path, body });
29
- const reply = (r: { status: number; json: unknown }) => new Response(JSON.stringify(r.json), { status: r.status, headers: { "Content-Type": "application/json" } });
30
- if (path === "/api/fuse/allocate") return reply({ status: 200, json: { slotId: slot.nonceB64, slot, chainId: "bitgraph:main" } });
31
- if (path === "/api/fuse/commit") return reply(answer(calls, body));
32
- if (path.startsWith("/api/proofs/")) return reply(lookup ? lookup(calls) : { status: 200, json: { proofs: [] } });
33
- return reply({ status: 404, json: { error: "no route" } });
34
- };
35
- return { calls, transport: { baseUrl: "https://example.test", fetch: f, recoveryAttempts: 2, recoveryDelayMs: 1 } };
36
- }
37
-
38
- describe("fuse(): the four beats", () => {
39
- test("trailer/1 over an existing original: allocate, fuse, hash, fill; Frame carries the unchanged proof", async () => {
40
- const proof = proofOf("trailer.proof.json");
41
- const { calls, transport } = fakeTransport(proof.slotAllocation!, () => ({ status: 200, json: { proof } }));
42
- const original = bytes("original.txt");
43
- const r = await fuse(builderFor("trailer/1", original), { placement: "trailer/1", original, fusedFile: "photo.jpg", transport });
44
- assert.equal(r.artifactDigestB64, proof.artifact.digestB64);
45
- assert.deepEqual(r.proof, proof);
46
- assert.equal(r.frame.type, "bitgraph-fuse/1");
47
- assert.equal(r.frame.manifest.placement, "trailer/1");
48
- assert.equal(r.frame.manifest.fusedFile, "photo.jpg");
49
- assert.deepEqual(r.frame.proof, proof);
50
- assert.equal(r.recovered, false);
51
- assert.equal(r.verification.category, "FUSED_DIRECT");
52
- assert.equal(r.fusedBytes, undefined, "byte-exact placements evaporate by default");
53
- // The commit carried the placement and origin in the attribution, and named the slot.
54
- const commit = calls.find((c) => c.path === "/api/fuse/commit")!.body as Record<string, unknown>;
55
- assert.equal(commit.slotId, proof.slotAllocation!.nonceB64);
56
- assert.deepEqual(commit.attribution, { name: "bitgraph-fuse/1", title: "trailer/1", message: r.originDigestB64 });
57
- assert.deepEqual(commit.digests, [{ digestB64: proof.artifact.digestB64, hashAlg: "sha256" }]);
58
- assert.equal(commit.chainId, "bitgraph:main");
59
- // The raw nonce appears in no error and nowhere but the slot field it must ride in.
60
- assert.ok(!JSON.stringify(commit.attribution).includes(proof.slotAllocation!.nonceB64));
61
- });
62
-
63
- test("produced/1 keeps its bytes and its payload view in the Frame", async () => {
64
- const proof = proofOf("produced-origin.proof.json");
65
- const { transport } = fakeTransport(proof.slotAllocation!, () => ({ status: 200, json: [proof] }));
66
- const originDigest = new Uint8Array(Buffer.from(proof.attribution!.message!, "base64"));
67
- const r = await fuse(builderFor("produced/1"), { placement: "produced/1", originDigest, transport });
68
- assert.ok(r.fusedBytes, "Form C keeps the fused bytes");
69
- assert.deepEqual(r.fusedBytes, bytes("produced-origin.json"));
70
- assert.ok(r.frame.fusePayload);
71
- assert.equal(r.frame.fusePayload!.type, "bitgraph-fuse/1");
72
- });
73
-
74
- test("a builder that drops the commitment is refused BEFORE any commit; the slot is left to expire", async () => {
75
- const proof = proofOf("trailer.proof.json");
76
- const { calls, transport } = fakeTransport(proof.slotAllocation!, () => ({ status: 200, json: { proof } }));
77
- await assert.rejects(
78
- fuse(() => bytes("original.txt"), { placement: "trailer/1", original: bytes("original.txt"), transport }),
79
- (e: FuseError) => e.code === "commitment-missing",
80
- );
81
- assert.equal(calls.filter((c) => c.path === "/api/fuse/commit").length, 0, "no commit was attempted");
82
- });
83
-
84
- test("a proof under a different slot is never labelled fused", async () => {
85
- const proof = proofOf("trailer.proof.json");
86
- const other = proofOf("trailer-undeclared.proof.json");
87
- const { transport } = fakeTransport(proof.slotAllocation!, () => ({ status: 200, json: { proof: other } }));
88
- await assert.rejects(
89
- fuse(builderFor("trailer/1", bytes("original.txt")), { placement: "trailer/1", original: bytes("original.txt"), transport }),
90
- (e: FuseError) => e.code === "slot-mismatch",
91
- );
92
- });
93
-
94
- test("409 slot-unavailable: the proof is read back by digest and matched on slotHashB64; nothing is re-allocated", async () => {
95
- const proof = proofOf("trailer.proof.json");
96
- const decoy = proofOf("trailer-undeclared.proof.json");
97
- const { calls, transport } = fakeTransport(
98
- proof.slotAllocation!,
99
- () => ({ status: 409, json: { error: "gone", code: "slot-unavailable" } }),
100
- () => ({ status: 200, json: { proofs: [{ proof: decoy }, { proof }] } }),
101
- );
102
- const r = await fuse(builderFor("trailer/1", bytes("original.txt")), { placement: "trailer/1", original: bytes("original.txt"), transport });
103
- assert.equal(r.recovered, true);
104
- assert.deepEqual(r.proof, proof);
105
- assert.equal(calls.filter((c) => c.path === "/api/fuse/allocate").length, 1, "allocated exactly once");
106
- });
107
-
108
- test("409 with no matching proof on the ledger is reported as slot-unavailable, not retried into a new slot", async () => {
109
- const proof = proofOf("trailer.proof.json");
110
- const { calls, transport } = fakeTransport(proof.slotAllocation!, () => ({ status: 409, json: { error: "gone", code: "slot-unavailable" } }));
111
- await assert.rejects(
112
- fuse(builderFor("trailer/1", bytes("original.txt")), { placement: "trailer/1", original: bytes("original.txt"), transport }),
113
- (e: FuseError) => e.code === "slot-unavailable" && !e.message.includes(proof.slotAllocation!.nonceB64),
114
- );
115
- assert.equal(calls.filter((c) => c.path === "/api/fuse/allocate").length, 1);
116
- });
117
-
118
- test("tee-restarting on commit: read back first, then report; a network failure after send does the same", async () => {
119
- const proof = proofOf("trailer.proof.json");
120
- const a = fakeTransport(proof.slotAllocation!, () => ({ status: 503, json: { error: "restarting", code: "tee-restarting" } }), () => ({ status: 200, json: { proofs: [{ proof }] } }));
121
- const r = await fuse(builderFor("trailer/1", bytes("original.txt")), { placement: "trailer/1", original: bytes("original.txt"), transport: a.transport });
122
- assert.equal(r.recovered, true);
123
- const b = fakeTransport(proof.slotAllocation!, () => ({ status: 503, json: { error: "restarting", code: "tee-restarting" } }));
124
- await assert.rejects(fuse(builderFor("trailer/1", bytes("original.txt")), { placement: "trailer/1", original: bytes("original.txt"), transport: b.transport }), (e: FuseError) => e.code === "tee-restarting");
125
- });
126
-
127
- test("bad inputs are refused before any request", async () => {
128
- const proof = proofOf("trailer.proof.json");
129
- const { calls, transport } = fakeTransport(proof.slotAllocation!, () => ({ status: 200, json: { proof } }));
130
- await assert.rejects(fuse(builderFor("trailer/1", bytes("original.txt")), { placement: "xmp/9" as never, original: bytes("original.txt"), transport }), (e: FuseError) => e.code === "bad-placement");
131
- await assert.rejects(fuse(builderFor("trailer/1"), { placement: "trailer/1", transport }), (e: FuseError) => e.code === "bad-input");
132
- await assert.rejects(fuse(builderFor("produced/1"), { placement: "produced/1", original: bytes("original.txt"), transport }), (e: FuseError) => e.code === "bad-input");
133
- assert.equal(calls.length, 0);
134
- assert.throws(() => builderFor("nope/1" as never), (e: FuseError) => e.code === "bad-placement");
135
- });
136
-
137
- test("a slot off the anchored chain is refused", async () => {
138
- const proof = proofOf("trailer.proof.json");
139
- const { transport } = fakeTransport({ ...proof.slotAllocation!, chainId: "global" }, () => ({ status: 200, json: { proof } }));
140
- await assert.rejects(fuse(builderFor("trailer/1", bytes("original.txt")), { placement: "trailer/1", original: bytes("original.txt"), transport }), (e: FuseError) => e.code === "allocate-failed" && /anchored chain/.test(e.message));
141
- });
142
- });