Storage and Operations
The Ledger Engine should utilize a key-value based database with support for transactions like RocksDB to store UTXOs and State Updates.
- UTXOStore: Hash(UTXO) -> Serialize(UTXO)
- Append-only
- Stores Raw UTXO by its hash
- SpentStore: SpentKey:Hash(UTXO) -> Boolean
- Append-only
- Flags a UTXO hash as spent
- SettledStore: SettledKey:Hash(UTXO) -> Boolean
- Append-only
- Flags a UTXO hash as settled
- WalletStore: Hash(Wallet,Asset,ChainId) -> Serialize(Hash(UTXO)[])
- Stores list of unspent UTXO hashes for a single trader and asset
- BalanceStore: BalanceKey:Hash(Wallet,Asset,ChainId) -> Uint
- Stores sum of unspent UTXO amounts for a single trader and asset
- 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
- LeafStore: Uint -> LeafHash
- Append-only
- Index of leaf in tree
processDeposit(Deposit deposit)
- MUST be atomic
- using
chainId
ofdeposit
, query for latest id fromChainSequenceIdStore.
Assert that thechainSequenceId
of the deposit is one after the latest id. Update the latest id. - hash
deposit
to obtain UTXO hashdepositHash
- Can be done in parallel:
- using
depositHash
, store the deposit inUTXOStore
- get wallet and asset from
deposit
, hash to get key forWalletStore.
- Get and deserialize existing list of hashes from
WalletStore
, appenddepositHash
, serialize and update value inWalletStore
- append
BalanceKey
to last key, get value fromBalanceStore
. Add amount from deposit, update value inBalanceStore
- using
SequenceKey
, get next sequence number fromSequenceStore
- using sequence number and the
deposit
, create aStateUpdate
- serialize
DepositParams
and store using sequence number as key inStateUpdateStore
processTrade(TradeParams trade)
- using
size
andprice
of trade, calculate amount of each asset that needs to be paid - Determine
trader
andasset
for side A of the trade. Hash to get key forWalletStore
. Get and deserialize list of UTXO hashes fromWalletStore
to use as inputs for this side of the trade. - Determine
trader
andasset
for side A of the trade. Hash to get key forWalletStore
. Get and deserialize list of UTXO hashes fromWalletStore
to use as inputs for this side of the trade. - Determine inputs and outputs for Side A:
- Iterate through list of UTXO hashes to use as inputs for side A
- Using UTXO hash as key, get and deserialize raw UTXO from
UTXOStore
- add UTXO amount to
totalInputAmount
and add UTXO hash to list ofinputs
- Compare amount from UTXO with remaining amount to be paid
- if amount of UTXO is less than or equal to remaining amount:
- create 1 ObligationUTXO crediting the entire UTXO amount (minus fees) to the counterparty
- append the resulting UTXO to list of outputs for side A. It will be processed as if it was a deposit for the counterparty.
- create 1 FeeUTXO crediting the settlement fee to the SDP placeholder address
- append the resulting UTXO to list of outputs for side A. It will be processed as if it was a deposit for the SDP address, except marked as spent
- create 1 FeeUTXO crediting the PI fee to the PI fee recipient address
- append the resulting UTXO to list of outputs for side A. It will be processed as if it was a deposit for the PI fee recipient address, except marked as spent
- if amount of UTXO is greater than remaining amount to be paid:
- create 1 ObligationUTXO crediting the UTXO amount minus the remaining amount (minus fees) to the counterparty
- append the resulting UTXO to list of outputs for side A. It will be processed as if it was a deposit for the counterparty.
- create 1 Obligation UTXO crediting the UTXO amount minus the remaining amount to the party (same trader as the input)
- append the resulting UTXO to list of outputs for side A. It will be processed as if it was a deposit for the party.
- create 1 FeeUTXO crediting the PI fee to the PI fee recipient address
- process the resulting UTXO as if it was a deposit for the PI fee recipient address, except mark it as spent
- create 1 FeeUTXO crediting the settlement fee to the SDP placeholder address
- process the resulting UTXO as if it was a deposit for the SDP address, except mark it as spent
- append the input to the list of inputs for side A. Update the remaining amount to be paid. Stop iterating if the remaining amount is 0.
- after iteration ends, use
trader
andasset
to construct key forBalanceStore
and query for the amount. SubtracttotalInputAmount
from the value and update it.
- repeat the above for side B of trade
- Mark all inputs in side A and side B as spent
- Iterate through all outputs of side A and side B and append them to the ledger
- using
SequenceKey
, get next sequence number fromSequenceStore
- using sequence number and inputs and outputs determine for each side , create
Trade
- serialize
Trade
and store using sequence number as key inStateUpdateStore
processSettlementRequest(SettlementRequest settlementRequest)
- using
chainId
ofsettlementRequest
, query for latest id fromChainSequenceIdStore.
Assert that thechainSequenceId
of the settlement request is one after the latest id. Update the latest Id. - using
trader
andasset
ofsettlementRequest
, construct key forWalletStore
and query for list of unspent UTXO hashes - Iterate through every UTXO hash
- append
SettledKey
to the hash and set it astrue
inSettledStore
- append
SpentKey
to the hash and set it astrue
inSpentStore
- use the hash to query for the UTXO in the
UTXOStore
. Query for the raw UTXO and add the amount of the UTXO to atotalAmount
- using
trader
andasset
ofsettlementRequest
, construct key forWalletStore
and set the value to empty - using
trader
andasset
ofsettlementRequest
, construct key forBalanceStore
and set the value to0
- using the
settlementRequest
and list of UTXO hashes, construct and return aSettlementAcknowledgement
- using
SequenceKey
, get next sequence number fromSequenceStore
- using sequence number,
SettlementRequest
, and inputs, createSettlementAcknowledgement
- serialize
SettlementAcknowledgement
and store using sequence number as key inStateUpdateStore
validateStateUpdate(SignedStateUpdate proposedStateUpdate, ChainEventStore eventStore)
- validate that the address recovered from the signature of the
proposedStateUpdate
matches the address of theparticipatingInterface
set in the state update - validate that the
sequenceId
of theproposedStateUpdate
is the next id in the sequence after the storedlastProcessedStateUpdate
- Deserialize the
structData
based on thetypeIdentifier
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
andchainSequenceId
of theproposedDeposit.deposit
, query for the corresponding event in theeventStore
- if event is missing, wait and try again
- assert that the event from the
eventStore
matches the deposit - get
Deposit
fromproposedDeposit
and use as input toprocessDeposit
- validate that the output generated by
processDeposit
matches the output inproposedDeposit
validateTrade(Trade proposedTrade)
- get
TradeParams
fromproposedTrade
and use as input inprocessTrade
- validate that the inputs and outputs generated by
processTrade
match the inputs and outputs in theproposedTrade
. Returntrue
if so, otherwise returnfalse
validateSettlement(SettlementAcknowledgement proposedSettlement, ChainEventStore eventStore)
- using
chainId
andchainSequenceId
of theproposedSettlement.settlementRequest
, query for the corresponding event in theeventStore
- if event is missing, wait and try again
- assert that the event from the
eventStore
matches the settlement request - get
SettlementRequest
fromproposedSettlement
and use as input inprocessSettlementRequest
- validate that the inputs generated by
processSettlementRequest
match the inputs in theproposedSettlement
. Returntrue
if so, otherwise returnfalse
Last modified 1mo ago