forge-trust-chain 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +368 -0
- package/package.json +55 -0
- package/src/cli/index.js +547 -0
- package/src/core/chain.js +186 -0
- package/src/core/merkle.js +131 -0
- package/src/core/trust-atom.js +125 -0
- package/src/core/trust-pixel.js +81 -0
- package/src/core/witness.js +377 -0
- package/src/mcp/server.js +534 -0
- package/src/scanner/index.js +437 -0
- package/src/store/store.js +133 -0
- package/src/test.js +266 -0
package/src/test.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FORGE Test Suite — verify all core components.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { hash, hashMany, createPixel, verifyPixel, bestWitness, selfWitness, bilateralWitness } from "./core/trust-pixel.js";
|
|
6
|
+
import { createAtom, verifyAtom, verifyChain } from "./core/trust-atom.js";
|
|
7
|
+
import { buildMerkleTree, getMerkleProof, verifyMerkleProof } from "./core/merkle.js";
|
|
8
|
+
import { TrustChain, findDivergence } from "./core/chain.js";
|
|
9
|
+
|
|
10
|
+
let passed = 0;
|
|
11
|
+
let failed = 0;
|
|
12
|
+
|
|
13
|
+
function test(name, fn) {
|
|
14
|
+
try {
|
|
15
|
+
fn();
|
|
16
|
+
console.log(` ✓ ${name}`);
|
|
17
|
+
passed++;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.log(` ✗ ${name}: ${e.message}`);
|
|
20
|
+
failed++;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function assert(condition, msg = "assertion failed") {
|
|
25
|
+
if (!condition) throw new Error(msg);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log("\n FORGE Test Suite\n");
|
|
29
|
+
|
|
30
|
+
// ---- TrustPixel ----
|
|
31
|
+
console.log(" ── TrustPixel ──");
|
|
32
|
+
|
|
33
|
+
test("hash is deterministic", () => {
|
|
34
|
+
assert(hash("hello") === hash("hello"));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("hash is collision-resistant", () => {
|
|
38
|
+
assert(hash("hello") !== hash("hello1"));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("hash handles objects", () => {
|
|
42
|
+
assert(hash({ a: 1, b: 2 }) === hash({ b: 2, a: 1 }), "key order should not matter");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("hashMany combines inputs", () => {
|
|
46
|
+
const h = hashMany("a", "b", "c");
|
|
47
|
+
assert(h.length === 64);
|
|
48
|
+
assert(hashMany("a", "b", "c") === hashMany("a", "b", "c"));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("createPixel creates valid pixel", () => {
|
|
52
|
+
const pixel = createPixel("test content");
|
|
53
|
+
assert(pixel.hash.length === 64);
|
|
54
|
+
assert(pixel.witnesses.length === 1);
|
|
55
|
+
assert(pixel.witnesses[0].type === "self");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("verifyPixel validates correctly", () => {
|
|
59
|
+
const pixel = createPixel("test content");
|
|
60
|
+
const result = verifyPixel(pixel, "test content");
|
|
61
|
+
assert(result.valid === true);
|
|
62
|
+
assert(result.certain === true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("verifyPixel rejects wrong content", () => {
|
|
66
|
+
const pixel = createPixel("test content");
|
|
67
|
+
const result = verifyPixel(pixel, "wrong content");
|
|
68
|
+
assert(result.valid === false);
|
|
69
|
+
assert(result.certain === false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("bestWitness ranks correctly", () => {
|
|
73
|
+
const pixel = createPixel("test", [selfWitness(), bilateralWitness("other")]);
|
|
74
|
+
const best = bestWitness(pixel);
|
|
75
|
+
assert(best.label === "bilateral");
|
|
76
|
+
assert(best.level === 2);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// ---- TrustAtom ----
|
|
80
|
+
console.log("\n ── TrustAtom ──");
|
|
81
|
+
|
|
82
|
+
test("createAtom produces valid atom", () => {
|
|
83
|
+
const atom = createAtom({ who: "user", from: "state1", action: "deploy", to: "state2" });
|
|
84
|
+
assert(atom.proof.length === 64);
|
|
85
|
+
assert(atom.prev[0] === "genesis");
|
|
86
|
+
assert(atom._raw.who === "user");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("verifyAtom validates self-consistency", () => {
|
|
90
|
+
const atom = createAtom({ who: "user", from: "a", action: "b", to: "c" });
|
|
91
|
+
assert(verifyAtom(atom) === true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("verifyAtom detects tampering", () => {
|
|
95
|
+
const atom = createAtom({ who: "user", from: "a", action: "b", to: "c" });
|
|
96
|
+
atom.action = hash("tampered");
|
|
97
|
+
assert(verifyAtom(atom) === false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("verifyChain validates linked atoms", () => {
|
|
101
|
+
const a1 = createAtom({ who: "u", from: "s0", action: "a1", to: "s1" });
|
|
102
|
+
const a2 = createAtom({ who: "u", from: "s1", action: "a2", to: "s2", prev: a1.proof });
|
|
103
|
+
const a3 = createAtom({ who: "u", from: "s2", action: "a3", to: "s3", prev: a2.proof });
|
|
104
|
+
const result = verifyChain([a1, a2, a3]);
|
|
105
|
+
assert(result.valid === true);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("verifyChain detects broken link", () => {
|
|
109
|
+
const a1 = createAtom({ who: "u", from: "s0", action: "a1", to: "s1" });
|
|
110
|
+
const a2 = createAtom({ who: "u", from: "s1", action: "a2", to: "s2", prev: "wrong_hash" });
|
|
111
|
+
const result = verifyChain([a1, a2]);
|
|
112
|
+
assert(result.valid === false);
|
|
113
|
+
assert(result.broken_at === 1);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// ---- Merkle ----
|
|
117
|
+
console.log("\n ── Merkle Tree ──");
|
|
118
|
+
|
|
119
|
+
test("buildMerkleTree produces root", () => {
|
|
120
|
+
const proofs = ["aaa", "bbb", "ccc", "ddd"].map(hash);
|
|
121
|
+
const tree = buildMerkleTree(proofs);
|
|
122
|
+
assert(tree.root.length === 64);
|
|
123
|
+
assert(tree.layers.length > 1);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("Merkle proof verifies correctly", () => {
|
|
127
|
+
const proofs = ["a", "b", "c", "d", "e"].map(hash);
|
|
128
|
+
const tree = buildMerkleTree(proofs);
|
|
129
|
+
const proof = getMerkleProof(tree.layers, 2);
|
|
130
|
+
const valid = verifyMerkleProof(proofs[2], proof, tree.root);
|
|
131
|
+
assert(valid === true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("Merkle proof rejects wrong leaf", () => {
|
|
135
|
+
const proofs = ["a", "b", "c", "d"].map(hash);
|
|
136
|
+
const tree = buildMerkleTree(proofs);
|
|
137
|
+
const proof = getMerkleProof(tree.layers, 2);
|
|
138
|
+
const valid = verifyMerkleProof(hash("wrong"), proof, tree.root);
|
|
139
|
+
assert(valid === false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// ---- Chain ----
|
|
143
|
+
console.log("\n ── TrustChain ──");
|
|
144
|
+
|
|
145
|
+
test("TrustChain records and verifies", () => {
|
|
146
|
+
const chain = new TrustChain("test-user");
|
|
147
|
+
chain.record({ action: "deploy", from: "s0", to: "s1" });
|
|
148
|
+
chain.record({ action: "configure", from: "s1", to: "s2" });
|
|
149
|
+
chain.record({ action: "enable ssl", from: "s2", to: "s3" });
|
|
150
|
+
assert(chain.length === 3);
|
|
151
|
+
assert(chain.verify().valid === true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("TrustChain detects tampering", () => {
|
|
155
|
+
const chain = new TrustChain("test-user");
|
|
156
|
+
chain.record({ action: "deploy", from: "s0", to: "s1" });
|
|
157
|
+
chain.record({ action: "configure", from: "s1", to: "s2" });
|
|
158
|
+
chain.atoms[1].action = hash("tampered");
|
|
159
|
+
assert(chain.verify().valid === false);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("TrustChain seals into Merkle block", () => {
|
|
163
|
+
const chain = new TrustChain("test-user");
|
|
164
|
+
chain.record({ action: "a1", from: "s0", to: "s1" });
|
|
165
|
+
chain.record({ action: "a2", from: "s1", to: "s2" });
|
|
166
|
+
const block = chain.seal();
|
|
167
|
+
assert(block !== null);
|
|
168
|
+
assert(block.root.length === 64);
|
|
169
|
+
assert(block.atom_count === 2);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("TrustChain Merkle proof works", () => {
|
|
173
|
+
const chain = new TrustChain("test-user");
|
|
174
|
+
for (let i = 0; i < 8; i++) {
|
|
175
|
+
chain.record({ action: `action-${i}`, from: `s${i}`, to: `s${i + 1}` });
|
|
176
|
+
}
|
|
177
|
+
chain.seal();
|
|
178
|
+
const proof = chain.proveAtom(3);
|
|
179
|
+
assert(proof !== null);
|
|
180
|
+
const valid = chain.verifyProof(proof.atom.proof, proof.merkle_proof, proof.merkle_root);
|
|
181
|
+
assert(valid === true);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("findDivergence detects cross-party disagreement", () => {
|
|
185
|
+
const chainA = new TrustChain("customer");
|
|
186
|
+
const chainB = new TrustChain("provider");
|
|
187
|
+
chainA.record({ action: "deploy", from: "s0", to: "s1" });
|
|
188
|
+
chainB.record({ action: "deploy", from: "s0", to: "s1" });
|
|
189
|
+
chainA.record({ action: "configure", from: "s1", to: "s2" });
|
|
190
|
+
chainB.record({ action: "terminate", from: "s1", to: "deleted" });
|
|
191
|
+
const div = findDivergence(chainA, chainB);
|
|
192
|
+
assert(div.diverged === true);
|
|
193
|
+
assert(div.at_index === 1);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ══════════════════════════════════════
|
|
197
|
+
// WITNESS TESTS
|
|
198
|
+
// ══════════════════════════════════════
|
|
199
|
+
|
|
200
|
+
import { saveWitness, loadWitnesses, witnessLevel, witnessSummary, createBilateralWitness } from "./core/witness.js";
|
|
201
|
+
import { rmSync } from "node:fs";
|
|
202
|
+
import { join } from "node:path";
|
|
203
|
+
import { homedir } from "node:os";
|
|
204
|
+
|
|
205
|
+
// Clean witness test data
|
|
206
|
+
const testWitnessDir = join(homedir(), ".forge", "witnesses");
|
|
207
|
+
|
|
208
|
+
test("witnessLevel returns Level 1 when no witnesses exist", () => {
|
|
209
|
+
const lvl = witnessLevel("ff".repeat(32));
|
|
210
|
+
assert(lvl.level === 1);
|
|
211
|
+
assert(lvl.label === "self");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("createBilateralWitness creates Level 2 witness", () => {
|
|
215
|
+
const testRoot = "ee".repeat(32);
|
|
216
|
+
const receipt = createBilateralWitness(testRoot, "counterparty@test.com");
|
|
217
|
+
assert(receipt.type === "bilateral");
|
|
218
|
+
assert(receipt.level === 2);
|
|
219
|
+
assert(receipt.counterparty === "counterparty@test.com");
|
|
220
|
+
assert(receipt.receipt_hash.length === 64);
|
|
221
|
+
// Clean up
|
|
222
|
+
try { rmSync(join(testWitnessDir, `${testRoot}.json`)); } catch {}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("witnessLevel upgrades after bilateral witness", () => {
|
|
226
|
+
const testRoot = "dd".repeat(32);
|
|
227
|
+
const lvl1 = witnessLevel(testRoot);
|
|
228
|
+
assert(lvl1.level === 1);
|
|
229
|
+
createBilateralWitness(testRoot, "partner@firm.com");
|
|
230
|
+
const lvl2 = witnessLevel(testRoot);
|
|
231
|
+
assert(lvl2.level === 2);
|
|
232
|
+
assert(lvl2.label === "bilateral");
|
|
233
|
+
// Clean up
|
|
234
|
+
try { rmSync(join(testWitnessDir, `${testRoot}.json`)); } catch {}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("witnessSummary includes upgrade path", () => {
|
|
238
|
+
const testRoot = "cc".repeat(32);
|
|
239
|
+
const summary = witnessSummary(testRoot);
|
|
240
|
+
assert(summary.current_level.level === 1);
|
|
241
|
+
assert(summary.upgrade_path.length === 3); // L2, L3, L4
|
|
242
|
+
assert(summary.upgrade_path[0].includes("Level 2"));
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("loadWitnesses returns empty for unknown root", () => {
|
|
246
|
+
const witnesses = loadWitnesses("bb".repeat(32));
|
|
247
|
+
assert(Array.isArray(witnesses));
|
|
248
|
+
assert(witnesses.length === 0);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("saveWitness and loadWitnesses round-trip", () => {
|
|
252
|
+
const testRoot = "aa".repeat(32);
|
|
253
|
+
saveWitness(testRoot, { type: "test", level: 3, data: "hello" });
|
|
254
|
+
const loaded = loadWitnesses(testRoot);
|
|
255
|
+
assert(loaded.length === 1);
|
|
256
|
+
assert(loaded[0].type === "test");
|
|
257
|
+
assert(loaded[0].level === 3);
|
|
258
|
+
// Clean up
|
|
259
|
+
try { rmSync(join(testWitnessDir, `${testRoot}.json`)); } catch {}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// ---- Summary ----
|
|
263
|
+
console.log("\n ────────────────────");
|
|
264
|
+
console.log(` ${passed} passed, ${failed} failed`);
|
|
265
|
+
if (failed > 0) process.exit(1);
|
|
266
|
+
console.log("");
|