mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: op-reth (#4377)
Co-authored-by: Roberto Bayardo <bayardo@alum.mit.edu> Co-authored-by: refcell.eth <abigger87@gmail.com> Co-authored-by: Roman Krasiuk <rokrassyuk@gmail.com> Co-authored-by: refcell <refcell@oplabs.co> Co-authored-by: nicolas <48695862+merklefruit@users.noreply.github.com>
This commit is contained in:
@ -22,3 +22,10 @@ revm.workspace = true
|
||||
# common
|
||||
tracing.workspace = true
|
||||
|
||||
[features]
|
||||
optimism = [
|
||||
"revm/optimism",
|
||||
"reth-primitives/optimism",
|
||||
"reth-consensus-common/optimism",
|
||||
"reth-interfaces/optimism",
|
||||
]
|
||||
|
||||
@ -32,3 +32,7 @@ pub use revm::{self, *};
|
||||
|
||||
/// Ethereum DAO hardfork state change data.
|
||||
pub mod eth_dao_fork;
|
||||
|
||||
/// Optimism-specific implementation and utilities for the executor
|
||||
#[cfg(feature = "optimism")]
|
||||
pub mod optimism;
|
||||
|
||||
175
crates/revm/src/optimism/mod.rs
Normal file
175
crates/revm/src/optimism/mod.rs
Normal file
@ -0,0 +1,175 @@
|
||||
use reth_interfaces::executor::{self as reth_executor, BlockExecutionError};
|
||||
use reth_primitives::{Block, Bytes, ChainSpec, Hardfork, TransactionKind, U256};
|
||||
use revm::{
|
||||
primitives::{BedrockSpec, RegolithSpec},
|
||||
L1BlockInfo,
|
||||
};
|
||||
|
||||
/// Optimism-specific processor implementation for the `EVMProcessor`
|
||||
pub mod processor;
|
||||
|
||||
/// Extracts the [L1BlockInfo] from the L2 block. The L1 info transaction is always the first
|
||||
/// transaction in the L2 block.
|
||||
pub fn extract_l1_info(block: &Block) -> Result<L1BlockInfo, BlockExecutionError> {
|
||||
let l1_info_tx_data = block
|
||||
.body
|
||||
.iter()
|
||||
.find(|tx| matches!(tx.kind(), TransactionKind::Call(to) if to == &revm::optimism::L1_BLOCK_CONTRACT))
|
||||
.ok_or(reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "could not find l1 block info tx in the L2 block".to_string(),
|
||||
}))
|
||||
.and_then(|tx| {
|
||||
tx.input().get(4..).ok_or(reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "could not get l1 block info tx calldata bytes".to_string(),
|
||||
}))
|
||||
})?;
|
||||
|
||||
parse_l1_info_tx(l1_info_tx_data)
|
||||
}
|
||||
|
||||
/// Parses the calldata of the [L1BlockInfo] transaction.
|
||||
pub fn parse_l1_info_tx(data: &[u8]) -> Result<L1BlockInfo, BlockExecutionError> {
|
||||
// The setL1BlockValues tx calldata must be exactly 260 bytes long, considering that
|
||||
// we already removed the first 4 bytes (the function selector). Detailed breakdown:
|
||||
// 32 bytes for the block number
|
||||
// + 32 bytes for the block timestamp
|
||||
// + 32 bytes for the base fee
|
||||
// + 32 bytes for the block hash
|
||||
// + 32 bytes for the block sequence number
|
||||
// + 32 bytes for the batcher hash
|
||||
// + 32 bytes for the fee overhead
|
||||
// + 32 bytes for the fee scalar
|
||||
if data.len() != 256 {
|
||||
return Err(reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "unexpected l1 block info tx calldata length found".to_string(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
let l1_base_fee = U256::try_from_be_slice(&data[64..96]).ok_or(
|
||||
reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "could not convert l1 base fee".to_string(),
|
||||
},
|
||||
),
|
||||
)?;
|
||||
let l1_fee_overhead = U256::try_from_be_slice(&data[192..224]).ok_or(
|
||||
reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "could not convert l1 fee overhead".to_string(),
|
||||
},
|
||||
),
|
||||
)?;
|
||||
let l1_fee_scalar = U256::try_from_be_slice(&data[224..256]).ok_or(
|
||||
reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "could not convert l1 fee scalar".to_string(),
|
||||
},
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(L1BlockInfo { l1_base_fee, l1_fee_overhead, l1_fee_scalar })
|
||||
}
|
||||
|
||||
/// An extension trait for [L1BlockInfo] that allows us to calculate the L1 cost of a transaction
|
||||
/// based off of the [ChainSpec]'s activated hardfork.
|
||||
pub trait RethL1BlockInfo {
|
||||
/// Forwards an L1 transaction calculation to revm and returns the gas cost.
|
||||
///
|
||||
/// ### Takes
|
||||
/// - `chain_spec`: The [ChainSpec] for the node.
|
||||
/// - `timestamp`: The timestamp of the current block.
|
||||
/// - `input`: The calldata of the transaction.
|
||||
/// - `is_deposit`: Whether or not the transaction is a deposit.
|
||||
fn l1_tx_data_fee(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
timestamp: u64,
|
||||
input: &Bytes,
|
||||
is_deposit: bool,
|
||||
) -> Result<U256, BlockExecutionError>;
|
||||
|
||||
/// Computes the data gas cost for an L2 transaction.
|
||||
///
|
||||
/// ### Takes
|
||||
/// - `chain_spec`: The [ChainSpec] for the node.
|
||||
/// - `timestamp`: The timestamp of the current block.
|
||||
/// - `input`: The calldata of the transaction.
|
||||
fn l1_data_gas(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
timestamp: u64,
|
||||
input: &Bytes,
|
||||
) -> Result<U256, BlockExecutionError>;
|
||||
}
|
||||
|
||||
impl RethL1BlockInfo for L1BlockInfo {
|
||||
fn l1_tx_data_fee(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
timestamp: u64,
|
||||
input: &Bytes,
|
||||
is_deposit: bool,
|
||||
) -> Result<U256, BlockExecutionError> {
|
||||
if is_deposit {
|
||||
return Ok(U256::ZERO)
|
||||
}
|
||||
|
||||
if chain_spec.is_fork_active_at_timestamp(Hardfork::Regolith, timestamp) {
|
||||
Ok(self.calculate_tx_l1_cost::<RegolithSpec>(input))
|
||||
} else if chain_spec.is_fork_active_at_timestamp(Hardfork::Bedrock, timestamp) {
|
||||
Ok(self.calculate_tx_l1_cost::<BedrockSpec>(input))
|
||||
} else {
|
||||
Err(reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "Optimism hardforks are not active".to_string(),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn l1_data_gas(
|
||||
&self,
|
||||
chain_spec: &ChainSpec,
|
||||
timestamp: u64,
|
||||
input: &Bytes,
|
||||
) -> Result<U256, BlockExecutionError> {
|
||||
if chain_spec.is_fork_active_at_timestamp(Hardfork::Regolith, timestamp) {
|
||||
Ok(self.data_gas::<RegolithSpec>(input))
|
||||
} else if chain_spec.is_fork_active_at_timestamp(Hardfork::Bedrock, timestamp) {
|
||||
Ok(self.data_gas::<BedrockSpec>(input))
|
||||
} else {
|
||||
Err(reth_executor::BlockExecutionError::OptimismBlockExecution(
|
||||
reth_executor::OptimismBlockExecutionError::L1BlockInfoError {
|
||||
message: "Optimism hardforks are not active".to_string(),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_l1_fee {
|
||||
#[test]
|
||||
fn sanity_l1_block() {
|
||||
use super::*;
|
||||
use reth_primitives::{hex_literal::hex, Bytes, Header, TransactionSigned};
|
||||
|
||||
let bytes = Bytes::from_static(&hex!("7ef9015aa044bae9d41b8380d781187b426c6fe43df5fb2fb57bd4466ef6a701e1f01e015694deaddeaddeaddeaddeaddeaddeaddeaddead000194420000000000000000000000000000000000001580808408f0d18001b90104015d8eb900000000000000000000000000000000000000000000000000000000008057650000000000000000000000000000000000000000000000000000000063d96d10000000000000000000000000000000000000000000000000000000000009f35273d89754a1e0387b89520d989d3be9c37c1f32495a88faf1ea05c61121ab0d1900000000000000000000000000000000000000000000000000000000000000010000000000000000000000002d679b567db6187c0c8323fa982cfb88b74dbcc7000000000000000000000000000000000000000000000000000000000000083400000000000000000000000000000000000000000000000000000000000f4240"));
|
||||
let l1_info_tx = TransactionSigned::decode_enveloped(bytes).unwrap();
|
||||
let mock_block = Block {
|
||||
header: Header::default(),
|
||||
body: vec![l1_info_tx],
|
||||
ommers: Vec::default(),
|
||||
withdrawals: None,
|
||||
};
|
||||
|
||||
let l1_info: L1BlockInfo = super::extract_l1_info(&mock_block).unwrap();
|
||||
assert_eq!(l1_info.l1_base_fee, U256::from(652_114));
|
||||
assert_eq!(l1_info.l1_fee_overhead, U256::from(2100));
|
||||
assert_eq!(l1_info.l1_fee_scalar, U256::from(1_000_000));
|
||||
}
|
||||
}
|
||||
150
crates/revm/src/optimism/processor.rs
Normal file
150
crates/revm/src/optimism/processor.rs
Normal file
@ -0,0 +1,150 @@
|
||||
use reth_interfaces::executor::{BlockExecutionError, BlockValidationError};
|
||||
use reth_primitives::{
|
||||
revm::compat::into_reth_log, revm_primitives::ResultAndState, Address, Block, Hardfork,
|
||||
Receipt, U256,
|
||||
};
|
||||
use reth_provider::{BlockExecutor, BlockExecutorStats, BundleStateWithReceipts};
|
||||
use revm::DatabaseCommit;
|
||||
use std::time::Instant;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::processor::{verify_receipt, EVMProcessor};
|
||||
|
||||
impl<'a> BlockExecutor for EVMProcessor<'a> {
|
||||
fn execute(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
) -> Result<(), BlockExecutionError> {
|
||||
let receipts = self.execute_inner(block, total_difficulty, senders)?;
|
||||
self.save_receipts(receipts)
|
||||
}
|
||||
|
||||
fn execute_and_verify_receipt(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
) -> Result<(), BlockExecutionError> {
|
||||
// execute block
|
||||
let receipts = self.execute_inner(block, total_difficulty, senders)?;
|
||||
|
||||
// TODO Before Byzantium, receipts contained state root that would mean that expensive
|
||||
// operation as hashing that is needed for state root got calculated in every
|
||||
// transaction This was replaced with is_success flag.
|
||||
// See more about EIP here: https://eips.ethereum.org/EIPS/eip-658
|
||||
if self.chain_spec.fork(Hardfork::Byzantium).active_at_block(block.header.number) {
|
||||
let time = Instant::now();
|
||||
if let Err(error) =
|
||||
verify_receipt(block.header.receipts_root, block.header.logs_bloom, receipts.iter())
|
||||
{
|
||||
debug!(target: "evm", ?error, ?receipts, "receipts verification failed");
|
||||
return Err(error)
|
||||
};
|
||||
self.stats.receipt_root_duration += time.elapsed();
|
||||
}
|
||||
|
||||
self.save_receipts(receipts)
|
||||
}
|
||||
|
||||
fn execute_transactions(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
) -> Result<(Vec<Receipt>, u64), BlockExecutionError> {
|
||||
self.init_env(&block.header, total_difficulty);
|
||||
|
||||
// perf: do not execute empty blocks
|
||||
if block.body.is_empty() {
|
||||
return Ok((Vec::new(), 0))
|
||||
}
|
||||
|
||||
let senders = self.recover_senders(&block.body, senders)?;
|
||||
|
||||
let is_regolith =
|
||||
self.chain_spec.fork(Hardfork::Regolith).active_at_timestamp(block.timestamp);
|
||||
|
||||
let mut cumulative_gas_used = 0;
|
||||
let mut receipts = Vec::with_capacity(block.body.len());
|
||||
for (transaction, sender) in block.body.iter().zip(senders) {
|
||||
let time = Instant::now();
|
||||
// The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior,
|
||||
// must be no greater than the block’s gasLimit.
|
||||
let block_available_gas = block.header.gas_limit - cumulative_gas_used;
|
||||
if transaction.gas_limit() > block_available_gas &&
|
||||
(is_regolith || !transaction.is_system_transaction())
|
||||
{
|
||||
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||
transaction_gas_limit: transaction.gas_limit(),
|
||||
block_available_gas,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
// Cache the depositor account prior to the state transition for the deposit nonce.
|
||||
//
|
||||
// Note that this *only* needs to be done post-regolith hardfork, as deposit nonces
|
||||
// were not introduced in Bedrock. In addition, regular transactions don't have deposit
|
||||
// nonces, so we don't need to touch the DB for those.
|
||||
let depositor = (is_regolith && transaction.is_deposit())
|
||||
.then(|| {
|
||||
self.db_mut()
|
||||
.load_cache_account(sender)
|
||||
.map(|acc| acc.account_info().unwrap_or_default())
|
||||
})
|
||||
.transpose()
|
||||
.map_err(|_| BlockExecutionError::ProviderError)?;
|
||||
|
||||
// Execute transaction.
|
||||
let ResultAndState { result, state } = self.transact(transaction, sender)?;
|
||||
trace!(
|
||||
target: "evm",
|
||||
?transaction, ?result, ?state,
|
||||
"Executed transaction"
|
||||
);
|
||||
self.stats.execution_duration += time.elapsed();
|
||||
let time = Instant::now();
|
||||
|
||||
self.db_mut().commit(state);
|
||||
|
||||
self.stats.apply_state_duration += time.elapsed();
|
||||
|
||||
// append gas used
|
||||
cumulative_gas_used += result.gas_used();
|
||||
|
||||
// Push transaction changeset and calculate header bloom filter for receipt.
|
||||
receipts.push(Receipt {
|
||||
tx_type: transaction.tx_type(),
|
||||
// Success flag was added in `EIP-658: Embedding transaction status code in
|
||||
// receipts`.
|
||||
success: result.is_success(),
|
||||
cumulative_gas_used,
|
||||
// convert to reth log
|
||||
logs: result.into_logs().into_iter().map(into_reth_log).collect(),
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: depositor.map(|account| account.nonce),
|
||||
});
|
||||
}
|
||||
|
||||
Ok((receipts, cumulative_gas_used))
|
||||
}
|
||||
|
||||
fn take_output_state(&mut self) -> BundleStateWithReceipts {
|
||||
let receipts = std::mem::take(&mut self.receipts);
|
||||
BundleStateWithReceipts::new(
|
||||
self.evm.db().unwrap().take_bundle(),
|
||||
receipts,
|
||||
self.first_block.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn stats(&self) -> BlockExecutorStats {
|
||||
self.stats.clone()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
self.evm.db.as_ref().map(|db| db.bundle_size_hint())
|
||||
}
|
||||
}
|
||||
@ -9,24 +9,26 @@ use reth_interfaces::{
|
||||
RethError,
|
||||
};
|
||||
use reth_primitives::{
|
||||
revm::{
|
||||
compat::into_reth_log,
|
||||
env::{fill_cfg_and_block_env, fill_tx_env},
|
||||
},
|
||||
revm::env::{fill_cfg_and_block_env, fill_tx_env},
|
||||
Address, Block, BlockNumber, Bloom, ChainSpec, Hardfork, Header, PruneMode, PruneModes,
|
||||
PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256,
|
||||
MINIMUM_PRUNING_DISTANCE, U256,
|
||||
};
|
||||
use reth_provider::{
|
||||
BlockExecutor, BlockExecutorStats, BundleStateWithReceipts, PrunableBlockExecutor,
|
||||
StateProvider,
|
||||
};
|
||||
use reth_provider::{BlockExecutor, BlockExecutorStats, PrunableBlockExecutor, StateProvider};
|
||||
use revm::{
|
||||
db::{states::bundle_state::BundleRetention, StateDBBox},
|
||||
primitives::ResultAndState,
|
||||
DatabaseCommit, State, EVM,
|
||||
State, EVM,
|
||||
};
|
||||
use std::{sync::Arc, time::Instant};
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use reth_primitives::revm::compat::into_reth_log;
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use reth_provider::BundleStateWithReceipts;
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use revm::DatabaseCommit;
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use tracing::{debug, trace};
|
||||
|
||||
/// EVMProcessor is a block executor that uses revm to execute blocks or multiple blocks.
|
||||
@ -49,9 +51,9 @@ use tracing::{debug, trace};
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct EVMProcessor<'a> {
|
||||
/// The configured chain-spec
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
pub(crate) chain_spec: Arc<ChainSpec>,
|
||||
/// revm instance that contains database and env environment.
|
||||
evm: EVM<StateDBBox<'a, RethError>>,
|
||||
pub(crate) evm: EVM<StateDBBox<'a, RethError>>,
|
||||
/// Hook and inspector stack that we want to invoke on that hook.
|
||||
stack: InspectorStack,
|
||||
/// The collection of receipts.
|
||||
@ -59,10 +61,10 @@ pub struct EVMProcessor<'a> {
|
||||
/// The inner vector stores receipts ordered by transaction number.
|
||||
///
|
||||
/// If receipt is None it means it is pruned.
|
||||
receipts: Receipts,
|
||||
pub(crate) receipts: Receipts,
|
||||
/// First block will be initialized to `None`
|
||||
/// and be set to the block number of first block executed.
|
||||
first_block: Option<BlockNumber>,
|
||||
pub(crate) first_block: Option<BlockNumber>,
|
||||
/// The maximum known block.
|
||||
tip: Option<BlockNumber>,
|
||||
/// Pruning configuration.
|
||||
@ -72,7 +74,7 @@ pub struct EVMProcessor<'a> {
|
||||
/// block. None means there isn't any kind of configuration.
|
||||
pruning_address_filter: Option<(u64, Vec<Address>)>,
|
||||
/// Execution stats
|
||||
stats: BlockExecutorStats,
|
||||
pub(crate) stats: BlockExecutorStats,
|
||||
}
|
||||
|
||||
impl<'a> EVMProcessor<'a> {
|
||||
@ -148,7 +150,7 @@ impl<'a> EVMProcessor<'a> {
|
||||
self.evm.db().expect("Database inside EVM is always set")
|
||||
}
|
||||
|
||||
fn recover_senders(
|
||||
pub(crate) fn recover_senders(
|
||||
&mut self,
|
||||
body: &[TransactionSigned],
|
||||
senders: Option<Vec<Address>>,
|
||||
@ -169,7 +171,7 @@ impl<'a> EVMProcessor<'a> {
|
||||
}
|
||||
|
||||
/// Initializes the config and block env.
|
||||
fn init_env(&mut self, header: &Header, total_difficulty: U256) {
|
||||
pub(crate) fn init_env(&mut self, header: &Header, total_difficulty: U256) {
|
||||
// Set state clear flag.
|
||||
let state_clear_flag =
|
||||
self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(header.number);
|
||||
@ -252,8 +254,16 @@ impl<'a> EVMProcessor<'a> {
|
||||
sender: Address,
|
||||
) -> Result<ResultAndState, BlockExecutionError> {
|
||||
// Fill revm structure.
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
fill_tx_env(&mut self.evm.env.tx, transaction, sender);
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
{
|
||||
let mut envelope_buf = Vec::with_capacity(transaction.length_without_header());
|
||||
transaction.encode_enveloped(&mut envelope_buf);
|
||||
fill_tx_env(&mut self.evm.env.tx, transaction, sender, envelope_buf.into());
|
||||
}
|
||||
|
||||
let hash = transaction.hash();
|
||||
let out = if self.stack.should_inspect(&self.evm.env, hash) {
|
||||
// execution with inspector.
|
||||
@ -271,79 +281,8 @@ impl<'a> EVMProcessor<'a> {
|
||||
out.map_err(|e| BlockValidationError::EVM { hash, error: e.into() }.into())
|
||||
}
|
||||
|
||||
/// Runs the provided transactions and commits their state to the run-time database.
|
||||
///
|
||||
/// The returned [BundleStateWithReceipts] can be used to persist the changes to disk, and
|
||||
/// contains the changes made by each transaction.
|
||||
///
|
||||
/// The changes in [BundleStateWithReceipts] have a transition ID associated with them: there is
|
||||
/// one transition ID for each transaction (with the first executed tx having transition ID
|
||||
/// 0, and so on).
|
||||
///
|
||||
/// The second returned value represents the total gas used by this block of transactions.
|
||||
pub fn execute_transactions(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
) -> Result<(Vec<Receipt>, u64), BlockExecutionError> {
|
||||
self.init_env(&block.header, total_difficulty);
|
||||
|
||||
// perf: do not execute empty blocks
|
||||
if block.body.is_empty() {
|
||||
return Ok((Vec::new(), 0))
|
||||
}
|
||||
|
||||
let senders = self.recover_senders(&block.body, senders)?;
|
||||
|
||||
let mut cumulative_gas_used = 0;
|
||||
let mut receipts = Vec::with_capacity(block.body.len());
|
||||
for (transaction, sender) in block.body.iter().zip(senders) {
|
||||
let time = Instant::now();
|
||||
// The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior,
|
||||
// must be no greater than the block’s gasLimit.
|
||||
let block_available_gas = block.header.gas_limit - cumulative_gas_used;
|
||||
if transaction.gas_limit() > block_available_gas {
|
||||
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||
transaction_gas_limit: transaction.gas_limit(),
|
||||
block_available_gas,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
// Execute transaction.
|
||||
let ResultAndState { result, state } = self.transact(transaction, sender)?;
|
||||
trace!(
|
||||
target: "evm",
|
||||
?transaction, ?result, ?state,
|
||||
"Executed transaction"
|
||||
);
|
||||
self.stats.execution_duration += time.elapsed();
|
||||
let time = Instant::now();
|
||||
|
||||
self.db_mut().commit(state);
|
||||
|
||||
self.stats.apply_state_duration += time.elapsed();
|
||||
|
||||
// append gas used
|
||||
cumulative_gas_used += result.gas_used();
|
||||
|
||||
// Push transaction changeset and calculate header bloom filter for receipt.
|
||||
receipts.push(Receipt {
|
||||
tx_type: transaction.tx_type(),
|
||||
// Success flag was added in `EIP-658: Embedding transaction status code in
|
||||
// receipts`.
|
||||
success: result.is_success(),
|
||||
cumulative_gas_used,
|
||||
// convert to reth log
|
||||
logs: result.into_logs().into_iter().map(into_reth_log).collect(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok((receipts, cumulative_gas_used))
|
||||
}
|
||||
|
||||
/// Execute the block, verify gas usage and apply post-block state changes.
|
||||
fn execute_inner(
|
||||
pub(crate) fn execute_inner(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
@ -457,6 +396,8 @@ impl<'a> EVMProcessor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Default Ethereum implementation of the [BlockExecutor] trait for the [EVMProcessor].
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
impl<'a> BlockExecutor for EVMProcessor<'a> {
|
||||
fn execute(
|
||||
&mut self,
|
||||
@ -495,6 +436,69 @@ impl<'a> BlockExecutor for EVMProcessor<'a> {
|
||||
self.save_receipts(receipts)
|
||||
}
|
||||
|
||||
fn execute_transactions(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
) -> Result<(Vec<Receipt>, u64), BlockExecutionError> {
|
||||
self.init_env(&block.header, total_difficulty);
|
||||
|
||||
// perf: do not execute empty blocks
|
||||
if block.body.is_empty() {
|
||||
return Ok((Vec::new(), 0))
|
||||
}
|
||||
|
||||
let senders = self.recover_senders(&block.body, senders)?;
|
||||
|
||||
let mut cumulative_gas_used = 0;
|
||||
let mut receipts = Vec::with_capacity(block.body.len());
|
||||
for (transaction, sender) in block.body.iter().zip(senders) {
|
||||
let time = Instant::now();
|
||||
// The sum of the transaction’s gas limit, Tg, and the gas utilized in this block prior,
|
||||
// must be no greater than the block’s gasLimit.
|
||||
let block_available_gas = block.header.gas_limit - cumulative_gas_used;
|
||||
if transaction.gas_limit() > block_available_gas {
|
||||
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||
transaction_gas_limit: transaction.gas_limit(),
|
||||
block_available_gas,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
// Execute transaction.
|
||||
let ResultAndState { result, state } = self.transact(transaction, sender)?;
|
||||
trace!(
|
||||
target: "evm",
|
||||
?transaction, ?result, ?state,
|
||||
"Executed transaction"
|
||||
);
|
||||
self.stats.execution_duration += time.elapsed();
|
||||
let time = Instant::now();
|
||||
|
||||
self.db_mut().commit(state);
|
||||
|
||||
self.stats.apply_state_duration += time.elapsed();
|
||||
|
||||
// append gas used
|
||||
cumulative_gas_used += result.gas_used();
|
||||
|
||||
// Push transaction changeset and calculate header bloom filter for receipt.
|
||||
receipts.push(Receipt {
|
||||
tx_type: transaction.tx_type(),
|
||||
// Success flag was added in `EIP-658: Embedding transaction status code in
|
||||
// receipts`.
|
||||
success: result.is_success(),
|
||||
cumulative_gas_used,
|
||||
// convert to reth log
|
||||
logs: result.into_logs().into_iter().map(into_reth_log).collect(),
|
||||
#[cfg(feature = "optimism")]
|
||||
deposit_nonce: None,
|
||||
});
|
||||
}
|
||||
|
||||
Ok((receipts, cumulative_gas_used))
|
||||
}
|
||||
|
||||
fn take_output_state(&mut self) -> BundleStateWithReceipts {
|
||||
let receipts = std::mem::take(&mut self.receipts);
|
||||
BundleStateWithReceipts::new(
|
||||
@ -564,7 +568,9 @@ mod tests {
|
||||
trie::AccountProof,
|
||||
Account, Bytecode, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, MAINNET,
|
||||
};
|
||||
use reth_provider::{AccountReader, BlockHashReader, StateRootProvider};
|
||||
use reth_provider::{
|
||||
AccountReader, BlockHashReader, BundleStateWithReceipts, StateRootProvider,
|
||||
};
|
||||
use revm::{Database, TransitionState};
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user