code wiki / _hdl_build / nx_pub_receipt.nx

nx_pub_receipt.nx source

↩ module page · 111 lines · 7652 B

1// nx_pub_receipt.nx -- THE PUBLISH RECEIPT / RETURN-AND-REPORT, stored on the SOVEREIGN nishi substrate (operator: 2// "don't use tsv or any other non-nishi-ecosystem"; [[feedback-no-3rd-party-tsv-sovereign-store]]). Receipts no 3// longer live in a flat .tsv -- each receipt is a CANON-ENCODED record (nx_canon_cid) in a content-addressed, 4// append-only, crash-safe seg_store (nx_seg_store), keyed by the artifact (sha|dest). The additive seg_store gives 5// the audit history (every state is a preserved segment; ss_hget returns the latest) for free -- no foreign format 6// on the data path. Designed to the breadth the sovereign researcher surfaced (knowledge/fetched/rcpt_*.raw): 7// ACK/NACK, return-receipt(query), DSN states, idempotence, atomic-commit, dead-letter, audit-trail, provenance, 8// + Ed25519 non-repudiation (each receipt SIGNED over status|sha|dest|verify|ts). 9// fields: status, requester, sha, dest, site, verify, ts, sig 10import "nx_seg_store.nx" // ss_begin / ss_add / ss_commit / ss_open / ss_hget 11import "nx_canon_cid.nx" // canon_encode 12import "nx_uxf_decode.nx" // canon_decode 13import "nx_ed25519_signature.nx" // ed25519_sign_full / ed25519_verify_full 14import "nx_syscalls.nx" 15 16const RC_NONE: i64 = 0 17const RC_PUBLISHED: i64 = 1 18const RC_HELD: i64 = 2 19const RC_REJECTED: i64 = 3 20const RC_FAILED: i64 = 4 21 22func rc_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 23func rc_streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while 1==1 { let ca: i64=a[i] as i64; let cb: i64=b[i] as i64; if ca!=cb {return 0} if ca==0 {return 1} i=i+1 } return 1 } 24func rc_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i]; o=o+1; i=i+1} return o } 25func rc_hexenc(inp: *u8, n: i64, out: *u8) -> i64 { let hx: *u8="0123456789abcdef" as *u8; var i: i64=0; while i<n { out[i*2]=hx[((inp[i] as i64)>>4)&15]; out[i*2+1]=hx[(inp[i] as i64)&15]; i=i+1 } out[n*2]=0 as u8; return n*2 } 26func rc_unhex1(c: i64) -> i64 { if c>=48 { if c<=57 {return c-48} } if c>=97 { if c<=102 {return c-87} } if c>=65 { if c<=70 {return c-55} } return 0 } 27func rc_unhex(inp: *u8, n: i64, out: *u8) -> i64 { var i: i64=0; while i<n { out[i]=((rc_unhex1(inp[i*2] as i64)<<4)|rc_unhex1(inp[i*2+1] as i64)) as u8; i=i+1 } return n } 28func rc_itoa(v: i64, out: *u8) -> i64 { let t: *u8=sys_mmap(28); var m: i64=v; 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} var i: i64=0; while i<k{out[i]=t[k-1-i];i=i+1} out[k]=0 as u8; return k } 29func rc_code_of(status: *u8) -> i64 { if rc_streq(status,"PUBLISHED" as *u8)==1{return 1} if rc_streq(status,"HELD" as *u8)==1{return 2} if rc_streq(status,"REJECTED" as *u8)==1{return 3} if rc_streq(status,"FAILED" as *u8)==1{return 4} return 0 } 30 31// the seg_store record key = "sha|dest" (the artifact's identity; provenance by construction). 32func rcpt_key(sha: *u8, dest: *u8, out: *u8) -> i64 { var o: i64=rc_cat(out,0,sha); out[o]=124 as u8; o=o+1; o=rc_cat(out,o,dest); out[o]=0 as u8; return o } 33// canonical signed binding: status|sha|dest|verify|ts. 34func rc_bind(buf: *u8, status: *u8, sha: *u8, dest: *u8, verify: *u8, tsstr: *u8) -> i64 { 35 var o: i64=0 36 o=rc_cat(buf,o,status); buf[o]=124 as u8;o=o+1 37 o=rc_cat(buf,o,sha); buf[o]=124 as u8;o=o+1 38 o=rc_cat(buf,o,dest); buf[o]=124 as u8;o=o+1 39 o=rc_cat(buf,o,verify); buf[o]=124 as u8;o=o+1 40 o=rc_cat(buf,o,tsstr); return o 41} 42func rc_sign(priv: *u8, status: *u8, sha: *u8, dest: *u8, verify: *u8, tsstr: *u8, out_sighex: *u8) -> i64 { 43 let b: *u8=sys_mmap(2048); let n: i64=rc_bind(b, status, sha, dest, verify, tsstr) 44 let sig: *u8=sys_mmap(64); ed25519_sign_full(priv, b, n, sig) 45 return rc_hexenc(sig, 64, out_sighex) 46} 47 48// read the latest receipt record for (sha,dest) from the seg_store -> decode into out_keys/out_vals. returns nf (>0) 49// or <=0 if absent. The additive store returns the LATEST committed value for the key (status transitions resolve). 50func rcpt_get(prefix: *u8, sha: *u8, dest: *u8, out_keys: *i64, out_vals: *i64, maxf: i64) -> i64 { 51 let h: *i64 = ss_open(prefix) 52 if (h as i64)==0 { return 0 } 53 let key: *u8=sys_mmap(512); rcpt_key(sha, dest, key) 54 let pp: *i64=sys_mmap(16) as *i64; let ll: *i64=sys_mmap(16) as *i64 55 if ss_hget(h, key, pp, ll) != 1 { return 0 } 56 return canon_decode(pp[0] as *u8, ll[0], out_keys, out_vals, maxf) 57} 58func rcpt_field(out_keys: *i64, out_vals: *i64, nf: i64, name: *u8) -> *u8 { 59 var i: i64=0; while i<nf { if rc_streq(out_keys[i] as *u8, name)==1 { return out_vals[i] as *u8 } i=i+1 } return 0 as *u8 60} 61// the latest status CODE for (sha,dest); RC_NONE if no receipt (so the submitter never gets a false "published"). 62func rcpt_status_code(prefix: *u8, sha: *u8, dest: *u8) -> i64 { 63 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 64 let nf: i64=rcpt_get(prefix, sha, dest, ok, ov, 64) 65 if nf<=0 { return RC_NONE } 66 let st: *u8=rcpt_field(ok, ov, nf, "status" as *u8) 67 if (st as i64)==0 { return RC_NONE } 68 return rc_code_of(st) 69} 70func rcpt_is_published(prefix: *u8, sha: *u8, dest: *u8) -> i64 { if rcpt_status_code(prefix, sha, dest)==RC_PUBLISHED {return 1} return 0 } 71 72// EMIT a signed receipt as a canon record in the seg_store. IDEMPOTENT (#10): if the latest status for (sha,dest) is 73// already this status, skip. Atomic + crash-safe (seg_store rename-commit). segid must be UNIQUE per commit (caller 74// passes a monotonic value, e.g. ts). returns 1 if emitted, 0 if skipped. 75func rcpt_emit(prefix: *u8, priv: *u8, status: *u8, requester: *u8, sha: *u8, dest: *u8, site: *u8, verify: *u8, ts: i64, segid: i64) -> i64 { 76 if rcpt_status_code(prefix, sha, dest)==rc_code_of(status) { return 0 } 77 let tsstr: *u8=sys_mmap(28); rc_itoa(ts, tsstr) 78 let sighex: *u8=sys_mmap(160); rc_sign(priv, status, sha, dest, verify, tsstr, sighex) 79 let keys: *i64=sys_mmap(8*9) as *i64; let vals: *i64=sys_mmap(8*9) as *i64 80 keys[0]="status" as *u8 as i64; vals[0]=status as i64 81 keys[1]="requester" as *u8 as i64; vals[1]=requester as i64 82 keys[2]="sha" as *u8 as i64; vals[2]=sha as i64 83 keys[3]="dest" as *u8 as i64; vals[3]=dest as i64 84 keys[4]="site" as *u8 as i64; vals[4]=site as i64 85 keys[5]="verify" as *u8 as i64; vals[5]=verify as i64 86 keys[6]="ts" as *u8 as i64; vals[6]=tsstr as i64 87 keys[7]="sig" as *u8 as i64; vals[7]=sighex as i64 88 let buf: *u8=sys_mmap(8192); let blen: i64=canon_encode(keys, vals, 8, buf) 89 let key: *u8=sys_mmap(512); rcpt_key(sha, dest, key) 90 let w: *i64=ss_begin() 91 ss_add(w, 1, key, buf, blen) 92 ss_commit(prefix, w, segid) 93 return 1 94} 95 96// VERIFY the latest receipt's Ed25519 signature with the publisher pubkey (NON-REPUDIATION). rebuilds the binding 97// from the stored canon fields. 1 iff the publisher signed exactly these (status,sha,dest,verify,ts); else 0. 98func rcpt_verify(prefix: *u8, pub: *u8, sha: *u8, dest: *u8) -> i64 { 99 let ok: *i64=sys_mmap(64*8) as *i64; let ov: *i64=sys_mmap(64*8) as *i64 100 let nf: i64=rcpt_get(prefix, sha, dest, ok, ov, 64) 101 if nf<=0 { return 0 } 102 let st: *u8=rcpt_field(ok,ov,nf,"status" as *u8) 103 let vf: *u8=rcpt_field(ok,ov,nf,"verify" as *u8) 104 let tss: *u8=rcpt_field(ok,ov,nf,"ts" as *u8) 105 let sg: *u8=rcpt_field(ok,ov,nf,"sig" as *u8) 106 if (st as i64)==0 { return 0 } 107 if (sg as i64)==0 { return 0 } 108 let b: *u8=sys_mmap(2048); let n: i64=rc_bind(b, st, sha, dest, vf, tss) 109 let sig: *u8=sys_mmap(64); rc_unhex(sg, 64, sig) 110 return ed25519_verify_full(pub, b, n, sig) 111}