Storage and Operations

The Ledger Engine should utilize a key-value based database with support for transactions like RocksDB to store balances and State Updates.

Balance Storage

  • BalanceStore: BalanceKey:Hash(Wallet,Asset,ChainId) -> Uint

    • Stores amount of an asset available for a trader. Defaults to 0.

  • LastBalanceUpdateStore: LastBalanceUpdateKey:Hash(Wallet,Asset,ChainId) -> Uint

    • Stores the sequence number of the last state update that affected the trader's balance of an asset. Defaults to 0.

State Update Storage

  • SequenceIdStore: SequenceKey -> Uint

    • Stores next sequence number

  • StateUpdateStore: StateUpdateKey:Uint -> Serialize(StateUpdate)

    • Stores raw State Update by its sequence number

  • ChainSequenceIdStore: Uint -> Uint

    • Maps chain ID to latest chain sequence ID

Merkle Storage

  • LeafStore: Uint -> LeafHash

    • Append-only

    • Index of leaf in tree

Operations

Generating State Updates

  • processDeposit(Deposit deposit)

    • MUST be atomic

    • using chainId of deposit, query for latest id from ChainSequenceIdStore. Assert that the chainSequenceId of the deposit is one after the latest id. Update the latest id.

    • hash deposit to obtain UTXO hash depositHash

    • Can be done in parallel:

      • using depositHash, store the deposit in UTXOStore

      • get wallet and asset from deposit, hash to get key for WalletStore.

        • append BalanceKey to last key, get value from BalanceStore. Add amount from deposit, update value in BalanceStore

      • using SequenceKey, get next sequence number from SequenceStore

        • using sequence number and the deposit , create a StateUpdate

        • serialize DepositParams and store using sequence number as key in StateUpdateStore

  • processTrade(TradeParams trade)

    • using size and price of trade, calculate amount of each asset that needs to be paid

    • Based on the parameters of the trade, determine the address of the maker and taker, as well as the address and chain ID of the base asset and counter asset, and query for the following:

      • maker's balance of base asset from BalanceStore and the last update from the LastUpdateBalanceStore

      • maker's balance of counter asset from BalanceStore and the last update from the LastUpdateBalanceStore

      • taker's balance of base asset from BalanceStore and the last update from the LastUpdateBalanceStore

      • taker's balance of counter asset from BalanceStore and the last update from the LastUpdateBalanceStore

    • If maker's order is a BUY and taker's order is a SELL, generate new balances for each side by doing the following:

      • decrement maker's balance of counter asset by size * price

      • increment taker's balance of counter asset by size * price

      • increment maker's balance of base asset by size

      • decrement taker's balance of base asset by size

      • If maker's order is a SELL and taker's order is a BUY, do same as above but switch maker and taker

    • Using the new balances from above, update the following:

      • maker's balance of base asset in BalanceStore

      • maker's balance of counter asset in BalanceStore

      • taker's balance of base asset in BalanceStore

      • taker's balance of counter asset in BalanceStore

    • using SequenceKey, get next sequence number from SequenceStore

      • use sequence number to update the following:

        • maker's last update for balance of base asset in LastBalanceUpdateStore

        • maker's last update for balance of counter asset in LastBalanceUpdateStore

        • taker's last update for balance of base asset in LastBalanceUpdateStore

        • taker's last update for balance of counter asset in LastBalanceUpdateStore

      • using sequence number, previous balances, sequence IDs of previous balances, and new balances:

        • serialize Trade and store using sequence number as key in StateUpdateStore

  • processSettlementRequest(SettlementRequest settlementRequest)

    • using chainId of settlementRequest, query for latest id from ChainSequenceIdStore. Assert that the chainSequenceId of the settlement request is one after the latest id. Update the latest Id.

    • using trader, asset, and chainId of settlementRequest, construct key for BalanceStore and LastBalanceUpdateStore, and query for each

    • using trader and asset of settlementRequest, construct key for BalanceStore and set the value to 0

    • using SequenceKey, get next sequence number from SequenceStore

      • using the settlementRequest, last balance update ID, old balance, new balance (zero), and sequence ID, create SettlementAcknowledgement

      • use sequence ID to update last balance update ID for trader and asset

      • serialize SettlementAcknowledgement and store using sequence number as key in StateUpdateStore

Validating State Updates

  • validateStateUpdate(SignedStateUpdate proposedStateUpdate, ChainEventStore eventStore)

    • validate that the address recovered from the signature of the proposedStateUpdate matches the address of the participatingInterface set in the state update

    • validate that the sequenceId of the proposedStateUpdate is the next id in the sequence after the stored lastProcessedStateUpdate

    • Deserialize the structData based on the typeIdentifier and call the corresponding function to validate the state update

    • if the validate function specific to the state update returns true, then commit the changes and return true

      • if invalid, forward the signed state update to the fraud engine

  • validateDeposit(DepositAcknowledgement proposedDeposit, ChainEventStore eventStore)

    • using chainId and chainSequenceId of the proposedDeposit.deposit, query for the corresponding event in the eventStore

      • if event is missing, wait and try again

    • assert that the event from the eventStore matches the deposit

    • get Deposit from proposedDeposit and use as input to processDeposit

    • validate that the output generated by processDeposit matches the output in proposedDeposit

  • validateTrade(Trade proposedTrade)

    • get TradeParams from proposedTrade and use as input in processTrade

    • validate that the balances and last update IDs generated by processTrade match the ones in the proposedTrade. Return true if so, otherwise return false

  • validateSettlement(SettlementAcknowledgement proposedSettlement, ChainEventStore eventStore)

    • using chainId and chainSequenceId of the proposedSettlement.settlementRequest, query for the corresponding event in the eventStore

      • if event is missing, wait and try again

    • assert that the event from the eventStore matches the settlement request

    • get SettlementRequest from proposedSettlement and use as input in processSettlementRequest

    • validate that the balances and last update IDs generated by processSettlementRequest match the ones in the proposedSettlement. Return true if so, otherwise return false

Last updated