mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-08 03:01:12 -04:00
refactor(examples): Use OpEvm from op-alloy-evm instead of op-revm for CustomEvm in custom_node example (#16417)
This commit is contained in:
@@ -1,21 +1,18 @@
|
||||
use crate::evm::{CustomEvmTransaction, CustomTxEnv};
|
||||
use alloy_evm::{precompiles::PrecompilesMap, Database, Evm, EvmEnv, EvmFactory};
|
||||
use alloy_primitives::{Address, Bytes, TxKind, U256};
|
||||
use op_alloy_consensus::OpTxType;
|
||||
use alloy_op_evm::{OpEvm, OpEvmFactory};
|
||||
use alloy_primitives::{Address, Bytes};
|
||||
use op_revm::{
|
||||
precompiles::OpPrecompiles, transaction::deposit::DepositTransactionParts, DefaultOp,
|
||||
L1BlockInfo, OpBuilder, OpHaltReason, OpSpecId, OpTransaction, OpTransactionError,
|
||||
precompiles::OpPrecompiles, L1BlockInfo, OpContext, OpHaltReason, OpSpecId, OpTransaction,
|
||||
OpTransactionError,
|
||||
};
|
||||
use reth_ethereum::evm::revm::{
|
||||
context::{result::ResultAndState, BlockEnv, CfgEnv, TxEnv},
|
||||
handler::{instructions::EthInstructions, PrecompileProvider},
|
||||
interpreter::{interpreter::EthInterpreter, InterpreterResult},
|
||||
context::{result::ResultAndState, BlockEnv, CfgEnv},
|
||||
handler::PrecompileProvider,
|
||||
interpreter::InterpreterResult,
|
||||
Context, Inspector, Journal,
|
||||
};
|
||||
use revm::{
|
||||
context_interface::result::EVMError, handler::EvmTr, inspector::NoOpInspector, ExecuteEvm,
|
||||
InspectEvm,
|
||||
};
|
||||
use revm::{context_interface::result::EVMError, inspector::NoOpInspector};
|
||||
use std::error::Error;
|
||||
|
||||
/// EVM context contains data that EVM needs for execution of [`CustomEvmTransaction`].
|
||||
@@ -23,16 +20,20 @@ pub type CustomContext<DB> =
|
||||
Context<BlockEnv, OpTransaction<CustomTxEnv>, CfgEnv<OpSpecId>, DB, Journal<DB>, L1BlockInfo>;
|
||||
|
||||
pub struct CustomEvm<DB: Database, I, P = OpPrecompiles> {
|
||||
inner:
|
||||
op_revm::OpEvm<CustomContext<DB>, I, EthInstructions<EthInterpreter, CustomContext<DB>>, P>,
|
||||
inspect: bool,
|
||||
inner: OpEvm<DB, I, P>,
|
||||
}
|
||||
|
||||
impl<DB: Database, I, P> CustomEvm<DB, I, P> {
|
||||
pub fn new(op: OpEvm<DB, I, P>) -> Self {
|
||||
Self { inner: op }
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB, I, P> Evm for CustomEvm<DB, I, P>
|
||||
where
|
||||
DB: Database,
|
||||
I: Inspector<CustomContext<DB>>,
|
||||
P: PrecompileProvider<CustomContext<DB>, Output = InterpreterResult>,
|
||||
I: Inspector<OpContext<DB>>,
|
||||
P: PrecompileProvider<OpContext<DB>, Output = InterpreterResult>,
|
||||
{
|
||||
type DB = DB;
|
||||
type Tx = CustomEvmTransaction;
|
||||
@@ -43,22 +44,20 @@ where
|
||||
type Inspector = I;
|
||||
|
||||
fn block(&self) -> &BlockEnv {
|
||||
&self.inner.ctx_ref().block
|
||||
self.inner.block()
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> u64 {
|
||||
self.inner.ctx_ref().cfg.chain_id
|
||||
self.inner.chain_id()
|
||||
}
|
||||
|
||||
fn transact_raw(
|
||||
&mut self,
|
||||
tx: Self::Tx,
|
||||
) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
|
||||
if self.inspect {
|
||||
self.inner.set_tx(tx.0);
|
||||
self.inner.inspect_replay()
|
||||
} else {
|
||||
self.inner.transact(tx.0)
|
||||
match tx {
|
||||
CustomEvmTransaction::Op(tx) => self.inner.transact_raw(tx),
|
||||
CustomEvmTransaction::Payment(..) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,116 +67,43 @@ where
|
||||
contract: Address,
|
||||
data: Bytes,
|
||||
) -> Result<ResultAndState<Self::HaltReason>, Self::Error> {
|
||||
let tx = CustomEvmTransaction(OpTransaction {
|
||||
base: CustomTxEnv(TxEnv {
|
||||
caller,
|
||||
kind: TxKind::Call(contract),
|
||||
// Explicitly set nonce to 0 so revm does not do any nonce checks
|
||||
nonce: 0,
|
||||
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: 0,
|
||||
// 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: Default::default(),
|
||||
// blob fields can be None for this tx
|
||||
blob_hashes: Vec::new(),
|
||||
max_fee_per_blob_gas: 0,
|
||||
tx_type: OpTxType::Deposit as u8,
|
||||
authorization_list: Default::default(),
|
||||
}),
|
||||
// The L1 fee is not charged for the EIP-4788 transaction, submit zero bytes for the
|
||||
// enveloped tx size.
|
||||
enveloped_tx: Some(Bytes::default()),
|
||||
deposit: Default::default(),
|
||||
});
|
||||
|
||||
let mut gas_limit = tx.0.base.0.gas_limit;
|
||||
let mut basefee = 0;
|
||||
let mut disable_nonce_check = true;
|
||||
|
||||
// ensure the block gas limit is >= the tx
|
||||
core::mem::swap(&mut self.inner.ctx().block.gas_limit, &mut gas_limit);
|
||||
// disable the base fee check for this call by setting the base fee to zero
|
||||
core::mem::swap(&mut self.inner.ctx().block.basefee, &mut basefee);
|
||||
// disable the nonce check
|
||||
core::mem::swap(&mut self.inner.ctx().cfg.disable_nonce_check, &mut disable_nonce_check);
|
||||
|
||||
let mut res = self.transact(tx);
|
||||
|
||||
// swap back to the previous gas limit
|
||||
core::mem::swap(&mut self.inner.ctx().block.gas_limit, &mut gas_limit);
|
||||
// swap back to the previous base fee
|
||||
core::mem::swap(&mut self.inner.ctx().block.basefee, &mut basefee);
|
||||
// swap back to the previous nonce check flag
|
||||
core::mem::swap(&mut self.inner.ctx().cfg.disable_nonce_check, &mut disable_nonce_check);
|
||||
|
||||
// NOTE: We assume that only the contract storage is modified. Revm currently marks the
|
||||
// caller and block beneficiary accounts as "touched" when we do the above transact calls,
|
||||
// and includes them in the result.
|
||||
//
|
||||
// We're doing this state cleanup to make sure that changeset only includes the changed
|
||||
// contract storage.
|
||||
if let Ok(res) = &mut res {
|
||||
res.state.retain(|addr, _| *addr == contract);
|
||||
}
|
||||
|
||||
res
|
||||
self.inner.transact_system_call(caller, contract, data)
|
||||
}
|
||||
|
||||
fn db_mut(&mut self) -> &mut Self::DB {
|
||||
&mut self.inner.ctx().journaled_state.database
|
||||
self.inner.db_mut()
|
||||
}
|
||||
|
||||
fn finish(self) -> (Self::DB, EvmEnv<Self::Spec>) {
|
||||
let Context { block: block_env, cfg: cfg_env, journaled_state, .. } = self.inner.0.ctx;
|
||||
|
||||
(journaled_state.database, EvmEnv { block_env, cfg_env })
|
||||
self.inner.finish()
|
||||
}
|
||||
|
||||
fn set_inspector_enabled(&mut self, enabled: bool) {
|
||||
self.inspect = enabled;
|
||||
self.inner.set_inspector_enabled(enabled)
|
||||
}
|
||||
|
||||
fn precompiles(&self) -> &Self::Precompiles {
|
||||
&self.inner.0.precompiles
|
||||
self.inner.precompiles()
|
||||
}
|
||||
|
||||
fn precompiles_mut(&mut self) -> &mut Self::Precompiles {
|
||||
&mut self.inner.0.precompiles
|
||||
self.inner.precompiles_mut()
|
||||
}
|
||||
|
||||
fn inspector(&self) -> &Self::Inspector {
|
||||
&self.inner.0.inspector
|
||||
self.inner.inspector()
|
||||
}
|
||||
|
||||
fn inspector_mut(&mut self) -> &mut Self::Inspector {
|
||||
&mut self.inner.0.inspector
|
||||
self.inner.inspector_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CustomEvmFactory;
|
||||
|
||||
impl CustomEvmFactory {
|
||||
fn default_tx() -> CustomEvmTransaction {
|
||||
CustomEvmTransaction(OpTransaction {
|
||||
base: CustomTxEnv::default(),
|
||||
enveloped_tx: Some(vec![0x00].into()),
|
||||
deposit: DepositTransactionParts::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
pub struct CustomEvmFactory(OpEvmFactory);
|
||||
|
||||
impl EvmFactory for CustomEvmFactory {
|
||||
type Evm<DB: Database, I: Inspector<CustomContext<DB>>> = CustomEvm<DB, I, Self::Precompiles>;
|
||||
type Context<DB: Database> = CustomContext<DB>;
|
||||
type Evm<DB: Database, I: Inspector<OpContext<DB>>> = CustomEvm<DB, I, Self::Precompiles>;
|
||||
type Context<DB: Database> = OpContext<DB>;
|
||||
type Tx = CustomEvmTransaction;
|
||||
type Error<DBError: Error + Send + Sync + 'static> = EVMError<DBError, OpTransactionError>;
|
||||
type HaltReason = OpHaltReason;
|
||||
@@ -189,19 +115,7 @@ impl EvmFactory for CustomEvmFactory {
|
||||
db: DB,
|
||||
input: EvmEnv<Self::Spec>,
|
||||
) -> Self::Evm<DB, NoOpInspector> {
|
||||
let spec_id = input.cfg_env.spec;
|
||||
CustomEvm {
|
||||
inner: Context::op()
|
||||
.with_tx(Self::default_tx().0)
|
||||
.with_db(db)
|
||||
.with_block(input.block_env)
|
||||
.with_cfg(input.cfg_env)
|
||||
.build_op_with_inspector(NoOpInspector {})
|
||||
.with_precompiles(PrecompilesMap::from_static(
|
||||
OpPrecompiles::new_with_spec(spec_id).precompiles(),
|
||||
)),
|
||||
inspect: false,
|
||||
}
|
||||
CustomEvm::new(self.0.create_evm(db, input))
|
||||
}
|
||||
|
||||
fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
|
||||
@@ -210,18 +124,6 @@ impl EvmFactory for CustomEvmFactory {
|
||||
input: EvmEnv<Self::Spec>,
|
||||
inspector: I,
|
||||
) -> Self::Evm<DB, I> {
|
||||
let spec_id = input.cfg_env.spec;
|
||||
CustomEvm {
|
||||
inner: Context::op()
|
||||
.with_tx(Self::default_tx().0)
|
||||
.with_db(db)
|
||||
.with_block(input.block_env)
|
||||
.with_cfg(input.cfg_env)
|
||||
.build_op_with_inspector(inspector)
|
||||
.with_precompiles(PrecompilesMap::from_static(
|
||||
OpPrecompiles::new_with_spec(spec_id).precompiles(),
|
||||
)),
|
||||
inspect: true,
|
||||
}
|
||||
CustomEvm::new(self.0.create_evm_with_inspector(db, input, inspector))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
use crate::primitives::{CustomTransaction, CustomTransactionEnvelope, TxPayment};
|
||||
use alloy_eips::{
|
||||
eip2718::{EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID},
|
||||
eip2930::AccessList,
|
||||
Typed2718,
|
||||
};
|
||||
use alloy_eips::{eip2930::AccessList, Typed2718};
|
||||
use alloy_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv};
|
||||
use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
|
||||
use op_revm::OpTransaction;
|
||||
@@ -14,7 +10,10 @@ use reth_ethereum::evm::{primitives::TransactionEnv, revm::context::TxEnv};
|
||||
///
|
||||
/// [`Evm`]: alloy_evm::Evm
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CustomEvmTransaction(pub OpTransaction<CustomTxEnv>);
|
||||
pub enum CustomEvmTransaction {
|
||||
Op(OpTransaction<TxEnv>),
|
||||
Payment(CustomTxEnv),
|
||||
}
|
||||
|
||||
/// A transaction environment is a set of information related to an Ethereum transaction that can be
|
||||
/// fed to [`Evm`] for execution.
|
||||
@@ -34,63 +33,108 @@ impl revm::context::Transaction for CustomEvmTransaction {
|
||||
Self: 'a;
|
||||
|
||||
fn tx_type(&self) -> u8 {
|
||||
self.0.tx_type()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.tx_type(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.tx_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn caller(&self) -> Address {
|
||||
self.0.caller()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.caller(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.caller(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.0.gas_limit()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.gas_limit(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.gas_limit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&self) -> U256 {
|
||||
self.0.value()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.value(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.value(),
|
||||
}
|
||||
}
|
||||
|
||||
fn input(&self) -> &Bytes {
|
||||
self.0.input()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.input(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.input(),
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
revm::context::Transaction::nonce(&self.0)
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => revm::context::Transaction::nonce(tx),
|
||||
CustomEvmTransaction::Payment(tx) => revm::context::Transaction::nonce(tx),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> TxKind {
|
||||
self.0.kind()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.kind(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.kind(),
|
||||
}
|
||||
}
|
||||
|
||||
fn chain_id(&self) -> Option<u64> {
|
||||
self.0.chain_id()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.chain_id(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.chain_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gas_price(&self) -> u128 {
|
||||
self.0.gas_price()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.gas_price(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.gas_price(),
|
||||
}
|
||||
}
|
||||
|
||||
fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
|
||||
self.0.access_list()
|
||||
Some(match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.base.access_list.iter(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.0.access_list.iter(),
|
||||
})
|
||||
}
|
||||
|
||||
fn blob_versioned_hashes(&self) -> &[B256] {
|
||||
self.0.blob_versioned_hashes()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.blob_versioned_hashes(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.blob_versioned_hashes(),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_fee_per_blob_gas(&self) -> u128 {
|
||||
self.0.max_fee_per_blob_gas()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.max_fee_per_blob_gas(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.max_fee_per_blob_gas(),
|
||||
}
|
||||
}
|
||||
|
||||
fn authorization_list_len(&self) -> usize {
|
||||
self.0.authorization_list_len()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.authorization_list_len(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.authorization_list_len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
|
||||
self.0.authorization_list()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.base.authorization_list.iter(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.0.authorization_list.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_priority_fee_per_gas(&self) -> Option<u128> {
|
||||
self.0.max_priority_fee_per_gas()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.max_priority_fee_per_gas(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.max_priority_fee_per_gas(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,43 +211,49 @@ impl revm::context::Transaction for CustomTxEnv {
|
||||
|
||||
impl TransactionEnv for CustomTxEnv {
|
||||
fn set_gas_limit(&mut self, gas_limit: u64) {
|
||||
self.0.gas_limit = gas_limit;
|
||||
self.0.set_gas_limit(gas_limit);
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
self.0.nonce
|
||||
self.0.nonce()
|
||||
}
|
||||
|
||||
fn set_nonce(&mut self, nonce: u64) {
|
||||
self.0.nonce = nonce;
|
||||
self.0.set_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;
|
||||
}
|
||||
self.0.set_access_list(access_list);
|
||||
}
|
||||
}
|
||||
|
||||
impl TransactionEnv for CustomEvmTransaction {
|
||||
fn set_gas_limit(&mut self, gas_limit: u64) {
|
||||
self.0.base.set_gas_limit(gas_limit)
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.set_gas_limit(gas_limit),
|
||||
CustomEvmTransaction::Payment(tx) => tx.set_gas_limit(gas_limit),
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
self.0.base.nonce()
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.nonce(),
|
||||
CustomEvmTransaction::Payment(tx) => tx.nonce(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_nonce(&mut self, nonce: u64) {
|
||||
self.0.base.set_nonce(nonce)
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.set_nonce(nonce),
|
||||
CustomEvmTransaction::Payment(tx) => tx.set_nonce(nonce),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_access_list(&mut self, access_list: AccessList) {
|
||||
self.0.base.set_access_list(access_list)
|
||||
match self {
|
||||
CustomEvmTransaction::Op(tx) => tx.set_access_list(access_list),
|
||||
CustomEvmTransaction::Payment(tx) => tx.set_access_list(access_list),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,33 +319,27 @@ impl FromTxWithEncoded<CustomTransactionEnvelope> for TxEnv {
|
||||
|
||||
impl FromRecoveredTx<CustomTransaction> for CustomEvmTransaction {
|
||||
fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self {
|
||||
Self(match tx {
|
||||
match tx {
|
||||
CustomTransaction::BuiltIn(tx) => {
|
||||
let tx = OpTransaction::<TxEnv>::from_recovered_tx(tx, sender);
|
||||
let base = CustomTxEnv(tx.base);
|
||||
|
||||
OpTransaction { base, enveloped_tx: tx.enveloped_tx, deposit: tx.deposit }
|
||||
Self::Op(OpTransaction::from_recovered_tx(tx, sender))
|
||||
}
|
||||
CustomTransaction::Other(tx) => {
|
||||
OpTransaction::new(CustomTxEnv(TxEnv::from_recovered_tx(tx, sender)))
|
||||
Self::Payment(CustomTxEnv(TxEnv::from_recovered_tx(tx, sender)))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTxWithEncoded<CustomTransaction> for CustomEvmTransaction {
|
||||
fn from_encoded_tx(tx: &CustomTransaction, sender: Address, encoded: Bytes) -> Self {
|
||||
Self(match tx {
|
||||
match tx {
|
||||
CustomTransaction::BuiltIn(tx) => {
|
||||
let tx = OpTransaction::<TxEnv>::from_encoded_tx(tx, sender, encoded);
|
||||
let base = CustomTxEnv(tx.base);
|
||||
|
||||
OpTransaction { base, enveloped_tx: tx.enveloped_tx, deposit: tx.deposit }
|
||||
Self::Op(OpTransaction::from_encoded_tx(tx, sender, encoded))
|
||||
}
|
||||
CustomTransaction::Other(tx) => {
|
||||
OpTransaction::new(CustomTxEnv(TxEnv::from_encoded_tx(tx, sender, encoded)))
|
||||
Self::Payment(CustomTxEnv(TxEnv::from_encoded_tx(tx, sender, encoded)))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user