eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1// 🤖 The ledger-stream hash chain (issue #1224): canonical bytes, key
2// derivation, link verification, and the tamper cases — every rule the
3// append/verify tools rely on, pinned without any IO.
4// node test/ledgerchain.test.mjs
5import assert from "node:assert/strict";
6import { canonicalBytes, sha256hex, buildEntry, verifyLink } from "../tools/ledgerchain.mjs";
7
8let failures = 0;
9function test(name, fn) {
10 try { fn(); console.log("PASS " + name); }
11 catch (e) { console.log("FAIL " + name + " — " + (e && e.message)); failures++; }
12}
13
14test("canonical bytes are key-order independent, deeply", () => {
15 const a = canonicalBytes({ b: 1, a: { d: [{ z: 1, y: 2 }], c: 3 } });
16 const b = canonicalBytes({ a: { c: 3, d: [{ y: 2, z: 1 }] }, b: 1 });
17 assert.equal(a.toString(), b.toString());
18 assert.notEqual(sha256hex(canonicalBytes({ a: 1 })), sha256hex(canonicalBytes({ a: 2 })));
19});
20
21test("genesis entry: null prev, key carries compact ts + own sha12", () => {
22 const { entry, bytes, sha256, key } = buildEntry({
23 ledger: "music", writer: "Paul Campbell", ts: "2026-07-21T09:34:13.000Z",
24 payload: { track: "Isn't She Lovely" }, prev: null,
25 });
26 assert.equal(entry.prev_key, null);
27 assert.equal(entry.prev_sha256, null);
28 assert.equal(sha256, sha256hex(bytes));
29 assert.equal(key, "ledgers/music/entries/20260721T093413Z-" + sha256.slice(0, 12) + ".json");
30});
31
32test("a two-entry chain round-trips through verifyLink", () => {
33 const g = buildEntry({ ledger: "music", writer: "w", ts: "2026-07-21T09:00:00Z", payload: { n: 1 }, prev: null });
34 const e2 = buildEntry({ ledger: "music", writer: "w", ts: "2026-07-21T09:05:00Z", payload: { n: 2 }, prev: { key: g.key, sha256: g.sha256 } });
35 const v2 = verifyLink({ bytes: e2.bytes, key: e2.key, claimedSha: e2.sha256 });
36 assert.ok(v2.ok, v2.reason);
37 assert.equal(v2.entry.prev_key, g.key);
38 const v1 = verifyLink({ bytes: g.bytes, key: v2.entry.prev_key, claimedSha: v2.entry.prev_sha256 });
39 assert.ok(v1.ok, v1.reason);
40 assert.equal(v1.entry.prev_key, null, "walk terminates at genesis");
41});
42
43test("tampered bytes break the link", () => {
44 const g = buildEntry({ ledger: "music", writer: "w", ts: "2026-07-21T09:00:00Z", payload: { n: 1 }, prev: null });
45 const tampered = Buffer.from(g.bytes.toString().replace('"n":1', '"n":9'));
46 const v = verifyLink({ bytes: tampered, key: g.key, claimedSha: g.sha256 });
47 assert.equal(v.ok, false);
48 assert.match(v.reason, /claims/);
49});
50
51test("an entry moved to a different key is caught", () => {
52 const g = buildEntry({ ledger: "music", writer: "w", ts: "2026-07-21T09:00:00Z", payload: { n: 1 }, prev: null });
53 const v = verifyLink({ bytes: g.bytes, key: "ledgers/music/entries/20990101T000000Z-" + g.sha256.slice(0, 12) + ".json", claimedSha: g.sha256 });
54 assert.equal(v.ok, false);
55 assert.match(v.reason, /derives/);
56});
57
58test("payloads are order-canonical too — same payload, same key", () => {
59 const p1 = buildEntry({ ledger: "l", writer: "w", ts: "2026-07-21T09:00:00Z", payload: { a: 1, b: 2 }, prev: null });
60 const p2 = buildEntry({ ledger: "l", writer: "w", ts: "2026-07-21T09:00:00Z", payload: { b: 2, a: 1 }, prev: null });
61 assert.equal(p1.key, p2.key);
62});
63
64if (failures > 0) { console.error(failures + " failure(s)"); process.exit(1); }
65