feat(examples): Make CustomEvmTransaction local and implement FromRecoveredTx and FromTxWithEncoded in custom_node example (#16415)

This commit is contained in:
Roman Hodulák
2025-05-22 16:21:03 +02:00
committed by GitHub
parent 50ab155b8d
commit b347d9d97b
2 changed files with 182 additions and 24 deletions

View File

@@ -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<DB> =
Context<BlockEnv, CustomEvmTransaction, CfgEnv<OpSpecId>, DB, Journal<DB>, L1BlockInfo>;
Context<BlockEnv, OpTransaction<CustomTxEnv>, CfgEnv<OpSpecId>, DB, Journal<DB>, L1BlockInfo>;
pub struct CustomEvm<DB: Database, I, P = OpPrecompiles> {
inner:
@@ -55,10 +55,10 @@ where
tx: Self::Tx,
) -> Result<ResultAndState<Self::HaltReason>, 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<ResultAndState<Self::HaltReason>, 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<DB: Database, I: Inspector<CustomContext<DB>>> = CustomEvm<DB, I, Self::Precompiles>;
type Context<DB: Database> = CustomContext<DB>;
@@ -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)

View File

@@ -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<CustomTxEnv>;
#[derive(Clone, Debug)]
pub struct CustomEvmTransaction(pub OpTransaction<CustomTxEnv>);
/// 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>
= <TxEnv as revm::context::Transaction>::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>
= <TxEnv as revm::context::Transaction>::AccessListItem<'a>
where
Self: 'a;
type Authorization<'a>
= <TxEnv as revm::context::Transaction>::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<u64> {
self.0.chain_id()
}
fn gas_price(&self) -> u128 {
self.0.gas_price()
}
fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
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<Item = Self::Authorization<'_>> {
self.0.authorization_list()
}
fn max_priority_fee_per_gas(&self) -> Option<u128> {
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<CustomTransaction> for CustomTxEnv {
fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self {
CustomTxEnv(match tx {
@@ -148,3 +266,41 @@ impl FromTxWithEncoded<CustomTransactionEnvelope> for TxEnv {
Self::from_recovered_tx(tx.inner.tx(), sender)
}
}
impl FromRecoveredTx<CustomTransaction> for CustomEvmTransaction {
fn from_recovered_tx(tx: &CustomTransaction, sender: Address) -> Self {
Self(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 }
}
CustomTransaction::Other(tx) => {
OpTransaction::new(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 {
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 }
}
CustomTransaction::Other(tx) => {
OpTransaction::new(CustomTxEnv(TxEnv::from_encoded_tx(tx, sender, encoded)))
}
})
}
}
impl IntoTxEnv<Self> for CustomEvmTransaction {
fn into_tx_env(self) -> Self {
self
}
}