Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions crates/optimism/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extern crate alloc;
use alloc::sync::Arc;
use alloy_consensus::{BlockHeader, Header};
use alloy_eips::Decodable2718;
use alloy_evm::{FromRecoveredTx, FromTxWithEncoded};
use alloy_evm::{EvmFactory, FromRecoveredTx, FromTxWithEncoded};
use alloy_op_evm::block::receipt_builder::OpReceiptBuilder;
use alloy_primitives::U256;
use core::fmt::Debug;
Expand All @@ -23,7 +23,8 @@ use op_alloy_rpc_types_engine::OpExecutionData;
use op_revm::{OpSpecId, OpTransaction};
use reth_chainspec::EthChainSpec;
use reth_evm::{
ConfigureEngineEvm, ConfigureEvm, EvmEnv, EvmEnvFor, ExecutableTxIterator, ExecutionCtxFor,
precompiles::PrecompilesMap, ConfigureEngineEvm, ConfigureEvm, EvmEnv, EvmEnvFor,
ExecutableTxIterator, ExecutionCtxFor, TransactionEnv,
};
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_forks::OpHardforks;
Expand Down Expand Up @@ -60,15 +61,19 @@ pub struct OpEvmConfig<
ChainSpec = OpChainSpec,
N: NodePrimitives = OpPrimitives,
R = OpRethReceiptBuilder,
EvmFactory = OpEvmFactory,
> {
/// Inner [`OpBlockExecutorFactory`].
pub executor_factory: OpBlockExecutorFactory<R, Arc<ChainSpec>>,
pub executor_factory: OpBlockExecutorFactory<R, Arc<ChainSpec>, EvmFactory>,
/// Optimism block assembler.
pub block_assembler: OpBlockAssembler<ChainSpec>,
_pd: core::marker::PhantomData<N>,
#[doc(hidden)]
pub _pd: core::marker::PhantomData<N>,
}

impl<ChainSpec, N: NodePrimitives, R: Clone> Clone for OpEvmConfig<ChainSpec, N, R> {
impl<ChainSpec, N: NodePrimitives, R: Clone, EvmFactory: Clone> Clone
for OpEvmConfig<ChainSpec, N, R, EvmFactory>
{
fn clone(&self) -> Self {
Self {
executor_factory: self.executor_factory.clone(),
Expand Down Expand Up @@ -98,14 +103,20 @@ impl<ChainSpec: OpHardforks, N: NodePrimitives, R> OpEvmConfig<ChainSpec, N, R>
_pd: core::marker::PhantomData,
}
}
}

impl<ChainSpec, N, R, EvmFactory> OpEvmConfig<ChainSpec, N, R, EvmFactory>
where
ChainSpec: OpHardforks,
N: NodePrimitives,
{
/// Returns the chain spec associated with this configuration.
pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
self.executor_factory.spec()
}
}

impl<ChainSpec, N, R> ConfigureEvm for OpEvmConfig<ChainSpec, N, R>
impl<ChainSpec, N, R, EvmF> ConfigureEvm for OpEvmConfig<ChainSpec, N, R, EvmF>
where
ChainSpec: EthChainSpec<Header = Header> + OpHardforks,
N: NodePrimitives<
Expand All @@ -117,12 +128,19 @@ where
>,
OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx> + FromTxWithEncoded<N::SignedTx>,
R: OpReceiptBuilder<Receipt: DepositReceipt, Transaction: SignedTransaction>,
EvmF: EvmFactory<
Tx: FromRecoveredTx<R::Transaction>
+ FromTxWithEncoded<R::Transaction>
+ TransactionEnv,
Precompiles = PrecompilesMap,
Spec = OpSpecId,
> + Debug,
Self: Send + Sync + Unpin + Clone + 'static,
{
type Primitives = N;
type Error = EIP1559ParamError;
type NextBlockEnvCtx = OpNextBlockEnvAttributes;
type BlockExecutorFactory = OpBlockExecutorFactory<R, Arc<ChainSpec>>;
type BlockExecutorFactory = OpBlockExecutorFactory<R, Arc<ChainSpec>, EvmF>;
type BlockAssembler = OpBlockAssembler<ChainSpec>;

fn block_executor_factory(&self) -> &Self::BlockExecutorFactory {
Expand Down
136 changes: 133 additions & 3 deletions crates/optimism/node/tests/it/builder.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
//! Node builder setup tests.

use alloy_primitives::{address, Bytes};
use core::marker::PhantomData;
use op_revm::{
precompiles::OpPrecompiles, OpContext, OpHaltReason, OpSpecId, OpTransaction,
OpTransactionError,
};
use reth_db::test_utils::create_test_rw_db;
use reth_evm::{precompiles::PrecompilesMap, Database, Evm, EvmEnv, EvmFactory};
use reth_node_api::{FullNodeComponents, NodeTypesWithDBAdapter};
use reth_node_builder::{Node, NodeBuilder, NodeConfig};
use reth_optimism_chainspec::BASE_MAINNET;
use reth_optimism_node::{args::RollupArgs, OpNode};
use reth_node_builder::{
components::ExecutorBuilder, BuilderContext, FullNodeTypes, Node, NodeBuilder, NodeConfig,
NodeTypes,
};
use reth_optimism_chainspec::{OpChainSpec, BASE_MAINNET, OP_SEPOLIA};
use reth_optimism_evm::{OpBlockExecutorFactory, OpEvm, OpEvmFactory, OpRethReceiptBuilder};
use reth_optimism_node::{args::RollupArgs, OpEvmConfig, OpExecutorBuilder, OpNode};
use reth_optimism_primitives::OpPrimitives;
use reth_provider::providers::BlockchainProvider;
use revm::{
context::{Cfg, ContextTr, TxEnv},
context_interface::result::EVMError,
inspector::NoOpInspector,
interpreter::interpreter::EthInterpreter,
precompile::{Precompile, PrecompileId, PrecompileOutput, PrecompileResult, Precompiles},
Inspector,
};
use std::sync::OnceLock;

#[test]
fn test_basic_setup() {
Expand Down Expand Up @@ -36,3 +57,112 @@ fn test_basic_setup() {
})
.check_launch();
}

#[test]
fn test_setup_custom_precompiles() {
/// Unichain custom precompiles.
struct UniPrecompiles;

impl UniPrecompiles {
/// Returns map of precompiles for Unichain.
fn precompiles(spec_id: OpSpecId) -> PrecompilesMap {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();

PrecompilesMap::from_static(INSTANCE.get_or_init(|| {
let mut precompiles = OpPrecompiles::new_with_spec(spec_id).precompiles().clone();
// Custom precompile.
let precompile = Precompile::new(
PrecompileId::custom("custom"),
address!("0x0000000000000000000000000000000000756e69"),
|_, _| PrecompileResult::Ok(PrecompileOutput::new(0, Bytes::new())),
);
precompiles.extend([precompile]);
precompiles
}))
}
}

/// Builds Unichain EVM configuration.
#[derive(Clone, Debug)]
struct UniEvmFactory;

impl EvmFactory for UniEvmFactory {
type Evm<DB: Database, I: Inspector<OpContext<DB>>> = OpEvm<DB, I, Self::Precompiles>;
type Context<DB: Database> = OpContext<DB>;
type Tx = OpTransaction<TxEnv>;
type Error<DBError: core::error::Error + Send + Sync + 'static> =
EVMError<DBError, OpTransactionError>;
type HaltReason = OpHaltReason;
type Spec = OpSpecId;
type Precompiles = PrecompilesMap;

fn create_evm<DB: Database>(
&self,
db: DB,
input: EvmEnv<OpSpecId>,
) -> Self::Evm<DB, NoOpInspector> {
let mut op_evm = OpEvmFactory::default().create_evm(db, input);
*op_evm.components_mut().2 = UniPrecompiles::precompiles(op_evm.ctx().cfg().spec());

op_evm
}

fn create_evm_with_inspector<
DB: Database,
I: Inspector<Self::Context<DB>, EthInterpreter>,
>(
&self,
db: DB,
input: EvmEnv<OpSpecId>,
inspector: I,
) -> Self::Evm<DB, I> {
let mut op_evm =
OpEvmFactory::default().create_evm_with_inspector(db, input, inspector);
*op_evm.components_mut().2 = UniPrecompiles::precompiles(op_evm.ctx().cfg().spec());

op_evm
}
}

/// Unichain executor builder.
struct UniExecutorBuilder;

impl<Node> ExecutorBuilder<Node> for UniExecutorBuilder
where
Node: FullNodeTypes<Types: NodeTypes<ChainSpec = OpChainSpec, Primitives = OpPrimitives>>,
{
type EVM = OpEvmConfig<
OpChainSpec,
<Node::Types as NodeTypes>::Primitives,
OpRethReceiptBuilder,
UniEvmFactory,
>;

async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
let OpEvmConfig { executor_factory, block_assembler, _pd: _ } =
OpExecutorBuilder::default().build_evm(ctx).await?;
let uni_executor_factory = OpBlockExecutorFactory::new(
*executor_factory.receipt_builder(),
ctx.chain_spec(),
UniEvmFactory,
);
let uni_evm_config = OpEvmConfig {
executor_factory: uni_executor_factory,
block_assembler,
_pd: PhantomData,
};
Ok(uni_evm_config)
}
}

NodeBuilder::new(NodeConfig::new(OP_SEPOLIA.clone()))
.with_database(create_test_rw_db())
.with_types::<OpNode>()
.with_components(
OpNode::default()
.components()
// Custom EVM configuration
.executor(UniExecutorBuilder),
)
.check_launch();
}
2 changes: 1 addition & 1 deletion examples/custom-evm/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ where
}
}

/// Returns precompiles for Fjor spec.
/// Returns precompiles for Prague spec.
pub fn prague_custom() -> &'static Precompiles {
static INSTANCE: OnceLock<Precompiles> = OnceLock::new();
INSTANCE.get_or_init(|| {
Expand Down