Appendix D: Central Bank Implementation
This appendix specifies a deterministic and fault-tolerant implementation of the Central Bank. Its purpose is to ensure that the operational behaviour of the system strictly enforces the invariants defined in Article XV: Monetary system, including the prohibition on negative balances, the integrity of all transfers, and the absence of discretionary authority. The design is distributed, mechanically verifiable, and resilient to crashes.
It is straightforward to implement a system that converges at quiescence and prevents negative balances even when nodes apply concurrent batches in different orders. The design is extremely efficient and scales to millions of transfers per second, enabling true micropayment-scale throughput.
1. Nodes, accounts, and local databases
The Central Bank executes on a collection of distributed machines called nodes. In any database state, let \(N\) be the current set of node identifiers. Each node has a unique identifier \(i \in N\).
Every Central Bank account belongs to exactly one node. Nodes create accounts, and a node forever owns the accounts it created. For each node \(i \in N\), let \(L_i\) be the current set of local account identifiers for accounts owned by node \(i\). Every Central Bank account in the current state is identified by a pair \((i,\ell)\) with \(i \in N\) and \(\ell \in L_i\). Here \(i\) is the node that created and owns the account, and \(\ell\) is the account's local identifier on that node.
Node \(i\) is authoritative for the balances of the accounts it owns. The value of the local database on node \(i\) is modelled as the tuple \[ D_i = (\beta_i,v_i,\chi(v_i)), \] where:
- \(\beta_i : L_i \to \text{Amount}_{\ge 0}\) maps each local account identifier \(\ell\) to the balance of account \((i,\ell)\);
- \(v_i : N \to \mathbb{N}\) is a vector time defining what batches have been applied to node \(i\)'s local database, where \(v_i(j)\) is the number of batches created by node \(j\) that have been applied;
- \(\chi(v_i)\) is the set of batches that have been applied. The notation \(\chi\) is defined in Section 3 after batches have been introduced.
A local database transaction on node \(i\) is an atomic update to \(D_i\). Database transaction atomicity ensures that \(D_i\) is either fully updated or not updated at all, so the local database remains self-consistent. No such database transaction requires network communication, distributed locking, two-phase commit, or coordination with another node.
2. Monetary transfers
A monetary transfer is represented by the tuple \[ (i_s,\ell_s,x,i_r,\ell_r,\alpha), \] representing a transfer of amount \(\alpha \ge 0\) from source account \((i_s,\ell_s)\) to receiving account \((i_r,\ell_r)\). The value \(x\) is a transfer number such that \((i_s,\ell_s,x)\) uniquely identifies the debit on account \((i_s,\ell_s)\), and is therefore the identity of the monetary transfer. This transfer may be created only by node \(i_s\), and only if \(\alpha \le \beta_{i_s}(\ell_s)\).
The transfer updates two balances:
- a debit at node \(i_s\), subtracting \(\alpha\) from \(\beta_{i_s}(\ell_s)\);
- a credit at node \(i_r\), adding \(\alpha\) to \(\beta_{i_r}(\ell_r)\).
3. Batches
A batch \(B_{i_s}(t)\) is a finite set of monetary transfers created by node \(i_s\), with batch identifier \((i_s,t)\), where \(t \in \mathbb{N}\) is zero-based. All transfers in \(B_{i_s}(t)\) have source node \(i_s\). The purpose of a batch is to record the credits that need to be applied at receiving nodes.
When node \(i_s\) begins a new batch, its index is \(t=v_{i_s}(i_s)\). The node advances \(v_{i_s}(i_s):=t+1\) and creates \(B_{i_s}(t)\) as an initially empty current batch. Within a local database transaction, each created transfer \((i_s,\ell_s,x,i_r,\ell_r,\alpha)\) debits \(\beta_{i_s}(\ell_s)\) and is appended to \(B_{i_s}(t)\). The batch may grow over many local database transactions until it is closed.
While accumulating \(B_{i_s}(t)\), node \(i_s\) may apply batches received from other nodes. Those batches can only apply credits, so they do not conflict with the transfers already accumulated in \(B_{i_s}(t)\).
Node \(i_s\) closes batch \(B_{i_s}(t)\) by making its current contents immutable, and may then begin accumulating a new batch \(B_{i_s}(t+1)\). A batch must be closed and durable before it is sent to a peer.
For a vector time \(v\), define its batch extent as \[ \chi(v) = \{\, B_j(t) \mid t \lt v(j) \,\}. \] Thus the third component of \(D_i\) records exactly \(\chi(v_i)\).
4. Streaming between nodes
Nodes maintain long-lived, bidirectional network connections, for example TCP streams. In this section, \(i\) is the sender, \(j\) is the receiver, and \(k\) is the creator of a batch. When nodes \(i\) and \(j\) first establish a connection, they send each other their current vector times \(v_i\) and \(v_j\).
After receiving \(v_j\), node \(i\) keeps a transient send cursor \(\widehat v_j\) for that connection, initially set to the received value of \(v_j\). This cursor is not part of \(D_i\). Node \(i\) sends node \(j\) the batches in \[ \chi(v_i)\setminus\chi(\widehat v_j). \] Batches are sent in transmission units that node \(j\) can process atomically. A transmission unit is a finite list of triples \((k,t,B_k(t))\), ordered by increasing \(t\) for each source node \(k\). After node \(i\) sends a unit, it advances \(\widehat v_j\) for the batches included in that unit. Node \(j\) does the same in the other direction. The connection remains established, and each node continues sending later batches to the peer in transmission units as they become available.
A node may synchronise with multiple peers at the same time. This may cause redundant copies of batches to be received. Redundant transmission is harmless because local application is controlled by the receiver's current \(v\).
5. Applying a received unit
Node \(j\) applies each received transmission unit in one local database transaction. The unit is processed in list order.
For each triple \((k,t,B_k(t))\) in the unit, node \(j\) applies \(B_k(t)\) if \(v_j(k)=t\). If \(v_j(k)\gt t\), the batch has already been applied and is skipped.
For each applied triple \((k,t,B_k(t))\), the database changes are:
- record \(B_k(t)\) in node \(j\)'s durable batch records;
- apply all credits in \(B_k(t)\) for accounts owned by node \(j\); and
- set \(v_j(k) := t+1\).
6. Crash recovery and error handling
Crash recovery is local to each node. After recovery, a node uses its durable local database, reconnects to peers, exchanges its recovered vector time, and receives any missing batches.
If node \(j\) fails before an application of a transmission unit commits, then none of that unit's updates are durable and the unit may be received again after recovery. If node \(j\) fails after commit, then the batch records, local balance updates, and vector time updates from the unit are all durable.
- Nodes may include checksums or equivalent integrity mechanisms to detect transmission errors.
- Malformed transmission units are rejected without changing the local database.
7. Why the algorithm works
Causality preservation
Given batches \(a\) and \(b\) we say \(a\) precedes \(b\), written as \(a \to b\) if \(a\) had been applied on the node where \(b\) was created. This is Lamport's happens-before relation restricted to batches. It is a strict partial order.
Let \(S\) be a set of batches which defines a global state. By global state we mean all the account balances in the system that would result if all nodes fully applied every batch in \(S\). We say \(S\) preserves causality if \((b \in S \;\land\; a \to b) \Rightarrow a \in S\).
If sets of batches \(S_1\) and \(S_2\) preserve causality, then \(S_1 \cup S_2\) and \(S_1 \cap S_2\) also preserve causality. Thus the causality-preserving database states form a lattice under set union and set intersection.
Synchronisation as join
For vector times \(v\) and \(w\), define the pointwise join \(v \vee w\): \[ (v \vee w)(i) = \max(v(i),w(i)). \] Pointwise join represents the set union: \[ \chi(v \vee w) = \chi(v) \cup \chi(w). \] When two nodes exchange their current vector times and then exchange the missing batches defined in Section 4 in an order respecting \(\to\), each node can advance to this join.
Synchronisation does not require conflict resolution or global ordering. It only requires each node to apply missing batches in an order respecting the precedes relation.
Preservation of causally valid local states
Claim: for every node \(i\), after every committed local database transaction, the vector time \(v_i\) denotes a causality-preserving batch set \(\chi(v_i)\), and the balances in node \(i\)'s local database are consistent with the projection of that batch set onto the accounts owned by node \(i\). Consequently, at quiescence, all nodes that have received the same closed batches agree on the corresponding global monetary state.
The proof is by induction over committed local database transactions. The induction invariant is that, for every node \(i\), its vector time \(v_i\) denotes a causality-preserving batch set \(\chi(v_i)\), and the node's local balances are consistent with the projection of that batch set onto the accounts owned by node \(i\). The empty initial batch set satisfies this invariant.
There are two ways for a node to advance. First, a node may create a new batch. The predecessors of that batch are the batches already applied at the creating node, which form a causality-preserving set by the induction hypothesis. Adding the new initially empty batch therefore preserves causality, and the transfer creation rule preserves non-negative balances as the batch grows. Second, a node may receive one or more batches from another node. The received transmission unit is committed atomically, and represents a causality-preserving extension of the receiver's current batch set. The receiver therefore moves from one causality-preserving set to another.
The implementation details refine this proof. Concurrent creation and receipt can be serialised according to the order in which local database transactions commit, because received batches only credit local accounts and cannot invalidate a debit that was already safe. Growing a batch over several local transactions is equivalent, for the closed-batch history, to creating its final contents when the batch is closed. A local transfer, where \(i_s=i_r\), may be handled as immediate self-delivery without changing the source batch history.
At quiescence, after all nodes required to maintain current Central Bank state have received all closed batches, their vector times denote the same causality-preserving batch set. Their local balance projections are therefore mutually consistent parts of a single global state.
Non-negative balances
A debit is created only by the node that owns the source account, and the transfer creation rule in Section 2 prevents such a debit from overdrawing that account. No remote node can create a debit against that account. Batches received from peers can only credit accounts owned by the receiving node. Therefore no account balance can become negative.
Exactly-once balance updates
Each transfer has a unique identity and belongs to exactly one source batch. At the source node, the database changes that create the transfer add it to that batch and apply the debit together. For received units, node \(j\) applies a triple \((k,t,B_k(t))\) only when \(v_j(k)=t\). Applying the credits and updating \(v_j(k)\) to \(t+1\) are made together. A later copy of the same batch is redundant because \(v_j(k)\gt t\). Therefore debits and credits are applied exactly once.
Crash safety
Each database transaction either commits all of its updates or none of them. Transfer creation commits the batch entry and debit together. Received unit application commits its batch records, credits, and vector time updates together. Advancing \(v_i(i)\) commits the vector time update. Recovery therefore returns the node to a database state.
No distributed database transaction
Network communication only transmits vector times and transmission units between nodes. All state changes are local database updates on one node. The algorithm therefore does not require distributed commit, distributed locking, or global consensus for transfer validation.
8. Invariants
This implementation preserves the following invariants required by Article XV: Monetary system:
- No account balance may become negative.
- All debits are created only by the authoritative node for the sending account.
- All database states are causality-preserving.
- All vector times describe batch prefixes applied at the recording node.
- All debits of transfers added to source batches are applied exactly once.
- All credits of transfers added to source batches are applied exactly once.
- All batch records are replayable, and sent batch records are never modified.
- The system is deterministic and free of discretionary behaviour.
9. Forwarding and retention
The core algorithm only requires that batches and vector times reach every node that needs them. The connection graph may be a spanning tree, a denser mesh, or a complete graph. In a tree, batches are forwarded across the connections that separate nodes that have a batch from nodes that still need it. In a denser graph, redundant paths can reduce latency.
For forwarding, an implementation may cache whole batches or decomposed parts of batches. A common decomposition is by receiving node, so a node forwards only the credits another node needs to apply. Such cached parts are an implementation detail; they preserve the source batch identifier \((i,t)\).
A local transfer has \(i_s=i_r\). It may be implemented by applying the debit and credit together on the creating node, while still recording the transfer in the source batch. This avoids making money temporarily unavailable within a node and does not change the batch-history model used above.
A certified retention vector time \(v_{\mathrm{retention}}\) may be established, provided that \(\chi(v_{\mathrm{retention}})\) is causality-preserving and every node required to maintain current Central Bank state has durably incorporated the batches in \(\chi(v_{\mathrm{retention}})\).
Where an account type has a constitutional guarantee that its transaction history will not be retained, transaction information for that account may be irreversibly deleted once it lies before the certified retention vector time. At that point every node required to maintain current Central Bank state has durably incorporated the transaction, so its identifying history is no longer required to propagate or recover its effect on account balances. The remaining state must preserve the resulting balances, vector times, conservation of currency, tax allocations, and protection against duplicate application without preserving information from which the protected transaction history can be reconstructed.
In normal operation, nodes continuously exchange and durably apply closed batches, so the certified retention vector time is expected to advance beyond a transaction within seconds. This expectation does not replace the certification requirement: protected information may be deleted only after the relevant transaction lies before the certified retention vector time. Deletion must include every Central Bank copy of the protected information, including batch records, replicas, indexes, caches, logs, backups, and diagnostic records.
Batches in \(\chi(v_{\mathrm{retention}})\) may be compacted, archived, checkpointed, or removed from hot operational storage, provided that the remaining records are sufficient to recover account balances, vector times, applicable audit obligations, and all invariants required by this Constitution. Such compaction or purging shall not alter any balance or permit a batch update to be applied more than once. Protected transaction information described above must be deleted rather than archived and is excluded from later audit obligations.