@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,446 +0,0 @@
1
- // Copyright (c) Mike Argento. All rights reserved. See LICENSE.
2
-
3
- /**
4
- * Temporal bounds tests: verified-witness anchors only, one-sided bounds
5
- * with honest semantics (not-before is grounded in block-hash
6
- * unpredictability; not-after is causal precedence whose wall-clock
7
- * reading carries an explicit freshness assumption), chain-link versus
8
- * counter-order evidence marked, unanchored segments reported, and
9
- * cross-epoch ordering only from non-overlapping covered bounds.
10
- */
11
-
12
- import { describe, it, before } from "node:test";
13
- import * as assert from "node:assert/strict";
14
- import { keccak_256 } from "@noble/hashes/sha3";
15
- import { sha256 } from "@noble/hashes/sha256";
16
- import {
17
- deriveTemporalBounds,
18
- identifyAnchors,
19
- ingestBundle,
20
- reconstructChains,
21
- verifyAnchorWitnesses,
22
- verifyObservedProofs,
23
- } from "@mikeargento/bitgraph-audit";
24
- import type {
25
- IngestResult,
26
- ReconstructionResult,
27
- TemporalAnalysis,
28
- TemporalSegment,
29
- } from "@mikeargento/bitgraph-audit";
30
- import type { BitGraphProof } from "@mikeargento/bitgraph-verify";
31
- import { computeProofHash, computeChainHash } from "@mikeargento/bitgraph-verify";
32
- import {
33
- b64,
34
- makeAnchorProof,
35
- makeEthereumHeader,
36
- makeKey,
37
- makeTempDir,
38
- proofJson,
39
- signBody,
40
- utf8,
41
- witnessJson,
42
- writeBundleDir,
43
- type ManualKey,
44
- } from "./audit-fixtures.js";
45
-
46
- const T1 = 1_000_000;
47
- const T2 = 2_000_000;
48
- const T3 = 3_000_000;
49
- const T_OVERLAP = 1_500_000;
50
-
51
- function header(blockNumber: number, timestamp: number): { rlpHex: string; hash: string } {
52
- const h = makeEthereumHeader({ blockNumber, timestamp });
53
- return { rlpHex: h.headerRlpHex, hash: `0x${Buffer.from(keccak_256(h.headerBytes)).toString("hex")}` };
54
- }
55
-
56
- const H1 = header(100, T1);
57
- const H2 = header(101, T2);
58
- const H3 = header(102, T3);
59
- const H4 = header(103, T_OVERLAP);
60
-
61
- async function userProof(
62
- key: ManualKey,
63
- epochId: string,
64
- slot: string,
65
- commit: string,
66
- prevB64?: string
67
- ): Promise<{ proof: BitGraphProof; proofHash: string; chainHash: string }> {
68
- const bytes = utf8(`temporal-${epochId}-${commit}`);
69
- const commitBody: BitGraphProof["commit"] = {
70
- nonceB64: b64(crypto.getRandomValues(new Uint8Array(16))),
71
- counter: commit,
72
- slotCounter: slot,
73
- ...(prevB64 !== undefined ? { prevB64 } : {}),
74
- epochId,
75
- };
76
- (commitBody as unknown as Record<string, unknown>)["chainId"] = "bitgraph:main";
77
- const proof = await signBody(
78
- key,
79
- { hashAlg: "sha256", digestB64: b64(sha256(bytes)) },
80
- commitBody,
81
- "test-measurement-temporal"
82
- );
83
- return { proof, proofHash: computeProofHash(proof), chainHash: computeChainHash(proof) };
84
- }
85
-
86
- interface Fixture {
87
- ingest: IngestResult;
88
- reconstruction: ReconstructionResult;
89
- analysis: TemporalAnalysis;
90
- hashes: Record<string, string>;
91
- }
92
-
93
- let fx: Fixture;
94
-
95
- function segmentOf(hash: string): TemporalSegment {
96
- const segment = fx.analysis.segments.find((s) => s.memberProofHashes.includes(hash));
97
- assert.ok(segment !== undefined, "every partitioned proof lands in a segment");
98
- return segment;
99
- }
100
-
101
- before(async () => {
102
- const hashes: Record<string, string> = {};
103
- const files: Record<string, string> = {};
104
-
105
- // Epoch E1 on one key: P1 <- A1(T1) <- P2 <- A2(T2) <- P3, plus P4 with
106
- // a dangling prevB64 (counter evidence only).
107
- const k1 = await makeKey();
108
- const p1 = await userProof(k1, "E1", "1", "2");
109
- const a1 = await makeAnchorProof({
110
- key: k1,
111
- blockHash: H1.hash,
112
- blockNumber: "100",
113
- epochId: "E1",
114
- counter: "4",
115
- slotCounter: "3",
116
- prevB64: p1.chainHash,
117
- chainId: "bitgraph:main",
118
- });
119
- const p2 = await userProof(k1, "E1", "5", "6", a1.chainHash);
120
- const a2 = await makeAnchorProof({
121
- key: k1,
122
- blockHash: H2.hash,
123
- blockNumber: "101",
124
- epochId: "E1",
125
- counter: "8",
126
- slotCounter: "7",
127
- prevB64: p2.chainHash,
128
- chainId: "bitgraph:main",
129
- });
130
- const p3 = await userProof(k1, "E1", "9", "10", a2.chainHash);
131
- const p4 = await userProof(k1, "E1", "11", "12", b64(sha256(utf8("dangling-predecessor"))));
132
-
133
- hashes["P1"] = p1.proofHash;
134
- hashes["A1"] = a1.proofHash;
135
- hashes["P2"] = p2.proofHash;
136
- hashes["A2"] = a2.proofHash;
137
- hashes["P3"] = p3.proofHash;
138
- hashes["P4"] = p4.proofHash;
139
- files["proofs/e1-p1.json"] = proofJson(p1.proof);
140
- files["proofs/e1-a1.json"] = proofJson(a1.proof);
141
- files["proofs/e1-p2.json"] = proofJson(p2.proof);
142
- files["proofs/e1-a2.json"] = proofJson(a2.proof);
143
- files["proofs/e1-p3.json"] = proofJson(p3.proof);
144
- files["proofs/e1-p4.json"] = proofJson(p4.proof);
145
- files["witnesses/h1.json"] = witnessJson({ headerRlpHex: H1.rlpHex, blockNumber: 100, blockHash: H1.hash });
146
- files["witnesses/h2.json"] = witnessJson({ headerRlpHex: H2.rlpHex, blockNumber: 101, blockHash: H2.hash });
147
-
148
- // Epoch E2: linked chain, no anchors at all (ordered-but-unanchored).
149
- const k2 = await makeKey();
150
- const q1 = await userProof(k2, "E2", "1", "2");
151
- const q2 = await userProof(k2, "E2", "3", "4", q1.chainHash);
152
- hashes["Q1"] = q1.proofHash;
153
- hashes["Q2"] = q2.proofHash;
154
- files["proofs/e2-q1.json"] = proofJson(q1.proof);
155
- files["proofs/e2-q2.json"] = proofJson(q2.proof);
156
-
157
- // Epoch E3: an anchor WITHOUT a witness (causal only, no wall clock).
158
- const k3 = await makeKey();
159
- const a3 = await makeAnchorProof({
160
- key: k3,
161
- blockHash: `0x${"77".repeat(32)}`,
162
- blockNumber: "999",
163
- epochId: "E3",
164
- counter: "2",
165
- slotCounter: "1",
166
- chainId: "bitgraph:main",
167
- });
168
- hashes["A3"] = a3.proofHash;
169
- files["proofs/e3-a3.json"] = proofJson(a3.proof);
170
-
171
- // Epoch EB: anchored at T3, all members at or after the anchor: its
172
- // covered lower bound sits above E1's covered upper bound (T2 < T3).
173
- const kb = await makeKey();
174
- const ab = await makeAnchorProof({
175
- key: kb,
176
- blockHash: H3.hash,
177
- blockNumber: "102",
178
- epochId: "EB",
179
- counter: "2",
180
- slotCounter: "1",
181
- chainId: "bitgraph:main",
182
- });
183
- const pb = await userProof(kb, "EB", "3", "4", ab.chainHash);
184
- hashes["AB"] = ab.proofHash;
185
- hashes["PB"] = pb.proofHash;
186
- files["proofs/eb-ab.json"] = proofJson(ab.proof);
187
- files["proofs/eb-pb.json"] = proofJson(pb.proof);
188
- files["witnesses/h3.json"] = witnessJson({ headerRlpHex: H3.rlpHex, blockNumber: 102, blockHash: H3.hash });
189
-
190
- // Epoch EC: anchored at T_OVERLAP (between T1 and T2): overlaps E1, so
191
- // no ordering pair may be asserted between E1 and EC.
192
- const kc = await makeKey();
193
- const ac = await makeAnchorProof({
194
- key: kc,
195
- blockHash: H4.hash,
196
- blockNumber: "103",
197
- epochId: "EC",
198
- counter: "2",
199
- slotCounter: "1",
200
- chainId: "bitgraph:main",
201
- });
202
- hashes["AC"] = ac.proofHash;
203
- files["proofs/ec-ac.json"] = proofJson(ac.proof);
204
- files["witnesses/h4.json"] = witnessJson({ headerRlpHex: H4.rlpHex, blockNumber: 103, blockHash: H4.hash });
205
-
206
- const dir = await makeTempDir("bg-audit-temporal-");
207
- await writeBundleDir(dir, files);
208
- const ingest = await ingestBundle(dir);
209
- await verifyObservedProofs(ingest);
210
- const reconstruction = await reconstructChains(ingest);
211
- const anchors = identifyAnchors(ingest);
212
- const witnesses = await verifyAnchorWitnesses(ingest, anchors);
213
- const analysis = deriveTemporalBounds(ingest, reconstruction, anchors, witnesses);
214
-
215
- fx = { ingest, reconstruction, analysis, hashes };
216
- });
217
-
218
- describe("audit temporal: verified anchors only", () => {
219
- it("splits anchors into verified and unverified wall-clock sources", () => {
220
- assert.deepEqual(
221
- fx.analysis.verifiedAnchorProofHashes,
222
- [fx.hashes["A1"], fx.hashes["A2"], fx.hashes["AB"], fx.hashes["AC"]].sort()
223
- );
224
- assert.deepEqual(fx.analysis.unverifiedAnchorProofHashes, [fx.hashes["A3"] as string]);
225
- });
226
-
227
- it("an anchor without a verified witness confers no bounds", () => {
228
- const segment = segmentOf(fx.hashes["A3"] as string);
229
- assert.equal(segment.status, "ordered-but-unanchored");
230
- assert.equal(segment.lowerBounds.length, 0);
231
- assert.equal(segment.upperBounds.length, 0);
232
- });
233
- });
234
-
235
- describe("audit temporal: one-sided bounds and brackets along chain links", () => {
236
- it("proofs before the first anchor get only a not-after bound", () => {
237
- const segment = segmentOf(fx.hashes["P1"] as string);
238
- assert.equal(segment.status, "upper-bounded");
239
- assert.equal(segment.lowerBounds.length, 0);
240
- assert.equal(segment.upperBounds.length, 1);
241
- const bound = segment.upperBounds[0]!;
242
- assert.equal(bound.kind, "not-after");
243
- assert.equal(bound.anchorProofHash, fx.hashes["A1"]);
244
- assert.equal(bound.timestamp, T1);
245
- assert.equal(bound.evidence, "chain-link");
246
- assert.equal(bound.weaker, false);
247
- assert.equal(bound.basis, "causal-precedence");
248
- // A following inbound anchor is an assumption about anchor latency, never a
249
- // proof-carried upper bound, and the report now says so in a machine field.
250
- assert.equal(bound.boundClass, "assumption");
251
- assert.match(bound.claim, /^NOT a proof-carried upper bound\./);
252
- assert.match(bound.claim, /assumes the anchor consumed a recently published block/);
253
- });
254
-
255
- it("proofs between two verified anchors are lower-bounded with a following anchor, by the tightest pair", () => {
256
- const segment = segmentOf(fx.hashes["P2"] as string);
257
- assert.equal(segment.status, "lower-bounded-with-following-anchor");
258
- // A1 shares the identical bound set (its own block for the lower
259
- // side, A2 for the upper) and groups into the same segment.
260
- assert.ok(segment.memberProofHashes.includes(fx.hashes["A1"] as string));
261
- const lower = segment.lowerBounds[0]!;
262
- assert.equal(lower.kind, "not-before");
263
- assert.equal(lower.boundClass, "evidence");
264
- assert.equal(lower.anchorProofHash, fx.hashes["A1"]);
265
- assert.equal(lower.timestamp, T1);
266
- assert.equal(lower.evidence, "chain-link");
267
- assert.equal(lower.basis, "block-hash-unpredictability");
268
- assert.match(lower.claim, /no earlier than/);
269
- // The not-before claim carries the genuine-public-block assumption: the
270
- // offline audit cannot confirm the anchored header is a real Ethereum
271
- // block (no proof-of-work, consensus, or chain-membership check).
272
- assert.match(lower.claim, /genuine, publicly published Ethereum block/);
273
- const upper = segment.upperBounds[0]!;
274
- assert.equal(upper.anchorProofHash, fx.hashes["A2"]);
275
- assert.equal(upper.timestamp, T2);
276
- });
277
-
278
- it("proofs after the last anchor get only the tightest not-before bound", () => {
279
- const segment = segmentOf(fx.hashes["P3"] as string);
280
- assert.equal(segment.status, "lower-bounded");
281
- assert.equal(segment.upperBounds.length, 0);
282
- const bound = segment.lowerBounds[0]!;
283
- assert.equal(bound.anchorProofHash, fx.hashes["A2"], "tightest lower is the latest anchor");
284
- assert.equal(bound.timestamp, T2);
285
- assert.equal(bound.evidence, "chain-link");
286
- });
287
- });
288
-
289
- describe("audit temporal: counter-only evidence is weaker and marked", () => {
290
- it("a chain-broken proof falls back to counter ordering", () => {
291
- const segment = segmentOf(fx.hashes["P4"] as string);
292
- assert.equal(segment.status, "lower-bounded");
293
- const bound = segment.lowerBounds[0]!;
294
- assert.equal(bound.evidence, "counter-order");
295
- assert.equal(bound.weaker, true);
296
- assert.equal(bound.anchorProofHash, fx.hashes["A2"]);
297
- assert.equal(bound.timestamp, T2);
298
- assert.match(bound.claim, /counter discipline/);
299
- });
300
- });
301
-
302
- describe("audit temporal: unanchored segments", () => {
303
- it("a partition with no verified anchor evidence is ordered-but-unanchored", () => {
304
- const segment = segmentOf(fx.hashes["Q1"] as string);
305
- assert.equal(segment.status, "ordered-but-unanchored");
306
- assert.ok(segment.memberProofHashes.includes(fx.hashes["Q2"] as string));
307
- assert.equal(segment.lowerBounds.length, 0);
308
- assert.equal(segment.upperBounds.length, 0);
309
- });
310
- });
311
-
312
- describe("audit temporal: epoch bounds and cross-epoch ordering", () => {
313
- it("populates EpochRecord.anchorBounds with covered one-sided bounds", () => {
314
- const epochs = new Map(
315
- fx.reconstruction.epochRelationships.epochs.map((e) => [e.epochId, e])
316
- );
317
- const e1 = epochs.get("E1")!;
318
- assert.ok(e1.anchorBounds !== undefined);
319
- const notBefore = e1.anchorBounds.find((b) => b.kind === "not-before")!;
320
- assert.equal(notBefore.witnessTimestamp, T1, "conservative representative: minimum lower bound");
321
- assert.equal(notBefore.coverage, "members-after-anchor");
322
- assert.equal(notBefore.coveredProofCount, 5, "A1, P2, A2, P3 by chain, P4 by counter");
323
- assert.equal(notBefore.totalProofCount, 6);
324
- const notAfter = e1.anchorBounds.find((b) => b.kind === "not-after")!;
325
- assert.equal(notAfter.witnessTimestamp, T2, "conservative representative: maximum upper bound");
326
- assert.equal(notAfter.coverage, "members-before-anchor");
327
- assert.equal(notAfter.coveredProofCount, 3, "P1, A1, P2");
328
- assert.match(notAfter.claim as string, /assumes the anchor consumed a recently published block/);
329
-
330
- const eb = epochs.get("EB")!;
331
- assert.ok(eb.anchorBounds !== undefined);
332
- assert.equal(eb.anchorBounds.length, 1);
333
- assert.equal(eb.anchorBounds[0]!.kind, "not-before");
334
-
335
- assert.equal(epochs.get("E2")!.anchorBounds, undefined, "no verified evidence, no bounds");
336
- });
337
-
338
- it("derives covered-portion ordering only from non-overlapping bounds", () => {
339
- // E1's covered upper bound (T2) sits strictly below EB's covered
340
- // lower bound (T3): one assumption-dependent pair. EC overlaps E1
341
- // (T_OVERLAP < T2) so no pair is asserted in either direction, and
342
- // overlap is never divergence.
343
- assert.equal(fx.analysis.anchorOrderedPairs.length, 1);
344
- const pair = fx.analysis.anchorOrderedPairs[0]!;
345
- assert.equal(pair.beforeEpochId, "E1");
346
- assert.equal(pair.afterEpochId, "EB");
347
- assert.equal(pair.upperBoundTimestamp, T2);
348
- assert.equal(pair.lowerBoundTimestamp, T3);
349
- assert.equal(pair.assumptionDependent, true);
350
- assert.equal(pair.beforeCoveredProofCount, 3);
351
- assert.equal(pair.beforeTotalProofCount, 6);
352
- assert.equal(pair.afterCoveredProofCount, 2);
353
- assert.equal(pair.afterTotalProofCount, 2);
354
- assert.match(pair.note, /covered portions only/);
355
- // The existing E1 -> EB pair rests on chain-link evidence on both sides.
356
- assert.equal(pair.upperEvidence, "chain-link");
357
- assert.equal(pair.lowerEvidence, "chain-link");
358
- assert.equal(pair.weaker, false);
359
- });
360
- });
361
-
362
- describe("audit temporal: cross-epoch ordering carries its evidence class", () => {
363
- it("a pair whose before-side rests on counter-order is marked weaker with the caveat", async () => {
364
- const T_LOW = 2_000_000;
365
- const T_HIGH = 3_000_000;
366
- const hLow = header(200, T_LOW);
367
- const hHigh = header(201, T_HIGH);
368
-
369
- // Epoch WA: a user proof PW (lower counter, no chain link to the
370
- // anchor) plus a genesis anchor AW. PW is bounded not-after AW only by
371
- // commit-counter ordering, so WA's covered not-after rests on the
372
- // weaker evidence class.
373
- const ka = await makeKey();
374
- const pw = await userProof(ka, "WA", "1", "2"); // genesis, counter 2
375
- const aw = await makeAnchorProof({
376
- key: ka,
377
- blockHash: hLow.hash,
378
- blockNumber: "200",
379
- epochId: "WA",
380
- counter: "10",
381
- slotCounter: "9",
382
- chainId: "bitgraph:main",
383
- });
384
-
385
- // Epoch WB: an anchor AB and its chain-linked successor PB, both
386
- // bounded not-before AB by a verified hash-link path.
387
- const kb = await makeKey();
388
- const ab = await makeAnchorProof({
389
- key: kb,
390
- blockHash: hHigh.hash,
391
- blockNumber: "201",
392
- epochId: "WB",
393
- counter: "2",
394
- slotCounter: "1",
395
- chainId: "bitgraph:main",
396
- });
397
- const pb = await userProof(kb, "WB", "3", "4", ab.chainHash);
398
-
399
- const dir = await makeTempDir("bg-audit-temporal-weaker-");
400
- await writeBundleDir(dir, {
401
- "proofs/wa-pw.json": proofJson(pw.proof),
402
- "proofs/wa-aw.json": proofJson(aw.proof),
403
- "proofs/wb-ab.json": proofJson(ab.proof),
404
- "proofs/wb-pb.json": proofJson(pb.proof),
405
- "witnesses/low.json": witnessJson({
406
- headerRlpHex: hLow.rlpHex,
407
- blockNumber: 200,
408
- blockHash: hLow.hash,
409
- }),
410
- "witnesses/high.json": witnessJson({
411
- headerRlpHex: hHigh.rlpHex,
412
- blockNumber: 201,
413
- blockHash: hHigh.hash,
414
- }),
415
- });
416
- const ingest = await ingestBundle(dir);
417
- await verifyObservedProofs(ingest);
418
- const reconstruction = await reconstructChains(ingest);
419
- const anchors = identifyAnchors(ingest);
420
- const witnesses = await verifyAnchorWitnesses(ingest, anchors);
421
- const analysis = deriveTemporalBounds(ingest, reconstruction, anchors, witnesses);
422
-
423
- // Exactly one cross-epoch ordering pair, WA before WB, marked weaker
424
- // because the before side (WA's covered not-after) rests on
425
- // counter-order evidence.
426
- assert.equal(analysis.anchorOrderedPairs.length, 1);
427
- const pair = analysis.anchorOrderedPairs[0]!;
428
- assert.equal(pair.beforeEpochId, "WA");
429
- assert.equal(pair.afterEpochId, "WB");
430
- assert.equal(pair.upperEvidence, "counter-order");
431
- assert.equal(pair.lowerEvidence, "chain-link");
432
- assert.equal(pair.weaker, true);
433
- assert.match(pair.note, /weaker evidence/);
434
-
435
- // WA's covered not-after epoch bound carries the evidence class and the
436
- // weaker-evidence caveat in its own claim string (so the JSON report
437
- // and the markdown both state it).
438
- const wa = reconstruction.epochRelationships.epochs.find((e) => e.epochId === "WA");
439
- assert.ok(wa?.anchorBounds !== undefined);
440
- const notAfter = wa.anchorBounds.find((b) => b.kind === "not-after");
441
- assert.ok(notAfter !== undefined);
442
- assert.equal(notAfter.evidence, "counter-order");
443
- assert.equal(notAfter.weaker, true);
444
- assert.match(notAfter.claim as string, /weaker evidence/);
445
- });
446
- });