Author: toly, co-founder of Solana Labs Source: X, @aeyakovenko Translation: Shan Ouba, Golden Finance
In Solana, state is organized as a flat key-value store, and programs execute on this state to update values, including critical voting programs for consensus. The main purpose of asynchronous execution is to allow voting programs to run independently of all other programs. To achieve this goal consistently, we need to ensure several key principles.
Execution Domains
An execution domain is a set of programs and the keys and values of their operations that are independent of each other when executed. Execution domains can run on different threads and cores and complete at different times on various machines. Importantly, one execution domain (e.g., domain A) cannot read or write any value in another domain (e.g., domain B). However, domains can share state that remains consistent during the execution of any one domain. A protocol is needed to synchronize state between domains and facilitate the movement of keys and values.
The payer of transaction fees determines the domain in which the transaction is executed.
Key Domains: VED and UED
In Solana, we focus on two domains: the Voting Execution Domain (VED) and the User Execution Domain (UED). The goal is to enable validators to vote before all user programs are executed.
Components of VED
System Programs: Used for transfers
Voting Program System Variables: Voting Program System Variables used for voting
Voting Program: The core voting program. This program is static and consistent and must be present in both UED and VED.
Voting Permissions: Accounts that have the right to vote
Voting Fee Payers: Accounts that pay fees associated with voting
Fee Payer Fund: An updateable account that can be used to move sols to VED
All other accounts are placed in UED
Calculate VED State
At the epoch boundary between N-1 and N, the runtime calculates the VED state of the N+1 domain. This calculation must be completed before the end of epoch N. UED calculations must not begin until epoch N-1 is complete.
Standards for VED State
Voting Accounts: Accounts that have staked more than 5000 sol and are active.
Fee Payers and Voting Permissions, Fee Payer Fund: Associated with the above voting accounts.
Newly created voting accounts are initially set to inactive. Users must activate these accounts, and once activated, they will be included in the next VED state calculation. Users can deactivate accounts, which will be removed from the VED state in epoch N+1.
Moving Funds in VED
The fee payer funds account declared in a voting account can be used to move funds in VED. After updating the fee payer funds account in a voting account, the current fee payer funds will remain active until the end of the next epoch. Once the recalculated VED becomes active, the new fee payer funds will enter the VED state and can be used to add additional sols to the fee payer. The old fee payer funds will enter the UED state, so funds can be transferred back to the account in the UED.
While it is possible to merge it with the fee payer, in order to minimize the number of signatures used per vote, the fee payer is typically set to the same value as the voting authority, so a separate account is needed to transfer sols between VED and UED.
Generic Execution Domains
The above approach is very specific to voting programs. It can be further generalized to any number of execution domains. Accounts keep track of the execution domains they are mapped to, just like an OS hypervisor keeps track of VMIDs in physical pages. For this particular project, a small subset of mappings are useful:
RX-UED: read-only and executable in UED
RX-UED-VED: read-only and executable in UED and VED
RW-VED: read-write in VED
RW-UED: read-write in UED
Since accounts cannot read and write in multiple domains simultaneously, the other mappings are invalid.
Programs: can only be mapped as RX-UED or RX-UED-VED, i.e. readable and executable in the user or both domains, but not writable. Any transaction that includes a program as writable must fail.
Accounts: can only be mapped as RW-VED or RW-UED.
The system snapshot configures the voting program and system program as RX-UED-VED. The system program provides the interface to move system and voting program accounts to and from VED. The actual update still takes a full epoch to activate.
Accounts can only be remapped between domains if the owner program is in the target domain. Therefore, only voting program and system program accounts can be moved between VED and UED.
With the universal approach, explicit fee payer funding accounts are no longer required. Developers can remap any system account between VED and UED, moving funds between domains.
Compute VED Hash
After receiving a block, the node executes all transactions. The process is as follows:
Transactions involving only VED accounts are executed.
Transactions with VED fee payers but mixed accounts fail.
All other transactions are skipped.
The resulting VED state update is used to calculate the bank hash, the VED hash. Validators can now vote using the VED hash instead of the old bank hash. If there has been a UED hash update since the last vote on this fork, that hash and slot will be included in the vote.
Compute UED Hash
After receiving a block, the node processes all transactions:
Accounts not in VED are considered part of UED.
Execute transactions involving only UED accounts.
Transactions with UED fee payers but mixed accounts fail.
All other transactions are skipped.
The resulting UED state update is used to compute the bank hash, i.e., the UED hash.
Finality
Finality remains unchanged.
Transaction Execution Status Codes
A side effect of asynchronous execution is that non-determinism may not be synchronously detected immediately upon a vote fork. If 1/3 or more validators submit different UED hashes, all nodes must stop confirming and voting, and alert the operator.
To obtain a status code, the RPC API request must allow the user to specify the observed stake that computes the same UED hash. Execution status can be represented as ExecutionStatus(signature, stake=X), where X can be 0 or some default value. In a setup with multiple nodes and clients, an X value of 0 is acceptable, giving users confidence that non-determinism is extremely unlikely.
Non-determinism is the only failure that needs to be detected before returning a status code for a confirmed transaction to the user, so the default value for X can typically be as low as the optimistic confirmation threshold or about 5%.
Leaders and Block Production
Leaders must be able to start creating blocks as soon as VED completes. This means that leaders will have partial and incomplete information about the state of any UED fee payers.
Feature Activation of Voting Programs
Voting programs must be designed with the assumption that VED votes may cross epoch boundaries when the feature is activated, operating at a different time than other transactions in UED that touch the voting program.
Rewards
Only voting accounts that are in VED state should receive rewards.
Punishments
All voting accounts, whether in active or VED state, are at risk of being slashed.
Global Clock Updates
The clock system variable is updated by voting. An alternative method should be used. Instead, each block leader can move the clock forward by up to 1 second. At least 40% of the network would need to be malicious for the clock to advance faster than actual time. The clock can also be moved forward by 400 milliseconds by default. Assuming a 50 millisecond drift, only 12.5% of leaders would need to adjust their clock time to stay in sync.