code wiki / (root) / nx_spore_up_test.nx

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}