code wiki / (root) / nx_torrent_seed_gate.nx

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}