-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(client):
StatelessL2BlockExecutor
(#210)
* feat(client): `StatelessL2BlockExecutor` Implements the `StatelessL2BlockExecutor`, capable of executing `L2PayloadAttributes` and returning the block header + receipts. The `StatelessL2BlockExecutor` is backed by the `TrieDB` in the `kona-mpt` crate, fetching necessary data from the initial state root as well as the starting parent block hash. * fix(client): Trie fixes, etc. x trim test files * big block testin' * checkpoint: big block works * checkpoint dep reordering * cleanups * more tests
- Loading branch information
Showing
34 changed files
with
6,021 additions
and
573 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
//! Contains logic specific to Canyon hardfork activation. | ||
use alloy_consensus::Header; | ||
use alloy_primitives::{address, b256, hex, Address, Bytes, B256}; | ||
use kona_derive::types::RollupConfig; | ||
use revm::{ | ||
primitives::{Account, Bytecode, HashMap}, | ||
DatabaseCommit, State, | ||
}; | ||
|
||
/// The address of the create2 deployer | ||
const CREATE_2_DEPLOYER_ADDR: Address = address!("13b0D85CcB8bf860b6b79AF3029fCA081AE9beF2"); | ||
|
||
/// The codehash of the create2 deployer contract. | ||
const CREATE_2_DEPLOYER_CODEHASH: B256 = | ||
b256!("b0550b5b431e30d38000efb7107aaa0ade03d48a7198a140edda9d27134468b2"); | ||
|
||
/// The raw bytecode of the create2 deployer contract. | ||
const CREATE_2_DEPLOYER_BYTECODE: [u8; 1584] = hex!("6080604052600436106100435760003560e01c8063076c37b21461004f578063481286e61461007157806356299481146100ba57806366cfa057146100da57600080fd5b3661004a57005b600080fd5b34801561005b57600080fd5b5061006f61006a366004610327565b6100fa565b005b34801561007d57600080fd5b5061009161008c366004610327565b61014a565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100c657600080fd5b506100916100d5366004610349565b61015d565b3480156100e657600080fd5b5061006f6100f53660046103ca565b610172565b61014582826040518060200161010f9061031a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f90910116604052610183565b505050565b600061015683836102e7565b9392505050565b600061016a8484846102f0565b949350505050565b61017d838383610183565b50505050565b6000834710156101f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b815160000361025f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f60448201526064016101eb565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610156576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f790000000000000060448201526064016101eb565b60006101568383305b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61014e806104ad83390190565b6000806040838503121561033a57600080fd5b50508035926020909101359150565b60008060006060848603121561035e57600080fd5b8335925060208401359150604084013573ffffffffffffffffffffffffffffffffffffffff8116811461039057600080fd5b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156103df57600080fd5b8335925060208401359150604084013567ffffffffffffffff8082111561040557600080fd5b818601915086601f83011261041957600080fd5b81358181111561042b5761042b61039b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156104715761047161039b565b8160405282815289602084870101111561048a57600080fd5b826020860160208301376000602084830101528095505050505050925092509256fe608060405234801561001057600080fd5b5061012e806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063249cb3fa14602d575b600080fd5b603c603836600460b1565b604e565b60405190815260200160405180910390f35b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16608857600060aa565b7fa2ef4600d742022d532d4747cb3547474667d6f13804902513b2ec01c848f4b45b9392505050565b6000806040838503121560c357600080fd5b82359150602083013573ffffffffffffffffffffffffffffffffffffffff8116811460ed57600080fd5b80915050925092905056fea26469706673582212205ffd4e6cede7d06a5daf93d48d0541fc68189eeb16608c1999a82063b666eb1164736f6c63430008130033a2646970667358221220fdc4a0fe96e3b21c108ca155438d37c9143fb01278a3c1d274948bad89c564ba64736f6c63430008130033"); | ||
|
||
/// The Canyon hardfork issues an irregular state transition that force-deploys the create2 | ||
/// deployer contract. This is done by directly setting the code of the create2 deployer account | ||
/// prior to executing any transactions on the timestamp activation of the fork. | ||
pub(crate) fn ensure_create2_deployer_canyon<DB>( | ||
db: &mut State<DB>, | ||
config: &RollupConfig, | ||
timestamp: u64, | ||
parent_header: &Header, | ||
) -> Result<(), DB::Error> | ||
where | ||
DB: revm::Database, | ||
{ | ||
// If the canyon hardfork is active at the current timestamp, and it was not active at the | ||
// previous block timestamp, then we need to force-deploy the create2 deployer contract. | ||
if config.is_canyon_active(timestamp) && !config.is_canyon_active(parent_header.timestamp) { | ||
// Load the create2 deployer account from the cache. | ||
let acc = db.load_cache_account(CREATE_2_DEPLOYER_ADDR)?; | ||
|
||
// Update the account info with the create2 deployer codehash and bytecode. | ||
let mut acc_info = acc.account_info().unwrap_or_default(); | ||
acc_info.code_hash = CREATE_2_DEPLOYER_CODEHASH; | ||
acc_info.code = Some(Bytecode::new_raw(Bytes::from_static(&CREATE_2_DEPLOYER_BYTECODE))); | ||
|
||
// Convert the cache account back into a revm account and mark it as touched. | ||
let mut revm_acc: Account = acc_info.into(); | ||
revm_acc.mark_touch(); | ||
|
||
// Commit the create2 deployer account to the database. | ||
db.commit(HashMap::from([(CREATE_2_DEPLOYER_ADDR, revm_acc)])); | ||
return Ok(()); | ||
} | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
//! Contains the logic for executing the pre-block beacon root call. | ||
use alloc::{boxed::Box, vec::Vec}; | ||
use alloy_consensus::constants::BEACON_ROOTS_ADDRESS; | ||
use alloy_primitives::{Address, Bytes, B256, U256}; | ||
use anyhow::{anyhow, Result}; | ||
use kona_derive::types::{L2PayloadAttributes, RollupConfig}; | ||
use revm::{ | ||
primitives::{ | ||
BlockEnv, CfgEnvWithHandlerCfg, Env, EnvWithHandlerCfg, OptimismFields, TransactTo, TxEnv, | ||
}, | ||
Database, DatabaseCommit, Evm, | ||
}; | ||
|
||
/// Execute the EIP-4788 pre-block beacon root contract call. | ||
pub(crate) fn pre_block_beacon_root_contract_call<DB: Database + DatabaseCommit>( | ||
db: &mut DB, | ||
config: &RollupConfig, | ||
block_number: u64, | ||
initialized_cfg: &CfgEnvWithHandlerCfg, | ||
initialized_block_env: &BlockEnv, | ||
payload: &L2PayloadAttributes, | ||
) -> Result<()> | ||
where | ||
DB::Error: core::fmt::Display, | ||
{ | ||
// apply pre-block EIP-4788 contract call | ||
let mut evm_pre_block = Evm::builder() | ||
.with_db(db) | ||
.with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env( | ||
initialized_cfg.clone(), | ||
initialized_block_env.clone(), | ||
Default::default(), | ||
)) | ||
.build(); | ||
|
||
// initialize a block from the env, because the pre block call needs the block itself | ||
apply_beacon_root_contract_call( | ||
config, | ||
payload.timestamp, | ||
block_number, | ||
payload.parent_beacon_block_root, | ||
&mut evm_pre_block, | ||
) | ||
} | ||
|
||
/// Apply the EIP-4788 pre-block beacon root contract call to a given EVM instance. | ||
fn apply_beacon_root_contract_call<EXT, DB: Database + DatabaseCommit>( | ||
config: &RollupConfig, | ||
timestamp: u64, | ||
block_number: u64, | ||
parent_beacon_block_root: Option<B256>, | ||
evm: &mut Evm<'_, EXT, DB>, | ||
) -> Result<()> | ||
where | ||
DB::Error: core::fmt::Display, | ||
{ | ||
if !config.is_ecotone_active(timestamp) { | ||
return Ok(()); | ||
} | ||
|
||
let parent_beacon_block_root = | ||
parent_beacon_block_root.ok_or(anyhow!("missing parent beacon block root"))?; | ||
|
||
// if the block number is zero (genesis block) then the parent beacon block root must | ||
// be 0x0 and no system transaction may occur as per EIP-4788 | ||
if block_number == 0 { | ||
if parent_beacon_block_root != B256::ZERO { | ||
anyhow::bail!("Cancun genesis block parent beacon block root must be 0x0"); | ||
} | ||
return Ok(()); | ||
} | ||
|
||
// Get the previous environment | ||
let previous_env = Box::new(evm.context.evm.env().clone()); | ||
|
||
// modify env for pre block call | ||
fill_tx_env_with_beacon_root_contract_call(&mut evm.context.evm.env, parent_beacon_block_root); | ||
|
||
let mut state = match evm.transact() { | ||
Ok(res) => res.state, | ||
Err(e) => { | ||
evm.context.evm.env = previous_env; | ||
anyhow::bail!("Failed to execute pre block call: {}", e); | ||
} | ||
}; | ||
|
||
state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS); | ||
state.remove(&evm.block().coinbase); | ||
|
||
evm.context.evm.db.commit(state); | ||
|
||
// re-set the previous env | ||
evm.context.evm.env = previous_env; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Fill transaction environment with the EIP-4788 system contract message data. | ||
/// | ||
/// This requirements for the beacon root contract call defined by | ||
/// [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) are: | ||
/// | ||
/// At the start of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. | ||
/// before processing any transactions), call [`BEACON_ROOTS_ADDRESS`] as | ||
/// [`SYSTEM_ADDRESS`](alloy_eips::eip4788::SYSTEM_ADDRESS) with the 32-byte input of | ||
/// `header.parent_beacon_block_root`. This will trigger the `set()` routine of the beacon roots | ||
/// contract. | ||
fn fill_tx_env_with_beacon_root_contract_call(env: &mut Env, parent_beacon_block_root: B256) { | ||
fill_tx_env_with_system_contract_call( | ||
env, | ||
alloy_eips::eip4788::SYSTEM_ADDRESS, | ||
BEACON_ROOTS_ADDRESS, | ||
parent_beacon_block_root.0.into(), | ||
); | ||
} | ||
|
||
/// Fill transaction environment with the system caller and the system contract address and message | ||
/// data. | ||
/// | ||
/// This is a system operation and therefore: | ||
/// * the call must execute to completion | ||
/// * the call does not count against the block’s gas limit | ||
/// * the call does not follow the EIP-1559 burn semantics - no value should be transferred as part | ||
/// of the call | ||
/// * if no code exists at the provided address, the call will fail silently | ||
fn fill_tx_env_with_system_contract_call( | ||
env: &mut Env, | ||
caller: Address, | ||
contract: Address, | ||
data: Bytes, | ||
) { | ||
env.tx = TxEnv { | ||
caller, | ||
transact_to: TransactTo::Call(contract), | ||
// Explicitly set nonce to None so revm does not do any nonce checks | ||
nonce: None, | ||
gas_limit: 30_000_000, | ||
value: U256::ZERO, | ||
data, | ||
// Setting the gas price to zero enforces that no value is transferred as part of the call, | ||
// and that the call will not count against the block's gas limit | ||
gas_price: U256::ZERO, | ||
// The chain ID check is not relevant here and is disabled if set to None | ||
chain_id: None, | ||
// Setting the gas priority fee to None ensures the effective gas price is derived from the | ||
// `gas_price` field, which we need to be zero | ||
gas_priority_fee: None, | ||
access_list: Vec::new(), | ||
// blob fields can be None for this tx | ||
blob_hashes: Vec::new(), | ||
max_fee_per_blob_gas: None, | ||
optimism: OptimismFields { | ||
source_hash: None, | ||
mint: None, | ||
is_system_transaction: Some(false), | ||
// The L1 fee is not charged for the EIP-4788 transaction, submit zero bytes for the | ||
// enveloped tx size. | ||
enveloped_tx: Some(Bytes::default()), | ||
}, | ||
}; | ||
|
||
// ensure the block gas limit is >= the tx | ||
env.block.gas_limit = U256::from(env.tx.gas_limit); | ||
|
||
// disable the base fee check for this call by setting the base fee to zero | ||
env.block.basefee = U256::ZERO; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
//! Contains the fetcher construction functions for the block executor's [TrieDB]. | ||
//! | ||
//! [TrieDB]: kona_mpt::TrieDB | ||
use crate::CachingOracle; | ||
use alloy_consensus::Header; | ||
use alloy_primitives::{Bytes, B256}; | ||
use alloy_rlp::Decodable; | ||
use anyhow::{anyhow, Result}; | ||
use kona_mpt::TrieDBFetcher; | ||
use kona_preimage::{PreimageKey, PreimageKeyType, PreimageOracleClient}; | ||
|
||
/// The [TrieDBFetcher] implementation for the block executor's [TrieDB]. | ||
/// | ||
/// TODO: Move this into the higher-level L2 chain fetcher, and also implement the [TrieDBFetcher] | ||
/// trait. | ||
/// | ||
/// [TrieDB]: kona_mpt::TrieDB | ||
#[derive(Debug)] | ||
pub struct TrieDBProvider<'a, const N: usize> { | ||
/// The inner caching oracle to fetch trie node preimages from. | ||
caching_oracle: &'a CachingOracle<N>, | ||
} | ||
|
||
impl<'a, const N: usize> TrieDBProvider<'a, N> { | ||
/// Constructs a new [TrieDBProvider] with the given [CachingOracle]. | ||
pub fn new(caching_oracle: &'a CachingOracle<N>) -> Self { | ||
Self { caching_oracle } | ||
} | ||
} | ||
|
||
impl<'a, const N: usize> TrieDBFetcher for TrieDBProvider<'a, N> { | ||
fn trie_node_preimage(&self, key: B256) -> Result<Bytes> { | ||
// Fetch the trie preimage from the caching oracle. | ||
kona_common::block_on(async move { | ||
self.caching_oracle | ||
.get(PreimageKey::new(*key, PreimageKeyType::Keccak256)) | ||
.await | ||
.map(Into::into) | ||
}) | ||
} | ||
|
||
fn bytecode_by_hash(&self, _: B256) -> Result<Bytes> { | ||
todo!() | ||
} | ||
|
||
fn header_by_hash(&self, hash: B256) -> Result<Header> { | ||
// Fetch the header from the caching oracle. | ||
kona_common::block_on(async move { | ||
let header_bytes = self | ||
.caching_oracle | ||
.get(PreimageKey::new(*hash, PreimageKeyType::Keccak256)) | ||
.await?; | ||
Header::decode(&mut header_bytes.as_slice()) | ||
.map_err(|e| anyhow!("Failed to RLP decode Header: {e}")) | ||
}) | ||
} | ||
} |
Oops, something went wrong.