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:
Georgios Konstantopoulos
2023-02-28 16:55:51 -07:00
committed by GitHub
parent 71bc1451af
commit 4285186dbd
12 changed files with 181 additions and 147 deletions

View File

@@ -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);