nx_fin_ledger.nx
buildroot/runtime/nx_fin_ledger.nx
about
nx_fin_ledger.nx -- R2 of THE NISHI FINANCIAL ECOSYSTEM: the content-addressed, append-only,
DOUBLE-ENTRY ledger. Net worth is a VIEW (folded from history), never a stored number.
WHY THIS SHAPE:
* NO-FLOAT: every amount is exact i64 minor units (cents); all arithmetic via nx_money (mny_add).
* ADDITIVE-ONLY (#13): there is NO delete/update of a transaction. History is the truth. A
correction is a new compensating transaction, never an edit.
* CONTENT-ADDRESSED (self-proof of integrity): each transaction is canon-encoded -> a CID
(nxc1-<sha256>); the CID is its storage key. Same content -> same id everywhere.
* IDEMPOTENT (#10): re-posting the SAME (extid + content) is a no-op (same CID already present).
`extid` is the caller's idempotency key so two GENUINELY distinct but identical-looking
transactions stay distinct (different extid -> different CID).
* NEVER-BRICK FINANCIAL ANALOG (#26): an UNBALANCED transaction (postings not netting to zero)
is REJECTED BY CONSTRUCTION before anything is written -- a corrupt entry can never be recorded.
SIGN CONVENTION (unified accounting-equation space):
Every posting is a signed cents delta. A transaction is VALID iff the signed sum of its postings
== 0 (debits == credits). Asset balances are positive (owned); liability balances are negative
(owed). NET WORTH = sum of balances over accounts whose class is asset or liability
(equity/income/expense are excluded -- they reconcile the books, they are not net worth).
Proof the math is real: paying a liability from an asset (asset down, liability up toward zero)
leaves net worth INVARIANT; spending on an expense (asset down, no asset/liability offset) lowers
net worth. The R2 gate asserts exactly these.
STORAGE: sovereign seg-store (nx_seg_store) under a path prefix (e.g. "knowledge/store/finled-").
key "fin:txnidx" -> newline-joined list of transaction CIDs (the append-only log index)
key "fin:acctidx" -> newline-joined list of account names
key <txn-CID> -> the canonical NXR1 transaction record
key "acctclass:<name>" -> the account's class string (NUL-terminated)
segid = sys_now_us(): microsecond ids are distinct across the fsync+rename gap between commits, so
no two commits share a segment file (a ms-resolution id could collide in a tight loop and lose a
record -- unacceptable for money). SCALING (#21): one segment per post is O(segments^2) at open;
nx_seg_store ss_compact merges segments -- that is the later durability/scale rung.
license_tier: ORIGINAL
dependencies 5 imports · 1 importers
imports: nx_syscalls.nxnx_money.nxnx_canon_cid.nxnx_uxf_decode.nxnx_seg_store.nx
imported by: nx_fin_ledger_gate.nx
structs
| none |
consts
| 40 | const K_MAGIC_262144: i64 = 262144 |
| 41 | const K_MAGIC_8192: i64 = 8192 |
| 42 | const K_MAGIC_1048576: i64 = 1048576 |
functions
| 44 | func fl_len(s: *u8) -> i64 |
| 50 | func fl_streq(a: *u8, b: *u8) -> i64 |
| 61 | func fl_cpy(dst: *u8, src: *u8) -> i64 |
| 69 | func fl_itoa(v: i64, out: *u8) -> i64 |
| 86 | func fl_atoi(s: *u8) -> i64 called by 1: fl_txn_acct_sum |
| 101 | func fl_pkey(c: i64, idx: i64, out: *u8) -> i64 |
| 116 | func fl_get(keys: *i64, vals: *i64, nf: i64, key: *u8) -> *u8 |
| 127 | func fl_idx_has(list: *u8, listlen: i64, item: *u8, il: i64) -> i64 |
| 162 | func fl_sum(amts: *i64, n: i64) -> i64 |
| 170 | func fl_txn_acct_sum(keys: *i64, vals: *i64, nf: i64, name: *u8) -> i64 |
| 194 | func fl_build_txn(extid: *u8, date: *u8, memo: *u8, accts: *i64, amts: *i64, n: i64, out: *u8) -> i64 |
| 222 | func fl_acct_open(prefix: *u8, name: *u8, class: *u8) -> i64 |
| 258 | func fl_post(prefix: *u8, extid: *u8, date: *u8, memo: *u8, accts: *i64, amts: *i64, n: i64, cidout: *u8) -> i64 |
| 294 | func fl_balance(prefix: *u8, name: *u8) -> i64 |
| 334 | func fl_networth(prefix: *u8) -> i64 |
| 378 | func fl_txn_count(prefix: *u8) -> i64 |
| 397 | func fl_get_txn(prefix: *u8, cid: *u8, ptrout: *i64, lenout: *i64) -> i64 |