code wiki / (root) / nx_trust_lib.nx

nx_trust_lib.nx source

↩ module page · 115 lines · 4091 B

1// nx_trust_lib.nx -- Nishi Office: client TRUST ACCOUNTING (F506, IOLTA / Utah RPC 1.15). 2// Append-only immutable trust ledger on the CID plane (history sacred = what bar audits want). 3// RPC 1.15 by construction: NO-COMMINGLING (per-client balances isolated), NO-OVERDRAW (a 4// disbursement exceeding a client's trust balance is REFUSED), THREE-WAY RECONCILIATION 5// (sum of client ledgers == trust account total; leg 3 = the bank statement a human matches). 6// Money = integer CENTS, no float (exact). DRY: reuses nx_matter_lib helpers. LIB (no main). 7// license_tier: ORIGINAL 8 9import "nx_matter_lib.nx" 10 11// parse a decimal string to i64 (cents) 12func tr_atoi(s: *u8) -> i64 { 13 var v: i64 = 0 14 var i: i64 = 0 15 while s[i] != (0 as u8) { 16 if s[i] >= 48 as u8 { if s[i] <= 57 as u8 { v = v * 10 + (s[i] - 48) } } 17 i = i + 1 18 } 19 return v 20} 21 22// format cents as "$X.XX" 23func tr_dollars(cents: i64, out: *u8) -> i64 { 24 var o: i64 = 0 25 out[o] = 36 as u8 26 o = o + 1 27 o = mt_catn(out, o, cents / 100) 28 out[o] = 46 as u8 29 o = o + 1 30 let c: i64 = cents % 100 31 out[o] = (48 + (c / 10)) as u8 32 o = o + 1 33 out[o] = (48 + (c % 10)) as u8 34 o = o + 1 35 out[o] = 0 as u8 36 return o 37} 38 39// append a trust ledger entry (deposit|disburse) for a client; key "<client>:<seq>". 40func trust_put(prefix: *u8, client: *u8, seq: i64, matter: *u8, ttype: *u8, cents: i64) -> i64 { 41 let id: *u8 = sys_mmap(128) 42 var o: i64 = mt_catcopy(id, 0, client) 43 id[o] = 58 as u8 44 o = o + 1 45 o = mt_catn(id, o, seq) 46 id[o] = 0 as u8 47 let amt: *u8 = sys_mmap(32) 48 var ao: i64 = mt_catn(amt, 0, cents) 49 amt[ao] = 0 as u8 50 let k: *i64 = sys_mmap(8 * 4) as *i64 51 let v: *i64 = sys_mmap(8 * 4) as *i64 52 k[0] = ("amount" as *u8) as i64 53 v[0] = amt as i64 54 k[1] = ("client" as *u8) as i64 55 v[1] = client as i64 56 k[2] = ("matter" as *u8) as i64 57 v[2] = matter as i64 58 k[3] = ("type" as *u8) as i64 59 v[3] = ttype as i64 60 let rec: *u8 = sys_mmap(1024) 61 let rl: i64 = canon_encode(k, v, 4, rec) 62 return reg_put(prefix, "trust:" as *u8, "trust:__idx__" as *u8, id, rec, rl) 63} 64 65// a client's trust balance in cents (sum deposits - disbursements) -- per-client, no commingling. 66func trust_balance(prefix: *u8, client: *u8) -> i64 { 67 let idx: *u8 = sys_mmap(1048576) 68 let ilen: i64 = reg_index(prefix, "trust:__idx__" as *u8, idx, 1048576) 69 let cp: *u8 = sys_mmap(128) 70 var cpl: i64 = mt_catcopy(cp, 0, client) 71 cp[cpl] = 58 as u8 72 cpl = cpl + 1 73 cp[cpl] = 0 as u8 74 let idb: *u8 = sys_mmap(256) 75 let po: *i64 = sys_mmap(16) as *i64 76 let lo: *i64 = sys_mmap(16) as *i64 77 let af: *u8 = sys_mmap(64) 78 let tf: *u8 = sys_mmap(32) 79 var bal: i64 = 0 80 var ls: i64 = 0 81 var i: i64 = 0 82 while i <= ilen { 83 var eol: i64 = 0 84 if i == ilen { eol = 1 } 85 if i < ilen { if idx[i] == 10 as u8 { eol = 1 } } 86 if eol == 1 { 87 if i > ls { 88 var c: i64 = 0 89 while ls + c < i { idb[c] = idx[ls + c]; c = c + 1 } 90 idb[c] = 0 as u8 91 var mtch: i64 = 1 92 var x: i64 = 0 93 while cp[x] != (0 as u8) { if idb[x] != cp[x] { mtch = 0 } x = x + 1 } 94 if mtch == 1 { 95 if reg_get(prefix, "trust:" as *u8, idb, po, lo) == 1 { 96 mt_field(po[0] as *u8, lo[0], "amount" as *u8, 6, af) 97 mt_field(po[0] as *u8, lo[0], "type" as *u8, 4, tf) 98 let amt: i64 = tr_atoi(af) 99 if mt_streq(tf, "deposit" as *u8) == 1 { bal = bal + amt } 100 if mt_streq(tf, "disburse" as *u8) == 1 { bal = bal - amt } 101 } 102 } 103 } 104 ls = i + 1 105 } 106 i = i + 1 107 } 108 return bal 109} 110 111// RPC 1.15 no-overdraw gate: 1 if client can disburse `cents`, else 0. 112func trust_disburse_ok(prefix: *u8, client: *u8, cents: i64) -> i64 { 113 if trust_balance(prefix, client) >= cents { return 1 } 114 return 0 115}