nx_substrate_manifest_test.nx source
↩ module page · 138 lines · 6319 B
1// nx_substrate_manifest_test.nx -- smoke for nx_substrate_manifest
2// EM-3 partial.
3//
4// Exercises:
5// 1. Allocation + canary + initial state
6// 2. Add expected hashes; n_expected increments
7// 3. Idempotent re-add
8// 4. is_complete + n_missing reflect state correctly
9// 5. INGEST happy path: bytes hash to claimed AND claimed is in manifest -> stored, received_flag set, n_received++
10// 6. NOT_IN_MANIFEST: ingest with claimed-hash NOT in expected set -> rejected; counter incremented
11// 7. HASH_MISMATCH: ingest with bytes that DON'T hash to claimed -> rejected; tamper counter
12// 8. ALREADY_RECEIVED: re-ingest same blob -> idempotent
13// 9. Multiple distinct ingests -> n_received tracks correctly; eventually is_complete
14// 10. Bad-input gates + canary tamper
15
16import "nx_syscalls.nx"
17import "nx_blob_store.nx"
18import "nx_substrate_manifest.nx"
19
20func _fill_pattern(buf: *u8, len: i64, seed: i64) -> i64 {
21 var i: i64 = 0
22 while i < len {
23 buf[i] = (((i * 13) + seed) & 255) as u8
24 i = i + 1
25 }
26 return 0
27}
28
29// Compute SHA-256-derived NxBlobHash by passing bytes through a
30// throwaway store (the store dedups internally; we use the hash
31// it returns).
32func _compute_hash(bytes: *u8, len: i64) -> *NxBlobHash {
33 let throwaway: *NxBlobStore = nx_blob_store_new()
34 let h: *NxBlobHash = nx_blob_hash_new()
35 nx_blob_store_put(throwaway, bytes, len, h)
36 return h
37}
38
39func main() -> i64 {
40 let store: *NxBlobStore = nx_blob_store_new()
41 let m: *NxSubstrateManifest = nx_substrate_manifest_new(store)
42
43 // ----- 1. Initial state -----
44 if nx_substrate_manifest_is_valid(m) != 1 { return 1 }
45 if nx_substrate_manifest_is_complete(m) != 0 { return 2 } // empty is not complete
46 if nx_substrate_manifest_n_missing(m) != 0 { return 3 }
47 if nx_substrate_manifest_n_rejected_unknown(m) != 0 { return 4 }
48 if nx_substrate_manifest_n_rejected_tamper(m) != 0 { return 5 }
49
50 // ----- 2. Build 3 expected blobs -----
51 let p1: *u8 = sys_mmap(64)
52 _fill_pattern(p1, 32, 10)
53 let h1: *NxBlobHash = _compute_hash(p1, 32)
54
55 let p2: *u8 = sys_mmap(64)
56 _fill_pattern(p2, 32, 20)
57 let h2: *NxBlobHash = _compute_hash(p2, 32)
58
59 let p3: *u8 = sys_mmap(64)
60 _fill_pattern(p3, 32, 30)
61 let h3: *NxBlobHash = _compute_hash(p3, 32)
62
63 // ----- 3. Add expected hashes -----
64 if nx_substrate_manifest_add_expected(m, h1) != NX_SM_INGESTED { return 6 }
65 if nx_substrate_manifest_add_expected(m, h2) != NX_SM_INGESTED { return 7 }
66 if nx_substrate_manifest_add_expected(m, h3) != NX_SM_INGESTED { return 8 }
67 if m.n_expected != 3 { return 9 }
68 if nx_substrate_manifest_n_missing(m) != 3 { return 10 }
69 if nx_substrate_manifest_is_complete(m) != 0 { return 11 }
70
71 // ----- 4. Idempotent re-add of an existing expected hash -----
72 if nx_substrate_manifest_add_expected(m, h1) != NX_SM_ALREADY_RECEIVED { return 12 }
73 if m.n_expected != 3 { return 13 } // count unchanged
74
75 // ----- 5. Ingest happy path: blob 1 -----
76 if nx_substrate_manifest_ingest(m, h1, p1, 32) != NX_SM_INGESTED { return 14 }
77 if m.n_received != 1 { return 15 }
78 if nx_substrate_manifest_n_missing(m) != 2 { return 16 }
79 if nx_substrate_manifest_is_complete(m) != 0 { return 17 }
80
81 // ----- 6. NOT_IN_MANIFEST: ingest a hash not in the expected set -----
82 let p_extra: *u8 = sys_mmap(64)
83 _fill_pattern(p_extra, 32, 999)
84 let h_extra: *NxBlobHash = _compute_hash(p_extra, 32)
85 if nx_substrate_manifest_ingest(m, h_extra, p_extra, 32) != NX_SM_NOT_IN_MANIFEST { return 18 }
86 if nx_substrate_manifest_n_rejected_unknown(m) != 1 { return 19 }
87 if m.n_received != 1 { return 20 } // unchanged
88
89 // ----- 7. HASH_MISMATCH: bytes don't hash to claimed -----
90 // Claim h2 but pass p3's bytes. Verifier hashes p3 -> h3 != h2 -> reject.
91 if nx_substrate_manifest_ingest(m, h2, p3, 32) != NX_SM_HASH_MISMATCH { return 21 }
92 if nx_substrate_manifest_n_rejected_tamper(m) != 1 { return 22 }
93 // The actual blob (h3 of p3) is now in the blob store via put_blob
94 // dedup; that's fine. But the manifest slot for h2 is STILL not
95 // marked received.
96 if m.n_received != 1 { return 23 }
97 if m.received_flags[1] != 0 { return 24 } // h2's slot still 0
98
99 // ----- 8. ALREADY_RECEIVED: re-ingest blob 1 -----
100 if nx_substrate_manifest_ingest(m, h1, p1, 32) != NX_SM_ALREADY_RECEIVED { return 25 }
101 if m.n_received != 1 { return 26 } // count unchanged
102
103 // ----- 9. Complete the manifest -----
104 if nx_substrate_manifest_ingest(m, h2, p2, 32) != NX_SM_INGESTED { return 27 }
105 if m.n_received != 2 { return 28 }
106 if nx_substrate_manifest_ingest(m, h3, p3, 32) != NX_SM_INGESTED { return 29 }
107 if m.n_received != 3 { return 30 }
108 if nx_substrate_manifest_is_complete(m) != 1 { return 31 }
109 if nx_substrate_manifest_n_missing(m) != 0 { return 32 }
110
111 // Verify each blob is queryable from the underlying L0 store.
112 if nx_blob_store_has(store, h1) != 1 { return 33 }
113 if nx_blob_store_has(store, h2) != 1 { return 34 }
114 if nx_blob_store_has(store, h3) != 1 { return 35 }
115
116 // ----- 10. Bad-input gates + canary tamper -----
117 let null_hash: *NxBlobHash = (0 as i64) as *NxBlobHash
118 let null_bytes: *u8 = (0 as i64) as *u8
119 if nx_substrate_manifest_add_expected(m, null_hash) != NX_SM_BAD_INPUT { return 36 }
120 if nx_substrate_manifest_ingest(m, null_hash, p1, 32) != NX_SM_BAD_INPUT { return 37 }
121 if nx_substrate_manifest_ingest(m, h1, null_bytes, 32) != NX_SM_BAD_INPUT { return 38 }
122 if nx_substrate_manifest_ingest(m, h1, p1, -1) != NX_SM_BAD_INPUT { return 39 }
123
124 // Canary tamper -> all ops refuse.
125 m.canary_post = 0xDEADBEEF
126 if nx_substrate_manifest_is_valid(m) != 0 { return 40 }
127 if nx_substrate_manifest_add_expected(m, h1) != NX_SM_TAMPER { return 41 }
128 if nx_substrate_manifest_ingest(m, h1, p1, 32) != NX_SM_TAMPER { return 42 }
129 if nx_substrate_manifest_is_complete(m) != 0 { return 43 }
130 if nx_substrate_manifest_n_missing(m) != -1 { return 44 }
131
132 // ----- 11. Sealed-enum gate -----
133 if nx_sm_verdict_is_valid(NX_SM_INGESTED) != 1 { return 45 }
134 if nx_sm_verdict_is_valid(-1) != 0 { return 46 }
135 if nx_sm_verdict_is_valid(NX_SM_N_VERDICTS) != 0 { return 47 }
136
137 return 0
138}