nx_torrent_seed_gate.nx source
↩ module page · 166 lines · 9319 B
1// nx_torrent_seed_gate.nx -- SOVEREIGN LOOPBACK GATE for the BitTorrent SEEDER (nx_torrent_seed).
2//
3// Proves the missing UPLOAD half end-to-end: a real leecher connects to OUR seeder and pulls VERIFIED
4// pieces off it. This is the exact inverse of nx_peer_download (which proved we can leech): here WE are
5// the source. bind-before-fork (no race); child = the seed core `ts_serve_peer` over a real temp file;
6// parent = a mock leecher that handshakes, reads our BITFIELD (all-present) + UNCHOKE, REQUESTs a piece
7// in 16-byte blocks, assembles it, and asserts (a) the bytes == the file's bytes, (b) sha1(piece) ==
8// sha1(file slice) [the BitTorrent integrity guarantee], and (c) a request past EOF on the last, partial
9// piece is CLAMPED to the real length. Deterministic; no external net; no-hang (socket timeouts).
10//
11// Run: ./_offc/nx_sov_build_run.elf nx_torrent_seed_gate (WOMB builds + runs; main() = the gate)
12// license_tier: ORIGINAL module: nishi-core.torrent.seed_gate
13// depends: nishi-core.torrent.seed, nishi-core.torrent.peerwire, nishi-core.crypto.sha1
14import "nx_torrent_seed.nx"
15import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
16import "nx_peerwire.nx"
17import "nx_sha1.nx"
18import "nx_gate_verdict.nx"
19
20const G_PLEN: i64 = 64
21const G_NPC: i64 = 3
22const G_TOTAL: i64 = 160 // 64 + 64 + 32 (last piece is PARTIAL -> exercises EOF clamp)
23const G_BLK: i64 = 16
24const G_PATH: *u8 = "/tmp/_nx_seed_test.bin" as *u8
25
26func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
27func g_wn(v: i64) -> i64 {
28 let t: *u8=sys_mmap(28); var m: i64=v; if m<0 {m=0-m; sys_write(1,"-" as *u8,1)}
29 var k: i64=0; if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1}
30 let b: *u8=sys_mmap(28); var i: i64=0; while i<k {b[i]=t[k-1-i]; i=i+1}; sys_write(1,b,k); return 0
31}
32
33func main() -> i64 {
34 let PORT: i64 = 53511
35 // --- deterministic file content (whole file kept in memory for comparison) ---
36 let filebuf: *u8 = sys_mmap(G_TOTAL + 16)
37 var i: i64 = 0
38 while i < G_TOTAL { filebuf[i] = ((i*131 + 17) & 0xff) as u8; i = i + 1 }
39 let wf: i64 = sys_openat_wr(G_PATH, 0x1a4)
40 if wf < 0 { g_w("SEED-GATE authored=organ file_write=FAIL verdict=RED\n" as *u8); sys_exit(1); return 1 }
41 sys_write(wf, filebuf, G_TOTAL); sys_close(wf)
42
43 // info_hash both sides agree on (20 bytes) + peer ids
44 let ih: *u8 = sys_mmap(20); i = 0; while i < 20 { ih[i] = (i+1) as u8; i = i + 1 }
45 let seed_pid: *u8 = sys_mmap(20); i = 0; while i < 20 { seed_pid[i] = 83 as u8; i = i + 1 } // 'S'
46 let leech_pid: *u8 = sys_mmap(20); i = 0; while i < 20 { leech_pid[i] = 76 as u8; i = i + 1 } // 'L'
47
48 let sa: *u8 = sys_mmap(16); ts_sockaddr(sa, PORT, 127, 0, 0, 1)
49 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
50 var bound: i64 = 0
51 if lfd >= 0 {
52 let one: *u8 = sys_mmap(4); one[0]=1 as u8; one[1]=0 as u8; one[2]=0 as u8; one[3]=0 as u8
53 sys_setsockopt(lfd, SOL_SOCKET, 2, one, 4)
54 sys_set_socket_timeout(lfd, 5)
55 if sys_bind(lfd, sa, 16) >= 0 { if sys_listen(lfd, 4) >= 0 { bound = 1 } }
56 }
57 if bound == 0 { g_w("SEED-GATE authored=organ bound=0 verdict=RED\n" as *u8); sys_exit(1); return 1 }
58
59 // ---- CHILD = the SEEDER (our organ under test) ----
60 let pid: i64 = sys_fork()
61 if pid == 0 {
62 let afd: i64 = sys_accept(lfd)
63 if afd >= 0 { ts_serve_peer(afd, ih, seed_pid, G_PATH, G_PLEN, G_TOTAL, G_NPC); sys_close(afd) }
64 sys_close(lfd)
65 sys_exit(0)
66 }
67
68 // ---- PARENT = the LEECHER (pulls from us + verifies) ----
69 let cfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
70 sys_set_socket_timeout(cfd, 5)
71 var conn_ok: i64=0; var hs_ok: i64=0; var bf_ok: i64=0; var unchoked: i64=0
72 var blocks_ok: i64=0; var sha_eq: i64=0; var disk_eq: i64=0; var clamp_ok: i64=0
73 if nx_connect_bounded(cfd, sa, 16, NX_CONN_DEFAULT_MS) >= 0 { conn_ok = 1 }
74 if conn_ok == 1 {
75 let ch: *u8 = sys_mmap(128); nx_pw_build_handshake(ih, leech_pid, ch); ts_write_n(cfd, ch, 68)
76 let ph: *u8 = sys_mmap(128)
77 if ts_read_n(cfd, ph, 68) == 68 {
78 let gih: *u8 = sys_mmap(20); let gpid: *u8 = sys_mmap(20)
79 if nx_pw_parse_handshake(ph, 68, gih, gpid) == 1 { hs_ok = 1 }
80 }
81 if hs_ok == 1 {
82 // announce interest (a real leecher does; the seed already unchokes)
83 let im: *u8 = sys_mmap(16); let il: i64 = nx_pw_build_msg(NX_PW_INTERESTED, 0 as *u8, 0, im); ts_write_n(cfd, im, il)
84 // read messages until unchoked, capturing the bitfield
85 let mb: *u8 = sys_mmap(4096)
86 var guard: i64 = 0
87 while unchoked == 0 { if guard >= 8 { unchoked = 0-9 } else {
88 let mlen: i64 = ts_read_msg(cfd, mb, 4096)
89 if mlen == 0-1 { guard = 99 } else { if mlen > 0 {
90 let id: i64 = nx_pw_msg_id(mb)
91 if id == NX_PW_BITFIELD {
92 let bfp: *u8 = mb + 5; let bflen: i64 = mlen - 1
93 var all: i64 = 1
94 var p: i64 = 0
95 while p < G_NPC { if nx_pw_bitfield_has(bfp, bflen, p) != 1 { all = 0 } p = p + 1 }
96 bf_ok = all
97 }
98 if id == NX_PW_UNCHOKE { unchoked = 1 }
99 } }
100 guard = guard + 1
101 } }
102 if unchoked == 1 {
103 // request piece 1 (a FULL, interior piece) in 16-byte blocks
104 let piece1: *u8 = sys_mmap(G_PLEN + 16)
105 let nblk: i64 = G_PLEN / G_BLK
106 var got: i64 = 1
107 var bi: i64 = 0
108 while bi < nblk {
109 let rq: *u8 = sys_mmap(32); let rl: i64 = nx_pw_build_request(1, bi*G_BLK, G_BLK, rq); ts_write_n(cfd, rq, rl)
110 let pb: *u8 = sys_mmap(256); let plen: i64 = ts_read_msg(cfd, pb, 256)
111 if plen >= 9 { if nx_pw_msg_id(pb) == NX_PW_PIECE {
112 let begin: i64 = _pw_get_u32(pb, 9)
113 var j: i64 = 0; while j < G_BLK { piece1[begin + j] = pb[13 + j]; j = j + 1 }
114 } else { got = 0 } } else { got = 0 }
115 bi = bi + 1
116 }
117 blocks_ok = got
118 if blocks_ok == 1 {
119 // (a) received bytes == the file's bytes for piece 1 (offset 64..128)
120 var de: i64 = 1; var j: i64 = 0
121 while j < G_PLEN { if piece1[j] != filebuf[G_PLEN + j] { de = 0; j = G_PLEN } else { j = j + 1 } }
122 disk_eq = de
123 // (b) sha1(received piece) == sha1(true file slice) -- the integrity guarantee
124 let gh: *u8 = sys_mmap(20); let eh: *u8 = sys_mmap(20)
125 let slice: *u8 = filebuf + G_PLEN
126 sha1(piece1, G_PLEN, gh); sha1(slice, G_PLEN, eh)
127 var se: i64 = 1; j = 0
128 while j < 20 { if gh[j] != eh[j] { se = 0; j = 20 } else { j = j + 1 } }
129 sha_eq = se
130 }
131 // (c) LAST piece (index 2) is partial (32B). Ask for 64 -> seeder must CLAMP to 32.
132 let rq2: *u8 = sys_mmap(32); let rl2: i64 = nx_pw_build_request(2, 0, 64, rq2); ts_write_n(cfd, rq2, rl2)
133 let pb2: *u8 = sys_mmap(256); let plen2: i64 = ts_read_msg(cfd, pb2, 256)
134 if plen2 >= 9 { if nx_pw_msg_id(pb2) == NX_PW_PIECE {
135 let blklen: i64 = plen2 - 9 // payload = id(1)+index(4)+begin(4)+block ; mlen = 1+8+block
136 if blklen == (G_TOTAL - 2*G_PLEN) { // == 32
137 var ce: i64 = 1; var j2: i64 = 0
138 while j2 < blklen { if pb2[13 + j2] != filebuf[2*G_PLEN + j2] { ce = 0; j2 = blklen } else { j2 = j2 + 1 } }
139 clamp_ok = ce
140 }
141 } }
142 }
143 }
144 }
145 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0); sys_close(cfd); sys_close(lfd)
146
147 g_w("SEED-GATE authored=organ conn_ok=" as *u8); g_wn(conn_ok)
148 g_w(" handshake_ok=" as *u8); g_wn(hs_ok)
149 g_w(" bitfield_all=" as *u8); g_wn(bf_ok)
150 g_w(" unchoked=" as *u8); g_wn(unchoked)
151 g_w(" blocks_ok=" as *u8); g_wn(blocks_ok)
152 g_w(" bytes_match_file=" as *u8); g_wn(disk_eq)
153 g_w(" sha1_verify=" as *u8); g_wn(sha_eq)
154 g_w(" eof_clamp_ok=" as *u8); g_wn(clamp_ok)
155 var allok: i64 = 0
156 if conn_ok==1 { if hs_ok==1 { if bf_ok==1 { if unchoked==1 { if blocks_ok==1 { if disk_eq==1 { if sha_eq==1 { if clamp_ok==1 { allok = 1 } } } } } } } }
157 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
158 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
159 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
160 let ctr__dry: *i64 = gv_ctr()
161 ctr__dry[0] = allok
162 ctr__dry[1] = 1
163 let rc__dry: i64 = gv_verdict("TORRENT-SEED-GATE" as *u8, ctr__dry, "a real leecher pulled sha1-verified pieces from OUR seeder)" as *u8)
164 sys_exit(rc__dry)
165 return rc__dry
166}