nx_log_chunked_test.nx source
↩ module page · 99 lines · 4589 B
1// nx_log_chunked_test.nx -- append-only Merkle-linked log shape.
2
3import "nx_syscalls.nx"
4import "nx_tier.nx"
5import "nx_sha256.nx"
6import "nx_cbor.nx"
7import "nx_log_chunked.nx"
8
9func main() -> nx_int {
10 // Small chunk + small manifest cap to force flush after a few spans.
11 let log: *ChunkedLog = nx_lc_alloc(512, 16)
12 if log.chunk_pos != 0 { return 1 }
13 if log.n_chunks != 0 { return 2 }
14 if log.last_hash_off != 0 - 1 { return 3 }
15
16 let attrs: *i64 = (sys_mmap(32)) as *i64
17 attrs[0] = 0xAA; attrs[1] = 100
18 attrs[2] = 0xBB; attrs[3] = 200
19
20 // ===== Append a few spans (no chunk overflow yet) =============
21 nx_lc_append_span(log, 1000, 1, 0 - 1, 0, 0, 100, attrs, 2, 1700000000)
22 nx_lc_append_span(log, 1000, 2, 1, 1, 100, 105, attrs, 2, 1700000001)
23 nx_lc_append_span(log, 1000, 3, 1, 2, 105, 110, attrs, 2, 1700000002)
24
25 if log.chunk_n_spans != 3 { return 10 }
26 if log.chunk_first_tid != 1000 { return 11 }
27 if log.chunk_last_tid != 1000 { return 12 }
28 if log.n_chunks != 0 { return 13 } // not flushed yet
29
30 // ===== Explicit flush =========================================
31 let rc1: nx_int = nx_lc_flush(log, 1700000003)
32 if rc1 != 0 { return 20 }
33 if log.n_chunks != 1 { return 21 }
34 if log.chunk_pos != 0 { return 22 }
35 if log.chunk_n_spans != 0 { return 23 }
36
37 // Manifest record sanity
38 if nx_lc_rec_get(log, 0, NX_LC_REC_F_INDEX) != 0 { return 24 }
39 if nx_lc_rec_get(log, 0, NX_LC_REC_F_N_SPANS) != 3 { return 25 }
40 if nx_lc_rec_get(log, 0, NX_LC_REC_F_WRITTEN_AT) != 1700000003 { return 26 }
41 if nx_lc_rec_get(log, 0, NX_LC_REC_F_PARENT_HASH_OFF) != 0 - 1 { return 27 }
42 if nx_lc_rec_get(log, 0, NX_LC_REC_F_FIRST_TRACE) != 1000 { return 28 }
43 if nx_lc_rec_get(log, 0, NX_LC_REC_F_LAST_TRACE) != 1000 { return 29 }
44
45 // ===== Second chunk: should chain parent_hash to chunk 0 =====
46 nx_lc_append_span(log, 2000, 4, 0 - 1, 0, 200, 210, attrs, 2, 1700000004)
47 nx_lc_append_span(log, 2001, 5, 4, 7, 210, 220, attrs, 2, 1700000005)
48 nx_lc_flush(log, 1700000006)
49 if log.n_chunks != 2 { return 30 }
50
51 // Parent-hash-off of chunk 1 must equal hash-off of chunk 0
52 let chunk0_hash_off: nx_int = nx_lc_rec_get(log, 0, NX_LC_REC_F_HASH_OFF)
53 let chunk1_parent_off: nx_int = nx_lc_rec_get(log, 1, NX_LC_REC_F_PARENT_HASH_OFF)
54 if chunk0_hash_off != chunk1_parent_off { return 31 }
55
56 // Last_trace bumped to 2001 in chunk 1
57 if nx_lc_rec_get(log, 1, NX_LC_REC_F_LAST_TRACE) != 2001 { return 32 }
58
59 // ===== Chain integrity verifier ===============================
60 if nx_lc_verify_chain(log) != NX_LC_VERIFY_VALID { return 40 }
61
62 // Tamper with the chain: corrupt chunk 1's parent_hash_off, then
63 // verify catches it.
64 log.manifest[1 * NX_LC_REC_FIELDS + NX_LC_REC_F_PARENT_HASH_OFF] = 0xDEAD
65 if nx_lc_verify_chain(log) != NX_LC_VERIFY_PARENT_BROKEN { return 41 }
66 // Repair (set back to the correct value)
67 log.manifest[1 * NX_LC_REC_FIELDS + NX_LC_REC_F_PARENT_HASH_OFF] = chunk0_hash_off
68 if nx_lc_verify_chain(log) != NX_LC_VERIFY_VALID { return 42 }
69
70 // Tamper with the index: set chunk 1's index to 5 -> ORDER_REVERSED
71 log.manifest[1 * NX_LC_REC_FIELDS + NX_LC_REC_F_INDEX] = 5
72 if nx_lc_verify_chain(log) != NX_LC_VERIFY_ORDER_REVERSED { return 43 }
73 log.manifest[1 * NX_LC_REC_FIELDS + NX_LC_REC_F_INDEX] = 1 // repair
74
75 // ===== Auto-flush via overflow =================================
76 //
77 // Fill the chunk to within 128 bytes of capacity to trigger the
78 // overflow-flush path on the next append.
79 log.chunk_pos = log.chunk_cap - 100 // < 128 free
80 log.chunk_n_spans = 1
81 log.chunk_first_tid = 3000
82 log.chunk_last_tid = 3000
83 nx_lc_append_span(log, 3000, 99, 0 - 1, 0, 0, 1, attrs, 2, 1700000010)
84 // Should have flushed -> n_chunks=3
85 if log.n_chunks != 3 { return 50 }
86
87 // ===== Verify the chain still valid after overflow flush =====
88 if nx_lc_verify_chain(log) != NX_LC_VERIFY_VALID { return 51 }
89
90 // ===== Verdict-band coverage =================================
91 if nx_lc_verify_verdict_is_valid(NX_LC_VERIFY_VALID) != 1 { return 60 }
92 if nx_lc_verify_verdict_is_valid(NX_LC_VERIFY_HASH_MISMATCH) != 1 { return 61 }
93 if nx_lc_verify_verdict_is_valid(NX_LC_VERIFY_PARENT_BROKEN) != 1 { return 62 }
94 if nx_lc_verify_verdict_is_valid(NX_LC_VERIFY_ORDER_REVERSED) != 1 { return 63 }
95 if nx_lc_verify_verdict_is_valid(0 - 1) != 0 { return 64 }
96 if nx_lc_verify_verdict_is_valid(NX_LC_VERIFY_N_VERDICTS) != 0 { return 65 }
97
98 return 0
99}