code wiki / _hdl_build / nx_pow_challenge.nx

nx_pow_challenge.nx source

↩ module page · 73 lines · 2864 B

1// nx_pow_challenge.nx -- proof-of-work client puzzle for the access wall's L7 DDoS shedding (hashcash; 2// Cloudflare "under attack"). Under high load/anomaly the server issues a fresh CHALLENGE + a difficulty D; 3// the client must find a SOLUTION such that sha256(challenge || solution) has >= D leading zero bits. The 4// server VERIFIES in O(1) (one sha256) but the client pays O(2^D) expected work -- the asymmetry that makes a 5// flood expensive for the attacker and a one-time cost for a legit client. Reuses the KAT-verified nx_sha256 6// (no rolled crypto). license_tier: ORIGINAL 7import "nx_sha256.nx" 8import "nx_syscalls.nx" 9 10// count leading zero BITS of a 32-byte hash. 11func pow_lzb(h: *u8) -> i64 { 12 var bits: i64 = 0 13 var i: i64 = 0 14 var go: i64 = 1 15 while go == 1 { 16 if i >= 32 { go = 0 } 17 else { 18 let byte: i64 = h[i] as i64 19 if byte == 0 { bits = bits + 8; i = i + 1 } 20 else { 21 var mask: i64 = 128 22 var inner: i64 = 1 23 while inner == 1 { 24 if mask == 0 { inner = 0 } 25 else { 26 if (byte & mask) == 0 { bits = bits + 1; mask = mask / 2 } 27 else { inner = 0 } 28 } 29 } 30 go = 0 31 } 32 } 33 } 34 return bits 35} 36 37// unsigned decimal -> string in out; returns length. 38func pow_u2s(v: i64, out: *u8) -> i64 { 39 if v == 0 { out[0] = 48 as u8; return 1 } 40 let t: *u8 = sys_mmap(28) 41 var m: i64 = v 42 var k: i64 = 0 43 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 44 var i: i64 = 0 45 while i < k { out[i] = t[k-1-i]; i = i + 1 } 46 return k 47} 48 49// VERIFY (server, O(1)): does sha256(challenge || solution) have >= difficulty leading zero bits? 1/0. 50func pow_verify(challenge: *u8, clen: i64, solution: *u8, slen: i64, difficulty: i64) -> i64 { 51 let buf: *u8 = sys_mmap(clen + slen + 16) 52 var i: i64 = 0 53 while i < clen { buf[i] = challenge[i]; i = i + 1 } 54 var j: i64 = 0 55 while j < slen { buf[clen + j] = solution[j]; j = j + 1 } 56 let h: *u8 = sys_mmap(32) 57 sha256_digest(buf, clen + slen, h) 58 if pow_lzb(h) >= difficulty { return 1 } 59 return 0 60} 61 62// SOLVE (client, O(2^D) expected): brute-force a numeric solution whose decimal string satisfies the puzzle. 63// writes the solution into sol_out, returns its length, or -1 if not found within max_tries (the server NEVER 64// runs this -- the cost asymmetry is the defense). 65func pow_solve(challenge: *u8, clen: i64, difficulty: i64, sol_out: *u8, max_tries: i64) -> i64 { 66 var nce: i64 = 0 67 while nce < max_tries { 68 let slen: i64 = pow_u2s(nce, sol_out) 69 if pow_verify(challenge, clen, sol_out, slen, difficulty) == 1 { return slen } 70 nce = nce + 1 71 } 72 return 0 - 1 73}