From 9e5e7d3ecdd9b5bc094b2eda3f215964d36b81f7 Mon Sep 17 00:00:00 2001 From: clabby Date: Mon, 2 Sep 2024 17:52:00 -0400 Subject: [PATCH] feat(executor): Expose full revm Handler (#475) * feat(executor): Expose full `revm` Handler Improves the API around the `revm` handler in the `StatelessL2BlockExecutor` to allow for a custom `HandleRegister` to be passed into the `StatelessL2BlockExecutorBuilder`. * doc lint lint --- .../{precompiles => handler}/bn128_pair.rs | 0 .../{precompiles => handler}/ecrecover.rs | 0 .../kzg_point_eval.rs | 0 bin/client/src/fault/handler/mod.rs | 49 ++++++++++++++ bin/client/src/fault/mod.rs | 4 +- bin/client/src/fault/precompiles/mod.rs | 65 ------------------- bin/client/src/kona.rs | 9 +-- crates/executor/benches/execution.rs | 3 +- crates/executor/src/builder.rs | 36 +++++----- crates/executor/src/lib.rs | 50 +++++++------- crates/executor/src/precompile.rs | 28 -------- 11 files changed, 93 insertions(+), 151 deletions(-) rename bin/client/src/fault/{precompiles => handler}/bn128_pair.rs (100%) rename bin/client/src/fault/{precompiles => handler}/ecrecover.rs (100%) rename bin/client/src/fault/{precompiles => handler}/kzg_point_eval.rs (100%) create mode 100644 bin/client/src/fault/handler/mod.rs delete mode 100644 bin/client/src/fault/precompiles/mod.rs delete mode 100644 crates/executor/src/precompile.rs diff --git a/bin/client/src/fault/precompiles/bn128_pair.rs b/bin/client/src/fault/handler/bn128_pair.rs similarity index 100% rename from bin/client/src/fault/precompiles/bn128_pair.rs rename to bin/client/src/fault/handler/bn128_pair.rs diff --git a/bin/client/src/fault/precompiles/ecrecover.rs b/bin/client/src/fault/handler/ecrecover.rs similarity index 100% rename from bin/client/src/fault/precompiles/ecrecover.rs rename to bin/client/src/fault/handler/ecrecover.rs diff --git a/bin/client/src/fault/precompiles/kzg_point_eval.rs b/bin/client/src/fault/handler/kzg_point_eval.rs similarity index 100% rename from bin/client/src/fault/precompiles/kzg_point_eval.rs rename to bin/client/src/fault/handler/kzg_point_eval.rs diff --git a/bin/client/src/fault/handler/mod.rs b/bin/client/src/fault/handler/mod.rs new file mode 100644 index 00000000..f1244ef2 --- /dev/null +++ b/bin/client/src/fault/handler/mod.rs @@ -0,0 +1,49 @@ +//! Contains the [KonaHandleRegister] function for registering the FPVM-accelerated precompiles. +//! +//! [KonaHandleRegister]: kona_executor::KonaHandleRegister + +use alloc::sync::Arc; +use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; +use revm::{ + handler::register::EvmHandler, precompile::PrecompileSpecId, primitives::SpecId, + ContextPrecompiles, State, +}; + +mod bn128_pair; +mod ecrecover; +mod kzg_point_eval; + +/// The [KonaHandleRegister] function for registering the FPVM-accelerated precompiles. +/// +/// [KonaHandleRegister]: kona_executor::KonaHandleRegister +pub(crate) fn fpvm_handle_register( + handler: &mut EvmHandler<'_, (), &mut State<&mut TrieDB>>, +) where + F: TrieDBFetcher, + H: TrieDBHinter, +{ + let spec_id = handler.cfg.spec_id; + + handler.pre_execution.load_precompiles = Arc::new(move || { + let mut ctx_precompiles = + ContextPrecompiles::new(PrecompileSpecId::from_spec_id(spec_id)).clone(); + + // Extend with FPVM-accelerated precompiles + let override_precompiles = [ + ecrecover::FPVM_ECRECOVER, + bn128_pair::FPVM_ECPAIRING, + kzg_point_eval::FPVM_KZG_POINT_EVAL, + ]; + ctx_precompiles.extend(override_precompiles); + + // Ensure the secp256r1 P256verify precompile is enabled in the FJORD spec + if spec_id.is_enabled_in(SpecId::FJORD) { + ctx_precompiles.extend([ + // EIP-7212: secp256r1 P256verify + revm::precompile::secp256r1::P256VERIFY, + ]); + } + + ctx_precompiles + }); +} diff --git a/bin/client/src/fault/mod.rs b/bin/client/src/fault/mod.rs index b23805f0..3f55e133 100644 --- a/bin/client/src/fault/mod.rs +++ b/bin/client/src/fault/mod.rs @@ -3,8 +3,8 @@ use kona_common::FileDescriptor; use kona_preimage::{HintWriter, OracleReader, PipeHandle}; -mod precompiles; -pub(crate) use precompiles::FPVMPrecompileOverride; +mod handler; +pub(crate) use handler::fpvm_handle_register; /// The global preimage oracle reader pipe. static ORACLE_READER_PIPE: PipeHandle = diff --git a/bin/client/src/fault/precompiles/mod.rs b/bin/client/src/fault/precompiles/mod.rs deleted file mode 100644 index 1d8e6082..00000000 --- a/bin/client/src/fault/precompiles/mod.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! Contains the [PrecompileOverride] trait implementation for the FPVM-accelerated precompiles. - -use alloc::sync::Arc; -use kona_executor::PrecompileOverride; -use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; -use revm::{ - handler::register::EvmHandler, precompile::PrecompileSpecId, primitives::SpecId, - ContextPrecompiles, State, -}; - -mod bn128_pair; -mod ecrecover; -mod kzg_point_eval; - -/// The [PrecompileOverride] implementation for the FPVM-accelerated precompiles. -#[derive(Debug)] -pub(crate) struct FPVMPrecompileOverride -where - F: TrieDBFetcher, - H: TrieDBHinter, -{ - _phantom: core::marker::PhantomData<(F, H)>, -} - -impl Default for FPVMPrecompileOverride -where - F: TrieDBFetcher, - H: TrieDBHinter, -{ - fn default() -> Self { - Self { _phantom: core::marker::PhantomData::<(F, H)> } - } -} - -impl PrecompileOverride for FPVMPrecompileOverride -where - F: TrieDBFetcher, - H: TrieDBHinter, -{ - fn set_precompiles(handler: &mut EvmHandler<'_, (), &mut State<&mut TrieDB>>) { - let spec_id = handler.cfg.spec_id; - - handler.pre_execution.load_precompiles = Arc::new(move || { - let mut ctx_precompiles = - ContextPrecompiles::new(PrecompileSpecId::from_spec_id(spec_id)).clone(); - - // Extend with FPVM-accelerated precompiles - let override_precompiles = [ - ecrecover::FPVM_ECRECOVER, - bn128_pair::FPVM_ECPAIRING, - kzg_point_eval::FPVM_KZG_POINT_EVAL, - ]; - ctx_precompiles.extend(override_precompiles); - - if spec_id.is_enabled_in(SpecId::FJORD) { - ctx_precompiles.extend([ - // EIP-7212: secp256r1 P256verify - revm::precompile::secp256r1::P256VERIFY, - ]); - } - - ctx_precompiles - }); - } -} diff --git a/bin/client/src/kona.rs b/bin/client/src/kona.rs index c37bd5f7..f6a0ad72 100644 --- a/bin/client/src/kona.rs +++ b/bin/client/src/kona.rs @@ -16,11 +16,10 @@ use kona_client::{ }; use kona_common_proc::client_entry; use kona_executor::StatelessL2BlockExecutor; -use kona_preimage::{HintWriter, OracleReader}; use kona_primitives::L2AttributesWithParent; pub(crate) mod fault; -use fault::{FPVMPrecompileOverride, HINT_WRITER, ORACLE_READER}; +use fault::{fpvm_handle_register, HINT_WRITER, ORACLE_READER}; /// The size of the LRU cache in the oracle. const ORACLE_LRU_SIZE: usize = 1024; @@ -61,15 +60,11 @@ fn main() -> Result<()> { .await?; let L2AttributesWithParent { attributes, .. } = driver.produce_disputed_payload().await?; - let precompile_overrides = FPVMPrecompileOverride::< - OracleL2ChainProvider>, - OracleL2ChainProvider>, - >::default(); let mut executor = StatelessL2BlockExecutor::builder(&boot.rollup_config) .with_parent_header(driver.take_l2_safe_head_header()) .with_fetcher(l2_provider.clone()) .with_hinter(l2_provider) - .with_precompile_overrides(precompile_overrides) + .with_handle_register(fpvm_handle_register) .build()?; let Header { number, .. } = *executor.execute_payload(attributes)?; let output_root = executor.compute_output_root()?; diff --git a/crates/executor/benches/execution.rs b/crates/executor/benches/execution.rs index 72241dc7..93a89e89 100644 --- a/crates/executor/benches/execution.rs +++ b/crates/executor/benches/execution.rs @@ -5,7 +5,7 @@ use alloy_primitives::{address, b256, hex, Bytes, B256}; use alloy_rlp::Decodable; use anyhow::{anyhow, Result}; use criterion::{criterion_group, criterion_main, Bencher, Criterion}; -use kona_executor::{NoPrecompileOverride, StatelessL2BlockExecutor}; +use kona_executor::StatelessL2BlockExecutor; use kona_mpt::{NoopTrieDBHinter, TrieDBFetcher}; use kona_primitives::{ L2PayloadAttributes, RollupConfig, OP_BASE_FEE_PARAMS, OP_CANYON_BASE_FEE_PARAMS, @@ -81,7 +81,6 @@ fn op_mainnet_exec_bench( .with_parent_header(pre_state_header.clone().seal_slow()) .with_fetcher(TestdataTrieDBFetcher::new(data_folder)) .with_hinter(NoopTrieDBHinter) - .with_precompile_overrides(NoPrecompileOverride) .build() .unwrap(); l2_block_executor.execute_payload(payload_attrs.clone()).unwrap(); diff --git a/crates/executor/src/builder.rs b/crates/executor/src/builder.rs index d1e02830..62a1a06d 100644 --- a/crates/executor/src/builder.rs +++ b/crates/executor/src/builder.rs @@ -1,46 +1,43 @@ //! Contains the builder pattern for the [StatelessL2BlockExecutor]. -use crate::{PrecompileOverride, StatelessL2BlockExecutor}; +use crate::StatelessL2BlockExecutor; use alloy_consensus::{Header, Sealable, Sealed}; use anyhow::Result; use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; use kona_primitives::RollupConfig; +use revm::{db::State, handler::register::EvmHandler}; + +/// A type alias for the [revm::handler::register::HandleRegister] for kona's block executor. +pub type KonaHandleRegister = + for<'i> fn(&mut EvmHandler<'i, (), &mut State<&mut TrieDB>>); /// The builder pattern for the [StatelessL2BlockExecutor]. #[derive(Debug)] -pub struct StatelessL2BlockExecutorBuilder<'a, F, H, PO> +pub struct StatelessL2BlockExecutorBuilder<'a, F, H> where F: TrieDBFetcher, H: TrieDBHinter, - PO: PrecompileOverride, { /// The [RollupConfig]. config: &'a RollupConfig, /// The parent [Header] to begin execution from. parent_header: Option>, - /// The precompile overrides to use during execution. - precompile_overrides: Option, + /// The [KonaHandleRegister] to use during execution. + handler_register: Option>, /// The [TrieDBFetcher] to fetch the state trie preimages. fetcher: Option, /// The [TrieDBHinter] to hint the state trie preimages. hinter: Option, } -impl<'a, F, H, PO> StatelessL2BlockExecutorBuilder<'a, F, H, PO> +impl<'a, F, H> StatelessL2BlockExecutorBuilder<'a, F, H> where F: TrieDBFetcher, H: TrieDBHinter, - PO: PrecompileOverride, { /// Instantiate a new builder with the given [RollupConfig]. pub fn with_config(config: &'a RollupConfig) -> Self { - Self { - config, - parent_header: None, - precompile_overrides: None, - fetcher: None, - hinter: None, - } + Self { config, parent_header: None, handler_register: None, fetcher: None, hinter: None } } /// Set the [Header] to begin execution from. @@ -61,14 +58,14 @@ where self } - /// Set the precompile overrides to use during execution. - pub fn with_precompile_overrides(mut self, precompile_overrides: PO) -> Self { - self.precompile_overrides = Some(precompile_overrides); + /// Set the [KonaHandleRegister] for execution. + pub fn with_handle_register(mut self, handler_register: KonaHandleRegister) -> Self { + self.handler_register = Some(handler_register); self } /// Build the [StatelessL2BlockExecutor] from the builder configuration. - pub fn build(self) -> Result> { + pub fn build(self) -> Result> { let fetcher = self.fetcher.ok_or(anyhow::anyhow!("Fetcher not set"))?; let hinter = self.hinter.ok_or(anyhow::anyhow!("Hinter not set"))?; let parent_header = self.parent_header.unwrap_or_else(|| { @@ -77,11 +74,10 @@ where }); let trie_db = TrieDB::new(parent_header.state_root, parent_header, fetcher, hinter); - Ok(StatelessL2BlockExecutor { config: self.config, trie_db, - _phantom: core::marker::PhantomData::, + handler_register: self.handler_register, }) } } diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index ae2e1064..3b787739 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -26,10 +26,7 @@ use revm::{ use tracing::{debug, info}; mod builder; -pub use builder::StatelessL2BlockExecutorBuilder; - -mod precompile; -pub use precompile::{NoPrecompileOverride, PrecompileOverride}; +pub use builder::{KonaHandleRegister, StatelessL2BlockExecutorBuilder}; mod eip4788; use eip4788::pre_block_beacon_root_contract_call; @@ -43,28 +40,26 @@ use util::{extract_tx_gas_limit, is_system_transaction, logs_bloom, receipt_enve /// The block executor for the L2 client program. Operates off of a [TrieDB] backed [State], /// allowing for stateless block execution of OP Stack blocks. #[derive(Debug)] -pub struct StatelessL2BlockExecutor<'a, F, H, PO> +pub struct StatelessL2BlockExecutor<'a, F, H> where F: TrieDBFetcher, H: TrieDBHinter, - PO: PrecompileOverride, { /// The [RollupConfig]. config: &'a RollupConfig, /// The inner state database component. trie_db: TrieDB, - /// Phantom data for the precompile overrides. - _phantom: core::marker::PhantomData, + /// The [KonaHandleRegister] to use during execution. + handler_register: Option>, } -impl<'a, F, H, PO> StatelessL2BlockExecutor<'a, F, H, PO> +impl<'a, F, H> StatelessL2BlockExecutor<'a, F, H> where F: TrieDBFetcher, H: TrieDBHinter, - PO: PrecompileOverride, { /// Constructs a new [StatelessL2BlockExecutorBuilder] with the given [RollupConfig]. - pub fn builder(config: &'a RollupConfig) -> StatelessL2BlockExecutorBuilder<'a, F, H, PO> { + pub fn builder(config: &'a RollupConfig) -> StatelessL2BlockExecutorBuilder<'a, F, H> { StatelessL2BlockExecutorBuilder::with_config(config) } @@ -128,15 +123,22 @@ where // Construct the block-scoped EVM with the given configuration. // The transaction environment is set within the loop for each transaction. - let mut evm = Evm::builder() - .with_db(&mut state) - .with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env( - initialized_cfg.clone(), - initialized_block_env.clone(), - Default::default(), - )) - .append_handler_register(PO::set_precompiles) - .build(); + let mut evm = { + let mut base = Evm::builder().with_db(&mut state).with_env_with_handler_cfg( + EnvWithHandlerCfg::new_with_cfg_env( + initialized_cfg.clone(), + initialized_block_env.clone(), + Default::default(), + ), + ); + + // If a handler register is provided, append it to the base EVM. + if let Some(handler) = self.handler_register { + base = base.append_handler_register(handler); + } + + base.build() + }; // Execute the transactions in the payload. let transactions = payload @@ -223,7 +225,7 @@ where cumulative_gas_used = cumulative_gas_used ); - // Drop the EVM to rid the exclusive reference to the database. + // Drop the EVM to free the exclusive reference to the database. drop(evm); // Merge all state transitions into the cache state. @@ -699,7 +701,6 @@ mod test { .with_parent_header(header.seal_slow()) .with_fetcher(TestdataTrieDBFetcher::new("block_120794432_exec")) .with_hinter(NoopTrieDBHinter) - .with_precompile_overrides(NoPrecompileOverride) .build() .unwrap(); @@ -753,7 +754,6 @@ mod test { .with_parent_header(parent_header.seal_slow()) .with_fetcher(TestdataTrieDBFetcher::new("block_121049889_exec")) .with_hinter(NoopTrieDBHinter) - .with_precompile_overrides(NoPrecompileOverride) .build() .unwrap(); @@ -811,7 +811,6 @@ mod test { .with_parent_header(parent_header.seal_slow()) .with_fetcher(TestdataTrieDBFetcher::new("block_121003241_exec")) .with_hinter(NoopTrieDBHinter) - .with_precompile_overrides(NoPrecompileOverride) .build() .unwrap(); @@ -876,7 +875,6 @@ mod test { .with_parent_header(parent_header.seal_slow()) .with_fetcher(TestdataTrieDBFetcher::new("block_121057303_exec")) .with_hinter(NoopTrieDBHinter) - .with_precompile_overrides(NoPrecompileOverride) .build() .unwrap(); @@ -935,7 +933,6 @@ mod test { .with_parent_header(parent_header.seal_slow()) .with_fetcher(TestdataTrieDBFetcher::new("block_121065789_exec")) .with_hinter(NoopTrieDBHinter) - .with_precompile_overrides(NoPrecompileOverride) .build() .unwrap(); @@ -1003,7 +1000,6 @@ mod test { .with_parent_header(parent_header.seal_slow()) .with_fetcher(TestdataTrieDBFetcher::new("block_121135704_exec")) .with_hinter(NoopTrieDBHinter) - .with_precompile_overrides(NoPrecompileOverride) .build() .unwrap(); diff --git a/crates/executor/src/precompile.rs b/crates/executor/src/precompile.rs deleted file mode 100644 index be086252..00000000 --- a/crates/executor/src/precompile.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Contains the [PrecompileOverride] trait. - -use kona_mpt::{TrieDB, TrieDBFetcher, TrieDBHinter}; -use revm::{db::State, handler::register::EvmHandler}; - -/// A trait for defining precompile overrides during execution. -pub trait PrecompileOverride -where - F: TrieDBFetcher, - H: TrieDBHinter, -{ - /// Set the precompiles to use during execution. - fn set_precompiles(handler: &mut EvmHandler<'_, (), &mut State<&mut TrieDB>>); -} - -/// Default implementation of [PrecompileOverride], which does not override any precompiles. -#[derive(Debug, Default)] -pub struct NoPrecompileOverride; - -impl PrecompileOverride for NoPrecompileOverride -where - F: TrieDBFetcher, - H: TrieDBHinter, -{ - fn set_precompiles(_: &mut EvmHandler<'_, (), &mut State<&mut TrieDB>>) { - // Do nothing - } -}