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}