# Transaction processing
Caution: Some details are subject to changes.
The check_tx
and deliver_tx
events share the same transaction processing code, the only difference is they operate
on different storage buffers (refer to buffered-storage).
In commit
event, the deliver_tx
buffer is flushed, while the check_tx
buffer is dropped.
# Transaction types
Enclave
Transfer
Deposit
Withdraw
Public
Unbond
Unjail
NodeJoin
The enclave transactions are obfuscated in enclave, so they can only be verified completely in the validation enclave, while the public transactions don't need enclave to verify.
The basic process procedure is like this:
let context = ...
match tx {
Enclave(tx) => {
let action = validate_enclave_tx(storage, context, tx)?;
execute_enclave_tx(storage, tx, action)?;
},
Public(tx) => {
process_public_tx(storage, context, tx)?;
}
}
# Context
struct ChainInfo {
/// minimal fee computed based on tx size expected to be paid
pub min_fee_computed: Fee,
/// network hexamedical ID
pub chain_hex_id: u8,
/// block time of current processing block
pub block_time: Timespec,
/// height of current processing block
pub block_height: BlockHeight,
/// related network parameters, used by validation of withdraw tx
pub unbonding_period: u32,
}
# Enclave transactions
# validate_enclave_tx
Check inputs unspent and load sealed log for them, and pass to validation enclave
Load staking state for withdraw and deposit transactions, and pass to validation enclave
Validation enclave decrypt the transaction, unseal the inputs and do the following validations:
Check tx attributes (
chain_hex_id
,app_version
)Check inputs/outputs basic
- Not empty
- No duplicates
Check input timelock
Verify inputs witnesses for transfer/deposit
Verify staking address witness for withdraw
Check staking state not jailed for withdraw/deposit
Check staking state for withdraw:
nonce
matchcontext.block_time >= unbonded_from
unbonded != 0
outputs.iter().all(|x| x.valid_from == account.unbonded_from)
Check fee and input/output sum amounts:
Transfer
sum_input - sum_output == min_fee
Return
sum_input - sum_output
as paid feeDeposit
sum_input > min_fee
Return
sum_input - min_fee
as deposit amountWithdraw
account.unbonded - sum_output == min_fee
Return
account.unbonded - sum_output
as paid fee
# execute_enclave_tx
The TxEnclaveAction
is returned as the result of success validation of enclave transaction:
enum TxEnclaveAction {
Transfer {
fee: Fee,
spend_utxo: Vec<TxoPointer>,
create_utxo: TxoIndex,
sealed_log: SealedLog,
},
Deposit {
fee: Fee,
spend_utxo: Vec<TxoPointer>,
deposit: (StakedStateAddress, Coin),
},
Withdraw {
fee: Fee,
// withdraw supposed to withdraw all unbonded,
// the amount here only to do sanity check
withdraw: (StakedStateAddress, Coin),
create_utxo: TxoIndex,
sealed_log: SealedLog,
},
}
It's executed as follows:
fee
Written into block result events, and get accumulated into reward pool afterward.
spend_utxo
/create_utxo
Modify UTxO storage
sealed_log
Write into storage
deposit
/withdraw
Modify staking state and related states
# Public transactions
Common validations:
- Check tx attributes (
chain_hex_id
,app_version
) - Verify staking witness
- Check the witness match the staking address
- Check
nonce
matches
# Unbond
The actual paid fee is context.min_fee_computed
.
Actions:
staking.bonded -= tx.value
staking.unbonded += tx.value - context.min_fee_computed
staking.unbonded_from = block_time + unbonding_period
Validations:
- Not jailed
- Coin computations no error, which implies:
tx.value >= context.min_fee_computed
staking.bonded >= tx.value
# Unjail
The actual paid fee is zero.
Action:
- Transit staking state from "jailed validator" to "inactive(unjailed) validator" (refer to staking state).
Validations:
- Already jailed
block_time >= jailed_until
# Node Join
The actual paid fee is zero.
Action
- Transit staking state from "clean staking" or "inactive(unjailed) validator" to active validator (refer to staking state)
Validations:
- Not jailed
- Not active
staking.bonded >= minimal_required_staking
- Validator address not used
- Validator address not banned
- Used validator address list not full when re-join with new validator key.
# Appendix
# Tx Storage
# KV Store
COL_TX_META
,txid -> BitVec
UTxO spent status.COL_ENCLAVE_TX
,txid -> sealed tx payload
- abci query sealed payload for tx inputs, pass them to tx-validation-enclave for it to validate the tx.
- For tx-query to query sealed tx payload.
COL_WITNESS
,txid -> TxWitness
Only for abci-queryCOL_BODIES
,txid -> Tx
Only for abci-query
# Merkle Trie
Staking states are stored in merkle trie.