Overview
This document details the steps I believe are necessary for Solana to transition towards a fully asynchronous-execution blockchain. In summary, the steps are as follows:
- Relax Transaction-level Constraints
- Relax Entry-level Constraints
- Final Constraint Relaxation
These three steps and the reasons for them are detailed in the following sections.
Requirements
For Solana to have asynchronous execution, there is one primary requirement: block-verification cannot rely on account-state. Account-state is necessarily dependent on the results of previous transactions. If the verification of a block requires the results of previous transactions, then the block cannot be executed asynchronously.
Current Constraints
The Solana protocol currently places many constraints at different levels. In this document, we will focus specifically on constraints that are placed on the actual contents of a block produced by a leader. These constraints come at three levels of abstraction, from highest to lowest level:
- Block-level constraints
- Entry-level constraints
- Transaction-level constraints
If constraints are broken at any of these levels, the entire block is deemed invalid by the network and is not included in the history moving forward. Thus it is important for the block-producer to ensure that all constraints are met before submitting the block to the network. However, many of these constraints are unnecessarily restrictive and can be relaxed to allow for optimizations as well as asynchronous execution.
It is important to state, that while we are proposing the relaxation of some of these constraints at the protocol level, this is not a proposal for changing the current requirements for a transaction to be executed. The relaxation of these constraint simply means that block-validation will not reject the entire block if one of the relaxed constraints is broken. For example, a transaction that cannot pay fees should not be executed in either the current protocol or with proposed changes, but with the proposed changes the rest of block is not marked as invalid.
Block-level Constraints
Block-level constraints are constraints that are placed on the entire block in aggregate. These constraints include, but are not limited to:
- The block must include 64 ticks
- The last entry in the block must be a tick
- Transactions within all block-entries must be within block limits (this is pending feature activation)
Entry-level Constraints
Entry-level constraints are constraints that are placed on the entries within a block. These constraints include, but are not limited to:
- Transactions within an entry cannot conflict with eachother
Transaction-level Constraints
Transaction-level constraints are constraints that are placed on individual transactions. Broadly, we can break these constraints into three main categories:
- Static constraints - constraints that can be verified without any additional state
- Bank-state constraints - constraints that can be verified with only the transaction and bank-state
- Account-state constraints - constraints that require account-state to verify
Static Transaction Constraints
These constraints are generally straight-forward to verify and there is no need to make any protocol changes on the constraints within this category to enable asynchronous execution in the protocol. These constraints include, but are not limited to:
- Transaction data-format compliance (i.e. transaction must be able to deserialize)
- Signature verification
- Transaction size
- Number of accounts
Bank-state Transaction Constraints
These constraints are a smaller category but require some additional state in order to verify the constraint. These constraints include, but are not limited to:
- Unique signature verification - i.e. the transaction is not already included in a recent block
- Age verification - i.e. the transaction is not too old to be included in a block
It is clear from the above list that these constraints require simple state knowledge of what transactions have been included in recent blocks, as well as the blockhashes of recent blocks. These constraints also do not get in the way of asynchronous execution, as they do not rely on account-state.
Account-state Transaction Constraints
These constraints are the most restrictive and require account-state to verify. These constraints include, but are not limited to:
- Address Lookup Table (ALT) resolution
- Nonce checks
- Fee-payer checks
- Executable checks - i.e. the transaction must be executable
“Executable checks” is more of a category of constraints rather than a single constraint, but for brevity we will refer to it as a single constraint. As a whole, this category of errors prevents asynchronous execution since the results of previous transactions are required to verify the constraints.
Executable Checks
- A valid requested_loaded_accounts_data_size_limit (pending
DdLwVYuvDz26JohmgSbA7mjpJFgX5zP2dkp8qsF2C33V
) - this is a static check, but the actual data size check later is not - Loaded account size does not exceed requested_loaded_accounts_data_size_limit (pending
DdLwVYuvDz26JohmgSbA7mjpJFgX5zP2dkp8qsF2C33V
) - Program accounts must be found
- Program accounts must be executable
- Account cannot be executable and writable if owner is not
BPFLoaderUpgradeab1e11111111111111111111111
BPFLoaderUpgradeab1e11111111111111111111111
must be present in transaction if account owned byBPFLoaderUpgradeab1e11111111111111111111111
and is writable- Executable
BPFLoaderUpgradeab1e11111111111111111111111
owned accounts must haveUpgradeableLoaderState::Program
state, and derived program data account must be found - If a writable account is owned by
Stake11111111111111111111111111111111111111
and the current slot cannot be within the epoch stakes reward distribution period (what does this mean?) - Cannot contain builtin loader ownership chains (pending
4UDcAfQ6EcA6bdcadkeHpkarkhZGJ7Bpq7wTAiRMjkoi
) - Call chains cannot exceed a depth of 5
- Must have valid compute budget instructions - this is a static check, and should be kept
A Possible Path Forward
In order to enable asynchronous execution, it is necessary to eliminate any depedence on account-state for block-verification. This means that all transaction-level account-state constraints must be removed. This also means that the entry-level constraint on account conflicts must be removed, since this constraint is inherently dependent on ALT resolution. Finally, the block-level constraint on block account write limits must be removed, since this constraint is also inherently dependent on ALT resolution.
A possible list of steps forward to achieve this goal are as follows:
- Relax Transaction-level Constraints
- Relax Entry-level Constraints
- Final Constraint Relaxation
Step 1: Relax Transaction-Level Constraints
The initial step can be to relax some, but not all, of the transaction-level account-state contraints. These constraints can be removed, with the exception of ALT resolution. ALT resolution cannot be removed at this step since it is required for both entry-level and block-level constraint verification.
It is important to note, that removing these constraints does not mean that transactions which break these previous constraints will be executed; they will not be. The relaxation of these constraints simply mean that the validators will not reject the entire block if a transaction breaks one of these constraints; that transaction will simply have no, or limited, state changes. By relaxing these constraints, it also gives the block-producer more flexibility in scheduling algorithms, and enables asynchronous execution during block-production.
This step also enables significant optimizations within block-verification; however, asynchronous execution is still not possible. The only remaining transaction-level account-state constraint is ALT resolution. ALT resolution is done with the state of ALT at the beginning of the slot, so this only requires we have the state of previous blocks, but do not need to execute any transactions within the block to verify our constraints.
Step 2: Relax Entry-Level Constraints
A next step, although this could technically be done in parallel with step 1, is to remove the entry-level constraint on self-conflicting transaction entries. This constraint is inherently dependent on ALT resolution, and thus blocks the removal of ALT resolution constraints at the transaction-level.
This step does not enable much more flexibility in block-production but does allow for some optimizations to be done with respect to entry creation. It should be acknowledged, that this relaxation would also greatly simplify the Jito transaction bundling, since all transactions within a bundle can go into a single entry.
Step 3: Final Constraint Relaxation
After the completion of steps 1 and 2, the final step is to remove the remaining transaction-level ALT resolution constraint, as well as block-level account write limit constraints. This is the most challenging, and likely controversial step and proposed change. By removing the block-level account write limit constraint, this allows for the possibility of blocks which are too expensive to execute within the 400ms time slot. However, this change also enables asynchronous execution during block-verification, which means that validators don’t necessarily need to execute the entire block within the 400ms time slot.
Drawbacks
There are several potential drawbacks to the proposed changes. Here, we will outline some of the potential drawbacks, breaking them up by the three steps outlined in the previous sections.
Drawbacks to Step 1
Drawbacks from step 1 mainly stem from the fact that we are relaxing the constraint that transactions must pay fees.
Malicious Leader Spams Non-Fee Paying Transactions
It is possible that this could lead to a spam attack, where a malicious leader could submit a large number of transactions that do not pay fees. However, since these transactions will not be executed, the only cost to the network is the cost of checking balance before execution, which is already something that must be done. The block-producer is only losing out on income by producing the block with such transactions in it.
There have been several proposals and discussions about the possibility of dynamic fees on Solana, which are updated based on the current network load, as measured by the CU utilization. If such dynamic fees reward the leader in any way, this incentivizes leaders to include dummy transactions in their blocks to increase their reward in subsequent blocks. If dynamic fees do not reward the leader, then the leader has no incentive to produce a block with non-fee paying transactions.
Malicious Users Spam Non-Fee Paying Transactions
By removing the constraint that transactions within the block must pay fees, this opens the possibility for malicious users to identify a bug and spam the network with non-fee paying transactions. This is a valid concern around the robustness of checks done by the block-producer, but again, the only cost to the network is the cost of checking balance before execution, which is already something that must be done. Currently, if such a bug is found, the malicious user can spam the network and get the leader’s entire block thrown out.
Drawbacks to Step 2
Drawbacks from step 2 are fairly minimal. The only potential drawback from this relaxation is that we can no longer easily/naively assume transactions within an entry can be executed in parallel.
Drawbacks to Step 3
Drawbacks from step 3 are the most significant, and the most controversial.
Blocks Too Expensive to Execute
The most obvious drawback is that this enables the possibility of blocks that are too expensive to execute within the 400ms time slot. This would mean that account state is not synchronous with the head of the blockchain, which could have serious impact within DeFi. However, this potentially offers an opportunity for specialized RPC providers which can provide synchronous account state for some subset of accounts, with replay prioritizing only transactions necessary to keep the desired state up-to-date. Likely, the best way to prevent this sort of attack is to introduce some sort of congestion control to keep all blocks from being full. This could possibly be achieved via some sort of dynamic fees.
Development Process
In the previous sections, we outlined a possible path forward to enable asynchronous execution. Whether or not Solana will actually move to an asynchronous execution future is still up for debate. However, I believe that the steps outlined in the previous sections are a good starting point for discussion. All three steps are consensus breaking, and thus should go through the SIMD process before any implementation is done.
Steps 1 and 2 do not bind us to any particular future path towards asynchronous execution and can be done independently of step 3. These two initial steps also offer significant advantages in terms of optimizations and flexibility in block-production. Because these two steps are independent of eachother, and indpendent of step 3, at minimum I beleive the process here should go through 3 SIMDs.
Step 3 is the most controversial, and there will likely be a lot of discussion and push-back on that SIMD. The other two, are relatively straight-forward and it’s unexpected for significant push-back; these are things that have been discussed for a long time and have just not been prioritized.