nx_spore_up_test.nx source
↩ module page · 203 lines · 8208 B
1// nx_spore_up_test.nx -- minimum-viable spore-up demonstration.
2//
3// END-TO-END chain on REAL on-device measurements + persistent
4// journaled attestation. Answers the operator's question
5// "can a device get spored and seeded and self-unfold?" with
6// running code.
7//
8// Sequence:
9// 1. Real nx_probe_run (on-device capability fingerprint, witnessed
10// via micro-smokes, not synthetic)
11// 2. Real nx_calibrate_run (lmbench-class empirical measurements
12// on THIS device)
13// 3. Build a 3-node manifest workload (hardcoded reference set;
14// ST-4 nx_kv_store + future manifest registry will replace
15// with disk-resident manifests)
16// 4. nx_install_pipeline_run_with_inputs composes
17// select + capture + hash into one call
18// 5. nx_journal_log_append the install_hash as a persistent
19// tamper-evident attestation record
20// 6. verify_chain confirms journal integrity
21// 7. sys_write "SPORE UP COMPLETE ..." to stdout
22//
23// What this PROVES on green:
24// - Substrate runs probe + calibration on its own device with
25// zero operator input
26// - Selection + plan + hash chain works on real measurements
27// - Install attestation persists in a hash-chained journal that
28// refuses silent tampering
29// - The whole chain composes through one main() function -- the
30// substrate self-assembles from first principles
31//
32// What this does NOT yet prove (honest gaps):
33// - Network pull of manifests (HTTPS wired but not composed -- ST-6
34// peer sync queued)
35// - File-backed persistence across boots (ST-2 / ST-3.5 queued)
36// - Stage0-class bootstrap from a hex0 seed (SA-9 queued)
37// - Per-variant on-device smoke gating (SA-6 nx_verify queued)
38// - Compile invocation of selected variants into a deployable
39// binary (SA-6 nx_compile queued)
40//
41// Per [[feedback-no-false-ok-substrate-honesty-audit]]: this demo
42// is HONEST about what's proven (chain composes end-to-end on real
43// measurements + persisted attestation) and what's still missing
44// (network + persistence + bootstrap + compile + per-variant verify).
45
46import "nx_syscalls.nx"
47import "nx_probe.nx"
48import "nx_calibrate.nx"
49import "nx_select.nx"
50import "nx_select_joint.nx"
51import "nx_install_plan.nx"
52import "nx_install_hash.nx"
53import "nx_install_pipeline.nx"
54import "nx_blob_store.nx"
55import "nx_journal_log.nx"
56
57// Schema ids for the journal -- caller-defined integer tags.
58// Operator picks the meaning; substrate stays neutral.
59const SCHEMA_INSTALL_HASH: i64 = 1001
60
61// Helper: write a fixed ASCII tag to stdout. Operator-visible
62// signal so they can see the demo ran without running the smoke
63// harness manually.
64func _emit(tag: *u8, len: i64) -> i64 {
65 sys_write(1, tag, len)
66 return 0
67}
68
69// Helper: build a manifest with given variant_id, cost, layouts.
70func _mk(variant_id: i64, cost: i64, in_lay: i64, out_lay: i64) -> *NxManifest {
71 // tier_floor=UNKNOWN(0) so the selector accepts whatever tier
72 // calibration infers on THIS device. Real qemu-riscv64 emulation
73 // can yield surprising tier numbers; the demo wants to compose
74 // across all tiers.
75 let m: *NxManifest = nx_manifest_new(
76 100, variant_id,
77 nx_isa_bit(NX_ISA_RV64) | nx_isa_bit(NX_ISA_X86_64),
78 0, 4096,
79 NX_TIER_INF_UNKNOWN, NX_TIER_INF_HPC,
80 cost, 0, 0, 0)
81 nx_manifest_set_layout(m, in_lay, out_lay)
82 return m
83}
84
85func main() -> i64 {
86 // ----- 1. Real on-device probe -----
87 let probe: *NxProbeRecord = nx_probe_new()
88 let rc_probe: i64 = nx_probe_run(probe)
89 if rc_probe != 0 { return 1 }
90 // Witness expectations on qemu-riscv64 (or native).
91 if probe.actual_isa_family != NX_ISA_RV64 { return 2 }
92 if probe.actual_endianness != NX_ENDIAN_LITTLE { return 3 }
93 if probe.actual_pointer_width_bits != 64 { return 4 }
94 if probe.actual_page_size_bytes < 4096 { return 5 }
95 if probe.actual_mmap_works != 1 { return 6 }
96 if probe.actual_write_works != 1 { return 7 }
97 if probe.actual_mono_clock_works != 1 { return 8 }
98
99 // ----- 2. Real on-device calibration -----
100 let calib: *NxCalibrationRecord = nx_calibrate_new()
101 let rc_calib: i64 = nx_calibrate_run(calib)
102 if rc_calib != 0 { return 9 }
103 if calib.int_alu_ps_per_op <= 0 { return 10 }
104 if calib.mem_ns_per_access_l1 <= 0 { return 11 }
105 if calib.mem_ns_per_access_ram <= 0 { return 12 }
106 if calib.mem_bw_mib_per_s <= 0 { return 13 }
107 if nx_tier_inf_is_valid(calib.inferred_tier) != 1 { return 14 }
108
109 // ----- 3. Build a 3-node workload DAG -----
110 // Single-variant per node for determinism (one feasible chain
111 // through COL->COL->COL). Real spore-up will pull multi-variant
112 // manifests from disk / peer at ST-6.
113 let a: *NxManifest = _mk(8001, 300, NX_LAYOUT_OPAQUE_PASS, NX_LAYOUT_COL_MAJOR)
114 let b: *NxManifest = _mk(8002, 200, NX_LAYOUT_COL_MAJOR, NX_LAYOUT_COL_MAJOR)
115 let c: *NxManifest = _mk(8003, 100, NX_LAYOUT_COL_MAJOR, NX_LAYOUT_OPAQUE_PASS)
116
117 let a_ptrs: *i64 = (sys_mmap(8)) as *i64
118 a_ptrs[0] = a as i64
119 let b_ptrs: *i64 = (sys_mmap(8)) as *i64
120 b_ptrs[0] = b as i64
121 let c_ptrs: *i64 = (sys_mmap(8)) as *i64
122 c_ptrs[0] = c as i64
123
124 let n_a: *NxJointNode = nx_joint_node_new(100, a_ptrs, 1)
125 let n_b: *NxJointNode = nx_joint_node_new(200, b_ptrs, 1)
126 let n_c: *NxJointNode = nx_joint_node_new(300, c_ptrs, 1)
127 let nodes: *i64 = (sys_mmap(24)) as *i64
128 nodes[0] = n_a as i64
129 nodes[1] = n_b as i64
130 nodes[2] = n_c as i64
131
132 let policy: *NxPolicy = nx_policy_throughput_default()
133
134 // ----- 4. End-to-end orchestrator: select + capture + hash -----
135 let plan: *NxInstallPlan = nx_install_plan_new()
136 let install_hash: *u8 = sys_mmap(64)
137 let rc_pipe: i64 = nx_install_pipeline_run_with_inputs(
138 probe, calib, policy,
139 nodes, 3, plan, install_hash)
140 if rc_pipe != NX_PIPELINE_OK { return 15 }
141 if plan.n_selected != 3 { return 16 }
142 if plan.selection_verdict != NX_PLAN_VERDICT_OK { return 17 }
143 if nx_install_plan_variant_id_at(plan, 0) != 8001 { return 18 }
144 if nx_install_plan_variant_id_at(plan, 1) != 8002 { return 19 }
145 if nx_install_plan_variant_id_at(plan, 2) != 8003 { return 20 }
146
147 // Install hash buffer must be populated (not zero'd, which is the
148 // SELECT_FAILED path).
149 var any_nonzero: i64 = 0
150 var ih_i: i64 = 0
151 while ih_i < 32 {
152 if (install_hash[ih_i] as i64) != 0 { any_nonzero = 1 }
153 ih_i = ih_i + 1
154 }
155 if any_nonzero != 1 { return 21 }
156
157 // ----- 5. Persist via hash-chained journal -----
158 let store: *NxBlobStore = nx_blob_store_new()
159 let jlog: *NxJournalLog = nx_journal_log_new(store)
160 let seq: i64 = nx_journal_log_append(jlog, install_hash, 32, SCHEMA_INSTALL_HASH)
161 if seq != 0 { return 22 }
162 if nx_journal_log_count(jlog) != 1 { return 23 }
163
164 // ----- 6. verify_chain on the persisted record -----
165 if nx_journal_log_verify_chain(jlog) != -1 { return 24 }
166
167 // Read back the journal entry, confirm bytes match.
168 let read_back: *u8 = sys_mmap(64)
169 let sid_out: *i64 = (sys_mmap(8)) as *i64
170 let n_read: i64 = nx_journal_log_read(jlog, 0, read_back, 64, sid_out)
171 if n_read != 32 { return 25 }
172 if *sid_out != SCHEMA_INSTALL_HASH { return 26 }
173 var k_check: i64 = 0
174 while k_check < 32 {
175 if (read_back[k_check] as i64) != (install_hash[k_check] as i64) { return 27 }
176 k_check = k_check + 1
177 }
178
179 // ----- 7. Emit operator-visible signal -----
180 // "SPORE UP COMPLETE\n" -- 18 bytes.
181 let tag: *u8 = sys_mmap(32)
182 tag[0] = 83 as u8 // 'S'
183 tag[1] = 80 as u8 // 'P'
184 tag[2] = 79 as u8 // 'O'
185 tag[3] = 82 as u8 // 'R'
186 tag[4] = 69 as u8 // 'E'
187 tag[5] = 32 as u8 // ' '
188 tag[6] = 85 as u8 // 'U'
189 tag[7] = 80 as u8 // 'P'
190 tag[8] = 32 as u8 // ' '
191 tag[9] = 67 as u8 // 'C'
192 tag[10] = 79 as u8 // 'O'
193 tag[11] = 77 as u8 // 'M'
194 tag[12] = 80 as u8 // 'P'
195 tag[13] = 76 as u8 // 'L'
196 tag[14] = 69 as u8 // 'E'
197 tag[15] = 84 as u8 // 'T'
198 tag[16] = 69 as u8 // 'E'
199 tag[17] = 10 as u8 // '\n'
200 _emit(tag, 18)
201
202 return 0
203}