code wiki / _hdl_build / nx_mp_daemon_gate.nx
nx_mp_daemon_gate.nx source
↩ module page · 105 lines · 5285 B
1// nx_mp_daemon_gate.nx -- proves the sovereign multiplayer daemon request-handler (nx_mp_daemon) over a relay.
2// NO sockets, NO js, NO curl: crafts real HTTP request bytes and drives mpd_pstate / mpd_roster directly.
3// 1) routing: /pstate->1, /roster->2, a file path->0 ; query ints parsed
4// 2) POST /pstate puts the body snapshot into the room (two players)
5// 3) GET /roster returns an HTTP response whose body = the OTHER players' [len][frame] records (byte-exact)
6// 4) room isolation (neg): /roster for a different room returns an empty body
7// Built nx_cc_sovereign -> nxasm_x86 (no gcc, no .sh). license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_gate_emit_lib.nx"
10import "nx_mp_daemon.nx" // brings nx_mp_relay + nx_room_relay transitively
11import "nx_gate_verdict.nx"
12
13func mk_post(req: *u8, rm: i64, pr: i64, frame: *u8, fl: i64) -> i64 {
14 var o: i64 = mpd_wstr(req, 0, "POST /pstate?r=" as *u8)
15 o = mpd_wn(req, o, rm)
16 o = mpd_wstr(req, o, "&p=" as *u8)
17 o = mpd_wn(req, o, pr)
18 o = mpd_wstr(req, o, " HTTP/1.1\r\nContent-Length: " as *u8)
19 o = mpd_wn(req, o, fl)
20 o = mpd_wstr(req, o, "\r\n\r\n" as *u8)
21 var k: i64 = 0
22 while k < fl { req[o + k] = frame[k]; k = k + 1 }
23 return o + fl
24}
25func rec_eq(body: *u8, o: i64, expect: *u8, n: i64) -> i64 {
26 if (body[o] as i64) != n { return 0 }
27 var k: i64 = 0
28 while k < n { if body[o + 1 + k] != expect[k] { return 0 } k = k + 1 }
29 return 1
30}
31
32func main() -> i64 {
33 g_puts("nx_mp_daemon gate (sovereign /pstate + /roster handler, NO sockets/js/curl)\n" as *u8)
34 var pass: i64 = 0; var total: i64 = 0
35
36 let N: i64 = 8
37 let SB: i64 = 64
38 let st: *i64 = sys_mmap((2 + 5*N) * 8) as *i64
39 let arena: *u8 = sys_mmap(N * SB)
40 rr_init(st, N, SB)
41 let path: *u8 = sys_mmap(1024)
42 let out: *u8 = sys_mmap(8192)
43 let req: *u8 = sys_mmap(8192)
44 let RM: i64 = 32368
45 let NOW: i64 = 1000
46
47 // 1) routing + query int parse
48 var r1: i64 = 1
49 mpd_path("POST /pstate?r=32368&p=100 HTTP/1.1\r\n\r\n" as *u8, 40, path)
50 if mpd_route(path) != 1 { r1 = 0 }
51 if mpd_qint(path, "r=" as *u8) != 32368 { r1 = 0 }
52 if mpd_qint(path, "p=" as *u8) != 100 { r1 = 0 }
53 mpd_path("GET /roster?r=5&p=9 HTTP/1.1\r\n\r\n" as *u8, 33, path)
54 if mpd_route(path) != 2 { r1 = 0 }
55 mpd_path("GET /pets3d.wasm HTTP/1.1\r\n\r\n" as *u8, 29, path)
56 if mpd_route(path) != 0 { r1 = 0 }
57 pass = pass + g_check("routing + query-int parse" as *u8, r1); total=total+1
58
59 // 2) POST /pstate for two players
60 let fa: *u8 = sys_mmap(64); var i: i64=0; while i<11 { fa[i]=(100+i) as u8; i=i+1 }
61 let fb: *u8 = sys_mmap(64); i=0; while i<11 { fb[i]=(40+i) as u8; i=i+1 }
62 let na: i64 = mk_post(req, RM, 100, fa, 11)
63 mpd_path(req, na, path); mpd_pstate(req, na, path, st, arena, SB, NOW, out)
64 let nb: i64 = mk_post(req, RM, 101, fb, 11)
65 mpd_path(req, nb, path); mpd_pstate(req, nb, path, st, arena, SB, NOW, out)
66 var r2: i64 = 1
67 if rr_live(st, RM, 100, NOW, 8000) < 0 { r2 = 0 }
68 if rr_live(st, RM, 101, NOW, 8000) < 0 { r2 = 0 }
69 pass = pass + g_check("POST /pstate stored both players in the room" as *u8, r2); total=total+1
70
71 // 3) GET /roster -> HTTP response whose body = the other players' records
72 var go: i64 = mpd_wstr(req, 0, "GET /roster?r=" as *u8)
73 go = mpd_wn(req, go, RM); go = mpd_wstr(req, go, "&p=999 HTTP/1.1\r\n\r\n" as *u8)
74 mpd_path(req, go, path)
75 let rlen: i64 = mpd_roster(path, st, arena, SB, NOW, 8000, out)
76 let bo: i64 = mpd_body_off(out, rlen) // skip the HTTP header
77 var r3: i64 = 1
78 if bo < 0 { r3 = 0 } else {
79 let body: *u8 = (out + bo) as *u8
80 let blen: i64 = rlen - bo
81 if mp_roster_count(body, blen) != 2 { r3 = 0 }
82 if rec_eq(body, 0, fa, 11) != 1 { r3 = 0 }
83 if rec_eq(body, 1 + 11, fb, 11) != 1 { r3 = 0 }
84 g_puts(" [measure] /roster response = " as *u8); g_pn(rlen); g_puts(" bytes (header + " as *u8); g_pn(blen); g_puts("-byte body, 2 records)\n" as *u8)
85 }
86 pass = pass + g_check("GET /roster body = other players' [len][frame] records, byte-exact" as *u8, r3); total=total+1
87
88 // 4) room isolation (neg): /roster for a different room -> empty body
89 var gx: i64 = mpd_wstr(req, 0, "GET /roster?r=11111&p=1 HTTP/1.1\r\n\r\n" as *u8)
90 mpd_path(req, gx, path)
91 let rl2: i64 = mpd_roster(path, st, arena, SB, NOW, 8000, out)
92 let bo2: i64 = mpd_body_off(out, rl2)
93 pass = pass + g_check("room isolation: empty roster body for another room (neg)" as *u8, (rl2 - bo2) == 0); total=total+1
94
95 g_puts("---- mp-daemon gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
96 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
97 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
98 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
99 let ctr__dry: *i64 = gv_ctr()
100 ctr__dry[0] = pass
101 ctr__dry[1] = total
102 let rc__dry: i64 = gv_verdict("MP-DAEMON-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
103 sys_exit(rc__dry)
104 return rc__dry
105}