Chapter 43: Account
This model is compatible with the distributed approach specified in
Appendix D:
Central Bank Implementation of the VoTa constitution. Each account belongs permanently to the
node identified by node, and that node is authoritative for the account's balance.
value TransferOut
{
TimeStamp time;
Account to;
Int64 amount where amount > 0;
}
value TransferIn
{
TimeStamp applied;
Account from;
Int64 amount where amount > 0;
Uint64 transferIndex;
}
entity Account
{
NodeId node;
Uint32 id;
Person owner;
Denomination denomination;
TransferOut[] transfersOut;
async → TransferIn[] transfersIn;
→ Int64 balance = transfersIn.amount.Σsum - transfersOut.amount.Σsum;
key {node,id};
constraints
{
let account = this in
transfersOut.(to.denomination = account.denomination);
}
}
Data type for amounts and balances
A transfer amount is a positive whole-number count of the account's denomination unit. Transfer
amounts and account balances are represented by Int64, whose range is \(-2^{63}\) to
\(2^{63}-1\). For a denomination of one US cent, the positive end of this range corresponds to more
than US$92.2 quadrillion, enough to represent the total global debt with plenty of room to spare.
This is consistent with the use of 64-bit integers or equivalent fixed-precision representations
for balances and transfer amounts in modern commercial-bank systems. SWIFT payment messages used
for clearing and settlement express amounts at the minor-unit resolution prescribed for each
currency; for USD, that resolution is one cent.
Account balances
An account balance always represents the number of units of the denomination owned by the account owner. A negative balance represents a liability: its magnitude is the number of units owed to someone else. This differs significantly from conventional account systems, which commonly record the magnitude of a liability as a positive balance in an account classified separately as a liability account.
A distinction is made between accounts used to record the asset and liability sides of loans and accounts used to record quantities of property. The latter generally have a constraint requiring their balance to be non-negative. For example, a warehouse cannot record a negative quantity of stock, and a Central Bank account cannot record a negative balance of Runes.
A Person's equity, or net position, is therefore related to the sum of the balances of all accounts they own, subject to their different denominations. Balances having the same denomination can be summed directly. Balances in different denominations must either remain as separate net positions or be converted using an explicitly chosen valuation before they can contribute to a single total.
Transfers are always positive amounts
Every transfer has a positive amount. A transfer is authorised by the owner of the sending account, and a positive amount subtracts value from that account and adds it to the receiving account. The sender can therefore give value but cannot take value from the recipient. If negative amounts were allowed, a sender could make their own balance increase while making the recipient's balance decrease, allowing them to take the recipient's property without the recipient's authorisation.
The positive-amount rule works whether either balance is positive or negative. For example, if one Person accepts another Person's liability, the accepting Person sends a positive amount from their account to the account of the Person being released from the liability. The sender's balance becomes more negative and the recipient's balance moves towards zero.
A negative transfer amount is unnecessary because it would be equivalent to a positive transfer with the sending and receiving accounts exchanged. Restricting amounts to be positive therefore gives every transfer a single canonical direction and avoids two different representations of the same operation.
Transfer identification
The transfersOut array is append-only: existing elements are never removed, reordered or
replaced. Its zero-based index therefore provides a stable logical transfer number. An outgoing
transfer is uniquely identified by the pair consisting of its source account and its index in that
account's transfersOut array. This remains its identity even if an implementation compacts
or otherwise reorganises the physical storage of the array.
An outgoing transfer is applied synchronously by the node that owns the source account. The source node appends the outgoing transfer and reduces the available balance together in one local database operation.
Asynchronous updates to transfersIn
The corresponding incoming transfer is applied asynchronously by the node that owns the destination
account. Its from and transferIndex identify the authoritative outgoing transfer.
Applying the incoming transfer increases the destination account's balance by its amount. Repeated
delivery does not create another incoming record. It is valid for an outgoing transfer to exist
before its incoming record; during that interval, its units are in transit. Every durable outgoing
transfer must eventually be delivered and applied exactly once.
This is an application of eventual consistency: the source and destination nodes may temporarily reflect different stages of a transfer, but converge once the incoming transfer has been delivered and applied. In normal operation this is expected to take only a fraction of a second. Longer delays arise when a network partition prevents communication or when a node has failed and must recover.
Appendix D uses immutable batches and vector times to provide durable delivery, causal ordering,
deduplication and crash recovery. Those mechanisms implement the relationship between
TransferOut and TransferIn, but are omitted here because this chapter describes
only the underlying accounts and transfers. Transfers between accounts on the same node may apply
both sides immediately in one local database operation.