From b347d9d97b6fd181d1d1724e8bcf3014ca86e4e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Hodul=C3=A1k?= Date: Thu, 22 May 2025 16:21:03 +0200 Subject: [PATCH] feat(examples): Make `CustomEvmTransaction` local and implement `FromRecoveredTx` and `FromTxWithEncoded` in `custom_node` example (#16415) --- examples/custom-node/src/evm/alloy.rs | 36 +++--- examples/custom-node/src/evm/env.rs | 170 ++++++++++++++++++++++++-- 2 files changed, 182 insertions(+), 24 deletions(-) diff --git a/examples/custom-node/src/evm/alloy.rs b/examples/custom-node/src/evm/alloy.rs index 7bf48acfc7..b79c4616bd 100644 --- a/examples/custom-node/src/evm/alloy.rs +++ b/examples/custom-node/src/evm/alloy.rs @@ -4,7 +4,7 @@ use alloy_primitives::{Address, Bytes, TxKind, U256}; use op_alloy_consensus::OpTxType; use op_revm::{ precompiles::OpPrecompiles, transaction::deposit::DepositTransactionParts, DefaultOp, - L1BlockInfo, OpBuilder, OpHaltReason, OpSpecId, OpTransactionError, + L1BlockInfo, OpBuilder, OpHaltReason, OpSpecId, OpTransaction, OpTransactionError, }; use reth_ethereum::evm::revm::{ context::{result::ResultAndState, BlockEnv, CfgEnv, TxEnv}, @@ -20,7 +20,7 @@ use std::error::Error; /// EVM context contains data that EVM needs for execution of [`CustomEvmTransaction`]. pub type CustomContext = - Context, DB, Journal, L1BlockInfo>; + Context, CfgEnv, DB, Journal, L1BlockInfo>; pub struct CustomEvm { inner: @@ -55,10 +55,10 @@ where tx: Self::Tx, ) -> Result, Self::Error> { if self.inspect { - self.inner.set_tx(tx); + self.inner.set_tx(tx.0); self.inner.inspect_replay() } else { - self.inner.transact(tx) + self.inner.transact(tx.0) } } @@ -68,7 +68,7 @@ where contract: Address, data: Bytes, ) -> Result, Self::Error> { - let tx = CustomEvmTransaction { + let tx = CustomEvmTransaction(OpTransaction { base: CustomTxEnv(TxEnv { caller, kind: TxKind::Call(contract), @@ -97,9 +97,9 @@ where // enveloped tx size. enveloped_tx: Some(Bytes::default()), deposit: Default::default(), - }; + }); - let mut gas_limit = tx.base.0.gas_limit; + let mut gas_limit = tx.0.base.0.gas_limit; let mut basefee = 0; let mut disable_nonce_check = true; @@ -165,6 +165,16 @@ where pub struct CustomEvmFactory; +impl CustomEvmFactory { + fn default_tx() -> CustomEvmTransaction { + CustomEvmTransaction(OpTransaction { + base: CustomTxEnv::default(), + enveloped_tx: Some(vec![0x00].into()), + deposit: DepositTransactionParts::default(), + }) + } +} + impl EvmFactory for CustomEvmFactory { type Evm>> = CustomEvm; type Context = CustomContext; @@ -182,11 +192,7 @@ impl EvmFactory for CustomEvmFactory { let spec_id = input.cfg_env.spec; CustomEvm { inner: Context::op() - .with_tx(CustomEvmTransaction { - base: CustomTxEnv::default(), - enveloped_tx: Some(vec![0x00].into()), - deposit: DepositTransactionParts::default(), - }) + .with_tx(Self::default_tx().0) .with_db(db) .with_block(input.block_env) .with_cfg(input.cfg_env) @@ -207,11 +213,7 @@ impl EvmFactory for CustomEvmFactory { let spec_id = input.cfg_env.spec; CustomEvm { inner: Context::op() - .with_tx(CustomEvmTransaction { - base: CustomTxEnv::default(), - enveloped_tx: Some(vec![0x00].into()), - deposit: DepositTransactionParts::default(), - }) + .with_tx(Self::default_tx().0) .with_db(db) .with_block(input.block_env) .with_cfg(input.cfg_env) diff --git a/examples/custom-node/src/evm/env.rs b/examples/custom-node/src/evm/env.rs index 1fe96b170d..d59c8e78b9 100644 --- a/examples/custom-node/src/evm/env.rs +++ b/examples/custom-node/src/evm/env.rs @@ -1,24 +1,29 @@ use crate::primitives::{CustomTransaction, CustomTransactionEnvelope, TxPayment}; -use alloy_eips::Typed2718; -use alloy_evm::{FromRecoveredTx, FromTxWithEncoded}; +use alloy_eips::{ + eip2718::{EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID}, + eip2930::AccessList, + Typed2718, +}; +use alloy_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv}; use alloy_primitives::{Address, Bytes, TxKind, B256, U256}; use op_revm::OpTransaction; -use reth_ethereum::evm::revm::context::TxEnv; +use reth_ethereum::evm::{primitives::TransactionEnv, revm::context::TxEnv}; /// An Optimism extended Ethereum transaction that can be fed to [`Evm`] because it contains /// [`CustomTxEnv`]. /// /// [`Evm`]: alloy_evm::Evm -pub type CustomEvmTransaction = OpTransaction; +#[derive(Clone, Debug)] +pub struct CustomEvmTransaction(pub OpTransaction); /// A transaction environment is a set of information related to an Ethereum transaction that can be /// fed to [`Evm`] for execution. /// /// [`Evm`]: alloy_evm::Evm -#[derive(Default)] +#[derive(Clone, Debug, Default)] pub struct CustomTxEnv(pub TxEnv); -impl revm::context::Transaction for CustomTxEnv { +impl revm::context::Transaction for CustomEvmTransaction { type AccessListItem<'a> = ::AccessListItem<'a> where @@ -49,7 +54,7 @@ impl revm::context::Transaction for CustomTxEnv { } fn nonce(&self) -> u64 { - self.0.nonce() + revm::context::Transaction::nonce(&self.0) } fn kind(&self) -> TxKind { @@ -89,6 +94,119 @@ impl revm::context::Transaction for CustomTxEnv { } } +impl revm::context::Transaction for CustomTxEnv { + type AccessListItem<'a> + = ::AccessListItem<'a> + where + Self: 'a; + type Authorization<'a> + = ::Authorization<'a> + where + Self: 'a; + + fn tx_type(&self) -> u8 { + self.0.tx_type() + } + + fn caller(&self) -> Address { + self.0.caller() + } + + fn gas_limit(&self) -> u64 { + self.0.gas_limit() + } + + fn value(&self) -> U256 { + self.0.value() + } + + fn input(&self) -> &Bytes { + self.0.input() + } + + fn nonce(&self) -> u64 { + revm::context::Transaction::nonce(&self.0) + } + + fn kind(&self) -> TxKind { + self.0.kind() + } + + fn chain_id(&self) -> Option { + self.0.chain_id() + } + + fn gas_price(&self) -> u128 { + self.0.gas_price() + } + + fn access_list(&self) -> Option>> { + self.0.access_list() + } + + fn blob_versioned_hashes(&self) -> &[B256] { + self.0.blob_versioned_hashes() + } + + fn max_fee_per_blob_gas(&self) -> u128 { + self.0.max_fee_per_blob_gas() + } + + fn authorization_list_len(&self) -> usize { + self.0.authorization_list_len() + } + + fn authorization_list(&self) -> impl Iterator> { + self.0.authorization_list() + } + + fn max_priority_fee_per_gas(&self) -> Option { + self.0.max_priority_fee_per_gas() + } +} + +impl TransactionEnv for CustomTxEnv { + fn set_gas_limit(&mut self, gas_limit: u64) { + self.0.gas_limit = gas_limit; + } + + fn nonce(&self) -> u64 { + self.0.nonce + } + + fn set_nonce(&mut self, nonce: u64) { + self.0.nonce = nonce; + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.0.access_list = access_list; + + if self.0.tx_type == LEGACY_TX_TYPE_ID { + // if this was previously marked as legacy tx, this must be upgraded to eip2930 with an + // accesslist + self.0.tx_type = EIP2930_TX_TYPE_ID; + } + } +} + +impl TransactionEnv for CustomEvmTransaction { + fn set_gas_limit(&mut self, gas_limit: u64) { + self.0.base.set_gas_limit(gas_limit) + } + + fn nonce(&self) -> u64 { + self.0.base.nonce() + } + + fn set_nonce(&mut self, nonce: u64) { + self.0.base.set_nonce(nonce) + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.0.base.set_access_list(access_list) + } +} + impl FromRecoveredTx for CustomTxEnv { fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self { CustomTxEnv(match tx { @@ -148,3 +266,41 @@ impl FromTxWithEncoded for TxEnv { Self::from_recovered_tx(tx.inner.tx(), sender) } } + +impl FromRecoveredTx for CustomEvmTransaction { + fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self { + Self(match tx { + CustomTransaction::BuiltIn(tx) => { + let tx = OpTransaction::::from_recovered_tx(tx, sender); + let base = CustomTxEnv(tx.base); + + OpTransaction { base, enveloped_tx: tx.enveloped_tx, deposit: tx.deposit } + } + CustomTransaction::Other(tx) => { + OpTransaction::new(CustomTxEnv(TxEnv::from_recovered_tx(tx, sender))) + } + }) + } +} + +impl FromTxWithEncoded for CustomEvmTransaction { + fn from_encoded_tx(tx: &CustomTransaction, sender: Address, encoded: Bytes) -> Self { + Self(match tx { + CustomTransaction::BuiltIn(tx) => { + let tx = OpTransaction::::from_encoded_tx(tx, sender, encoded); + let base = CustomTxEnv(tx.base); + + OpTransaction { base, enveloped_tx: tx.enveloped_tx, deposit: tx.deposit } + } + CustomTransaction::Other(tx) => { + OpTransaction::new(CustomTxEnv(TxEnv::from_encoded_tx(tx, sender, encoded))) + } + }) + } +} + +impl IntoTxEnv for CustomEvmTransaction { + fn into_tx_env(self) -> Self { + self + } +}