use crate::{
db::Database,
interpreter::{
analysis::to_analysed, gas, return_ok, AccountLoad, Eip7702CodeLoad, InstructionResult,
InterpreterResult, SStoreResult, SelfDestructResult, StateLoad,
},
journaled_state::JournaledState,
primitives::{
AccessListItem, Account, Address, AnalysisKind, Bytecode, Bytes, CfgEnv, EVMError, Env,
Eof, HashSet, Spec,
SpecId::{self, *},
B256, EOF_MAGIC_BYTES, EOF_MAGIC_HASH, U256,
},
JournalCheckpoint,
};
use std::{boxed::Box, sync::Arc};
#[derive(Debug)]
pub struct InnerEvmContext<DB: Database> {
pub env: Box<Env>,
pub journaled_state: JournaledState,
pub db: DB,
pub error: Result<(), EVMError<DB::Error>>,
#[cfg(feature = "optimism")]
pub l1_block_info: Option<crate::optimism::L1BlockInfo>,
}
impl<DB: Database + Clone> Clone for InnerEvmContext<DB>
where
DB::Error: Clone,
{
fn clone(&self) -> Self {
Self {
env: self.env.clone(),
journaled_state: self.journaled_state.clone(),
db: self.db.clone(),
error: self.error.clone(),
#[cfg(feature = "optimism")]
l1_block_info: self.l1_block_info.clone(),
}
}
}
impl<DB: Database> InnerEvmContext<DB> {
pub fn new(db: DB) -> Self {
Self {
env: Box::default(),
journaled_state: JournaledState::new(SpecId::LATEST, HashSet::default()),
db,
error: Ok(()),
#[cfg(feature = "optimism")]
l1_block_info: None,
}
}
#[inline]
pub fn new_with_env(db: DB, env: Box<Env>) -> Self {
Self {
env,
journaled_state: JournaledState::new(SpecId::LATEST, HashSet::default()),
db,
error: Ok(()),
#[cfg(feature = "optimism")]
l1_block_info: None,
}
}
#[inline]
pub fn with_db<ODB: Database>(self, db: ODB) -> InnerEvmContext<ODB> {
InnerEvmContext {
env: self.env,
journaled_state: self.journaled_state,
db,
error: Ok(()),
#[cfg(feature = "optimism")]
l1_block_info: self.l1_block_info,
}
}
#[inline]
pub const fn spec_id(&self) -> SpecId {
self.journaled_state.spec
}
#[inline]
pub fn load_access_list(&mut self) -> Result<(), EVMError<DB::Error>> {
for AccessListItem {
address,
storage_keys,
} in self.env.tx.access_list.iter()
{
self.journaled_state.initial_account_load(
*address,
storage_keys.iter().map(|i| U256::from_be_bytes(i.0)),
&mut self.db,
)?;
}
Ok(())
}
#[inline]
pub fn env(&mut self) -> &mut Env {
&mut self.env
}
pub fn cfg(&self) -> &CfgEnv {
&self.env.cfg
}
#[inline]
pub fn take_error(&mut self) -> Result<(), EVMError<DB::Error>> {
core::mem::replace(&mut self.error, Ok(()))
}
#[inline]
pub fn block_hash(&mut self, number: u64) -> Result<B256, EVMError<DB::Error>> {
self.db.block_hash(number).map_err(EVMError::Database)
}
#[inline]
pub fn touch(&mut self, address: &Address) {
self.journaled_state.touch(address);
}
#[inline]
pub fn load_account(
&mut self,
address: Address,
) -> Result<StateLoad<&mut Account>, EVMError<DB::Error>> {
self.journaled_state.load_account(address, &mut self.db)
}
#[inline]
pub fn load_account_delegated(
&mut self,
address: Address,
) -> Result<AccountLoad, EVMError<DB::Error>> {
self.journaled_state
.load_account_delegated(address, &mut self.db)
}
#[inline]
pub fn balance(&mut self, address: Address) -> Result<StateLoad<U256>, EVMError<DB::Error>> {
self.journaled_state
.load_account(address, &mut self.db)
.map(|acc| acc.map(|a| a.info.balance))
}
#[inline]
pub fn code(
&mut self,
address: Address,
) -> Result<Eip7702CodeLoad<Bytes>, EVMError<DB::Error>> {
let a = self.journaled_state.load_code(address, &mut self.db)?;
let code = a.info.code.as_ref().unwrap();
if code.is_eof() {
return Ok(Eip7702CodeLoad::new_not_delegated(
EOF_MAGIC_BYTES.clone(),
a.is_cold,
));
}
if let Bytecode::Eip7702(code) = code {
let address = code.address();
let is_cold = a.is_cold;
let delegated_account = self.journaled_state.load_code(address, &mut self.db)?;
let delegated_code = delegated_account.info.code.as_ref().unwrap();
let bytes = if delegated_code.is_eof() {
EOF_MAGIC_BYTES.clone()
} else {
delegated_code.original_bytes()
};
return Ok(Eip7702CodeLoad::new(
StateLoad::new(bytes, is_cold),
delegated_account.is_cold,
));
}
Ok(Eip7702CodeLoad::new_not_delegated(
code.original_bytes(),
a.is_cold,
))
}
#[inline]
pub fn code_hash(
&mut self,
address: Address,
) -> Result<Eip7702CodeLoad<B256>, EVMError<DB::Error>> {
let acc = self.journaled_state.load_code(address, &mut self.db)?;
if acc.is_empty() {
return Ok(Eip7702CodeLoad::new_not_delegated(B256::ZERO, acc.is_cold));
}
let code = acc.info.code.as_ref().unwrap();
if let Bytecode::Eip7702(code) = code {
let address = code.address();
let is_cold = acc.is_cold;
let delegated_account = self.journaled_state.load_code(address, &mut self.db)?;
let hash = if delegated_account.is_empty() {
B256::ZERO
} else if delegated_account.info.code.as_ref().unwrap().is_eof() {
EOF_MAGIC_HASH
} else {
delegated_account.info.code_hash
};
return Ok(Eip7702CodeLoad::new(
StateLoad::new(hash, is_cold),
delegated_account.is_cold,
));
}
let hash = if code.is_eof() {
EOF_MAGIC_HASH
} else {
acc.info.code_hash
};
Ok(Eip7702CodeLoad::new_not_delegated(hash, acc.is_cold))
}
#[inline]
pub fn sload(
&mut self,
address: Address,
index: U256,
) -> Result<StateLoad<U256>, EVMError<DB::Error>> {
self.journaled_state.sload(address, index, &mut self.db)
}
#[inline]
pub fn sstore(
&mut self,
address: Address,
index: U256,
value: U256,
) -> Result<StateLoad<SStoreResult>, EVMError<DB::Error>> {
self.journaled_state
.sstore(address, index, value, &mut self.db)
}
#[inline]
pub fn tload(&mut self, address: Address, index: U256) -> U256 {
self.journaled_state.tload(address, index)
}
#[inline]
pub fn tstore(&mut self, address: Address, index: U256, value: U256) {
self.journaled_state.tstore(address, index, value)
}
#[inline]
pub fn selfdestruct(
&mut self,
address: Address,
target: Address,
) -> Result<StateLoad<SelfDestructResult>, EVMError<DB::Error>> {
self.journaled_state
.selfdestruct(address, target, &mut self.db)
}
pub fn eofcreate_return<SPEC: Spec>(
&mut self,
interpreter_result: &mut InterpreterResult,
address: Address,
journal_checkpoint: JournalCheckpoint,
) {
if interpreter_result.result != InstructionResult::ReturnContract {
self.journaled_state.checkpoint_revert(journal_checkpoint);
return;
}
if interpreter_result.output.len() > self.cfg().max_code_size() {
self.journaled_state.checkpoint_revert(journal_checkpoint);
interpreter_result.result = InstructionResult::CreateContractSizeLimit;
return;
}
let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT;
if !interpreter_result.gas.record_cost(gas_for_code) {
self.journaled_state.checkpoint_revert(journal_checkpoint);
interpreter_result.result = InstructionResult::OutOfGas;
return;
}
self.journaled_state.checkpoint_commit();
let bytecode =
Eof::decode(interpreter_result.output.clone()).expect("Eof is already verified");
self.journaled_state
.set_code(address, Bytecode::Eof(Arc::new(bytecode)));
}
#[inline]
pub fn call_return(
&mut self,
interpreter_result: &InterpreterResult,
journal_checkpoint: JournalCheckpoint,
) {
if matches!(interpreter_result.result, return_ok!()) {
self.journaled_state.checkpoint_commit();
} else {
self.journaled_state.checkpoint_revert(journal_checkpoint);
}
}
#[inline]
pub fn create_return<SPEC: Spec>(
&mut self,
interpreter_result: &mut InterpreterResult,
address: Address,
journal_checkpoint: JournalCheckpoint,
) {
if !matches!(interpreter_result.result, return_ok!()) {
self.journaled_state.checkpoint_revert(journal_checkpoint);
return;
}
if SPEC::enabled(LONDON) && interpreter_result.output.first() == Some(&0xEF) {
self.journaled_state.checkpoint_revert(journal_checkpoint);
interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
return;
}
if SPEC::enabled(SPURIOUS_DRAGON)
&& interpreter_result.output.len() > self.cfg().max_code_size()
{
self.journaled_state.checkpoint_revert(journal_checkpoint);
interpreter_result.result = InstructionResult::CreateContractSizeLimit;
return;
}
let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT;
if !interpreter_result.gas.record_cost(gas_for_code) {
if SPEC::enabled(HOMESTEAD) {
self.journaled_state.checkpoint_revert(journal_checkpoint);
interpreter_result.result = InstructionResult::OutOfGas;
return;
} else {
interpreter_result.output = Bytes::new();
}
}
self.journaled_state.checkpoint_commit();
let bytecode = match self.env.cfg.perf_analyse_created_bytecodes {
AnalysisKind::Raw => Bytecode::new_legacy(interpreter_result.output.clone()),
AnalysisKind::Analyse => {
to_analysed(Bytecode::new_legacy(interpreter_result.output.clone()))
}
};
self.journaled_state.set_code(address, bytecode);
interpreter_result.result = InstructionResult::Return;
}
}