nx_unchoke_gate.nx source
↩ module page · 102 lines · 7677 B
1// nx_unchoke_gate.nx -- EXECUTION-level proof of the bulk unchoke-patience fix (the seedeval "IGNORING" bug:
2// holders exist but we never pulled). Drives the REAL worker child tg_from_peer against a seeder that stays
3// CHOKED for ~9s (past the old 6s bail) then unchokes. Pre-fix: the child bailed at 6s -> 0 pieces. Post-fix:
4// it keep-alives through the choke, catches the unchoke, downloads byte-perfect. Also exercises the untested
5// keep-alive path (must not corrupt the stream / hang). license_tier: ORIGINAL
6// depends: nx_torrent_get (tg_from_peer), nx_torrent_mkinfo (mk_build_info)
7import "nx_torrent_get.nx"
8import "nx_torrent_mkinfo.nx"
9
10func uc_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
11func uc_n(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } let t: *u8=sys_mmap(28); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } while k>0 { k=k-1; sys_write(1,(((t as i64)+k) as *u8),1) } return 0 }
12func uc_mkdir(p: *u8) -> i64 { __syscall(258, AT_FDCWD, p as i64, 0x1ff, 0, 0, 0); return 0 } // mkdirat 0777 (EEXIST harmless)
13func uc_unlink(p: *u8) -> i64 { __syscall(263, AT_FDCWD, p as i64, 0, 0, 0, 0); return 0 }
14func uc_writef(p: *u8, b: *u8, n: i64) -> i64 { let fd: i64=sys_openat_wr(p, 0x1a4); if fd<0 { return 0-1 } sys_write(fd, b, n); sys_close(fd); return 0 }
15
16// delayed-unchoke seeder: handshake, bitfield(all), STAY CHOKED `delay` s (draining the leecher's ext-handshake
17// + keep-alives), then UNCHOKE + serve requests from srcpath. Plaintext (the default worker path).
18func uc_serve(afd: i64, ih: *u8, spid: *u8, srcpath: *u8, plen: i64, total: i64, npc: i64, delay: i64) -> i64 {
19 let ctx: *MseCtx = sys_mmap(MSE_CTX_BYTES) as *MseCtx; mse_ctx_plain(ctx)
20 let ph: *u8 = sys_mmap(128); if mse_read(ctx, afd, ph, 68) != 68 { return 0 } // read leecher handshake
21 let ch: *u8 = sys_mmap(128); nx_pw_build_handshake(ih, spid, ch); ch[25]=(ch[25] as i64 | 0x10) as u8; mse_write(ctx, afd, ch, 68)
22 let nby: i64 = (npc+7)/8; let bf: *u8 = sys_mmap(nby+16); var z: i64=0; while z<nby { bf[z]=0 as u8; z=z+1 }
23 var p: i64=0; while p<npc { let by: i64=p/8; bf[by]=((bf[by] as i64) | (1 << (7-(p-by*8)))) as u8; p=p+1 }
24 let bm: *u8 = sys_mmap(nby+32); mse_write(ctx, afd, bm, nx_pw_build_msg(NX_PW_BITFIELD, bf, nby, bm)) // advertise all pieces
25 // STAY CHOKED for `delay` s, draining whatever the leecher sends (ext handshake + keep-alives)
26 sys_set_socket_timeout(afd, 1)
27 let t0: i64 = sys_now_realtime_sec()
28 while sys_now_realtime_sec() - t0 < delay { let junk: *u8 = sys_mmap(65536); tg_read_msg(ctx, afd, junk, 65536) }
29 let um: *u8 = sys_mmap(16); mse_write(ctx, afd, um, nx_pw_build_msg(NX_PW_UNCHOKE, 0 as *u8, 0, um)) // finally unchoke
30 // serve requests (bounded)
31 sys_set_socket_timeout(afd, 3)
32 let srcfd: i64 = sys_openat_rd(srcpath); var served: i64=0; var guard: i64=0
33 while guard < 300 {
34 let mb: *u8 = sys_mmap(65536); let ml: i64 = tg_read_msg(ctx, afd, mb, 65536)
35 if ml > 0 { if nx_pw_msg_id(mb) == NX_PW_REQUEST {
36 let idx: i64=_pw_get_u32(mb,5); let beg: i64=_pw_get_u32(mb,9); let ln: i64=_pw_get_u32(mb,13)
37 if ln>0 { if ln<=16384 {
38 let sp: *u8=sys_mmap(ln+32)
39 sp[0]=((idx>>24)&0xff) as u8; sp[1]=((idx>>16)&0xff) as u8; sp[2]=((idx>>8)&0xff) as u8; sp[3]=(idx&0xff) as u8
40 sp[4]=((beg>>24)&0xff) as u8; sp[5]=((beg>>16)&0xff) as u8; sp[6]=((beg>>8)&0xff) as u8; sp[7]=(beg&0xff) as u8
41 sys_lseek(srcfd, idx*plen+beg, 0); let got: i64=sys_read(srcfd, (sp as i64 + 8) as *u8, ln)
42 if got==ln { let sm: *u8=sys_mmap(ln+32); mse_write(ctx, afd, sm, nx_pw_build_msg(NX_PW_PIECE, sp, 8+ln, sm)); served=served+1 }
43 } }
44 } }
45 if ml < 0 { guard=guard+15 }
46 guard=guard+1
47 }
48 sys_close(srcfd); return served
49}
50
51func main() -> i64 {
52 uc_w("UNCHOKE-GATE authored=organ\n" as *u8)
53 let plen: i64=64; let total: i64=200; let npc: i64=4; let PORT: i64=53713; let DELAY: i64=9
54 uc_mkdir("/tmp/_nx_uc" as *u8)
55 let src: *u8 = "/tmp/_nx_uc/src.bin" as *u8
56 let outp: *u8 = "/tmp/_nx_uc/out.bin" as *u8
57 let donep: *u8 = "/tmp/_nx_uc/download.done" as *u8
58 let wantedp: *u8 = "/tmp/_nx_uc/download.wanted" as *u8
59 let npcp: *u8 = "/tmp/_nx_uc/download.npc" as *u8
60 let metap: *u8 = "/tmp/_nx_uc/download.meta" as *u8
61 let statp: *u8 = "/tmp/_nx_uc/download.status" as *u8
62 let ctlp: *u8 = "/tmp/_nx_uc/download.control" as *u8
63 let availp: *u8 = "/tmp/_nx_uc/download.avail" as *u8
64 let inflp: *u8 = "/tmp/_nx_uc/download.inflight" as *u8
65 let streamp: *u8 = "/tmp/_nx_uc/download.stream" as *u8
66 let pexp: *u8 = "/tmp/_nx_uc/download.pex" as *u8
67 uc_unlink(outp); uc_unlink(donep); uc_unlink(wantedp); uc_unlink(availp); uc_unlink(inflp); uc_unlink(streamp); uc_unlink(ctlp); uc_unlink(pexp); uc_unlink(npcp)
68 // build the source file + originate metadata (info dict + per-piece hashes + info_hash)
69 let fb: *u8 = sys_mmap(total+16); var i: i64=0; while i<total { fb[i]=((i*97+5)&0xff) as u8; i=i+1 }
70 uc_writef(src, fb, total)
71 let info: *u8 = sys_mmap(npc*20+8192); let pieces: *u8 = sys_mmap(npc*20+64); let ih: *u8 = sys_mmap(20)
72 let ilen: i64 = mk_build_info(src, "src.bin" as *u8, plen, total, npc, info, pieces, ih)
73 if ilen<=0 { uc_w("mk_build_info FAIL verdict=RED\n" as *u8); sys_exit(1); return 1 }
74 uc_writef(metap, info, ilen) // seed the metadata cache -> tg_from_peer skips ut_metadata (mdone immediately)
75 // listener
76 let sa: *u8 = sys_mmap(16); cl_sockaddr(sa, PORT, 127, 0, 0, 1)
77 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
78 let one: *u8 = sys_mmap(4); one[0]=1 as u8; sys_setsockopt(lfd, SOL_SOCKET, 2, one, 4)
79 if sys_bind(lfd, sa, 16) < 0 { uc_w("bind FAIL verdict=RED\n" as *u8); sys_exit(1); return 1 }
80 sys_listen(lfd, 4)
81 let spid: *u8 = sys_mmap(20); i=0; while i<20 { spid[i]=83 as u8; i=i+1 }
82 let lpid: *u8 = sys_mmap(20); i=0; while i<20 { lpid[i]=76 as u8; i=i+1 }
83 let pid: i64 = sys_fork()
84 if pid == 0 {
85 let afd: i64 = sys_accept(lfd); if afd>=0 { uc_serve(afd, ih, spid, src, plen, total, npc, DELAY); sys_close(afd) }
86 sys_close(lfd); sys_exit(0)
87 }
88 // parent = the REAL worker child, against 127.0.0.1:PORT
89 let ipv: i64 = (127<<24)|(0<<16)|(0<<8)|1
90 let t0: i64 = sys_now_realtime_sec()
91 let have: i64 = tg_from_peer(ipv, PORT, ih, lpid, outp, donep, npcp, metap, statp, ctlp, availp, inflp, streamp, pexp, 1)
92 let elapsed: i64 = sys_now_realtime_sec() - t0
93 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0); sys_close(lfd)
94 // verify: downloaded pieces + file byte-equal to source
95 let ob: *u8 = sys_mmap(total+64); let ofd: i64 = sys_openat_rd(outp); var on: i64=0; if ofd>=0 { on=sys_read(ofd, ob, total+64); sys_close(ofd) }
96 var file_eq: i64=1; if on<total { file_eq=0 } else { i=0; while i<total { if ob[i]!=fb[i] { file_eq=0; i=total } else { i=i+1 } } }
97 uc_w(" have=" as *u8); uc_n(have); uc_w(" elapsed=" as *u8); uc_n(elapsed); uc_w("s (waited past the old 6s bail) file_eq=" as *u8); uc_n(file_eq); uc_w("\n" as *u8)
98 if have>0 { if file_eq==1 { if elapsed>=DELAY {
99 uc_w("UNCHOKE-GATE verdict=GREEN (leecher WAITED through a 9s choke, caught the unchoke, downloaded byte-perfect)\n" as *u8); sys_exit(0); return 0
100 } } }
101 uc_w("UNCHOKE-GATE verdict=RED (pre-fix behavior: bailed before the unchoke)\n" as *u8); sys_exit(1); return 1
102}