code wiki / _hdl_build / nx_vizsla_budget_gate.nx

nx_vizsla_budget_gate.nx source

↩ module page · 324 lines · 13426 B

1// nx_vizsla_budget_gate.nx -- VIZSLA V1 gate: the budget ledger is gate-proven 2// against a HAND-TOTALED fixture month (construction-known sums; the oracle is 3// arithmetic done by hand in this file's comments, not the module under test). 4// Fixture store lives under an epoch-unique /tmp prefix => every run starts on 5// a virgin store AND the module's prefix-derived log keeps fixtures out of the 6// family ledger log (isolation by construction). 7// 8// month1 (segid 1001), hand-totaled: 9// rent 120000 | groceries 4500+6250+3250=14000 | dining 2200+5800=8000 | fun 999 10// => total txns=7 spent=142999; env rent=120000 OK(0), groceries=15000 OK(+1000), 11// dining=7500 OVER(-500); fun UNBUDGETED(-999) 12// month2 (segid 1002): rent 120000 + groceries 5100 + dining 1500 = 126600 13// => ALL: groceries 19100, txns=10, spent=269599 14// 15// Rows: 16// 1 loud-fail missing txn file => exit 1 17// 2 load-month1 scanned=7 new=7 committed seg-1001 18// 3 hand-totaled groceries row exact + TOTAL txns=7 spent=142999 19// 4 envelope-over dining delta=-500 state=OVER 20// 5 unbudgeted fun named state=UNBUDGETED (never silently dropped) 21// 6 idempotent re-load month1 => new=0 dup_instore=7 segment=none (law 10) 22// 7 additive-month2 month2 lands; ALL report groceries=19100 txns=10 23// 8 TIME-TRAVEL report asof=1001 AFTER month2 == byte-identical pre-month2 report 24// 9 determinism ALL report twice => byte-identical 25// 10 verify-clean CID recompute over 10 entries => verdict=CLEAN exit 0 26// 11 TAMPER-EVIDENCE one flipped byte in seg-1002.docs => verdict=TAMPERED exit 1 27// Evidence: VIZSLA-BUDGET-GATE line -> stdout + knowledge/status/vizsla_gate.log; 28// exit 0 iff 11/11. 29// spec: knowledge/specs/2026-06-10-nishi-vizsla-ladder.md license_tier: ORIGINAL 30import "nx_syscalls.nx" 31import "nx_gate_verdict.nx" 32 33func bg_slen(s: *u8) -> i64 { 34 var n: i64 = 0 35 while s[n] != (0 as u8) { n = n + 1 } 36 return n 37} 38 39func bg_p(s: *u8) -> i64 { 40 sys_write(1, s, bg_slen(s)) 41 return 0 42} 43 44func bg_cat(dst: *u8, off: i64, s: *u8) -> i64 { 45 var i: i64 = 0 46 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 47 return off + i 48} 49 50func bg_catn(dst: *u8, off: i64, v: i64) -> i64 { 51 var o: i64 = off 52 var m: i64 = v 53 if m < 0 { dst[o] = 45 as u8; o = o + 1; m = 0 - m } 54 let t: *u8 = sys_mmap(28) 55 var k: i64 = 0 56 if m == 0 { t[0] = 48 as u8; k = 1 } 57 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 58 var i: i64 = 0 59 while i < k { dst[o + i] = t[k - 1 - i]; i = i + 1 } 60 return o + k 61} 62 63func bg_write(path: *u8, content: *u8) -> i64 { 64 let fd: i64 = sys_openat_wr(path, 0x1a4) 65 if fd < 0 { return 0 - 1 } 66 sys_write(fd, content, bg_slen(content)) 67 sys_close(fd) 68 return 0 69} 70 71func bg_readall(path: *u8, szout: *i64) -> *u8 { 72 let fd: i64 = sys_openat_rd(path) 73 if fd < 0 { szout[0] = 0 - 1; return 0 as *u8 } 74 let sz: i64 = sys_lseek(fd, 0, 2) 75 sys_lseek(fd, 0, 0) 76 let buf: *u8 = sys_mmap(sz + 64) 77 var got: i64 = 0 78 var n: i64 = 1 79 while n > 0 { 80 n = sys_read(fd, (buf as i64 + got) as *u8, 65536) 81 if n > 0 { got = got + n } 82 } 83 sys_close(fd) 84 szout[0] = got 85 return buf 86} 87 88// run elf with up to 4 args, stdout+stderr -> outpath; returns WEXITSTATUS or 128+sig 89func bg_run(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, outpath: *u8) -> i64 { 90 let pid: i64 = sys_fork() 91 if pid == 0 { 92 if (outpath as i64) != 0 { 93 let ofd: i64 = sys_openat_wr(outpath, 0x1a4) 94 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } 95 } 96 let argv: *i64 = sys_mmap(64) as *i64 97 argv[0] = elf as i64 98 var i: i64 = 1 99 if (a1 as i64) != 0 { argv[i] = a1 as i64; i = i + 1 } 100 if (a2 as i64) != 0 { argv[i] = a2 as i64; i = i + 1 } 101 if (a3 as i64) != 0 { argv[i] = a3 as i64; i = i + 1 } 102 if (a4 as i64) != 0 { argv[i] = a4 as i64; i = i + 1 } 103 argv[i] = 0 104 let envp: *i64 = sys_mmap(16) as *i64 105 envp[0] = 0 106 sys_execve(elf, argv, envp) 107 sys_exit(127) 108 } 109 let st: *i64 = sys_mmap(16) as *i64 110 sys_wait4(pid, st, 0) 111 let sig: i64 = st[0] & 0x7f 112 if sig != 0 { return 128 + sig } 113 return (st[0] >> 8) & 0xff 114} 115 116func bg_has(path: *u8, needle: *u8) -> i64 { 117 let szp: *i64 = sys_mmap(16) as *i64 118 let b: *u8 = bg_readall(path, szp) 119 let sz: i64 = szp[0] 120 let n: i64 = bg_slen(needle) 121 if sz < n { return 0 } 122 var i: i64 = 0 123 while i + n <= sz { 124 var ok: i64 = 1 125 var j: i64 = 0 126 while j < n { 127 if b[i + j] != needle[j] { ok = 0; j = n } else { j = j + 1 } 128 } 129 if ok == 1 { return 1 } 130 i = i + 1 131 } 132 return 0 133} 134 135func bg_fileeq(p1: *u8, p2: *u8) -> i64 { 136 let s1: *i64 = sys_mmap(16) as *i64 137 let s2: *i64 = sys_mmap(16) as *i64 138 let b1: *u8 = bg_readall(p1, s1) 139 let b2: *u8 = bg_readall(p2, s2) 140 if s1[0] != s2[0] { return 0 } 141 if s1[0] <= 0 { return 0 } 142 var i: i64 = 0 143 while i < s1[0] { 144 if b1[i] != b2[i] { return 0 } 145 i = i + 1 146 } 147 return 1 148} 149 150// flip one byte `fromend` before EOF (tamper injection; same size => same layout) 151func bg_flip(path: *u8, fromend: i64) -> i64 { 152 let szp: *i64 = sys_mmap(16) as *i64 153 let b: *u8 = bg_readall(path, szp) 154 let sz: i64 = szp[0] 155 if sz <= fromend { return 0 - 1 } 156 let off: i64 = sz - fromend 157 b[off] = (b[off] as i64 ^ 0x41) as u8 158 let fd: i64 = sys_openat_wr(path, 0x1a4) 159 if fd < 0 { return 0 - 1 } 160 sys_write(fd, b, sz) 161 sys_close(fd) 162 return 0 163} 164 165func bg_row(name: *u8, pass: i64) -> i64 { 166 bg_p("ROW " as *u8) 167 bg_p(name) 168 if pass == 1 { bg_p(" PASS\n" as *u8) } else { bg_p(" FAIL\n" as *u8) } 169 return pass 170} 171 172func main(argc: i64, argv: *i64) -> i64 { 173 bg_p("=== VIZSLA BUDGET GATE: hand-totaled ledger KATs on a virgin fixture store ===\n" as *u8) 174 var bud: *u8 = "buildroot/_build/nx_vizsla_budget.sov.elf" as *u8 175 if argc > 1 { bud = argv[1] as *u8 } 176 // self-heal: rebuild the instrument via the durable runner if /tmp was wiped 177 let pr: i64 = sys_openat_rd(bud) 178 if pr >= 0 { sys_close(pr) } 179 else { 180 bg_p(" instrument missing -> rebuilding via durable runner\n" as *u8) 181 bg_run("_offc/nx_sov_build_run.elf" as *u8, "nx_vizsla_budget" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, "/tmp/vzb_rebuild.out" as *u8) 182 } 183 184 // epoch-unique virgin store prefix: /tmp/vzbG<epoch>- 185 let pfx: *u8 = sys_mmap(128) 186 var po: i64 = 0 187 po = bg_cat(pfx, po, "/tmp/vzbG" as *u8) 188 po = bg_catn(pfx, po, sys_now_us()) 189 po = bg_cat(pfx, po, "-" as *u8) 190 pfx[po] = 0 as u8 191 192 // hand-totaled fixtures (sums in the header comment ARE the oracle) 193 let m1: *u8 = "TXN 2026-05-01 120000 rent may-rent\nTXN 2026-05-03 4500 groceries heb\nTXN 2026-05-05 2200 dining tacos\nTXN 2026-05-10 6250 groceries costco\nTXN 2026-05-17 3250 groceries heb\nTXN 2026-05-19 5800 dining anniversary\nTXN 2026-05-21 999 fun arcade\n" as *u8 194 bg_write("/tmp/vzb_m1.txt" as *u8, m1) 195 let m2: *u8 = "TXN 2026-06-02 120000 rent june-rent\nTXN 2026-06-04 5100 groceries heb\nTXN 2026-06-08 1500 dining pizza\n" as *u8 196 bg_write("/tmp/vzb_m2.txt" as *u8, m2) 197 let env: *u8 = "ENV rent 120000\nENV groceries 15000\nENV dining 7500\n" as *u8 198 bg_write("/tmp/vzb_env.txt" as *u8, env) 199 200 let fenv: *u8 = "/tmp/vzb_env.txt" as *u8 201 var pass: i64 = 0 202 var r: i64 = 0 203 204 // row 1: loud-fail on missing txn file 205 let rc1: i64 = bg_run(bud, "load" as *u8, "/tmp/vzb_m1_NOPE.txt" as *u8, pfx, 0 as *u8, "/tmp/vzb_out1.txt" as *u8) 206 r = 0 207 if rc1 == 1 { r = 1 } 208 pass = pass + bg_row("loud-fail-missing-txnfile" as *u8, r) 209 210 // row 2: load month1 into seg-1001 211 let rc2: i64 = bg_run(bud, "load" as *u8, "/tmp/vzb_m1.txt" as *u8, pfx, "1001" as *u8, "/tmp/vzb_out2.txt" as *u8) 212 r = 0 213 if rc2 == 0 { 214 if bg_has("/tmp/vzb_out2.txt" as *u8, "VIZSLA-LEDGER-LOAD scanned=7 new=7 dup_infile=0 dup_instore=0 segment=seg-1001" as *u8) == 1 { r = 1 } 215 } 216 pass = pass + bg_row("load-month1-seg1001" as *u8, r) 217 218 // row 3: hand-totaled month (asof=1001 view; this exact file is row 8's oracle) 219 let rc3: i64 = bg_run(bud, "report" as *u8, pfx, fenv, "1001" as *u8, "/tmp/vzb_out3.txt" as *u8) 220 r = 0 221 if rc3 == 0 { 222 if bg_has("/tmp/vzb_out3.txt" as *u8, "VIZSLA-LEDGER category=groceries spent=14000 budget=15000 delta=1000 state=OK" as *u8) == 1 { 223 if bg_has("/tmp/vzb_out3.txt" as *u8, "VIZSLA-LEDGER-TOTAL txns=7 spent=142999" as *u8) == 1 { r = 1 } 224 } 225 } 226 pass = pass + bg_row("hand-totaled-month" as *u8, r) 227 228 // row 4: envelope over-spend named with exact delta 229 r = bg_has("/tmp/vzb_out3.txt" as *u8, "VIZSLA-LEDGER category=dining spent=8000 budget=7500 delta=-500 state=OVER" as *u8) 230 pass = pass + bg_row("envelope-over-delta" as *u8, r) 231 232 // row 5: unbudgeted category is NAMED, never silently dropped 233 r = bg_has("/tmp/vzb_out3.txt" as *u8, "VIZSLA-LEDGER category=fun spent=999 budget=0 delta=-999 state=UNBUDGETED" as *u8) 234 pass = pass + bg_row("unbudgeted-named" as *u8, r) 235 236 // row 6: idempotent re-load (law 10): all 7 dedup vs store, nothing committed 237 let rc6: i64 = bg_run(bud, "load" as *u8, "/tmp/vzb_m1.txt" as *u8, pfx, "1009" as *u8, "/tmp/vzb_out6.txt" as *u8) 238 r = 0 239 if rc6 == 0 { 240 if bg_has("/tmp/vzb_out6.txt" as *u8, "VIZSLA-LEDGER-LOAD scanned=7 new=0 dup_infile=0 dup_instore=7 segment=none" as *u8) == 1 { r = 1 } 241 } 242 pass = pass + bg_row("idempotent-reload" as *u8, r) 243 244 // row 7: month2 is additive; ALL view shows combined hand totals 245 let rc7: i64 = bg_run(bud, "load" as *u8, "/tmp/vzb_m2.txt" as *u8, pfx, "1002" as *u8, "/tmp/vzb_out7.txt" as *u8) 246 var rc7b: i64 = 1 247 rc7b = bg_run(bud, "report" as *u8, pfx, fenv, 0 as *u8, "/tmp/vzb_out7b.txt" as *u8) 248 r = 0 249 if rc7 == 0 { if rc7b == 0 { 250 if bg_has("/tmp/vzb_out7.txt" as *u8, " new=3 " as *u8) == 1 { 251 if bg_has("/tmp/vzb_out7b.txt" as *u8, "VIZSLA-LEDGER-TOTAL txns=10 spent=269599" as *u8) == 1 { r = 1 } 252 } 253 } } 254 pass = pass + bg_row("additive-month2" as *u8, r) 255 256 // row 8: TIME TRAVEL -- asof=1001 AFTER month2 == the pre-month2 report, byte-identical 257 bg_run(bud, "report" as *u8, pfx, fenv, "1001" as *u8, "/tmp/vzb_out8.txt" as *u8) 258 r = bg_fileeq("/tmp/vzb_out3.txt" as *u8, "/tmp/vzb_out8.txt" as *u8) 259 pass = pass + bg_row("time-travel-asof" as *u8, r) 260 261 // row 9: determinism -- ALL report twice, byte-identical 262 bg_run(bud, "report" as *u8, pfx, fenv, 0 as *u8, "/tmp/vzb_out9.txt" as *u8) 263 r = bg_fileeq("/tmp/vzb_out7b.txt" as *u8, "/tmp/vzb_out9.txt" as *u8) 264 pass = pass + bg_row("determinism-byte-identical" as *u8, r) 265 266 // row 10: tamper-evidence baseline -- untouched store verifies CLEAN 267 let rc10: i64 = bg_run(bud, "verify" as *u8, pfx, 0 as *u8, 0 as *u8, "/tmp/vzb_out10.txt" as *u8) 268 r = 0 269 if rc10 == 0 { 270 if bg_has("/tmp/vzb_out10.txt" as *u8, "VIZSLA-LEDGER-VERIFY entries=10 bad=0 verdict=CLEAN" as *u8) == 1 { r = 1 } 271 } 272 pass = pass + bg_row("verify-clean" as *u8, r) 273 274 // row 11: TAMPER-EVIDENCE -- flip one byte inside seg-1002's last value 275 let tpath: *u8 = sys_mmap(256) 276 var to: i64 = 0 277 to = bg_cat(tpath, to, pfx) 278 to = bg_cat(tpath, to, "seg-1002.docs" as *u8) 279 tpath[to] = 0 as u8 280 bg_flip(tpath, 3) 281 let rc11: i64 = bg_run(bud, "verify" as *u8, pfx, 0 as *u8, 0 as *u8, "/tmp/vzb_out11.txt" as *u8) 282 r = 0 283 if rc11 == 1 { 284 if bg_has("/tmp/vzb_out11.txt" as *u8, "verdict=TAMPERED" as *u8) == 1 { 285 if bg_has("/tmp/vzb_out11.txt" as *u8, "VIZSLA-LEDGER-TAMPER segment=seg-1002" as *u8) == 1 { r = 1 } 286 } 287 } 288 pass = pass + bg_row("tamper-evidence-flipped-byte" as *u8, r) 289 290 let permil: i64 = (pass * 1000) / 11 291 let logfd: i64 = sys_openat_append("knowledge/status/vizsla_gate.log" as *u8, 0x1a4) 292 var fdi: i64 = 0 293 while fdi < 2 { 294 var fd: i64 = 1 295 if fdi == 1 { fd = logfd } 296 if fd > 0 { 297 let line: *u8 = sys_mmap(256) 298 var o: i64 = 0 299 o = bg_cat(line, o, "VIZSLA-BUDGET-GATE epoch=" as *u8) 300 o = bg_catn(line, o, sys_now_realtime_sec()) 301 o = bg_cat(line, o, " rows=11 pass=" as *u8) 302 o = bg_catn(line, o, pass) 303 o = bg_cat(line, o, " permil=" as *u8) 304 o = bg_catn(line, o, permil) 305 if pass == 11 { 306 o = bg_cat(line, o, " verdict=GREEN\n" as *u8) 307 } else { 308 o = bg_cat(line, o, " verdict=RED\n" as *u8) 309 } 310 sys_write(fd, line, o) 311 } 312 fdi = fdi + 1 313 } 314 if logfd > 0 { sys_close(logfd) } 315 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 316 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 317 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 318 let ctr__dry: *i64 = gv_ctr() 319 ctr__dry[0] = pass 320 ctr__dry[1] = 11 321 let rc__dry: i64 = gv_verdict("VIZSLA-BUDGET-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 322 sys_exit(rc__dry) 323 return rc__dry 324}