code wiki / _hdl_build / nx_mp_live_gate.nx
nx_mp_live_gate.nx source
↩ module page · 122 lines · 6157 B
1// nx_mp_live_gate.nx -- SOVEREIGN end-to-end LIVE proof of the running multiplayer daemon (no curl, no node):
2// a pure-syscall HTTP client POSTs two players' snapshots to the LIVE nx_mp_serve on 127.0.0.1:7702, then GETs
3// the roster and confirms the response body carries BOTH players' [len][frame] records. Proves the live relay
4// works (shared-mmap rr_post across forked handlers), not just that a page serves.
5// Run AFTER `nx_mp_up`: ./_offc/nx_sov_build_run.elf nx_mp_live_gate
6// license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
9import "nx_gate_emit_lib.nx"
10import "nx_gate_verdict.nx"
11
12func slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
13// connect a fresh TCP socket to 127.0.0.1:port; returns fd or -1
14func conn(port: i64) -> i64 {
15 let addr: *u8 = sys_mmap(16)
16 addr[0]=2 as u8; addr[1]=0 as u8
17 addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8
18 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8
19 var z: i64=8; while z<16 { addr[z]=0 as u8; z=z+1 }
20 let fd: i64 = sys_socket(2,1,0)
21 if fd<0 { return 0-1 }
22 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0-1 }
23 return fd
24}
25// POST a `flen`-byte snapshot to /pstate?r=<rm>&p=<pr>
26func post_pstate(port: i64, rm: i64, pr: i64, frame: *u8, flen: i64) -> i64 {
27 let fd: i64 = conn(port)
28 if fd < 0 { return 0-1 }
29 let req: *u8 = sys_mmap(256)
30 var o: i64 = 0
31 let h1: *u8 = "POST /pstate?r=" as *u8; var i: i64=0; while h1[i]!=(0 as u8){req[o]=h1[i];o=o+1;i=i+1}
32 var m: i64=rm; let t: *u8 = sys_mmap(16); var k: i64=0
33 if m==0 {t[0]=48 as u8;k=1} else { while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} }
34 var j: i64=k-1; while j>=0 {req[o]=t[j];o=o+1;j=j-1}
35 let h2: *u8 = "&p=" as *u8; i=0; while h2[i]!=(0 as u8){req[o]=h2[i];o=o+1;i=i+1}
36 m=pr;k=0; if m==0 {t[0]=48 as u8;k=1} else { while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} }
37 j=k-1; while j>=0 {req[o]=t[j];o=o+1;j=j-1}
38 let h3: *u8 = " HTTP/1.0\r\nContent-Length: 11\r\n\r\n" as *u8; i=0; while h3[i]!=(0 as u8){req[o]=h3[i];o=o+1;i=i+1}
39 var b: i64=0; while b<flen {req[o]=frame[b];o=o+1;b=b+1}
40 sys_write(fd, req, o)
41 let rb: *u8 = sys_mmap(256); sys_read(fd, rb, 255) // drain ack
42 sys_close(fd)
43 return 0
44}
45// GET /roster?r=<rm>&p=<self> into out; returns bytes read
46func get_roster(port: i64, rm: i64, self_pr: i64, out: *u8) -> i64 {
47 let fd: i64 = conn(port)
48 if fd < 0 { return 0-1 }
49 let req: *u8 = sys_mmap(256)
50 var o: i64 = 0
51 let h1: *u8 = "GET /roster?r=" as *u8; var i: i64=0; while h1[i]!=(0 as u8){req[o]=h1[i];o=o+1;i=i+1}
52 var m: i64=rm; let t: *u8 = sys_mmap(16); var k: i64=0
53 if m==0 {t[0]=48 as u8;k=1} else { while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} }
54 var j: i64=k-1; while j>=0 {req[o]=t[j];o=o+1;j=j-1}
55 let h2: *u8 = "&p=" as *u8; i=0; while h2[i]!=(0 as u8){req[o]=h2[i];o=o+1;i=i+1}
56 m=self_pr;k=0; if m==0 {t[0]=48 as u8;k=1} else { while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} }
57 j=k-1; while j>=0 {req[o]=t[j];o=o+1;j=j-1}
58 let h3: *u8 = " HTTP/1.0\r\nHost: x\r\n\r\n" as *u8; i=0; while h3[i]!=(0 as u8){req[o]=h3[i];o=o+1;i=i+1}
59 sys_write(fd, req, o)
60 let n: i64 = sys_read(fd, out, 4095)
61 sys_close(fd)
62 return n
63}
64// offset of the body (past "\r\n\r\n") in a response of length n
65func body_off(buf: *u8, n: i64) -> i64 {
66 var i: i64=0
67 while i+3 < n { if buf[i]==(13 as u8){if buf[i+1]==(10 as u8){if buf[i+2]==(13 as u8){if buf[i+3]==(10 as u8){return i+4}}}} i=i+1 }
68 return 0-1
69}
70// count [len][frame] records in a body
71func rec_count(body: *u8, total: i64) -> i64 {
72 var o: i64=0; var c: i64=0; var go: i64=1
73 while go==1 { if o>=total {go=0} else { let fl: i64=body[o] as i64; o=o+1+fl; c=c+1 } }
74 return c
75}
76
77func main() -> i64 {
78 g_puts("nx_mp_live gate (sovereign HTTP client vs the LIVE daemon on :7702, NO curl/node)\n" as *u8)
79 var pass: i64 = 0; var total: i64 = 0
80 let PORT: i64 = 7702
81 let RM: i64 = 99
82
83 // reachability
84 let probe: i64 = conn(PORT)
85 if probe < 0 { g_puts(" FAIL daemon not reachable on :7702 -- run nx_mp_up first\n" as *u8); g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1 }
86 sys_close(probe)
87
88 let fa: *u8 = sys_mmap(16); var i: i64=0; while i<11 { fa[i]=(100+i) as u8; i=i+1 }
89 let fb: *u8 = sys_mmap(16); i=0; while i<11 { fb[i]=(40+i) as u8; i=i+1 }
90 post_pstate(PORT, RM, 1, fa, 11)
91 post_pstate(PORT, RM, 2, fb, 11)
92
93 let out: *u8 = sys_mmap(4096)
94 let n: i64 = get_roster(PORT, RM, 9, out)
95 let bo: i64 = body_off(out, n)
96 var r1: i64 = 1
97 var nrec: i64 = 0
98 if bo < 0 { r1 = 0 } else {
99 let body: *u8 = (out + bo) as *u8
100 let blen: i64 = n - bo
101 nrec = rec_count(body, blen)
102 if nrec != 2 { r1 = 0 }
103 g_puts(" [measure] LIVE /roster body = " as *u8); g_pn(blen); g_puts(" bytes, " as *u8); g_pn(nrec); g_puts(" player records\n" as *u8)
104 }
105 pass = pass + g_check("LIVE relay: 2 players POSTed -> roster returns both (shared-mmap across forks)" as *u8, r1); total=total+1
106
107 // isolation: an empty room returns 0 records
108 let n2: i64 = get_roster(PORT, 12345, 9, out)
109 let bo2: i64 = body_off(out, n2)
110 pass = pass + g_check("LIVE room isolation: empty room -> 0 records (neg)" as *u8, (n2 - bo2) == 0); total=total+1
111
112 g_puts("---- mp-live gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
113 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
114 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
115 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
116 let ctr__dry: *i64 = gv_ctr()
117 ctr__dry[0] = pass
118 ctr__dry[1] = total
119 let rc__dry: i64 = gv_verdict("MP-LIVE-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
120 sys_exit(rc__dry)
121 return rc__dry
122}