use crate::{
block::BlockEnv, cfg::CfgEnv, journaled_state::JournaledState as JournaledStateImpl, tx::TxEnv,
};
use bytecode::{Bytecode, EOF_MAGIC_BYTES, EOF_MAGIC_HASH};
use context_interface::{
block::BlockSetter,
journaled_state::{AccountLoad, Eip7702CodeLoad},
result::EVMError,
transaction::TransactionSetter,
Block, BlockGetter, Cfg, CfgGetter, DatabaseGetter, ErrorGetter, JournalStateGetter,
Transaction, TransactionGetter,
};
use database_interface::{Database, EmptyDB};
use derive_where::derive_where;
use interpreter::{as_u64_saturated, Host, SStoreResult, SelfDestructResult, StateLoad};
use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256};
use specification::hardfork::SpecId;
#[derive_where(Clone, Debug; BLOCK, CFG, CHAIN, TX, DB, <DB as Database>::Error)]
pub struct Context<BLOCK = BlockEnv, TX = TxEnv, CFG = CfgEnv, DB: Database = EmptyDB, CHAIN = ()> {
pub tx: TX,
pub block: BLOCK,
pub cfg: CFG,
pub journaled_state: JournaledStateImpl<DB>,
pub chain: CHAIN,
pub error: Result<(), <DB as Database>::Error>,
}
impl Default for Context {
fn default() -> Self {
Self::new(EmptyDB::new(), SpecId::LATEST)
}
}
impl Context {
pub fn builder() -> Self {
Self::new(EmptyDB::new(), SpecId::LATEST)
}
}
impl<BLOCK: Block + Default, TX: Transaction + Default, DB: Database, CHAIN: Default>
Context<BLOCK, TX, CfgEnv, DB, CHAIN>
{
pub fn new(db: DB, spec: SpecId) -> Self {
Self {
tx: TX::default(),
block: BLOCK::default(),
cfg: CfgEnv {
spec,
..Default::default()
},
journaled_state: JournaledStateImpl::new(SpecId::LATEST, db),
chain: Default::default(),
error: Ok(()),
}
}
}
impl<BLOCK, TX, CFG, DB, CHAIN> Context<BLOCK, TX, CFG, DB, CHAIN>
where
BLOCK: Block,
TX: Transaction,
CFG: Cfg,
DB: Database,
{
#[inline]
pub fn code(
&mut self,
address: Address,
) -> Result<Eip7702CodeLoad<Bytes>, <DB as Database>::Error> {
let a = self.journaled_state.load_code(address)?;
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)?;
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,
))
}
pub fn with_db<ODB: Database>(self, db: ODB) -> Context<BLOCK, TX, CFG, ODB, CHAIN> {
let spec = self.cfg.spec().into();
Context {
tx: self.tx,
block: self.block,
cfg: self.cfg,
journaled_state: JournaledStateImpl::new(spec, db),
chain: self.chain,
error: Ok(()),
}
}
pub fn with_block<OB: Block>(self, block: OB) -> Context<OB, TX, CFG, DB, CHAIN> {
Context {
tx: self.tx,
block,
cfg: self.cfg,
journaled_state: self.journaled_state,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_tx<OTX: Transaction>(self, tx: OTX) -> Context<BLOCK, OTX, CFG, DB, CHAIN> {
Context {
tx,
block: self.block,
cfg: self.cfg,
journaled_state: self.journaled_state,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_chain<OC>(self, chain: OC) -> Context<BLOCK, TX, CFG, DB, OC> {
Context {
tx: self.tx,
block: self.block,
cfg: self.cfg,
journaled_state: self.journaled_state,
chain,
error: Ok(()),
}
}
pub fn with_cfg<OCFG: Cfg>(mut self, cfg: OCFG) -> Context<BLOCK, TX, OCFG, DB, CHAIN> {
self.journaled_state.set_spec_id(cfg.spec().into());
Context {
tx: self.tx,
block: self.block,
cfg,
journaled_state: self.journaled_state,
chain: self.chain,
error: Ok(()),
}
}
#[must_use]
pub fn modify_cfg_chained<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut CFG),
{
f(&mut self.cfg);
self.journaled_state.set_spec_id(self.cfg.spec().into());
self
}
#[must_use]
pub fn modify_block_chained<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut BLOCK),
{
self.modify_block(f);
self
}
#[must_use]
pub fn modify_tx_chained<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut TX),
{
self.modify_tx(f);
self
}
#[must_use]
pub fn modify_chain_chained<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut CHAIN),
{
self.modify_chain(f);
self
}
#[must_use]
pub fn modify_db_chained<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut DB),
{
self.modify_db(f);
self
}
#[must_use]
pub fn modify_journal_chained<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut JournaledStateImpl<DB>),
{
self.modify_journal(f);
self
}
pub fn modify_block<F>(&mut self, f: F)
where
F: FnOnce(&mut BLOCK),
{
f(&mut self.block);
}
pub fn modify_tx<F>(&mut self, f: F)
where
F: FnOnce(&mut TX),
{
f(&mut self.tx);
}
pub fn modify_cfg<F>(&mut self, f: F)
where
F: FnOnce(&mut CFG),
{
f(&mut self.cfg);
self.journaled_state.set_spec_id(self.cfg.spec().into());
}
pub fn modify_chain<F>(&mut self, f: F)
where
F: FnOnce(&mut CHAIN),
{
f(&mut self.chain);
}
pub fn modify_db<F>(&mut self, f: F)
where
F: FnOnce(&mut DB),
{
f(&mut self.journaled_state.database);
}
pub fn modify_journal<F>(&mut self, f: F)
where
F: FnOnce(&mut JournaledStateImpl<DB>),
{
f(&mut self.journaled_state);
}
#[inline]
pub fn code_hash(
&mut self,
address: Address,
) -> Result<Eip7702CodeLoad<B256>, <DB as Database>::Error> {
let acc = self.journaled_state.load_code(address)?;
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)?;
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))
}
}
impl<BLOCK: Block, TX: Transaction, CFG: Cfg, DB: Database, CHAIN> Host
for Context<BLOCK, TX, CFG, DB, CHAIN>
{
type BLOCK = BLOCK;
type TX = TX;
type CFG = CFG;
fn tx(&self) -> &Self::TX {
&self.tx
}
fn block(&self) -> &Self::BLOCK {
&self.block
}
fn cfg(&self) -> &Self::CFG {
&self.cfg
}
fn block_hash(&mut self, requested_number: u64) -> Option<B256> {
let block_number = as_u64_saturated!(*self.block().number());
let Some(diff) = block_number.checked_sub(requested_number) else {
return Some(B256::ZERO);
};
if diff == 0 {
return Some(B256::ZERO);
}
if diff <= BLOCK_HASH_HISTORY {
return self
.journaled_state
.database
.block_hash(requested_number)
.map_err(|e| self.error = Err(e))
.ok();
}
Some(B256::ZERO)
}
fn load_account_delegated(&mut self, address: Address) -> Option<AccountLoad> {
self.journaled_state
.load_account_delegated(address)
.map_err(|e| self.error = Err(e))
.ok()
}
fn balance(&mut self, address: Address) -> Option<StateLoad<U256>> {
self.journaled_state
.load_account(address)
.map_err(|e| self.error = Err(e))
.map(|acc| acc.map(|a| a.info.balance))
.ok()
}
fn code(&mut self, address: Address) -> Option<Eip7702CodeLoad<Bytes>> {
self.code(address).map_err(|e| self.error = Err(e)).ok()
}
fn code_hash(&mut self, address: Address) -> Option<Eip7702CodeLoad<B256>> {
self.code_hash(address)
.map_err(|e| self.error = Err(e))
.ok()
}
fn sload(&mut self, address: Address, index: U256) -> Option<StateLoad<U256>> {
self.journaled_state
.sload(address, index)
.map_err(|e| self.error = Err(e))
.ok()
}
fn sstore(
&mut self,
address: Address,
index: U256,
value: U256,
) -> Option<StateLoad<SStoreResult>> {
self.journaled_state
.sstore(address, index, value)
.map_err(|e| self.error = Err(e))
.ok()
}
fn tload(&mut self, address: Address, index: U256) -> U256 {
self.journaled_state.tload(address, index)
}
fn tstore(&mut self, address: Address, index: U256, value: U256) {
self.journaled_state.tstore(address, index, value)
}
fn log(&mut self, log: Log) {
self.journaled_state.log(log);
}
fn selfdestruct(
&mut self,
address: Address,
target: Address,
) -> Option<StateLoad<SelfDestructResult>> {
self.journaled_state
.selfdestruct(address, target)
.map_err(|e| self.error = Err(e))
.ok()
}
}
impl<BLOCK, TX, DB: Database, CFG: Cfg, CHAIN> CfgGetter for Context<BLOCK, TX, CFG, DB, CHAIN> {
type Cfg = CFG;
fn cfg(&self) -> &Self::Cfg {
&self.cfg
}
}
impl<BLOCK, TX, SPEC, DB: Database, CHAIN> JournalStateGetter
for Context<BLOCK, TX, SPEC, DB, CHAIN>
{
type Journal = JournaledStateImpl<DB>;
fn journal(&mut self) -> &mut Self::Journal {
&mut self.journaled_state
}
}
impl<BLOCK, TX, SPEC, DB: Database, CHAIN> DatabaseGetter for Context<BLOCK, TX, SPEC, DB, CHAIN> {
type Database = DB;
fn db(&mut self) -> &mut Self::Database {
&mut self.journaled_state.database
}
}
impl<BLOCK, TX: Transaction, SPEC, DB: Database, CHAIN> ErrorGetter
for Context<BLOCK, TX, SPEC, DB, CHAIN>
{
type Error = EVMError<DB::Error, TX::TransactionError>;
fn take_error(&mut self) -> Result<(), Self::Error> {
core::mem::replace(&mut self.error, Ok(())).map_err(EVMError::Database)
}
}
impl<BLOCK, TX: Transaction, SPEC, DB: Database, CHAIN> TransactionGetter
for Context<BLOCK, TX, SPEC, DB, CHAIN>
{
type Transaction = TX;
fn tx(&self) -> &Self::Transaction {
&self.tx
}
}
impl<BLOCK, TX: Transaction, SPEC, DB: Database, CHAIN> TransactionSetter
for Context<BLOCK, TX, SPEC, DB, CHAIN>
{
fn set_tx(&mut self, tx: <Self as TransactionGetter>::Transaction) {
self.tx = tx;
}
}
impl<BLOCK: Block, TX, SPEC, DB: Database, CHAIN> BlockGetter
for Context<BLOCK, TX, SPEC, DB, CHAIN>
{
type Block = BLOCK;
fn block(&self) -> &Self::Block {
&self.block
}
}
impl<BLOCK: Block, TX, SPEC, DB: Database, CHAIN> BlockSetter
for Context<BLOCK, TX, SPEC, DB, CHAIN>
{
fn set_block(&mut self, block: <Self as BlockGetter>::Block) {
self.block = block;
}
}