@mikeargento/bitgraph 1.9.1 → 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.
- package/package.json +9 -6
- package/dist/__tests__/audit-anchors.test.d.ts +0 -2
- package/dist/__tests__/audit-anchors.test.d.ts.map +0 -1
- package/dist/__tests__/audit-anchors.test.js +0 -152
- package/dist/__tests__/audit-anchors.test.js.map +0 -1
- package/dist/__tests__/audit-anomalies.test.d.ts +0 -2
- package/dist/__tests__/audit-anomalies.test.d.ts.map +0 -1
- package/dist/__tests__/audit-anomalies.test.js +0 -663
- package/dist/__tests__/audit-anomalies.test.js.map +0 -1
- package/dist/__tests__/audit-attestation.test.d.ts +0 -2
- package/dist/__tests__/audit-attestation.test.d.ts.map +0 -1
- package/dist/__tests__/audit-attestation.test.js +0 -403
- package/dist/__tests__/audit-attestation.test.js.map +0 -1
- package/dist/__tests__/audit-bench-smoke.test.d.ts +0 -2
- package/dist/__tests__/audit-bench-smoke.test.d.ts.map +0 -1
- package/dist/__tests__/audit-bench-smoke.test.js +0 -87
- package/dist/__tests__/audit-bench-smoke.test.js.map +0 -1
- package/dist/__tests__/audit-boundary.test.d.ts +0 -20
- package/dist/__tests__/audit-boundary.test.d.ts.map +0 -1
- package/dist/__tests__/audit-boundary.test.js +0 -84
- package/dist/__tests__/audit-boundary.test.js.map +0 -1
- package/dist/__tests__/audit-cli.test.d.ts +0 -2
- package/dist/__tests__/audit-cli.test.d.ts.map +0 -1
- package/dist/__tests__/audit-cli.test.js +0 -239
- package/dist/__tests__/audit-cli.test.js.map +0 -1
- package/dist/__tests__/audit-dependencies.test.d.ts +0 -2
- package/dist/__tests__/audit-dependencies.test.d.ts.map +0 -1
- package/dist/__tests__/audit-dependencies.test.js +0 -116
- package/dist/__tests__/audit-dependencies.test.js.map +0 -1
- package/dist/__tests__/audit-fixtures.d.ts +0 -265
- package/dist/__tests__/audit-fixtures.d.ts.map +0 -1
- package/dist/__tests__/audit-fixtures.js +0 -511
- package/dist/__tests__/audit-fixtures.js.map +0 -1
- package/dist/__tests__/audit-ingest.test.d.ts +0 -2
- package/dist/__tests__/audit-ingest.test.d.ts.map +0 -1
- package/dist/__tests__/audit-ingest.test.js +0 -473
- package/dist/__tests__/audit-ingest.test.js.map +0 -1
- package/dist/__tests__/audit-memory-ingest.test.d.ts +0 -12
- package/dist/__tests__/audit-memory-ingest.test.d.ts.map +0 -1
- package/dist/__tests__/audit-memory-ingest.test.js +0 -135
- package/dist/__tests__/audit-memory-ingest.test.js.map +0 -1
- package/dist/__tests__/audit-property.test.d.ts +0 -2
- package/dist/__tests__/audit-property.test.d.ts.map +0 -1
- package/dist/__tests__/audit-property.test.js +0 -771
- package/dist/__tests__/audit-property.test.js.map +0 -1
- package/dist/__tests__/audit-real-fixtures.test.d.ts +0 -2
- package/dist/__tests__/audit-real-fixtures.test.d.ts.map +0 -1
- package/dist/__tests__/audit-real-fixtures.test.js +0 -132
- package/dist/__tests__/audit-real-fixtures.test.js.map +0 -1
- package/dist/__tests__/audit-real-live.test.d.ts +0 -2
- package/dist/__tests__/audit-real-live.test.d.ts.map +0 -1
- package/dist/__tests__/audit-real-live.test.js +0 -107
- package/dist/__tests__/audit-real-live.test.js.map +0 -1
- package/dist/__tests__/audit-reconstruct.test.d.ts +0 -2
- package/dist/__tests__/audit-reconstruct.test.d.ts.map +0 -1
- package/dist/__tests__/audit-reconstruct.test.js +0 -287
- package/dist/__tests__/audit-reconstruct.test.js.map +0 -1
- package/dist/__tests__/audit-report.test.d.ts +0 -2
- package/dist/__tests__/audit-report.test.d.ts.map +0 -1
- package/dist/__tests__/audit-report.test.js +0 -186
- package/dist/__tests__/audit-report.test.js.map +0 -1
- package/dist/__tests__/audit-roundtrip.test.d.ts +0 -2
- package/dist/__tests__/audit-roundtrip.test.d.ts.map +0 -1
- package/dist/__tests__/audit-roundtrip.test.js +0 -368
- package/dist/__tests__/audit-roundtrip.test.js.map +0 -1
- package/dist/__tests__/audit-temporal.test.d.ts +0 -2
- package/dist/__tests__/audit-temporal.test.d.ts.map +0 -1
- package/dist/__tests__/audit-temporal.test.js +0 -364
- package/dist/__tests__/audit-temporal.test.js.map +0 -1
- package/dist/__tests__/audit-tiers.test.d.ts +0 -2
- package/dist/__tests__/audit-tiers.test.d.ts.map +0 -1
- package/dist/__tests__/audit-tiers.test.js +0 -282
- package/dist/__tests__/audit-tiers.test.js.map +0 -1
- package/dist/__tests__/audit-witness-real.test.d.ts +0 -19
- package/dist/__tests__/audit-witness-real.test.d.ts.map +0 -1
- package/dist/__tests__/audit-witness-real.test.js +0 -82
- package/dist/__tests__/audit-witness-real.test.js.map +0 -1
- package/dist/__tests__/audit-witness.test.d.ts +0 -2
- package/dist/__tests__/audit-witness.test.d.ts.map +0 -1
- package/dist/__tests__/audit-witness.test.js +0 -308
- package/dist/__tests__/audit-witness.test.js.map +0 -1
- package/dist/__tests__/canonical.test.d.ts +0 -2
- package/dist/__tests__/canonical.test.d.ts.map +0 -1
- package/dist/__tests__/canonical.test.js +0 -162
- package/dist/__tests__/canonical.test.js.map +0 -1
- package/dist/__tests__/chain-hash-anchor.test.d.ts +0 -22
- package/dist/__tests__/chain-hash-anchor.test.d.ts.map +0 -1
- package/dist/__tests__/chain-hash-anchor.test.js +0 -55
- package/dist/__tests__/chain-hash-anchor.test.js.map +0 -1
- package/dist/__tests__/commit-service.integration.test.d.ts +0 -2
- package/dist/__tests__/commit-service.integration.test.d.ts.map +0 -1
- package/dist/__tests__/commit-service.integration.test.js +0 -95
- package/dist/__tests__/commit-service.integration.test.js.map +0 -1
- package/dist/__tests__/constructor.test.d.ts +0 -2
- package/dist/__tests__/constructor.test.d.ts.map +0 -1
- package/dist/__tests__/constructor.test.js +0 -267
- package/dist/__tests__/constructor.test.js.map +0 -1
- package/dist/__tests__/declared-proof-fixture.d.ts +0 -66
- package/dist/__tests__/declared-proof-fixture.d.ts.map +0 -1
- package/dist/__tests__/declared-proof-fixture.js +0 -66
- package/dist/__tests__/declared-proof-fixture.js.map +0 -1
- package/dist/__tests__/fuse-inline.test.d.ts +0 -2
- package/dist/__tests__/fuse-inline.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-inline.test.js +0 -181
- package/dist/__tests__/fuse-inline.test.js.map +0 -1
- package/dist/__tests__/fuse-placement.test.d.ts +0 -2
- package/dist/__tests__/fuse-placement.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-placement.test.js +0 -31
- package/dist/__tests__/fuse-placement.test.js.map +0 -1
- package/dist/__tests__/fuse-roundtrip.test.d.ts +0 -2
- package/dist/__tests__/fuse-roundtrip.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-roundtrip.test.js +0 -241
- package/dist/__tests__/fuse-roundtrip.test.js.map +0 -1
- package/dist/__tests__/fuse-sdk.test.d.ts +0 -2
- package/dist/__tests__/fuse-sdk.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-sdk.test.js +0 -117
- package/dist/__tests__/fuse-sdk.test.js.map +0 -1
- package/dist/__tests__/fuse-set-sdk.test.d.ts +0 -2
- package/dist/__tests__/fuse-set-sdk.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-set-sdk.test.js +0 -721
- package/dist/__tests__/fuse-set-sdk.test.js.map +0 -1
- package/dist/__tests__/fuse-set.test.d.ts +0 -2
- package/dist/__tests__/fuse-set.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-set.test.js +0 -889
- package/dist/__tests__/fuse-set.test.js.map +0 -1
- package/dist/__tests__/fuse-set2.test.d.ts +0 -2
- package/dist/__tests__/fuse-set2.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-set2.test.js +0 -294
- package/dist/__tests__/fuse-set2.test.js.map +0 -1
- package/dist/__tests__/fuse-vectors.test.d.ts +0 -2
- package/dist/__tests__/fuse-vectors.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-vectors.test.js +0 -175
- package/dist/__tests__/fuse-vectors.test.js.map +0 -1
- package/dist/__tests__/fuse-verify.test.d.ts +0 -2
- package/dist/__tests__/fuse-verify.test.d.ts.map +0 -1
- package/dist/__tests__/fuse-verify.test.js +0 -250
- package/dist/__tests__/fuse-verify.test.js.map +0 -1
- package/dist/__tests__/hello-world.test.d.ts +0 -2
- package/dist/__tests__/hello-world.test.d.ts.map +0 -1
- package/dist/__tests__/hello-world.test.js +0 -9
- package/dist/__tests__/hello-world.test.js.map +0 -1
- package/dist/__tests__/mock-proof-fixture.d.ts +0 -15
- package/dist/__tests__/mock-proof-fixture.d.ts.map +0 -1
- package/dist/__tests__/mock-proof-fixture.js +0 -34
- package/dist/__tests__/mock-proof-fixture.js.map +0 -1
- package/dist/__tests__/policy-enforcement.test.d.ts +0 -2
- package/dist/__tests__/policy-enforcement.test.d.ts.map +0 -1
- package/dist/__tests__/policy-enforcement.test.js +0 -188
- package/dist/__tests__/policy-enforcement.test.js.map +0 -1
- package/dist/__tests__/proof-hash-regression.test.d.ts +0 -2
- package/dist/__tests__/proof-hash-regression.test.d.ts.map +0 -1
- package/dist/__tests__/proof-hash-regression.test.js +0 -95
- package/dist/__tests__/proof-hash-regression.test.js.map +0 -1
- package/dist/__tests__/proof-hash.test.d.ts +0 -2
- package/dist/__tests__/proof-hash.test.d.ts.map +0 -1
- package/dist/__tests__/proof-hash.test.js +0 -116
- package/dist/__tests__/proof-hash.test.js.map +0 -1
- package/dist/__tests__/proof-integrity.test.d.ts +0 -2
- package/dist/__tests__/proof-integrity.test.d.ts.map +0 -1
- package/dist/__tests__/proof-integrity.test.js +0 -454
- package/dist/__tests__/proof-integrity.test.js.map +0 -1
- package/dist/__tests__/realistic-proof-fixture.d.ts +0 -56
- package/dist/__tests__/realistic-proof-fixture.d.ts.map +0 -1
- package/dist/__tests__/realistic-proof-fixture.js +0 -57
- package/dist/__tests__/realistic-proof-fixture.js.map +0 -1
- package/dist/__tests__/run.test.d.ts +0 -2
- package/dist/__tests__/run.test.d.ts.map +0 -1
- package/dist/__tests__/run.test.js +0 -178
- package/dist/__tests__/run.test.js.map +0 -1
- package/dist/__tests__/verifier.test.d.ts +0 -2
- package/dist/__tests__/verifier.test.d.ts.map +0 -1
- package/dist/__tests__/verifier.test.js +0 -531
- package/dist/__tests__/verifier.test.js.map +0 -1
- package/dist/canonical.d.ts +0 -54
- package/dist/canonical.d.ts.map +0 -1
- package/dist/canonical.js +0 -119
- package/dist/canonical.js.map +0 -1
- package/dist/proof-hash.d.ts +0 -9
- package/dist/proof-hash.d.ts.map +0 -1
- package/dist/proof-hash.js +0 -75
- package/dist/proof-hash.js.map +0 -1
- package/dist/types.d.ts +0 -705
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/verifier.d.ts +0 -27
- package/dist/verifier.d.ts.map +0 -1
- package/dist/verifier.js +0 -844
- package/dist/verifier.js.map +0 -1
- package/src/__tests__/audit-anchors.test.ts +0 -188
- package/src/__tests__/audit-anomalies.test.ts +0 -815
- package/src/__tests__/audit-attestation.test.ts +0 -529
- package/src/__tests__/audit-bench-smoke.test.ts +0 -108
- package/src/__tests__/audit-boundary.test.ts +0 -96
- package/src/__tests__/audit-cli.test.ts +0 -288
- package/src/__tests__/audit-dependencies.test.ts +0 -141
- package/src/__tests__/audit-fixtures.ts +0 -736
- package/src/__tests__/audit-ingest.test.ts +0 -575
- package/src/__tests__/audit-memory-ingest.test.ts +0 -156
- package/src/__tests__/audit-property.test.ts +0 -892
- package/src/__tests__/audit-real-fixtures.test.ts +0 -153
- package/src/__tests__/audit-real-live.test.ts +0 -128
- package/src/__tests__/audit-reconstruct.test.ts +0 -353
- package/src/__tests__/audit-report.test.ts +0 -222
- package/src/__tests__/audit-roundtrip.test.ts +0 -464
- package/src/__tests__/audit-temporal.test.ts +0 -446
- package/src/__tests__/audit-tiers.test.ts +0 -343
- package/src/__tests__/audit-witness-real.test.ts +0 -92
- package/src/__tests__/audit-witness.test.ts +0 -345
- package/src/__tests__/canonical.test.ts +0 -253
- package/src/__tests__/chain-hash-anchor.test.ts +0 -67
- package/src/__tests__/commit-service.integration.test.ts +0 -117
- package/src/__tests__/constructor.test.ts +0 -340
- package/src/__tests__/declared-proof-fixture.ts +0 -65
- package/src/__tests__/fuse-inline.test.ts +0 -211
- package/src/__tests__/fuse-placement.test.ts +0 -32
- package/src/__tests__/fuse-roundtrip.test.ts +0 -243
- package/src/__tests__/fuse-sdk.test.ts +0 -142
- package/src/__tests__/fuse-set-sdk.test.ts +0 -852
- package/src/__tests__/fuse-set.test.ts +0 -1027
- package/src/__tests__/fuse-set2.test.ts +0 -343
- package/src/__tests__/fuse-vectors.test.ts +0 -218
- package/src/__tests__/fuse-verify.test.ts +0 -278
- package/src/__tests__/hello-world.test.ts +0 -10
- package/src/__tests__/mock-proof-fixture.ts +0 -36
- package/src/__tests__/policy-enforcement.test.ts +0 -226
- package/src/__tests__/proof-hash-regression.test.ts +0 -107
- package/src/__tests__/proof-hash.test.ts +0 -129
- package/src/__tests__/proof-integrity.test.ts +0 -576
- package/src/__tests__/real-fixtures/anchor-after-112762.json +0 -47
- package/src/__tests__/real-fixtures/anchor-with-ethereum-112758.json +0 -47
- package/src/__tests__/real-fixtures/live-proof-0.json +0 -42
- package/src/__tests__/real-fixtures/live-proof-1.json +0 -43
- package/src/__tests__/real-fixtures/live-proof-2.json +0 -43
- package/src/__tests__/real-fixtures/successor-112760.json +0 -38
- package/src/__tests__/real-fixtures/witness-25525146.json +0 -6
- package/src/__tests__/real-fixtures/witness-25525147.json +0 -6
- package/src/__tests__/realistic-proof-fixture.ts +0 -57
- package/src/__tests__/verifier.test.ts +0 -624
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Argento Computing Inc. All rights reserved. See LICENSE.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Real-fixture audit test (Phase 4e, G8 honesty).
|
|
5
|
-
*
|
|
6
|
-
* Builds a bundle from the repo's real embedded fixtures and asserts the
|
|
7
|
-
* audit reports them honestly:
|
|
8
|
-
*
|
|
9
|
-
* - REALISTIC_PROOF mirrors what the TEE actually produces: a
|
|
10
|
-
* real-shaped Ethereum anchor proof with a TRUNCATED attestation blob
|
|
11
|
-
* and a placeholder signature. It must be observed, identified as an
|
|
12
|
-
* Ethereum anchor by its SIGNED attribution, fail verification with
|
|
13
|
-
* the verifier's precise NON-version reason (the placeholder
|
|
14
|
-
* signature), and its attestation must report document-present-but-
|
|
15
|
-
* invalid (the truncated blob), never a pass and never a version
|
|
16
|
-
* failure.
|
|
17
|
-
*
|
|
18
|
-
* - MOCK_PROOF is placeholder values throughout; it fails on the first
|
|
19
|
-
* canonical check its placeholders trip (the non-base64 digest), also
|
|
20
|
-
* a non-version reason.
|
|
21
|
-
*
|
|
22
|
-
* The fixtures are used verbatim from their shared modules and are never
|
|
23
|
-
* edited.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
import { describe, it, before, after } from "node:test";
|
|
27
|
-
import * as assert from "node:assert/strict";
|
|
28
|
-
import { rm } from "node:fs/promises";
|
|
29
|
-
import { computeProofHash } from "@mikeargento/bitgraph-verify";
|
|
30
|
-
import { buildJsonReport, runAudit } from "@mikeargento/bitgraph-audit";
|
|
31
|
-
import type { AuditResult } from "@mikeargento/bitgraph-audit";
|
|
32
|
-
import { makeTempDir, writeBundleDir } from "./audit-fixtures.js";
|
|
33
|
-
import { REALISTIC_PROOF } from "./realistic-proof-fixture.js";
|
|
34
|
-
import { MOCK_PROOF } from "./mock-proof-fixture.js";
|
|
35
|
-
|
|
36
|
-
describe("audit over the repo's real embedded fixtures", () => {
|
|
37
|
-
let dir: string;
|
|
38
|
-
let result: AuditResult;
|
|
39
|
-
const realisticHash = computeProofHash(REALISTIC_PROOF as unknown as Record<string, unknown>);
|
|
40
|
-
const mockHash = computeProofHash(MOCK_PROOF as unknown as Record<string, unknown>);
|
|
41
|
-
|
|
42
|
-
before(async () => {
|
|
43
|
-
dir = await makeTempDir("bitgraph-audit-real-fixtures-");
|
|
44
|
-
await writeBundleDir(dir, {
|
|
45
|
-
"proofs/realistic.json": JSON.stringify(REALISTIC_PROOF),
|
|
46
|
-
"proofs/mock.json": JSON.stringify(MOCK_PROOF),
|
|
47
|
-
});
|
|
48
|
-
result = await runAudit(dir);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
after(async () => {
|
|
52
|
-
await rm(dir, { recursive: true, force: true });
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("observes both fixtures; neither is a version reject", () => {
|
|
56
|
-
assert.equal(result.ingest.counts.observed, 2);
|
|
57
|
-
assert.equal(result.ingest.counts.unsupportedVersion, 0);
|
|
58
|
-
const hashes = result.ingest.proofs.map((p) => p.proofHash).sort();
|
|
59
|
-
assert.deepEqual(hashes, [realisticHash, mockHash].sort());
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("REALISTIC_PROOF fails verification with the precise non-version signature reason", () => {
|
|
63
|
-
const proof = result.ingest.proofs.find((p) => p.proofHash === realisticHash);
|
|
64
|
-
assert.equal(proof?.verification?.tier, "integrity");
|
|
65
|
-
assert.equal(proof?.verification?.status, "failed");
|
|
66
|
-
assert.equal(
|
|
67
|
-
proof?.verification?.reason,
|
|
68
|
-
"signature verification failed: signature does not match"
|
|
69
|
-
);
|
|
70
|
-
assert.ok(!(proof?.verification?.reason as string).includes("version"));
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("MOCK_PROOF fails verification with a precise non-version placeholder reason", () => {
|
|
74
|
-
const proof = result.ingest.proofs.find((p) => p.proofHash === mockHash);
|
|
75
|
-
assert.equal(proof?.verification?.status, "failed");
|
|
76
|
-
assert.equal(proof?.verification?.reason, "artifact.digestB64 is not valid base64");
|
|
77
|
-
assert.ok(!(proof?.verification?.reason as string).includes("version"));
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("nothing passes: no proof is ever reported verified or artifact-unavailable", () => {
|
|
81
|
-
assert.equal(result.verification.verified, 0);
|
|
82
|
-
assert.equal(result.verification.artifactUnavailable, 0);
|
|
83
|
-
assert.equal(result.verification.failed, 2);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("REALISTIC_PROOF is identified as an Ethereum anchor by its signed attribution", () => {
|
|
87
|
-
assert.equal(result.anchors.anchors.length, 1);
|
|
88
|
-
const anchor = result.anchors.anchors[0]!;
|
|
89
|
-
assert.equal(anchor.proofHash, realisticHash);
|
|
90
|
-
assert.equal(anchor.blockNumber, "24800448");
|
|
91
|
-
assert.equal(
|
|
92
|
-
anchor.blockHash,
|
|
93
|
-
"0x28ed3639cd705fb8cb2b915c1991e9f808b40e775bc8eb540702942729fec2c0"
|
|
94
|
-
);
|
|
95
|
-
// No unsigned metadata on the fixture: absent corroboration, not disagreement.
|
|
96
|
-
assert.equal(anchor.metadataCorroboration, "absent");
|
|
97
|
-
// The verification failure rides along unmodified.
|
|
98
|
-
assert.equal(anchor.verificationStatus, "failed");
|
|
99
|
-
// MOCK_PROOF's attribution ("test") never identifies an anchor.
|
|
100
|
-
assert.ok(!result.anchors.anchors.some((a) => a.proofHash === mockHash));
|
|
101
|
-
// No witness in the bundle: causal identification only, no wall-clock evidence.
|
|
102
|
-
assert.deepEqual(result.temporal.verifiedAnchorProofHashes, []);
|
|
103
|
-
assert.deepEqual(result.temporal.unverifiedAnchorProofHashes, [realisticHash]);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("REALISTIC_PROOF attestation reports document-present-but-invalid, never a pass", () => {
|
|
107
|
-
const record = result.attestations.records.find((r) => r.proofHash === realisticHash);
|
|
108
|
-
assert.ok(record !== undefined);
|
|
109
|
-
assert.equal(record.declaredMeasurementPresent, true);
|
|
110
|
-
assert.equal(
|
|
111
|
-
record.declaredMeasurement,
|
|
112
|
-
"638d655ad6091bed5c358628b7780de0cdbe138a37fe09d52bf8021a720680a2b3c730fee9f6bef79c1dbe68ef3cdd94"
|
|
113
|
-
);
|
|
114
|
-
assert.equal(record.attestationFormat, "aws-nitro");
|
|
115
|
-
assert.equal(record.documentPresent, true);
|
|
116
|
-
assert.equal(record.documentValidated, false);
|
|
117
|
-
// The truncated blob fails with a precise reason, never silently.
|
|
118
|
-
assert.equal(typeof record.validationFailure, "string");
|
|
119
|
-
assert.ok((record.validationFailure as string).length > 0);
|
|
120
|
-
// Facts derived from an unvalidated document are never asserted.
|
|
121
|
-
assert.equal(record.pcr0MatchesDeclared, undefined);
|
|
122
|
-
assert.equal(record.userDataBoundToProof, undefined);
|
|
123
|
-
// Aggregates: documents present on both fixtures, zero validated.
|
|
124
|
-
assert.equal(result.attestations.counts.proofsWithDocument, 2);
|
|
125
|
-
assert.equal(result.attestations.counts.documentsValidated, 0);
|
|
126
|
-
assert.equal(result.attestations.counts.documentsFailed, 2);
|
|
127
|
-
assert.equal(result.attestations.counts.pcr0Matches, 0);
|
|
128
|
-
assert.equal(result.attestations.counts.userDataBound, 0);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("the exact anomaly code multiset is honest: crypto and topology findings, no version failure", () => {
|
|
132
|
-
const report = buildJsonReport(result);
|
|
133
|
-
const codes = report.anomalies.map((a) => a.code).sort();
|
|
134
|
-
assert.deepEqual(codes, [
|
|
135
|
-
"attestation-invalid", // MOCK_PROOF's placeholder attestation blob
|
|
136
|
-
"attestation-invalid", // REALISTIC_PROOF's truncated attestation blob
|
|
137
|
-
"chain-break-malformed", // MOCK_PROOF's placeholder prevB64 ("prev==")
|
|
138
|
-
]);
|
|
139
|
-
// REALISTIC_PROOF's real-shaped prevB64 into unobserved history is the
|
|
140
|
-
// frontier of its (single-proof) partition, so it is an expected bundle
|
|
141
|
-
// boundary, not a chain-integrity anomaly.
|
|
142
|
-
assert.equal(report.boundaryEntryPoints.length, 1);
|
|
143
|
-
assert.ok(!codes.includes("unsupported-version"));
|
|
144
|
-
assert.equal(report.summary.fullyVerified, 0);
|
|
145
|
-
// Still exit 3: attestation-invalid trips verification, chain-break-malformed
|
|
146
|
-
// trips chain anomalies. The boundary alone would never set an exit bit.
|
|
147
|
-
assert.deepEqual(report.summary.exit, {
|
|
148
|
-
verificationFailures: true,
|
|
149
|
-
chainAnomaliesOrDivergences: true,
|
|
150
|
-
code: 3,
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
});
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Argento Computing Inc. All rights reserved. See LICENSE.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Real-ledger regression test for the audit pipeline.
|
|
5
|
-
*
|
|
6
|
-
* These three proofs (real-fixtures/live-proof-0.json .. live-proof-2.json)
|
|
7
|
-
* were captured from the live BitGraph ledger: a contiguous single-epoch
|
|
8
|
-
* chain (genesis commit counter 2, then 4, then 6, each linked to its
|
|
9
|
-
* predecessor by commit.prevB64). They are stored-form, carrying the
|
|
10
|
-
* ledger-added proofHash field.
|
|
11
|
-
*
|
|
12
|
-
* This test exists specifically because the synthetic fixtures shared the
|
|
13
|
-
* audit tool's ORIGINAL wrong assumptions and so masked two real bugs:
|
|
14
|
-
*
|
|
15
|
-
* Bug A: real slot allocations carry NO time field (the enclave builds
|
|
16
|
-
* slot bodies without a clock). The verifier used to require it, so real
|
|
17
|
-
* slots failed verification. If this test's proofs verify at the
|
|
18
|
-
* integrity tier (0 failed), Bug A is fixed.
|
|
19
|
-
*
|
|
20
|
-
* Bug B: commit.prevB64 references computeChainHash(predecessor) (SHA-256
|
|
21
|
-
* over the whole canonical proof minus the ledger proofHash), NOT
|
|
22
|
-
* computeProofHash (the signed-body subset). The synthetic fixtures built
|
|
23
|
-
* their prevB64 from computeProofHash, the same wrong value the tool
|
|
24
|
-
* assumed, so they linked "by accident". Real proofs use the true chain
|
|
25
|
-
* hash. If this real chain reconstructs as ONE intact component with ZERO
|
|
26
|
-
* anomalies, Bug B is fixed.
|
|
27
|
-
*
|
|
28
|
-
* No synthetic construction: the assertions ride entirely on real bytes.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
import { describe, it, before, after } from "node:test";
|
|
32
|
-
import * as assert from "node:assert/strict";
|
|
33
|
-
import { mkdtemp, mkdir, writeFile, rm, readFile } from "node:fs/promises";
|
|
34
|
-
import { tmpdir } from "node:os";
|
|
35
|
-
import { join } from "node:path";
|
|
36
|
-
import { fileURLToPath } from "node:url";
|
|
37
|
-
import {
|
|
38
|
-
runAudit,
|
|
39
|
-
buildJsonReport,
|
|
40
|
-
computeExitFlags,
|
|
41
|
-
type AuditResult,
|
|
42
|
-
} from "@mikeargento/bitgraph-audit";
|
|
43
|
-
import { computeProofHash, type BitGraphProof } from "@mikeargento/bitgraph-verify";
|
|
44
|
-
|
|
45
|
-
/** Filesystem-safe token from a base64/arbitrary string. */
|
|
46
|
-
function safe(value: string): string {
|
|
47
|
-
return value.replace(/[^A-Za-z0-9]/g, "_");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
describe("audit: real live-ledger chain (Bug A + Bug B regression)", () => {
|
|
51
|
-
let dir: string;
|
|
52
|
-
let result: AuditResult;
|
|
53
|
-
let proofs: Array<BitGraphProof & { proofHash?: string }>;
|
|
54
|
-
|
|
55
|
-
before(async () => {
|
|
56
|
-
// Load the three captured real proofs from src/__tests__/real-fixtures.
|
|
57
|
-
// The test runs from dist/__tests__, so reach back into src.
|
|
58
|
-
proofs = [];
|
|
59
|
-
for (const i of [0, 1, 2]) {
|
|
60
|
-
const url = new URL(
|
|
61
|
-
`../../src/__tests__/real-fixtures/live-proof-${i}.json`,
|
|
62
|
-
import.meta.url
|
|
63
|
-
);
|
|
64
|
-
const raw = await readFile(fileURLToPath(url), "utf8");
|
|
65
|
-
proofs.push(JSON.parse(raw) as BitGraphProof & { proofHash?: string });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Write a valid directory bundle: proofs/<epoch>/<counter>-<hash>.json.
|
|
69
|
-
// Bytes are the exact captured stored-form JSON; no re-serialization of
|
|
70
|
-
// the signed content, so nothing about the real proof is altered.
|
|
71
|
-
dir = await mkdtemp(join(tmpdir(), "bitgraph-audit-real-live-"));
|
|
72
|
-
for (const proof of proofs) {
|
|
73
|
-
const epochDir = join(dir, "proofs", safe(proof.commit.epochId as string));
|
|
74
|
-
await mkdir(epochDir, { recursive: true });
|
|
75
|
-
const name = `${proof.commit.counter}-${safe(computeProofHash(proof))}.json`;
|
|
76
|
-
await writeFile(join(epochDir, name), JSON.stringify(proof));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
result = await runAudit(dir);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
after(async () => {
|
|
83
|
-
if (dir !== undefined) await rm(dir, { recursive: true, force: true });
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("verifies every real proof at the integrity tier (Bug A: clockless slots verify)", () => {
|
|
87
|
-
// All three are observed, none rejected as an unsupported version.
|
|
88
|
-
assert.equal(result.ingest.counts.observed, 3);
|
|
89
|
-
assert.equal(result.ingest.counts.unsupportedVersion, 0);
|
|
90
|
-
|
|
91
|
-
// Bug A proof: real slot allocations carry no time field. If the
|
|
92
|
-
// verifier still required it, these would be "failed", not
|
|
93
|
-
// "artifact-unavailable". No artifact bytes are in the bundle, so the
|
|
94
|
-
// integrity tier is the ceiling and every proof must pass it.
|
|
95
|
-
assert.equal(result.verification.failed, 0);
|
|
96
|
-
assert.equal(result.verification.verified, 0);
|
|
97
|
-
assert.equal(result.verification.artifactUnavailable, 3);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("reconstructs the real chain as one intact component (Bug B: prevB64 is the chain hash)", () => {
|
|
101
|
-
const report = buildJsonReport(result);
|
|
102
|
-
|
|
103
|
-
// One signer, one epoch, one chain -> exactly one partition.
|
|
104
|
-
assert.equal(result.reconstruction.partitions.length, 1);
|
|
105
|
-
const partition = result.reconstruction.partitions[0]!;
|
|
106
|
-
|
|
107
|
-
// The three proofs link into a SINGLE connected component of all three
|
|
108
|
-
// members. This only holds if commit.prevB64 resolves against
|
|
109
|
-
// computeChainHash(predecessor); against computeProofHash it would
|
|
110
|
-
// fragment into three genesis components with chain breaks.
|
|
111
|
-
assert.equal(partition.components.length, 1);
|
|
112
|
-
assert.equal(partition.components[0]!.memberProofHashes.length, 3);
|
|
113
|
-
|
|
114
|
-
// No anomalies of any kind, no divergences, chain intact.
|
|
115
|
-
assert.equal(result.anomalies.anomalies.length, 0);
|
|
116
|
-
assert.equal(result.anomalies.divergences.length, 0);
|
|
117
|
-
assert.equal(report.summary.chainIntact, true);
|
|
118
|
-
assert.equal(report.summary.partitionsIntact, 1);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("exits clean (code 0) over the real bundle", () => {
|
|
122
|
-
assert.deepEqual(computeExitFlags(result), {
|
|
123
|
-
verificationFailures: false,
|
|
124
|
-
chainAnomaliesOrDivergences: false,
|
|
125
|
-
code: 0,
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
});
|
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Argento Computing Inc. All rights reserved. See LICENSE.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Tests for @mikeargento/bitgraph-audit causal reconstruction:
|
|
5
|
-
* partitioning per (signer key, epochId, chainId), chain components from
|
|
6
|
-
* prevB64 hash links, genesis and terminal identification, counter
|
|
7
|
-
* evidence, and epoch relationships (epochLink lineage edges, hard-edge
|
|
8
|
-
* ordering, observed-but-unordered epochs).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, test, after } from "node:test";
|
|
12
|
-
import * as assert from "node:assert/strict";
|
|
13
|
-
import { rm } from "node:fs/promises";
|
|
14
|
-
import {
|
|
15
|
-
classifyAnomalies,
|
|
16
|
-
ingestBundle,
|
|
17
|
-
reconstructChains,
|
|
18
|
-
verifyObservedProofs,
|
|
19
|
-
} from "@mikeargento/bitgraph-audit";
|
|
20
|
-
import type { IngestResult, ReconstructionResult, AnomalyReport } from "@mikeargento/bitgraph-audit";
|
|
21
|
-
import { sha256 } from "@noble/hashes/sha256";
|
|
22
|
-
import {
|
|
23
|
-
b64,
|
|
24
|
-
healthyPairs,
|
|
25
|
-
makeConstructorProof,
|
|
26
|
-
makeCounterChain,
|
|
27
|
-
makeEpochLinkProof,
|
|
28
|
-
makeKey,
|
|
29
|
-
makeTempDir,
|
|
30
|
-
proofJson,
|
|
31
|
-
utf8,
|
|
32
|
-
writeBundleDir,
|
|
33
|
-
} from "./audit-fixtures.js";
|
|
34
|
-
|
|
35
|
-
const tempDirs: string[] = [];
|
|
36
|
-
|
|
37
|
-
after(async () => {
|
|
38
|
-
for (const dir of tempDirs) {
|
|
39
|
-
await rm(dir, { recursive: true, force: true });
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
async function auditBundle(files: Record<string, Uint8Array | string>): Promise<{
|
|
44
|
-
ingest: IngestResult;
|
|
45
|
-
reconstruction: ReconstructionResult;
|
|
46
|
-
report: AnomalyReport;
|
|
47
|
-
}> {
|
|
48
|
-
const dir = await makeTempDir("bitgraph-audit-reconstruct-");
|
|
49
|
-
tempDirs.push(dir);
|
|
50
|
-
await writeBundleDir(dir, files);
|
|
51
|
-
const ingest = await ingestBundle(dir);
|
|
52
|
-
await verifyObservedProofs(ingest);
|
|
53
|
-
const reconstruction = await reconstructChains(ingest);
|
|
54
|
-
const report = await classifyAnomalies(ingest, reconstruction);
|
|
55
|
-
return { ingest, reconstruction, report };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
|
-
// The critical regression: a healthy chain is clean
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
|
|
62
|
-
describe("reconstruct: healthy slot/commit chain", () => {
|
|
63
|
-
test("one partition, one component, zero anomalies, zero unexplained positions", async () => {
|
|
64
|
-
const chain = await makeCounterChain({
|
|
65
|
-
epochId: "epoch-healthy",
|
|
66
|
-
chainId: "bitgraph:main",
|
|
67
|
-
pairs: healthyPairs(4), // slot 1/commit 2 ... slot 7/commit 8
|
|
68
|
-
});
|
|
69
|
-
const { ingest, reconstruction, report } = await auditBundle({
|
|
70
|
-
"proofs/p0.json": proofJson(chain.proofs[0]!.proof),
|
|
71
|
-
"proofs/p1.json": proofJson(chain.proofs[1]!.proof),
|
|
72
|
-
"proofs/p2.json": proofJson(chain.proofs[2]!.proof),
|
|
73
|
-
"proofs/p3.json": proofJson(chain.proofs[3]!.proof),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
assert.equal(ingest.proofs.length, 4);
|
|
77
|
-
assert.equal(reconstruction.partitions.length, 1);
|
|
78
|
-
const partition = reconstruction.partitions[0]!;
|
|
79
|
-
assert.deepEqual(partition.key, {
|
|
80
|
-
publicKeyB64: chain.key.publicKeyB64,
|
|
81
|
-
epochId: "epoch-healthy",
|
|
82
|
-
chainId: "bitgraph:main",
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
assert.equal(partition.components.length, 1);
|
|
86
|
-
const component = partition.components[0]!;
|
|
87
|
-
assert.deepEqual(
|
|
88
|
-
component.memberProofHashes,
|
|
89
|
-
chain.proofs.map((p) => p.proofHash),
|
|
90
|
-
"link order must follow the hash chain from genesis to terminal"
|
|
91
|
-
);
|
|
92
|
-
assert.deepEqual(component.genesisProofHashes, [chain.proofs[0]!.proofHash]);
|
|
93
|
-
assert.deepEqual(component.terminalProofHashes, [chain.proofs[3]!.proofHash]);
|
|
94
|
-
assert.deepEqual(component.brokenLinkProofHashes, []);
|
|
95
|
-
assert.equal(component.hasCounterEvidence, true);
|
|
96
|
-
assert.deepEqual(component.positionRange, { min: "1", max: "8" });
|
|
97
|
-
|
|
98
|
-
// THE critical regression: a healthy slot/commit chain produces NO
|
|
99
|
-
// unexplained positions and no anomalies of any kind.
|
|
100
|
-
assert.deepEqual(report.anomalies, []);
|
|
101
|
-
assert.deepEqual(report.divergences, []);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("reconstruction is link-driven: file order and counters are not the mechanism", async () => {
|
|
105
|
-
const chain = await makeCounterChain({
|
|
106
|
-
epochId: "epoch-shuffled",
|
|
107
|
-
pairs: healthyPairs(3),
|
|
108
|
-
});
|
|
109
|
-
// Write files in an order that reverses the causal order.
|
|
110
|
-
const { reconstruction } = await auditBundle({
|
|
111
|
-
"a-last.json": proofJson(chain.proofs[2]!.proof),
|
|
112
|
-
"b-middle.json": proofJson(chain.proofs[1]!.proof),
|
|
113
|
-
"c-first.json": proofJson(chain.proofs[0]!.proof),
|
|
114
|
-
});
|
|
115
|
-
const component = reconstruction.partitions[0]!.components[0]!;
|
|
116
|
-
assert.deepEqual(
|
|
117
|
-
component.memberProofHashes,
|
|
118
|
-
chain.proofs.map((p) => p.proofHash)
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("a counterless link chain still reconstructs as one component", async () => {
|
|
123
|
-
const chain = await makeCounterChain({
|
|
124
|
-
epochId: "epoch-no-counters",
|
|
125
|
-
pairs: [{}, {}, {}], // epochId only: hash links are the only evidence
|
|
126
|
-
});
|
|
127
|
-
const { reconstruction, report } = await auditBundle({
|
|
128
|
-
"p0.json": proofJson(chain.proofs[0]!.proof),
|
|
129
|
-
"p1.json": proofJson(chain.proofs[1]!.proof),
|
|
130
|
-
"p2.json": proofJson(chain.proofs[2]!.proof),
|
|
131
|
-
});
|
|
132
|
-
const partition = reconstruction.partitions[0]!;
|
|
133
|
-
assert.equal(partition.components.length, 1);
|
|
134
|
-
const component = partition.components[0]!;
|
|
135
|
-
assert.deepEqual(
|
|
136
|
-
component.memberProofHashes,
|
|
137
|
-
chain.proofs.map((p) => p.proofHash)
|
|
138
|
-
);
|
|
139
|
-
assert.equal(component.hasCounterEvidence, false);
|
|
140
|
-
assert.equal(component.positionRange, undefined);
|
|
141
|
-
assert.deepEqual(report.anomalies, []);
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// ---------------------------------------------------------------------------
|
|
146
|
-
// Partitioning
|
|
147
|
-
// ---------------------------------------------------------------------------
|
|
148
|
-
|
|
149
|
-
describe("reconstruct: partitioning", () => {
|
|
150
|
-
test("two independent lineages are never merged", async () => {
|
|
151
|
-
const chainA = await makeCounterChain({ epochId: "epoch-A", pairs: healthyPairs(2) });
|
|
152
|
-
const chainB = await makeCounterChain({ epochId: "epoch-B", pairs: healthyPairs(2) });
|
|
153
|
-
const { reconstruction, report } = await auditBundle({
|
|
154
|
-
"a0.json": proofJson(chainA.proofs[0]!.proof),
|
|
155
|
-
"a1.json": proofJson(chainA.proofs[1]!.proof),
|
|
156
|
-
"b0.json": proofJson(chainB.proofs[0]!.proof),
|
|
157
|
-
"b1.json": proofJson(chainB.proofs[1]!.proof),
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
assert.equal(reconstruction.partitions.length, 2);
|
|
161
|
-
const keys = reconstruction.partitions.map((p) => p.key.publicKeyB64).sort();
|
|
162
|
-
assert.deepEqual(keys, [chainA.key.publicKeyB64, chainB.key.publicKeyB64].sort());
|
|
163
|
-
// Same counters on both chains, yet no collisions: counters are
|
|
164
|
-
// partition-local.
|
|
165
|
-
assert.deepEqual(report.anomalies, []);
|
|
166
|
-
assert.deepEqual(report.divergences, []);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("same signer and epoch on different chainIds are separate partitions (G6)", async () => {
|
|
170
|
-
const key = await makeKey();
|
|
171
|
-
const main = await makeCounterChain({
|
|
172
|
-
key,
|
|
173
|
-
epochId: "epoch-multichain",
|
|
174
|
-
chainId: "bitgraph:main",
|
|
175
|
-
pairs: healthyPairs(2),
|
|
176
|
-
payloadPrefix: "main",
|
|
177
|
-
});
|
|
178
|
-
const globalChain = await makeCounterChain({
|
|
179
|
-
key,
|
|
180
|
-
epochId: "epoch-multichain",
|
|
181
|
-
pairs: healthyPairs(2), // no chainId: normalized "global"
|
|
182
|
-
payloadPrefix: "global",
|
|
183
|
-
});
|
|
184
|
-
const { reconstruction, report } = await auditBundle({
|
|
185
|
-
"m0.json": proofJson(main.proofs[0]!.proof),
|
|
186
|
-
"m1.json": proofJson(main.proofs[1]!.proof),
|
|
187
|
-
"g0.json": proofJson(globalChain.proofs[0]!.proof),
|
|
188
|
-
"g1.json": proofJson(globalChain.proofs[1]!.proof),
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
assert.equal(reconstruction.partitions.length, 2);
|
|
192
|
-
assert.deepEqual(
|
|
193
|
-
reconstruction.partitions.map((p) => p.key.chainId).sort(),
|
|
194
|
-
["bitgraph:main", "global"]
|
|
195
|
-
);
|
|
196
|
-
assert.deepEqual(report.anomalies, []);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test("chainless proofs stay outside partitions as observed-but-unchained", async () => {
|
|
200
|
-
const chain = await makeCounterChain({ epochId: "epoch-with-stray", pairs: healthyPairs(2) });
|
|
201
|
-
const chainless = await makeConstructorProof(); // no counter, no epochId
|
|
202
|
-
const { ingest, reconstruction, report } = await auditBundle({
|
|
203
|
-
"p0.json": proofJson(chain.proofs[0]!.proof),
|
|
204
|
-
"p1.json": proofJson(chain.proofs[1]!.proof),
|
|
205
|
-
"stray.json": proofJson(chainless.proof),
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
assert.equal(ingest.proofs.length, 3);
|
|
209
|
-
assert.equal(reconstruction.partitions.length, 1);
|
|
210
|
-
assert.equal(reconstruction.partitions[0]!.memberProofHashes.length, 2);
|
|
211
|
-
const strayHash = ingest.proofs.find((p) => p.chainless)!.proofHash;
|
|
212
|
-
assert.deepEqual(reconstruction.unchainedProofHashes, [strayHash]);
|
|
213
|
-
// Observed-but-unchained is not an anomaly.
|
|
214
|
-
assert.deepEqual(report.anomalies, []);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
test("a proof with chain fields but no signer key is unpartitioned", async () => {
|
|
218
|
-
const broken = JSON.stringify({
|
|
219
|
-
version: "bitgraph/1",
|
|
220
|
-
artifact: { hashAlg: "sha256", digestB64: b64(sha256(utf8("broken-payload"))) },
|
|
221
|
-
commit: { nonceB64: "AAAA", counter: "2", epochId: "epoch-broken" },
|
|
222
|
-
signer: { publicKeyB64: 42, signatureB64: "AAAA" },
|
|
223
|
-
});
|
|
224
|
-
const { ingest, reconstruction } = await auditBundle({ "broken.json": broken });
|
|
225
|
-
assert.equal(ingest.proofs.length, 1);
|
|
226
|
-
assert.equal(reconstruction.partitions.length, 0);
|
|
227
|
-
assert.deepEqual(reconstruction.unpartitionedProofHashes, [ingest.proofs[0]!.proofHash]);
|
|
228
|
-
// The verification dimension reports the structural failure.
|
|
229
|
-
assert.equal(ingest.proofs[0]!.verification!.status, "failed");
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// ---------------------------------------------------------------------------
|
|
234
|
-
// Missing proofs split components
|
|
235
|
-
// ---------------------------------------------------------------------------
|
|
236
|
-
|
|
237
|
-
describe("reconstruct: missing middle proof", () => {
|
|
238
|
-
test("splits the partition into two components with the break identified", async () => {
|
|
239
|
-
const chain = await makeCounterChain({ epochId: "epoch-gap", pairs: healthyPairs(3) });
|
|
240
|
-
// Omit the middle proof (slot 3 / commit 4).
|
|
241
|
-
const { reconstruction } = await auditBundle({
|
|
242
|
-
"p0.json": proofJson(chain.proofs[0]!.proof),
|
|
243
|
-
"p2.json": proofJson(chain.proofs[2]!.proof),
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
const partition = reconstruction.partitions[0]!;
|
|
247
|
-
assert.equal(partition.components.length, 2);
|
|
248
|
-
const [first, second] = partition.components;
|
|
249
|
-
assert.deepEqual(first!.memberProofHashes, [chain.proofs[0]!.proofHash]);
|
|
250
|
-
assert.deepEqual(first!.genesisProofHashes, [chain.proofs[0]!.proofHash]);
|
|
251
|
-
assert.deepEqual(first!.terminalProofHashes, [chain.proofs[0]!.proofHash]);
|
|
252
|
-
assert.deepEqual(second!.memberProofHashes, [chain.proofs[2]!.proofHash]);
|
|
253
|
-
assert.deepEqual(second!.genesisProofHashes, []);
|
|
254
|
-
assert.deepEqual(second!.brokenLinkProofHashes, [chain.proofs[2]!.proofHash]);
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
// ---------------------------------------------------------------------------
|
|
259
|
-
// Epoch relationships
|
|
260
|
-
// ---------------------------------------------------------------------------
|
|
261
|
-
|
|
262
|
-
describe("reconstruct: epoch relationships", () => {
|
|
263
|
-
test("a valid epochLink produces a hard lineage edge and linked ordering", async () => {
|
|
264
|
-
const chainA = await makeCounterChain({ epochId: "epoch-lineage-A", pairs: healthyPairs(2) });
|
|
265
|
-
const terminalA = chainA.proofs[1]!;
|
|
266
|
-
const genesisB = await makeEpochLinkProof({
|
|
267
|
-
prevEpochId: "epoch-lineage-A",
|
|
268
|
-
prevCounter: terminalA.proof.commit.counter as string,
|
|
269
|
-
prevProofHashB64: terminalA.chainHash,
|
|
270
|
-
prevPublicKeyB64: chainA.key.publicKeyB64,
|
|
271
|
-
toEpochId: "epoch-lineage-B",
|
|
272
|
-
counter: "2",
|
|
273
|
-
slotCounter: "1",
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
const { reconstruction, report } = await auditBundle({
|
|
277
|
-
"a0.json": proofJson(chainA.proofs[0]!.proof),
|
|
278
|
-
"a1.json": proofJson(terminalA.proof),
|
|
279
|
-
"b-genesis.json": proofJson(genesisB.proof),
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
const { edges, epochs, orderedPairs } = reconstruction.epochRelationships;
|
|
283
|
-
assert.equal(edges.length, 1);
|
|
284
|
-
const edge = edges[0]!;
|
|
285
|
-
assert.equal(edge.resolution, "matched");
|
|
286
|
-
assert.equal(edge.fromEpochId, "epoch-lineage-A");
|
|
287
|
-
assert.equal(edge.toEpochId, "epoch-lineage-B");
|
|
288
|
-
assert.equal(edge.predecessorProofHash, terminalA.proofHash);
|
|
289
|
-
assert.equal(edge.metadataConsistent, true);
|
|
290
|
-
assert.equal(edge.referencedProofIsTerminal, true);
|
|
291
|
-
assert.equal(edge.viaProofValid, true);
|
|
292
|
-
assert.equal(edge.predecessorValid, true);
|
|
293
|
-
assert.equal(edge.hardEdge, true);
|
|
294
|
-
|
|
295
|
-
assert.deepEqual(orderedPairs, [
|
|
296
|
-
{ beforeEpochId: "epoch-lineage-A", afterEpochId: "epoch-lineage-B" },
|
|
297
|
-
]);
|
|
298
|
-
const epochA = epochs.find((e) => e.epochId === "epoch-lineage-A")!;
|
|
299
|
-
const epochB = epochs.find((e) => e.epochId === "epoch-lineage-B")!;
|
|
300
|
-
assert.equal(epochA.ordering, "linked");
|
|
301
|
-
assert.equal(epochB.ordering, "linked");
|
|
302
|
-
assert.deepEqual(epochA.linkedSuccessorEpochIds, ["epoch-lineage-B"]);
|
|
303
|
-
assert.deepEqual(epochB.linkedPredecessorEpochIds, ["epoch-lineage-A"]);
|
|
304
|
-
// The extension point stays untouched by reconstruction.
|
|
305
|
-
assert.equal(epochA.anchorBounds, undefined);
|
|
306
|
-
|
|
307
|
-
// Valid lineage is not an anomaly.
|
|
308
|
-
assert.deepEqual(report.anomalies, []);
|
|
309
|
-
assert.deepEqual(report.divergences, []);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
test("a dangling epochLink yields no hard edge and no ordering", async () => {
|
|
313
|
-
const genesis = await makeEpochLinkProof({
|
|
314
|
-
prevEpochId: "epoch-never-observed",
|
|
315
|
-
prevCounter: "10",
|
|
316
|
-
prevProofHashB64: b64(sha256(utf8("terminal-never-observed"))),
|
|
317
|
-
toEpochId: "epoch-dangling-B",
|
|
318
|
-
counter: "2",
|
|
319
|
-
slotCounter: "1",
|
|
320
|
-
});
|
|
321
|
-
const { reconstruction } = await auditBundle({
|
|
322
|
-
"b-genesis.json": proofJson(genesis.proof),
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
const { edges, epochs, orderedPairs } = reconstruction.epochRelationships;
|
|
326
|
-
assert.equal(edges.length, 1);
|
|
327
|
-
assert.equal(edges[0]!.resolution, "dangling");
|
|
328
|
-
assert.equal(edges[0]!.hardEdge, false);
|
|
329
|
-
assert.deepEqual(orderedPairs, []);
|
|
330
|
-
assert.equal(epochs.find((e) => e.epochId === "epoch-dangling-B")!.ordering, "observed-but-unordered");
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
test("epochs with no lineage evidence are observed-but-unordered, never divergence", async () => {
|
|
334
|
-
const chainA = await makeCounterChain({ epochId: "epoch-solo-A", pairs: healthyPairs(2) });
|
|
335
|
-
const chainB = await makeCounterChain({ epochId: "epoch-solo-B", pairs: healthyPairs(2) });
|
|
336
|
-
const { reconstruction, report } = await auditBundle({
|
|
337
|
-
"a0.json": proofJson(chainA.proofs[0]!.proof),
|
|
338
|
-
"a1.json": proofJson(chainA.proofs[1]!.proof),
|
|
339
|
-
"b0.json": proofJson(chainB.proofs[0]!.proof),
|
|
340
|
-
"b1.json": proofJson(chainB.proofs[1]!.proof),
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
const { epochs, edges, orderedPairs } = reconstruction.epochRelationships;
|
|
344
|
-
assert.equal(edges.length, 0);
|
|
345
|
-
assert.deepEqual(orderedPairs, []);
|
|
346
|
-
for (const epoch of epochs) {
|
|
347
|
-
assert.equal(epoch.ordering, "observed-but-unordered");
|
|
348
|
-
}
|
|
349
|
-
// Concurrent-or-unordered is never divergence.
|
|
350
|
-
assert.deepEqual(report.divergences, []);
|
|
351
|
-
assert.deepEqual(report.anomalies, []);
|
|
352
|
-
});
|
|
353
|
-
});
|