From 105aa3f549b9f55d329b585ce8aa8875a6242e0f Mon Sep 17 00:00:00 2001 From: Kien Nguyen Date: Thu, 5 Sep 2024 15:38:13 +0700 Subject: [PATCH 1/3] feat: add PevmChain::Transaction, PevmChain::TxEnvError, PevmChain::get_tx_env --- src/chain.rs | 15 ++++++++++--- src/chain/ethereum.rs | 36 +++++++++++++++++++++++++++++++ src/compat.rs | 48 ++---------------------------------------- src/pevm.rs | 10 ++++----- tests/common/runner.rs | 2 +- 5 files changed, 56 insertions(+), 55 deletions(-) diff --git a/src/chain.rs b/src/chain.rs index 239e5227..bd74d0d5 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use alloy_primitives::{B256, U256}; -use alloy_rpc_types::{BlockTransactions, Header, Transaction}; +use alloy_rpc_types::{BlockTransactions, Header}; use revm::{ primitives::{BlockEnv, SpecId, TxEnv}, Handler, @@ -22,12 +22,18 @@ pub enum RewardPolicy { /// Custom behaviours for different chains & networks pub trait PevmChain: Debug { + /// The transaction type + type Transaction: Debug + Clone + PartialEq; + /// The error type for [Self::get_block_spec]. type BlockSpecError: Debug + Clone + PartialEq; /// The error type for [Self::get_gas_price]. type GasPriceError: Debug + Clone + PartialEq; + /// The error type for [Self::get_tx_env]. + type TxEnvError: Debug + Clone + PartialEq; + /// Get chain id. fn id(&self) -> u64; @@ -35,7 +41,7 @@ pub trait PevmChain: Debug { fn get_block_spec(&self, header: &Header) -> Result; /// Get tx gas price. - fn get_gas_price(&self, tx: &Transaction) -> Result; + fn get_gas_price(&self, tx: &Self::Transaction) -> Result; /// Build [MvMemory] fn build_mv_memory( @@ -61,9 +67,12 @@ pub trait PevmChain: Debug { fn calculate_receipt_root( &self, spec_id: SpecId, - txs: &BlockTransactions, + txs: &BlockTransactions, tx_results: &[PevmTxExecutionResult], ) -> B256; + + /// Get [TxEnv] + fn get_tx_env(&self, tx: Self::Transaction) -> Result; } mod ethereum; diff --git a/src/chain/ethereum.rs b/src/chain/ethereum.rs index a22317df..c38a63e0 100644 --- a/src/chain/ethereum.rs +++ b/src/chain/ethereum.rs @@ -20,6 +20,13 @@ use crate::{ mv_memory::MvMemory, BuildIdentityHasher, MemoryLocation, PevmTxExecutionResult, TxIdx, }; +/// Represents errors that can occur when parsing transactions +#[derive(Debug, Clone, PartialEq)] +pub enum EthereumTxEnvError { + OverflowedGasLimit, + GasPriceError(EthereumGasPriceError), +} + /// Implementation of [PevmChain] for Ethereum #[derive(Debug, Clone, PartialEq)] pub struct PevmEthereum { @@ -56,8 +63,10 @@ pub enum EthereumGasPriceError { } impl PevmChain for PevmEthereum { + type Transaction = Transaction; type BlockSpecError = EthereumBlockSpecError; type GasPriceError = EthereumGasPriceError; + type TxEnvError = EthereumTxEnvError; fn id(&self) -> u64 { self.id @@ -193,4 +202,31 @@ impl PevmChain for PevmEthereum { } hash_builder.root() } + + /// Get the REVM tx envs of an Alloy block. + // https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/revm/env.rs#L234-L339 + // https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/alloy_compat.rs#L112-L233 + // TODO: Properly test this. + fn get_tx_env(&self, tx: Self::Transaction) -> Result { + Ok(TxEnv { + caller: tx.from, + gas_limit: tx + .gas + .try_into() + .map_err(|_| EthereumTxEnvError::OverflowedGasLimit)?, + gas_price: self + .get_gas_price(&tx) + .map_err(EthereumTxEnvError::GasPriceError)?, + gas_priority_fee: tx.max_priority_fee_per_gas.map(U256::from), + transact_to: tx.to.into(), + value: tx.value, + data: tx.input, + nonce: Some(tx.nonce), + chain_id: tx.chain_id, + access_list: tx.access_list.unwrap_or_default().into(), + blob_hashes: tx.blob_versioned_hashes.unwrap_or_default(), + max_fee_per_blob_gas: tx.max_fee_per_blob_gas.map(U256::from), + authorization_list: None, // TODO: Support in the upcoming hardfork + }) + } } diff --git a/src/compat.rs b/src/compat.rs index 55932df0..32a7436d 100644 --- a/src/compat.rs +++ b/src/compat.rs @@ -1,10 +1,8 @@ // TODO: Support custom chains like OP & RISE // Ideally REVM & Alloy would provide all these. -use alloy_rpc_types::{Header, Transaction}; -use revm::primitives::{BlobExcessGasAndPrice, BlockEnv, TransactTo, TxEnv, U256}; - -use crate::chain::PevmChain; +use alloy_rpc_types::Header; +use revm::primitives::{BlobExcessGasAndPrice, BlockEnv, U256}; /// Get the REVM block env of an Alloy block. // https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/revm/env.rs#L23-L48 @@ -24,45 +22,3 @@ pub(crate) fn get_block_env(header: &Header) -> BlockEnv { .map(|excess_blob_gas| BlobExcessGasAndPrice::new(excess_blob_gas as u64)), } } - -/// Represents errors that can occur when parsing transactions -#[derive(Debug, Clone, PartialEq)] -pub enum TransactionParsingError { - OverflowedGasLimit, - GasPriceError(C::GasPriceError), - MissingMaxFeePerGas, - InvalidType(u8), -} - -/// Get the REVM tx envs of an Alloy block. -// https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/revm/env.rs#L234-L339 -// https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/alloy_compat.rs#L112-L233 -// TODO: Properly test this. -pub(crate) fn get_tx_env( - chain: &C, - tx: Transaction, -) -> Result> { - Ok(TxEnv { - caller: tx.from, - gas_limit: tx - .gas - .try_into() - .map_err(|_| TransactionParsingError::OverflowedGasLimit)?, - gas_price: chain - .get_gas_price(&tx) - .map_err(TransactionParsingError::GasPriceError)?, - gas_priority_fee: tx.max_priority_fee_per_gas.map(U256::from), - transact_to: match tx.to { - Some(address) => TransactTo::Call(address), - None => TransactTo::Create, - }, - value: tx.value, - data: tx.input, - nonce: Some(tx.nonce), - chain_id: tx.chain_id, - access_list: tx.access_list.unwrap_or_default().0, - blob_hashes: tx.blob_versioned_hashes.unwrap_or_default(), - max_fee_per_blob_gas: tx.max_fee_per_blob_gas.map(U256::from), - authorization_list: None, // TODO: Support in the upcoming hardfork - }) -} diff --git a/src/pevm.rs b/src/pevm.rs index c80908df..5b935e43 100644 --- a/src/pevm.rs +++ b/src/pevm.rs @@ -20,7 +20,7 @@ use revm::{ use crate::{ chain::PevmChain, - compat::{get_block_env, get_tx_env, TransactionParsingError}, + compat::get_block_env, mv_memory::MvMemory, scheduler::Scheduler, storage::StorageWrapper, @@ -36,7 +36,7 @@ pub enum PevmError { /// Transactions lack information for execution. MissingTransactionData, /// Invalid input transaction. - InvalidTransaction(TransactionParsingError), + InvalidTransaction(C::TxEnvError), /// Storage error. // TODO: More concrete types than just an arbitrary string. StorageError(String), @@ -98,7 +98,7 @@ impl Pevm { &mut self, storage: &S, chain: &C, - block: Block, + block: Block, concurrency_level: NonZeroUsize, force_sequential: bool, ) -> PevmResult { @@ -109,8 +109,8 @@ impl Pevm { let tx_envs = match block.transactions { BlockTransactions::Full(txs) => txs .into_iter() - .map(|tx| get_tx_env(chain, tx)) - .collect::, TransactionParsingError<_>>>() + .map(|tx| chain.get_tx_env(tx)) + .collect::, _>>() .map_err(PevmError::InvalidTransaction)?, _ => return Err(PevmError::MissingTransactionData), }; diff --git a/tests/common/runner.rs b/tests/common/runner.rs index f23c4e1a..d0902dc6 100644 --- a/tests/common/runner.rs +++ b/tests/common/runner.rs @@ -49,7 +49,7 @@ pub fn test_execute_revm(storage: S, txs: Vec) pub fn test_execute_alloy( storage: &S, chain: &C, - block: Block, + block: Block, must_match_block_header: bool, ) { let concurrency_level = thread::available_parallelism().unwrap_or(NonZeroUsize::MIN); From 687fbe76cbedcf3e6ea058e44eabe0a73a7fa78d Mon Sep 17 00:00:00 2001 From: Kien Nguyen Date: Thu, 5 Sep 2024 15:40:10 +0700 Subject: [PATCH 2/3] feat: remove PevmChain::GasPriceError, PevmChain::get_gas_price --- src/chain.rs | 8 +------- src/chain/ethereum.rs | 41 +++++++++++++++++++---------------------- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/chain.rs b/src/chain.rs index bd74d0d5..8ea7b531 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; -use alloy_primitives::{B256, U256}; +use alloy_primitives::B256; use alloy_rpc_types::{BlockTransactions, Header}; use revm::{ primitives::{BlockEnv, SpecId, TxEnv}, @@ -28,9 +28,6 @@ pub trait PevmChain: Debug { /// The error type for [Self::get_block_spec]. type BlockSpecError: Debug + Clone + PartialEq; - /// The error type for [Self::get_gas_price]. - type GasPriceError: Debug + Clone + PartialEq; - /// The error type for [Self::get_tx_env]. type TxEnvError: Debug + Clone + PartialEq; @@ -40,9 +37,6 @@ pub trait PevmChain: Debug { /// Get block's [SpecId] fn get_block_spec(&self, header: &Header) -> Result; - /// Get tx gas price. - fn get_gas_price(&self, tx: &Self::Transaction) -> Result; - /// Build [MvMemory] fn build_mv_memory( &self, diff --git a/src/chain/ethereum.rs b/src/chain/ethereum.rs index c38a63e0..9bff851f 100644 --- a/src/chain/ethereum.rs +++ b/src/chain/ethereum.rs @@ -62,10 +62,27 @@ pub enum EthereumGasPriceError { MissingMaxFeePerGas, } +fn get_ethereum_gas_price(tx: &Transaction) -> Result { + let tx_type_raw: u8 = tx.transaction_type.unwrap_or_default(); + let Ok(tx_type) = TxType::try_from(tx_type_raw) else { + return Err(EthereumGasPriceError::InvalidType(tx_type_raw)); + }; + + match tx_type { + TxType::Legacy | TxType::Eip2930 => tx + .gas_price + .map(U256::from) + .ok_or(EthereumGasPriceError::MissingGasPrice), + TxType::Eip1559 | TxType::Eip4844 | TxType::Eip7702 => tx + .max_fee_per_gas + .map(U256::from) + .ok_or(EthereumGasPriceError::MissingMaxFeePerGas), + } +} + impl PevmChain for PevmEthereum { type Transaction = Transaction; type BlockSpecError = EthereumBlockSpecError; - type GasPriceError = EthereumGasPriceError; type TxEnvError = EthereumTxEnvError; fn id(&self) -> u64 { @@ -111,24 +128,6 @@ impl PevmChain for PevmEthereum { }) } - fn get_gas_price(&self, tx: &Transaction) -> Result { - let tx_type_raw: u8 = tx.transaction_type.unwrap_or_default(); - let Ok(tx_type) = TxType::try_from(tx_type_raw) else { - return Err(EthereumGasPriceError::InvalidType(tx_type_raw)); - }; - - match tx_type { - TxType::Legacy | TxType::Eip2930 => tx - .gas_price - .map(U256::from) - .ok_or(EthereumGasPriceError::MissingGasPrice), - TxType::Eip1559 | TxType::Eip4844 | TxType::Eip7702 => tx - .max_fee_per_gas - .map(U256::from) - .ok_or(EthereumGasPriceError::MissingMaxFeePerGas), - } - } - fn build_mv_memory( &self, hasher: &ahash::RandomState, @@ -214,9 +213,7 @@ impl PevmChain for PevmEthereum { .gas .try_into() .map_err(|_| EthereumTxEnvError::OverflowedGasLimit)?, - gas_price: self - .get_gas_price(&tx) - .map_err(EthereumTxEnvError::GasPriceError)?, + gas_price: get_ethereum_gas_price(&tx).map_err(EthereumTxEnvError::GasPriceError)?, gas_priority_fee: tx.max_priority_fee_per_gas.map(U256::from), transact_to: tx.to.into(), value: tx.value, From ab786ce2a12ebd2531eca52a36fa0af745392bb9 Mon Sep 17 00:00:00 2001 From: Kien Nguyen Date: Thu, 5 Sep 2024 18:16:49 +0700 Subject: [PATCH 3/3] refactor: resolve code reviews --- src/chain.rs | 8 ++--- src/chain/ethereum.rs | 81 +++++++++++++++++++++---------------------- src/pevm.rs | 2 +- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/chain.rs b/src/chain.rs index 8ea7b531..6addccae 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -29,7 +29,7 @@ pub trait PevmChain: Debug { type BlockSpecError: Debug + Clone + PartialEq; /// The error type for [Self::get_tx_env]. - type TxEnvError: Debug + Clone + PartialEq; + type TransactionParsingError: Debug + Clone + PartialEq; /// Get chain id. fn id(&self) -> u64; @@ -37,6 +37,9 @@ pub trait PevmChain: Debug { /// Get block's [SpecId] fn get_block_spec(&self, header: &Header) -> Result; + /// Get [TxEnv] + fn get_tx_env(&self, tx: Self::Transaction) -> Result; + /// Build [MvMemory] fn build_mv_memory( &self, @@ -64,9 +67,6 @@ pub trait PevmChain: Debug { txs: &BlockTransactions, tx_results: &[PevmTxExecutionResult], ) -> B256; - - /// Get [TxEnv] - fn get_tx_env(&self, tx: Self::Transaction) -> Result; } mod ethereum; diff --git a/src/chain/ethereum.rs b/src/chain/ethereum.rs index 9bff851f..acba528f 100644 --- a/src/chain/ethereum.rs +++ b/src/chain/ethereum.rs @@ -9,7 +9,7 @@ use alloy_chains::NamedChain; use alloy_consensus::{ReceiptEnvelope, TxType}; use alloy_primitives::{B256, U256}; use alloy_provider::network::eip2718::Encodable2718; -use alloy_rpc_types::{BlockTransactions, Header, Transaction}; +use alloy_rpc_types::{BlockTransactions, Header}; use revm::{ primitives::{BlockEnv, SpecId, TxEnv}, Handler, @@ -20,13 +20,6 @@ use crate::{ mv_memory::MvMemory, BuildIdentityHasher, MemoryLocation, PevmTxExecutionResult, TxIdx, }; -/// Represents errors that can occur when parsing transactions -#[derive(Debug, Clone, PartialEq)] -pub enum EthereumTxEnvError { - OverflowedGasLimit, - GasPriceError(EthereumGasPriceError), -} - /// Implementation of [PevmChain] for Ethereum #[derive(Debug, Clone, PartialEq)] pub struct PevmEthereum { @@ -51,9 +44,11 @@ pub enum EthereumBlockSpecError { MissingTotalDifficulty, } -/// Error type for [PevmEthereum::get_gas_price]. +/// Represents errors that can occur when parsing transactions #[derive(Debug, Clone, PartialEq)] -pub enum EthereumGasPriceError { +pub enum EthereumTransactionParsingError { + /// [tx.gas] is overflowed. + OverflowedGasLimit, /// [tx.type] is invalid. InvalidType(u8), /// [tx.gas_price] is none. @@ -62,28 +57,30 @@ pub enum EthereumGasPriceError { MissingMaxFeePerGas, } -fn get_ethereum_gas_price(tx: &Transaction) -> Result { +fn get_ethereum_gas_price( + tx: &alloy_rpc_types::Transaction, +) -> Result { let tx_type_raw: u8 = tx.transaction_type.unwrap_or_default(); let Ok(tx_type) = TxType::try_from(tx_type_raw) else { - return Err(EthereumGasPriceError::InvalidType(tx_type_raw)); + return Err(EthereumTransactionParsingError::InvalidType(tx_type_raw)); }; match tx_type { TxType::Legacy | TxType::Eip2930 => tx .gas_price .map(U256::from) - .ok_or(EthereumGasPriceError::MissingGasPrice), + .ok_or(EthereumTransactionParsingError::MissingGasPrice), TxType::Eip1559 | TxType::Eip4844 | TxType::Eip7702 => tx .max_fee_per_gas .map(U256::from) - .ok_or(EthereumGasPriceError::MissingMaxFeePerGas), + .ok_or(EthereumTransactionParsingError::MissingMaxFeePerGas), } } impl PevmChain for PevmEthereum { - type Transaction = Transaction; + type Transaction = alloy_rpc_types::Transaction; type BlockSpecError = EthereumBlockSpecError; - type TxEnvError = EthereumTxEnvError; + type TransactionParsingError = EthereumTransactionParsingError; fn id(&self) -> u64 { self.id @@ -128,6 +125,31 @@ impl PevmChain for PevmEthereum { }) } + /// Get the REVM tx envs of an Alloy block. + // https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/revm/env.rs#L234-L339 + // https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/alloy_compat.rs#L112-L233 + // TODO: Properly test this. + fn get_tx_env(&self, tx: Self::Transaction) -> Result { + Ok(TxEnv { + caller: tx.from, + gas_limit: tx + .gas + .try_into() + .map_err(|_| EthereumTransactionParsingError::OverflowedGasLimit)?, + gas_price: get_ethereum_gas_price(&tx)?, + gas_priority_fee: tx.max_priority_fee_per_gas.map(U256::from), + transact_to: tx.to.into(), + value: tx.value, + data: tx.input, + nonce: Some(tx.nonce), + chain_id: tx.chain_id, + access_list: tx.access_list.unwrap_or_default().into(), + blob_hashes: tx.blob_versioned_hashes.unwrap_or_default(), + max_fee_per_blob_gas: tx.max_fee_per_blob_gas.map(U256::from), + authorization_list: None, // TODO: Support in the upcoming hardfork + }) + } + fn build_mv_memory( &self, hasher: &ahash::RandomState, @@ -164,7 +186,7 @@ impl PevmChain for PevmEthereum { fn calculate_receipt_root( &self, _spec_id: SpecId, - txs: &BlockTransactions, + txs: &BlockTransactions, tx_results: &[PevmTxExecutionResult], ) -> B256 { // 1. Create an iterator of ReceiptEnvelope @@ -201,29 +223,4 @@ impl PevmChain for PevmEthereum { } hash_builder.root() } - - /// Get the REVM tx envs of an Alloy block. - // https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/revm/env.rs#L234-L339 - // https://github.com/paradigmxyz/reth/blob/280aaaedc4699c14a5b6e88f25d929fe22642fa3/crates/primitives/src/alloy_compat.rs#L112-L233 - // TODO: Properly test this. - fn get_tx_env(&self, tx: Self::Transaction) -> Result { - Ok(TxEnv { - caller: tx.from, - gas_limit: tx - .gas - .try_into() - .map_err(|_| EthereumTxEnvError::OverflowedGasLimit)?, - gas_price: get_ethereum_gas_price(&tx).map_err(EthereumTxEnvError::GasPriceError)?, - gas_priority_fee: tx.max_priority_fee_per_gas.map(U256::from), - transact_to: tx.to.into(), - value: tx.value, - data: tx.input, - nonce: Some(tx.nonce), - chain_id: tx.chain_id, - access_list: tx.access_list.unwrap_or_default().into(), - blob_hashes: tx.blob_versioned_hashes.unwrap_or_default(), - max_fee_per_blob_gas: tx.max_fee_per_blob_gas.map(U256::from), - authorization_list: None, // TODO: Support in the upcoming hardfork - }) - } } diff --git a/src/pevm.rs b/src/pevm.rs index 5b935e43..42adee94 100644 --- a/src/pevm.rs +++ b/src/pevm.rs @@ -36,7 +36,7 @@ pub enum PevmError { /// Transactions lack information for execution. MissingTransactionData, /// Invalid input transaction. - InvalidTransaction(C::TxEnvError), + InvalidTransaction(C::TransactionParsingError), /// Storage error. // TODO: More concrete types than just an arbitrary string. StorageError(String),