code wiki / (root) / nx_commons_ledger.nx

nx_commons_ledger.nx source

↩ module page · 221 lines · 9575 B

1// nx_commons_ledger.nx -- the LEDGER->STANDING bridge. Turns a plane of transfer rows into the 2// per-member quantities nx_commons_lib scores. 3// 4// WHY: nx_mycorrhiza held flows in sys_mmap and nothing else. A currency whose record of who-gave-what 5// dies with the process is not a currency. Persistence itself is SOLVED and not rebuilt here -- 6// nx_plane_append is the known good (original bytes held for restore, absent plane REFUSED rather than 7// conjured, prefix-preservation checked BEFORE commit, row count re-verified AFTER, and it states its 8// own honest limit: it serialises only the appends made through it). This organ supplies the two things 9// that were missing: the row GRAMMAR and the AGGREGATOR. 10// 11// ★★★★★★ THE REAL WIN: TWO OF THE BENCH'S MAGIC INPUTS BECOME DERIVED QUANTITIES. 12// nx_commonsbench_gate had to be TOLD each member's passed_outside and outside_partners. A number a 13// fixture hands you is an assumption; a number the ledger computes is a measurement. From transfer 14// rows alone: 15// gave[i] = sum of units where i is the giver 16// took[i] = sum of units where i is the receiver 17// passed[i] = units i gave to parties i NEVER RECEIVED FROM -- onward circulation, not return trips 18// partners[i] = distinct counterparties 19// The passed[] rule is what makes the sybil ring fall out of the arithmetic instead of being asserted: 20// a ring trades 2000 in and 2000 out but every counterparty is one it also received from, so its 21// onward circulation is ZERO by measurement. A janitor gives to twelve people he never took from, so 22// nearly all of his giving counts. NOBODY HAD TO LABEL EITHER OF THEM. 23// 24// IDEMPOTENCY (law 10): appending the same contribution twice would mint standing from nothing. Dedup 25// is by record id, and the comparator and the writer share ONE canonical-form function by construction 26// -- the estate has already paid for the alternative, where nx_debt's deduper compared the raw text 27// while its writer appended a suffix afterwards, so the two never converged. 28// 29// Integer only. license_tier: ORIGINAL No hw writes (Rule 26). 30import "nx_syscalls.nx" 31const CL_MAGIC_1469598103934665603: i64 = 1469598103934665603 32 33const CL_MAX_M: i64 = 64 // members per aggregation window 34const CL_NOSLOT: i64 = 0 - 1 35 36// ---- canonical form: the ONE definition both the writer and the comparator use ---------------- 37// A contribution is identified by (giver, receiver, kind, units, at). Two rows with the same key are 38// the same event recorded twice, not two events. Exposed so no caller is tempted to reinvent it. 39func cl_row_key(giver: i64, receiver: i64, kind: i64, units: i64, at: i64) -> i64 { 40 var h: i64 = CL_MAGIC_1469598103934665603 41 h = h * 31 + giver 42 h = h * 31 + receiver 43 h = h * 31 + kind 44 h = h * 31 + units 45 h = h * 31 + at 46 if h < 0 { h = 0 - h } 47 return h 48} 49 50// Is row i a duplicate of an EARLIER row? First occurrence wins; history is additive, never rewritten. 51func cl_is_dup(keys: *i64, i: i64) -> i64 { 52 var j: i64 = 0 53 while j < i { 54 if keys[j] == keys[i] { return 1 } 55 j = j + 1 56 } 57 return 0 58} 59 60func cl_slot(ids: *i64, m: i64, id: i64) -> i64 { 61 var i: i64 = 0 62 while i < m { if ids[i] == id { return i } i = i + 1 } 63 return CL_NOSLOT 64} 65 66// ---- ROW GRAMMAR: emitter and parser live TOGETHER --------------------------------------------- 67// Deliberately in one file. A writer and a reader that disagree about canonical form never converge, 68// and this estate has already paid for that twice (nx_debt's deduper vs its writer; the tree-canon 69// comparator vs its manifest). Split these across two organs and the ledger silently rots. 70// Grammar: <giver>\t<receiver>\t<kind>\t<units>\t<at>\n 71const CL_TAB: i64 = 9 72const CL_NL: i64 = 10 73const CL_MINUS: i64 = 45 74const CL_ZERO: i64 = 48 75 76func cl_ndigits(m: i64) -> i64 { 77 if m == 0 { return 1 } 78 var c: i64 = 0 79 var v: i64 = m 80 while v > 0 { c = c + 1; v = v / 10 } 81 return c 82} 83 84// ALLOCATION-FREE on purpose. The obvious version mmaps a scratch buffer per call and never frees it; 85// at page granularity that is 4096B leaked PER CALL, the exact defect that took 28.5GB of a 36GB host. 86func cl_putn(dst: *u8, off: i64, v: i64) -> i64 { 87 var m: i64 = v 88 var p: i64 = off 89 if m < 0 { dst[p] = CL_MINUS as u8; p = p + 1; m = 0 - m } 90 let d: i64 = cl_ndigits(m) 91 var i: i64 = d - 1 92 var mm: i64 = m 93 while i >= 0 { dst[p + i] = (CL_ZERO + (mm % 10)) as u8; mm = mm / 10; i = i - 1 } 94 return p + d 95} 96 97func cl_emit_row(giver: i64, receiver: i64, kind: i64, units: i64, at: i64, 98 dst: *u8, off: i64) -> i64 { 99 var p: i64 = cl_putn(dst, off, giver) 100 dst[p] = CL_TAB as u8; p = p + 1 101 p = cl_putn(dst, p, receiver) 102 dst[p] = CL_TAB as u8; p = p + 1 103 p = cl_putn(dst, p, kind) 104 dst[p] = CL_TAB as u8; p = p + 1 105 p = cl_putn(dst, p, units) 106 dst[p] = CL_TAB as u8; p = p + 1 107 p = cl_putn(dst, p, at) 108 dst[p] = (0 as u8) 109 return p 110} 111 112// read one integer field, advancing pos past the delimiter 113func cl_field(buf: *u8, n: i64, pos: *i64) -> i64 { 114 var p: i64 = pos[0] 115 var neg: i64 = 0 116 if p < n { if buf[p] == (CL_MINUS as u8) { neg = 1; p = p + 1 } } 117 var v: i64 = 0 118 while p < n { 119 let c: i64 = buf[p] as i64 120 if c < CL_ZERO { p = p + 1; pos[0] = p; if neg == 1 { return 0 - v } return v } 121 if c > CL_ZERO + 9 { p = p + 1; pos[0] = p; if neg == 1 { return 0 - v } return v } 122 v = v * 10 + (c - CL_ZERO) 123 p = p + 1 124 } 125 pos[0] = p 126 if neg == 1 { return 0 - v } 127 return v 128} 129 130// Parse a loaded plane back into row arrays. Rows the grammar cannot read are SKIPPED, never guessed: 131// a ledger that invents a value it could not parse is worse than one that reports fewer rows. 132func cl_parse_rows(buf: *u8, n: i64, giver: *i64, receiver: *i64, units: *i64, at: *i64, 133 keys: *i64, maxrows: i64) -> i64 { 134 let pos: *i64 = sys_mmap(8) as *i64 135 pos[0] = 0 136 var r: i64 = 0 137 while r < maxrows { 138 if pos[0] >= n { return r } 139 let g: i64 = cl_field(buf, n, pos) 140 if pos[0] >= n { return r } 141 let c: i64 = cl_field(buf, n, pos) 142 let k: i64 = cl_field(buf, n, pos) 143 let u: i64 = cl_field(buf, n, pos) 144 let a: i64 = cl_field(buf, n, pos) 145 if g > 0 { 146 if c > 0 { 147 giver[r] = g; receiver[r] = c; units[r] = u; at[r] = a 148 keys[r] = cl_row_key(g, c, k, u, a) 149 r = r + 1 150 } 151 } 152 } 153 return r 154} 155 156// ---- the aggregator --------------------------------------------------------------------------- 157// rows: giver[], receiver[], units[], keys[] of length n. members: ids[] of length m. 158// Fills gave/took/passed/partners. Duplicate keys are counted ONCE. Returns rows actually counted. 159func cl_aggregate(giver: *i64, receiver: *i64, units: *i64, keys: *i64, n: i64, 160 ids: *i64, m: i64, 161 gave: *i64, took: *i64, passed: *i64, partners: *i64) -> i64 { 162 if m > CL_MAX_M { return 0 } 163 var i: i64 = 0 164 while i < m { gave[i] = 0; took[i] = 0; passed[i] = 0; partners[i] = 0; i = i + 1 } 165 let recv_from: *u8 = sys_mmap(m * m) 166 let seen: *u8 = sys_mmap(m * m) 167 var z: i64 = 0 168 while z < m * m { recv_from[z] = 0 as u8; seen[z] = 0 as u8; z = z + 1 } 169 170 // PASS 1 -- totals and the received-from relation. Both passes skip duplicates identically; a 171 // deduper that ran in only one pass would leave the two halves disagreeing about what happened. 172 var counted: i64 = 0 173 var r: i64 = 0 174 while r < n { 175 if cl_is_dup(keys, r) == 0 { 176 let g: i64 = cl_slot(ids, m, giver[r]) 177 let c: i64 = cl_slot(ids, m, receiver[r]) 178 if g != CL_NOSLOT { 179 if c != CL_NOSLOT { 180 gave[g] = gave[g] + units[r] 181 took[c] = took[c] + units[r] 182 recv_from[c * m + g] = 1 as u8 183 if seen[g * m + c] == (0 as u8) { seen[g * m + c] = 1 as u8; partners[g] = partners[g] + 1 } 184 if seen[c * m + g] == (0 as u8) { seen[c * m + g] = 1 as u8; partners[c] = partners[c] + 1 } 185 counted = counted + 1 186 } 187 } 188 } 189 r = r + 1 190 } 191 192 // PASS 2 -- onward circulation. Giving to someone you never took from is passing value ON; 193 // giving back to a source is a return trip, which is what a closed ring does all day. 194 var r2: i64 = 0 195 while r2 < n { 196 if cl_is_dup(keys, r2) == 0 { 197 let g2: i64 = cl_slot(ids, m, giver[r2]) 198 let c2: i64 = cl_slot(ids, m, receiver[r2]) 199 if g2 != CL_NOSLOT { 200 if c2 != CL_NOSLOT { 201 if recv_from[g2 * m + c2] == (0 as u8) { passed[g2] = passed[g2] + units[r2] } 202 } 203 } 204 } 205 r2 = r2 + 1 206 } 207 return counted 208} 209 210// ---- conservation ------------------------------------------------------------------------------- 211// A partition is a claim, so check that the parts SUM. In a closed ledger every unit given is a unit 212// taken; if these diverge the aggregation dropped or double-counted rows and every standing computed 213// from it is worthless. Returns 1 when the books balance. 214func cl_conserved(gave: *i64, took: *i64, m: i64) -> i64 { 215 var sg: i64 = 0 216 var st: i64 = 0 217 var i: i64 = 0 218 while i < m { sg = sg + gave[i]; st = st + took[i]; i = i + 1 } 219 if sg == st { return 1 } 220 return 0 221}