Transaction Execution
Transaction execution in Thru follows a structured four-phase pipeline that ensures consistency, security, and resource management. Each phase has specific responsibilities and failure conditions.
-
Transaction Validation
This phase occurs before a transaction can be included in a block. Validation failures result in invalid blocks and consensus rejection.
Signature Verification
- Transaction signature must be valid for the fee payer’s public key
- Ed25519 signature verification on transaction payload
Account Structure Validation
- Account addresses must be properly sorted (read-write accounts in ascending order, then read-only accounts in ascending order - see transaction format)
- No duplicate account addresses across any account lists
- Total account count must not exceed
TN_TXN_MAX_ACCOUNTSlimit - Fee payer and program accounts must be distinct
Transaction Format Validation
- Transaction version must be supported
- Transaction flags must be valid
- Transaction size must not exceed MTU limits
-
Pre-Execution
Pre-execution prepares the transaction context and performs economic checks. If any pre-execution check fails, the transaction is rejected and VM execution does not occur. Even so, some state changes (nonce advancement, fee collection) may occur during this phase that do persist.
Temporal Validation
- Current block slot must be ≥ transaction’s
start_slot - Current block slot must be < transaction’s
expiry_slot
Fee Payer Account Setup
- Fee payer account must exist or be created with a valid state proof
- State proof validation (if present):
- Cryptographic proof verification against current state root
- Proof type must be
CREATIONorEXISTING - Proof path must represent valid Merkle tree path
Nonce Processing
- Transaction nonce must exactly match fee payer’s current nonce
- Nonce is advanced immediately upon validation
Fee Processing
- Fee payer balance must be sufficient for transaction fee
- Transaction fee is collected after nonce advancement
- Current block slot must be ≥ transaction’s
-
VM Execution
The virtual machine executes the transaction’s program with strict resource limits and safety constraints.
Program Validation
- Program account must exist and be marked as executable
- Program data must be valid bytecode
Resource Constraints
- Compute Units: Execution must not exceed
requested_compute_units(see compute units) - Memory: VM memory access must stay within allocated segments
VM Execution Environment
- Program runs in isolated VM with segmented memory (
tn_vm_base.h:TN_VM_SEG_*) - System calls provide controlled access to accounts and blockchain state
- Execution can terminate with success or various fault conditions:
TN_VM_SUCCESS: Normal completionTN_VM_FAULT_REVERT: Program-initiated revertTN_VM_FAULT_SIGCU: Compute units exhaustedTN_VM_FAULT_SIGSU: State units exhausted
State Unit Calculation State units are computed based on account data size changes:
state_bytes_net = max(0, bytes_added - bytes_removed)state_units_consumed = ceil(state_bytes_net / TN_RUNTIME_PAGE_SZ) -
Post-Execution
Post-execution finalizes state changes and handles compressed account proofs.
State Unit Validation
- State Units: After VM execution, state size changes are calculated and must not exceed
requested_state_units(see state units) - Transactions that exceed state unit limits are failed with
TN_VM_FAULT_SIGSU
State Finalization
- Account state changes are committed to storage if execution succeeded
- Failed executions preserve nonce advancement and fee collection only
Account Compression Processing
- Accounts that are compressed during execution are removed from the active state set
- State Units: After VM execution, state size changes are calculated and must not exceed
Execution Results
Section titled “Execution Results”The execution result includes:
execution_result: VM exit codecompute_units_consumed: Actual compute units usedstate_units_consumed: State units consumed by account changesuser_error_code: Program-specific error informationerror_program_acc_idx: Account index of the program that caused the error, useful for identifying the faulting program in cross-program invocation (CPI) scenarios- Memory fault details (if applicable)
Fee Distribution
Section titled “Fee Distribution”Transaction fees are collected from each fee payer during pre-execution, but they are distributed once per block, not per transaction. After every transaction in a block has executed — and before the block’s state hash is sealed — the runtime distributes the block’s accumulated native transaction fees on a single, unified completion path. That path is identical for live block production, BTP replay, and journal replay, so every node arrives at the same post-distribution block state.
The destination depends on whether the block’s producer account is live in state at the end of the block (present, not deleted, and not compressed):
- Producer live → claimed. The fees are credited to the block producer’s native balance. The amount is reported as
claimed_fees. - Producer not live → burned. The fees are credited to a fixed null/burn account established at genesis. The amount is reported as
burned_fees.
The liveness check is evaluated against the block’s own final state, so an account created, deleted, or compressed within the same block is judged on that end-of-block state.
burned_fees and claimed_fees are disjoint by destination: a block has a single producer, so exactly one of them is non-zero, and burned_fees + claimed_fees always equals the block’s total collected fees.
Null / burn account
Section titled “Null / burn account”The burn account is permanently unspendable: its owner is the all-zero public key (not the System Program), and no executable program is installed at its address, so no program can ever be authorized to move its balance. It is also marked uncompressable, so it is always present as a live record. Its balance therefore grows monotonically as a transparent, on-chain counter of fees burned for blocks whose producer was absent.
These per-block amounts are surfaced through slot metrics — see GetSlotMetrics and StreamSlotMetrics, whose responses carry burned_fees and claimed_fees.
Errors
Section titled “Errors”For detailed error codes and their descriptions, see Runtime Errors.