nx_spore_up_network_test.nx source
↩ module page · 263 lines · 11363 B
1// nx_spore_up_network_test.nx -- full ecosystem evolution flow on
2// SYNTHETIC PEER.
3//
4// Extends the v1 spore-up demo (nx_spore_up_test.nx) to ADD the
5// NETWORK phase: spore declares a manifest of expected substrate
6// chunks; a mock peer "delivers" those chunks; manifest verifies
7// each; completion gates SA-arc assembly; install_hash journaled.
8//
9// This is the second of the spore-up demonstration pair:
10// v1 -- nx_spore_up_test.nx -- SA arc on real measurements
11// v2 -- THIS file -- v1 + network ingest via manifest
12//
13// Lifecycle modeled (per NISHI_ECOSYSTEM_EVOLUTION_ROADMAP.md):
14// Phase 1 (Spore) -- synthetic: spore-author declared 3 expected hashes
15// Phase 2 (Network) -- mock peer delivers blobs; manifest verifies +
16// ingests each; defenses (NOT_IN_MANIFEST +
17// HASH_MISMATCH) exercised
18// Phase 3 (Substrate)-- SA arc assembly on real measurements
19// Phase 4 (Seed) -- journal append of install_hash + verify_chain
20// Phase 5 (Reproduce)-- (queued; this demo does not yet emit a new spore)
21//
22// What this PROVES:
23// - Spore-declared expected manifest is enforced (peer cannot push
24// arbitrary content)
25// - Hash-verification on every received blob (corrupted transit
26// refused)
27// - Completion gate halts assembly until ALL expected blobs received
28// - SA arc still composes on top of the verified-substrate phase
29// - Full chain end-to-end: probe -> calibrate -> manifest-complete
30// -> selection -> plan -> hash -> journaled attestation
31//
32// What this does NOT yet prove (queued):
33// - REAL network transport (HTTPS / serial / USB) -- mock peer here
34// is in-memory
35// - Spore signing (Ed25519) -- composes at EM-9
36// - Stage0 hex0 bootstrap -- composes at SA-9
37// - AVR codegen / Arduino real hardware -- EM-10 / EM-12
38
39import "nx_syscalls.nx"
40import "nx_probe.nx"
41import "nx_calibrate.nx"
42import "nx_select.nx"
43import "nx_select_joint.nx"
44import "nx_install_plan.nx"
45import "nx_install_hash.nx"
46import "nx_install_pipeline.nx"
47import "nx_blob_store.nx"
48import "nx_journal_log.nx"
49import "nx_substrate_manifest.nx"
50
51const SCHEMA_INSTALL_HASH: i64 = 1001
52
53// Compute a known hash for a known byte pattern via L0 store. Used
54// to prebuild the spore's "expected hashes" + the mock peer's
55// known content addresses.
56func _compute_hash(bytes: *u8, len: i64) -> *NxBlobHash {
57 let scratch: *NxBlobStore = nx_blob_store_new()
58 let h: *NxBlobHash = nx_blob_hash_new()
59 nx_blob_store_put(scratch, bytes, len, h)
60 return h
61}
62
63// Fill a buffer with a deterministic pattern keyed by `seed`.
64func _fill(buf: *u8, len: i64, seed: i64) -> i64 {
65 var i: i64 = 0
66 while i < len {
67 buf[i] = (((i * 19) + seed) & 255) as u8
68 i = i + 1
69 }
70 return 0
71}
72
73func _mk_manifest_variant(variant_id: i64, cost: i64, in_lay: i64, out_lay: i64) -> *NxManifest {
74 let m: *NxManifest = nx_manifest_new(
75 100, variant_id,
76 nx_isa_bit(NX_ISA_RV64) | nx_isa_bit(NX_ISA_X86_64),
77 0, 4096,
78 NX_TIER_INF_UNKNOWN, NX_TIER_INF_HPC,
79 cost, 0, 0, 0)
80 nx_manifest_set_layout(m, in_lay, out_lay)
81 return m
82}
83
84func main() -> i64 {
85 // ========================================================
86 // PHASE 1 -- Spore-author declares 3 expected substrate blobs
87 // ========================================================
88 // The "spore" carries a list of expected hashes. Here we
89 // synthesize that list by pre-computing hashes for 3 known
90 // payloads.
91 let payload_a: *u8 = sys_mmap(64)
92 _fill(payload_a, 32, 100)
93 let h_a: *NxBlobHash = _compute_hash(payload_a, 32)
94
95 let payload_b: *u8 = sys_mmap(64)
96 _fill(payload_b, 32, 200)
97 let h_b: *NxBlobHash = _compute_hash(payload_b, 32)
98
99 let payload_c: *u8 = sys_mmap(64)
100 _fill(payload_c, 32, 300)
101 let h_c: *NxBlobHash = _compute_hash(payload_c, 32)
102
103 // ========================================================
104 // PHASE 2 -- Spore germinates: build manifest + ingest from
105 // a mock peer.
106 // ========================================================
107 let main_store: *NxBlobStore = nx_blob_store_new()
108 let manifest: *NxSubstrateManifest = nx_substrate_manifest_new(main_store)
109 if nx_substrate_manifest_is_valid(manifest) != 1 { return 1 }
110
111 // Spore declares the expected set.
112 if nx_substrate_manifest_add_expected(manifest, h_a) != NX_SM_INGESTED { return 2 }
113 if nx_substrate_manifest_add_expected(manifest, h_b) != NX_SM_INGESTED { return 3 }
114 if nx_substrate_manifest_add_expected(manifest, h_c) != NX_SM_INGESTED { return 4 }
115 if nx_substrate_manifest_is_complete(manifest) != 0 { return 5 }
116 if nx_substrate_manifest_n_missing(manifest) != 3 { return 6 }
117
118 // Mock peer delivers blob A first.
119 if nx_substrate_manifest_ingest(manifest, h_a, payload_a, 32) != NX_SM_INGESTED { return 7 }
120 if nx_substrate_manifest_n_missing(manifest) != 2 { return 8 }
121
122 // ----- ADVERSARIAL: peer pushes a blob whose hash is NOT in
123 // the spore's expected set. Must be refused. -----
124 let payload_evil: *u8 = sys_mmap(64)
125 _fill(payload_evil, 32, 9999)
126 let h_evil: *NxBlobHash = _compute_hash(payload_evil, 32)
127 if nx_substrate_manifest_ingest(manifest, h_evil, payload_evil, 32) != NX_SM_NOT_IN_MANIFEST { return 9 }
128 if manifest.n_rejected_unknown != 1 { return 10 }
129
130 // ----- ADVERSARIAL: peer claims to deliver blob B but actually
131 // sends C's bytes (transit corruption / replacement). Must be
132 // refused. -----
133 if nx_substrate_manifest_ingest(manifest, h_b, payload_c, 32) != NX_SM_HASH_MISMATCH { return 11 }
134 if manifest.n_rejected_tamper != 1 { return 12 }
135 // Verifies that despite the rejection, the manifest's state is
136 // unchanged for blob B.
137 if manifest.received_flags[1] != 0 { return 13 }
138 if nx_substrate_manifest_n_missing(manifest) != 2 { return 14 }
139
140 // Peer recovers + delivers blob B correctly.
141 if nx_substrate_manifest_ingest(manifest, h_b, payload_b, 32) != NX_SM_INGESTED { return 15 }
142 if nx_substrate_manifest_n_missing(manifest) != 1 { return 16 }
143
144 // Peer delivers blob C.
145 if nx_substrate_manifest_ingest(manifest, h_c, payload_c, 32) != NX_SM_INGESTED { return 17 }
146
147 // ----- COMPLETION GATE -----
148 if nx_substrate_manifest_is_complete(manifest) != 1 { return 18 }
149 if nx_substrate_manifest_n_missing(manifest) != 0 { return 19 }
150
151 // Every expected blob now in the L0 store, hash-verified.
152 if nx_blob_store_has(main_store, h_a) != 1 { return 20 }
153 if nx_blob_store_has(main_store, h_b) != 1 { return 21 }
154 if nx_blob_store_has(main_store, h_c) != 1 { return 22 }
155
156 // ========================================================
157 // PHASE 3 -- Substrate assembly via the SA arc.
158 // ========================================================
159 let probe: *NxProbeRecord = nx_probe_new()
160 if nx_probe_run(probe) != 0 { return 23 }
161 if probe.actual_isa_family != NX_ISA_RV64 { return 24 }
162
163 let calib: *NxCalibrationRecord = nx_calibrate_new()
164 if nx_calibrate_run(calib) != 0 { return 25 }
165 if calib.mem_bw_mib_per_s <= 0 { return 26 }
166
167 // Build a 3-node workload DAG (one variant per node for
168 // determinism; in real spore-up the variants would come from
169 // the verified blobs above).
170 let v_a: *NxManifest = _mk_manifest_variant(8001, 300, NX_LAYOUT_OPAQUE_PASS, NX_LAYOUT_COL_MAJOR)
171 let v_b: *NxManifest = _mk_manifest_variant(8002, 200, NX_LAYOUT_COL_MAJOR, NX_LAYOUT_COL_MAJOR)
172 let v_c: *NxManifest = _mk_manifest_variant(8003, 100, NX_LAYOUT_COL_MAJOR, NX_LAYOUT_OPAQUE_PASS)
173 let va_ptrs: *i64 = (sys_mmap(8)) as *i64
174 va_ptrs[0] = v_a as i64
175 let vb_ptrs: *i64 = (sys_mmap(8)) as *i64
176 vb_ptrs[0] = v_b as i64
177 let vc_ptrs: *i64 = (sys_mmap(8)) as *i64
178 vc_ptrs[0] = v_c as i64
179 let n_a: *NxJointNode = nx_joint_node_new(100, va_ptrs, 1)
180 let n_b: *NxJointNode = nx_joint_node_new(200, vb_ptrs, 1)
181 let n_c: *NxJointNode = nx_joint_node_new(300, vc_ptrs, 1)
182 let nodes: *i64 = (sys_mmap(24)) as *i64
183 nodes[0] = n_a as i64
184 nodes[1] = n_b as i64
185 nodes[2] = n_c as i64
186
187 let policy: *NxPolicy = nx_policy_throughput_default()
188 let plan: *NxInstallPlan = nx_install_plan_new()
189 let install_hash: *u8 = sys_mmap(64)
190 if nx_install_pipeline_run_with_inputs(probe, calib, policy,
191 nodes, 3, plan, install_hash)
192 != NX_PIPELINE_OK { return 27 }
193 if plan.n_selected != 3 { return 28 }
194 if plan.selection_verdict != NX_PLAN_VERDICT_OK { return 29 }
195 if nx_install_plan_variant_id_at(plan, 0) != 8001 { return 30 }
196 if nx_install_plan_variant_id_at(plan, 1) != 8002 { return 31 }
197 if nx_install_plan_variant_id_at(plan, 2) != 8003 { return 32 }
198
199 // ========================================================
200 // PHASE 4 -- Seed: persistent journaled attestation.
201 // ========================================================
202 let jlog: *NxJournalLog = nx_journal_log_new(main_store)
203 let seq: i64 = nx_journal_log_append(jlog, install_hash, 32, SCHEMA_INSTALL_HASH)
204 if seq != 0 { return 33 }
205 if nx_journal_log_verify_chain(jlog) != -1 { return 34 }
206
207 // Round-trip the journal entry, confirm bytes match install_hash.
208 let read_back: *u8 = sys_mmap(64)
209 let sid_out: *i64 = (sys_mmap(8)) as *i64
210 let n_read: i64 = nx_journal_log_read(jlog, 0, read_back, 64, sid_out)
211 if n_read != 32 { return 35 }
212 if *sid_out != SCHEMA_INSTALL_HASH { return 36 }
213 var k: i64 = 0
214 while k < 32 {
215 if (read_back[k] as i64) != (install_hash[k] as i64) { return 37 }
216 k = k + 1
217 }
218
219 // ----- Audit counters surfaced -----
220 // Manifest rejected 2 attacks (1 unknown + 1 tamper); 3 blobs
221 // verified-and-stored.
222 if nx_substrate_manifest_n_rejected_unknown(manifest) != 1 { return 38 }
223 if nx_substrate_manifest_n_rejected_tamper(manifest) != 1 { return 39 }
224 if manifest.n_received != 3 { return 40 }
225
226 // ========================================================
227 // PHASE 5 -- (queued) Reproduce: mature substrate would emit
228 // a new spore for downstream devices. Not yet implemented.
229 // ========================================================
230
231 // ----- Operator-visible signal -----
232 // "SPORE UP NETWORK COMPLETE\n" -- 26 bytes.
233 let tag: *u8 = sys_mmap(32)
234 tag[0] = 83 as u8 // 'S'
235 tag[1] = 80 as u8 // 'P'
236 tag[2] = 79 as u8 // 'O'
237 tag[3] = 82 as u8 // 'R'
238 tag[4] = 69 as u8 // 'E'
239 tag[5] = 32 as u8 // ' '
240 tag[6] = 85 as u8 // 'U'
241 tag[7] = 80 as u8 // 'P'
242 tag[8] = 32 as u8 // ' '
243 tag[9] = 78 as u8 // 'N'
244 tag[10] = 69 as u8 // 'E'
245 tag[11] = 84 as u8 // 'T'
246 tag[12] = 87 as u8 // 'W'
247 tag[13] = 79 as u8 // 'O'
248 tag[14] = 82 as u8 // 'R'
249 tag[15] = 75 as u8 // 'K'
250 tag[16] = 32 as u8 // ' '
251 tag[17] = 67 as u8 // 'C'
252 tag[18] = 79 as u8 // 'O'
253 tag[19] = 77 as u8 // 'M'
254 tag[20] = 80 as u8 // 'P'
255 tag[21] = 76 as u8 // 'L'
256 tag[22] = 69 as u8 // 'E'
257 tag[23] = 84 as u8 // 'T'
258 tag[24] = 69 as u8 // 'E'
259 tag[25] = 10 as u8 // '\n'
260 sys_write(1, tag, 26)
261
262 return 0
263}