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.