mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
refactor: Execution Stage owns Executor (#1568)
Co-authored-by: Roman Krasiuk <rokrassyuk@gmail.com> Co-authored-by: Oliver Nordbjerg <hi@notbjerg.me>
This commit is contained in:
committed by
GitHub
parent
71bc1451af
commit
4285186dbd
@@ -21,26 +21,47 @@ use revm::{
|
||||
primitives::{Account as RevmAccount, AccountInfo, Bytecode, ResultAndState},
|
||||
EVM,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Main block executor
|
||||
pub struct Executor<'a, DB>
|
||||
where
|
||||
DB: StateProvider,
|
||||
{
|
||||
chain_spec: &'a ChainSpec,
|
||||
/// The configured chain-spec
|
||||
pub chain_spec: Arc<ChainSpec>,
|
||||
evm: EVM<&'a mut SubState<DB>>,
|
||||
stack: InspectorStack,
|
||||
}
|
||||
|
||||
impl<'a, DB> From<ChainSpec> for Executor<'a, DB>
|
||||
where
|
||||
DB: StateProvider,
|
||||
{
|
||||
/// Instantiates a new executor from the chainspec. Must call
|
||||
/// `with_db` to set the database before executing.
|
||||
fn from(chain_spec: ChainSpec) -> Self {
|
||||
let evm = EVM::new();
|
||||
Executor {
|
||||
chain_spec: Arc::new(chain_spec),
|
||||
evm,
|
||||
stack: InspectorStack::new(InspectorStackConfig::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, DB> Executor<'a, DB>
|
||||
where
|
||||
DB: StateProvider,
|
||||
{
|
||||
/// Creates a new executor from the given chain spec and database.
|
||||
pub fn new(chain_spec: &'a ChainSpec, db: &'a mut SubState<DB>) -> Self {
|
||||
pub fn new(chain_spec: Arc<ChainSpec>, db: &'a mut SubState<DB>) -> Self {
|
||||
let mut evm = EVM::new();
|
||||
evm.database(db);
|
||||
|
||||
Executor { chain_spec, evm, stack: InspectorStack::new(InspectorStackConfig::default()) }
|
||||
}
|
||||
|
||||
@@ -50,10 +71,21 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
fn db(&mut self) -> &mut SubState<DB> {
|
||||
/// Gives a reference to the database
|
||||
pub fn db(&mut self) -> &mut SubState<DB> {
|
||||
self.evm.db().expect("db to not be moved")
|
||||
}
|
||||
|
||||
/// Overrides the database
|
||||
pub fn with_db<OtherDB: StateProvider>(
|
||||
&self,
|
||||
db: &'a mut SubState<OtherDB>,
|
||||
) -> Executor<'a, OtherDB> {
|
||||
let mut evm = EVM::new();
|
||||
evm.database(db);
|
||||
Executor { chain_spec: self.chain_spec.clone(), evm, stack: self.stack.clone() }
|
||||
}
|
||||
|
||||
fn recover_senders(
|
||||
&self,
|
||||
body: &[TransactionSigned],
|
||||
@@ -75,7 +107,7 @@ where
|
||||
fill_cfg_and_block_env(
|
||||
&mut self.evm.env.cfg,
|
||||
&mut self.evm.env.block,
|
||||
self.chain_spec,
|
||||
&self.chain_spec,
|
||||
header,
|
||||
total_difficulty,
|
||||
);
|
||||
@@ -341,6 +373,30 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute and verify block
|
||||
pub fn execute_and_verify_receipt(
|
||||
&mut self,
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
) -> Result<ExecutionResult, Error> {
|
||||
let execution_result = self.execute(block, total_difficulty, senders)?;
|
||||
|
||||
let receipts_iter =
|
||||
execution_result.tx_changesets.iter().map(|changeset| &changeset.receipt);
|
||||
|
||||
if self.chain_spec.fork(Hardfork::Byzantium).active_at_block(block.header.number) {
|
||||
verify_receipt(block.header.receipts_root, block.header.logs_bloom, receipts_iter)?;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Ok(execution_result)
|
||||
}
|
||||
|
||||
/// Runs a single transaction in the configured environment and proceeds
|
||||
/// to return the result and state diff (without applying it).
|
||||
///
|
||||
@@ -464,30 +520,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute and verify block
|
||||
pub fn execute_and_verify_receipt<DB: StateProvider>(
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
chain_spec: &ChainSpec,
|
||||
db: &mut SubState<DB>,
|
||||
) -> Result<ExecutionResult, Error> {
|
||||
let execution_result = execute(block, total_difficulty, senders, chain_spec, db)?;
|
||||
|
||||
let receipts_iter = execution_result.tx_changesets.iter().map(|changeset| &changeset.receipt);
|
||||
|
||||
if chain_spec.fork(Hardfork::Byzantium).active_at_block(block.header.number) {
|
||||
verify_receipt(block.header.receipts_root, block.header.logs_bloom, receipts_iter)?;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Ok(execution_result)
|
||||
}
|
||||
|
||||
/// Verify receipts
|
||||
pub fn verify_receipt<'a>(
|
||||
expected_receipts_root: H256,
|
||||
@@ -511,22 +543,6 @@ pub fn verify_receipt<'a>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify block. Execute all transaction and compare results.
|
||||
/// Returns ChangeSet on transaction granularity.
|
||||
/// NOTE: If block reward is still active (Before Paris/Merge) we would return
|
||||
/// additional TransactionStatechangeset for account that receives the reward.
|
||||
pub fn execute<DB: StateProvider>(
|
||||
block: &Block,
|
||||
total_difficulty: U256,
|
||||
senders: Option<Vec<Address>>,
|
||||
chain_spec: &ChainSpec,
|
||||
db: &mut SubState<DB>,
|
||||
) -> Result<ExecutionResult, Error> {
|
||||
let mut executor = Executor::new(chain_spec, db)
|
||||
.with_stack(InspectorStack::new(InspectorStackConfig::default()));
|
||||
executor.execute(block, total_difficulty, senders)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -640,13 +656,13 @@ mod tests {
|
||||
);
|
||||
|
||||
// spec at berlin fork
|
||||
let chain_spec = ChainSpecBuilder::mainnet().berlin_activated().build();
|
||||
let chain_spec = Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build());
|
||||
|
||||
let mut db = SubState::new(State::new(db));
|
||||
|
||||
// execute chain and verify receipts
|
||||
let out =
|
||||
execute_and_verify_receipt(&block, U256::ZERO, None, &chain_spec, &mut db).unwrap();
|
||||
let mut executor = Executor::new(chain_spec, &mut db);
|
||||
let out = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap();
|
||||
|
||||
assert_eq!(out.tx_changesets.len(), 1, "Should executed one transaction");
|
||||
|
||||
@@ -765,21 +781,23 @@ mod tests {
|
||||
beneficiary_balance += i;
|
||||
}
|
||||
|
||||
let chain_spec = ChainSpecBuilder::from(&*MAINNET)
|
||||
.homestead_activated()
|
||||
.with_fork(Hardfork::Dao, ForkCondition::Block(1))
|
||||
.build();
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
.homestead_activated()
|
||||
.with_fork(Hardfork::Dao, ForkCondition::Block(1))
|
||||
.build(),
|
||||
);
|
||||
|
||||
let mut db = SubState::new(State::new(db));
|
||||
// execute chain and verify receipts
|
||||
let out = execute_and_verify_receipt(
|
||||
&Block { header, body: vec![], ommers: vec![], withdrawals: None },
|
||||
U256::ZERO,
|
||||
None,
|
||||
&chain_spec,
|
||||
&mut db,
|
||||
)
|
||||
.unwrap();
|
||||
let mut executor = Executor::new(chain_spec, &mut db);
|
||||
let out = executor
|
||||
.execute_and_verify_receipt(
|
||||
&Block { header, body: vec![], ommers: vec![], withdrawals: None },
|
||||
U256::ZERO,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(out.tx_changesets.len(), 0, "No tx");
|
||||
|
||||
// Check if cache is set
|
||||
@@ -858,13 +876,13 @@ mod tests {
|
||||
);
|
||||
|
||||
// spec at berlin fork
|
||||
let chain_spec = ChainSpecBuilder::mainnet().berlin_activated().build();
|
||||
let chain_spec = Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build());
|
||||
|
||||
let mut db = SubState::new(State::new(db));
|
||||
|
||||
// execute chain and verify receipts
|
||||
let out =
|
||||
execute_and_verify_receipt(&block, U256::ZERO, None, &chain_spec, &mut db).unwrap();
|
||||
let mut executor = Executor::new(chain_spec, &mut db);
|
||||
let out = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap();
|
||||
|
||||
assert_eq!(out.tx_changesets.len(), 1, "Should executed one transaction");
|
||||
|
||||
@@ -907,17 +925,17 @@ mod tests {
|
||||
Address::from_str("c94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap();
|
||||
|
||||
// spec at shanghai fork
|
||||
let chain_spec = ChainSpecBuilder::mainnet().shanghai_activated().build();
|
||||
let chain_spec = Arc::new(ChainSpecBuilder::mainnet().shanghai_activated().build());
|
||||
|
||||
let mut db = SubState::new(State::new(StateProviderTest::default()));
|
||||
|
||||
// execute chain and verify receipts
|
||||
let out =
|
||||
execute_and_verify_receipt(&block, U256::ZERO, None, &chain_spec, &mut db).unwrap();
|
||||
let mut executor = Executor::new(chain_spec, &mut db);
|
||||
let out = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap();
|
||||
assert_eq!(out.tx_changesets.len(), 0, "No tx");
|
||||
|
||||
let withdrawal_sum = withdrawals.iter().fold(U256::ZERO, |sum, w| sum + w.amount_wei());
|
||||
let beneficiary_account = db.accounts.get(&withdrawal_beneficiary).unwrap();
|
||||
let beneficiary_account = executor.db().accounts.get(&withdrawal_beneficiary).unwrap();
|
||||
assert_eq!(beneficiary_account.info.balance, withdrawal_sum);
|
||||
assert_eq!(beneficiary_account.info.nonce, 0);
|
||||
assert_eq!(beneficiary_account.account_state, AccountState::StorageCleared);
|
||||
@@ -931,8 +949,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Execute same block again
|
||||
let out =
|
||||
execute_and_verify_receipt(&block, U256::ZERO, None, &chain_spec, &mut db).unwrap();
|
||||
let out = executor.execute_and_verify_receipt(&block, U256::ZERO, None).unwrap();
|
||||
assert_eq!(out.tx_changesets.len(), 0, "No tx");
|
||||
|
||||
assert_eq!(out.block_changesets.len(), 1);
|
||||
|
||||
Reference in New Issue
Block a user