use crate::{block::BlockEnv, cfg::CfgEnv, journal::Journal, tx::TxEnv, LocalContext};
use context_interface::{
cfg::GasParams,
context::{ContextError, ContextSetters, SStoreResult, SelfDestructResult, StateLoad},
host::LoadError,
journaled_state::AccountInfoLoad,
Block, Cfg, ContextTr, Host, JournalTr, LocalContextTr, Transaction, TransactionType,
};
use database_interface::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef};
use derive_where::derive_where;
use primitives::{
hardfork::SpecId, hints_util::cold_path, Address, Log, StorageKey, StorageValue, B256, U256,
};
#[derive_where(Clone, Debug; BLOCK, CFG, CHAIN, TX, DB, JOURNAL, <DB as Database>::Error, LOCAL)]
pub struct Context<
BLOCK = BlockEnv,
TX = TxEnv,
CFG = CfgEnv,
DB: Database = EmptyDB,
JOURNAL: JournalTr<Database = DB> = Journal<DB>,
CHAIN = (),
LOCAL: LocalContextTr = LocalContext,
> {
pub block: BLOCK,
pub tx: TX,
pub cfg: CFG,
pub journaled_state: JOURNAL,
pub chain: CHAIN,
pub local: LOCAL,
pub error: Result<(), ContextError<DB::Error>>,
}
impl<
BLOCK: Block,
TX: Transaction,
DB: Database,
CFG: Cfg,
JOURNAL: JournalTr<Database = DB>,
CHAIN,
LOCAL: LocalContextTr,
> ContextTr for Context<BLOCK, TX, CFG, DB, JOURNAL, CHAIN, LOCAL>
{
type Block = BLOCK;
type Tx = TX;
type Cfg = CFG;
type Db = DB;
type Journal = JOURNAL;
type Chain = CHAIN;
type Local = LOCAL;
#[inline]
fn all(
&self,
) -> (
&Self::Block,
&Self::Tx,
&Self::Cfg,
&Self::Db,
&Self::Journal,
&Self::Chain,
&Self::Local,
) {
let block = &self.block;
let tx = &self.tx;
let cfg = &self.cfg;
let db = self.journaled_state.db();
let journal = &self.journaled_state;
let chain = &self.chain;
let local = &self.local;
(block, tx, cfg, db, journal, chain, local)
}
#[inline]
fn all_mut(
&mut self,
) -> (
&Self::Block,
&Self::Tx,
&Self::Cfg,
&mut Self::Journal,
&mut Self::Chain,
&mut Self::Local,
) {
let block = &self.block;
let tx = &self.tx;
let cfg = &self.cfg;
let journal = &mut self.journaled_state;
let chain = &mut self.chain;
let local = &mut self.local;
(block, tx, cfg, journal, chain, local)
}
#[inline]
fn error(&mut self) -> &mut Result<(), ContextError<<Self::Db as Database>::Error>> {
&mut self.error
}
}
impl<
BLOCK: Block,
TX: Transaction,
DB: Database,
CFG: Cfg,
JOURNAL: JournalTr<Database = DB>,
CHAIN,
LOCAL: LocalContextTr,
> ContextSetters for Context<BLOCK, TX, CFG, DB, JOURNAL, CHAIN, LOCAL>
{
fn set_tx(&mut self, tx: Self::Tx) {
self.tx = tx;
}
fn set_block(&mut self, block: Self::Block) {
self.block = block;
}
}
impl<
BLOCK: Block + Default,
TX: Transaction + Default,
DB: Database,
JOURNAL: JournalTr<Database = DB>,
CHAIN: Default,
LOCAL: LocalContextTr + Default,
SPEC: Default + Into<SpecId> + Clone,
> Context<BLOCK, TX, CfgEnv<SPEC>, DB, JOURNAL, CHAIN, LOCAL>
{
pub fn new(db: DB, spec: SPEC) -> Self {
let cfg = CfgEnv::new_with_spec(spec);
let mut journaled_state = JOURNAL::new(db);
journaled_state.set_spec_id(cfg.spec.clone().into());
journaled_state.set_eip7708_config(
cfg.amsterdam_eip7708_disabled,
cfg.amsterdam_eip7708_delayed_burn_disabled,
);
Self {
tx: TX::default(),
block: BLOCK::default(),
cfg,
local: LOCAL::default(),
journaled_state,
chain: Default::default(),
error: Ok(()),
}
}
}
impl<BLOCK, TX, CFG, DB, JOURNAL, CHAIN, LOCAL> Context<BLOCK, TX, CFG, DB, JOURNAL, CHAIN, LOCAL>
where
BLOCK: Block,
TX: Transaction,
CFG: Cfg,
DB: Database,
JOURNAL: JournalTr<Database = DB>,
LOCAL: LocalContextTr,
{
pub fn with_new_journal<OJOURNAL: JournalTr<Database = DB>>(
self,
mut journal: OJOURNAL,
) -> Context<BLOCK, TX, CFG, DB, OJOURNAL, CHAIN, LOCAL> {
journal.set_spec_id(self.cfg.spec().into());
journal.set_eip7708_config(
self.cfg.is_eip7708_disabled(),
self.cfg.is_eip7708_delayed_burn_disabled(),
);
Context {
tx: self.tx,
block: self.block,
cfg: self.cfg,
journaled_state: journal,
local: self.local,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_db<ODB: Database>(
self,
db: ODB,
) -> Context<BLOCK, TX, CFG, ODB, Journal<ODB>, CHAIN, LOCAL> {
let mut journaled_state = Journal::new(db);
journaled_state.set_spec_id(self.cfg.spec().into());
journaled_state.set_eip7708_config(
self.cfg.is_eip7708_disabled(),
self.cfg.is_eip7708_delayed_burn_disabled(),
);
Context {
tx: self.tx,
block: self.block,
cfg: self.cfg,
journaled_state,
local: self.local,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_ref_db<ODB: DatabaseRef>(
self,
db: ODB,
) -> Context<BLOCK, TX, CFG, WrapDatabaseRef<ODB>, Journal<WrapDatabaseRef<ODB>>, CHAIN, LOCAL>
{
let mut journaled_state = Journal::new(WrapDatabaseRef(db));
journaled_state.set_spec_id(self.cfg.spec().into());
journaled_state.set_eip7708_config(
self.cfg.is_eip7708_disabled(),
self.cfg.is_eip7708_delayed_burn_disabled(),
);
Context {
tx: self.tx,
block: self.block,
cfg: self.cfg,
journaled_state,
local: self.local,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_block<OB: Block>(
self,
block: OB,
) -> Context<OB, TX, CFG, DB, JOURNAL, CHAIN, LOCAL> {
Context {
tx: self.tx,
block,
cfg: self.cfg,
journaled_state: self.journaled_state,
local: self.local,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_tx<OTX: Transaction>(
self,
tx: OTX,
) -> Context<BLOCK, OTX, CFG, DB, JOURNAL, CHAIN, LOCAL> {
Context {
tx,
block: self.block,
cfg: self.cfg,
journaled_state: self.journaled_state,
local: self.local,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_chain<OC>(self, chain: OC) -> Context<BLOCK, TX, CFG, DB, JOURNAL, OC, LOCAL> {
Context {
tx: self.tx,
block: self.block,
cfg: self.cfg,
journaled_state: self.journaled_state,
local: self.local,
chain,
error: Ok(()),
}
}
pub fn with_cfg<OCFG: Cfg>(
mut self,
cfg: OCFG,
) -> Context<BLOCK, TX, OCFG, DB, JOURNAL, CHAIN, LOCAL> {
self.journaled_state.set_spec_id(cfg.spec().into());
self.journaled_state.set_eip7708_config(
cfg.is_eip7708_disabled(),
cfg.is_eip7708_delayed_burn_disabled(),
);
Context {
tx: self.tx,
block: self.block,
cfg,
journaled_state: self.journaled_state,
local: self.local,
chain: self.chain,
error: Ok(()),
}
}
pub fn with_local<OL: LocalContextTr>(
self,
local: OL,
) -> Context<BLOCK, TX, CFG, DB, JOURNAL, CHAIN, OL> {
Context {
tx: self.tx,
block: self.block,
cfg: self.cfg,
journaled_state: self.journaled_state,
local,
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.journaled_state.set_eip7708_config(
self.cfg.is_eip7708_disabled(),
self.cfg.is_eip7708_delayed_burn_disabled(),
);
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 JOURNAL),
{
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());
self.journaled_state.set_eip7708_config(
self.cfg.is_eip7708_disabled(),
self.cfg.is_eip7708_delayed_burn_disabled(),
);
}
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(self.journaled_state.db_mut());
}
pub fn modify_journal<F>(&mut self, f: F)
where
F: FnOnce(&mut JOURNAL),
{
f(&mut self.journaled_state);
}
pub fn modify_local<F>(&mut self, f: F)
where
F: FnOnce(&mut LOCAL),
{
f(&mut self.local);
}
}
impl<
BLOCK: Block,
TX: Transaction,
CFG: Cfg,
DB: Database,
JOURNAL: JournalTr<Database = DB>,
CHAIN,
LOCAL: LocalContextTr,
> Host for Context<BLOCK, TX, CFG, DB, JOURNAL, CHAIN, LOCAL>
{
fn basefee(&self) -> U256 {
U256::from(self.block().basefee())
}
fn blob_gasprice(&self) -> U256 {
U256::from(self.block().blob_gasprice().unwrap_or(0))
}
fn gas_limit(&self) -> U256 {
U256::from(self.block().gas_limit())
}
fn difficulty(&self) -> U256 {
self.block().difficulty()
}
fn prevrandao(&self) -> Option<U256> {
self.block().prevrandao().map(|r| r.into())
}
#[inline]
fn gas_params(&self) -> &GasParams {
self.cfg().gas_params()
}
fn is_amsterdam_eip8037_enabled(&self) -> bool {
self.cfg().is_amsterdam_eip8037_enabled()
}
fn block_number(&self) -> U256 {
self.block().number()
}
fn timestamp(&self) -> U256 {
U256::from(self.block().timestamp())
}
fn beneficiary(&self) -> Address {
self.block().beneficiary()
}
fn slot_num(&self) -> U256 {
U256::from(self.block().slot_num())
}
fn chain_id(&self) -> U256 {
U256::from(self.cfg().chain_id())
}
fn effective_gas_price(&self) -> U256 {
let basefee = self.block().basefee();
U256::from(self.tx().effective_gas_price(basefee as u128))
}
fn caller(&self) -> Address {
self.tx().caller()
}
fn blob_hash(&self, number: usize) -> Option<U256> {
let tx = &self.tx();
if tx.tx_type() != TransactionType::Eip4844 {
return None;
}
tx.blob_versioned_hashes()
.get(number)
.map(|t| U256::from_be_bytes(t.0))
}
fn max_initcode_size(&self) -> usize {
self.cfg().max_initcode_size()
}
fn block_hash(&mut self, requested_number: u64) -> Option<B256> {
self.db_mut()
.block_hash(requested_number)
.map_err(|e| {
cold_path();
*self.error() = Err(e.into());
})
.ok()
}
fn tload(&mut self, address: Address, index: StorageKey) -> StorageValue {
self.journal_mut().tload(address, index)
}
fn tstore(&mut self, address: Address, index: StorageKey, value: StorageValue) {
self.journal_mut().tstore(address, index, value)
}
fn log(&mut self, log: Log) {
self.journal_mut().log(log);
}
#[inline]
fn selfdestruct(
&mut self,
address: Address,
target: Address,
skip_cold_load: bool,
) -> Result<StateLoad<SelfDestructResult>, LoadError> {
self.journal_mut()
.selfdestruct(address, target, skip_cold_load)
.map_err(|e| {
cold_path();
let (ret, err) = e.into_parts();
if let Some(err) = err {
*self.error() = Err(err.into());
}
ret
})
}
#[inline]
fn sstore_skip_cold_load(
&mut self,
address: Address,
key: StorageKey,
value: StorageValue,
skip_cold_load: bool,
) -> Result<StateLoad<SStoreResult>, LoadError> {
self.journal_mut()
.sstore_skip_cold_load(address, key, value, skip_cold_load)
.map_err(|e| {
cold_path();
let (ret, err) = e.into_parts();
if let Some(err) = err {
*self.error() = Err(err.into());
}
ret
})
}
#[inline]
fn sload_skip_cold_load(
&mut self,
address: Address,
key: StorageKey,
skip_cold_load: bool,
) -> Result<StateLoad<StorageValue>, LoadError> {
self.journal_mut()
.sload_skip_cold_load(address, key, skip_cold_load)
.map_err(|e| {
cold_path();
let (ret, err) = e.into_parts();
if let Some(err) = err {
*self.error() = Err(err.into());
}
ret
})
}
#[inline]
fn load_account_info_skip_cold_load(
&mut self,
address: Address,
load_code: bool,
skip_cold_load: bool,
) -> Result<AccountInfoLoad<'_>, LoadError> {
match self.journaled_state.load_account_info_skip_cold_load(
address,
load_code,
skip_cold_load,
) {
Ok(a) => Ok(a),
Err(e) => {
cold_path();
let (ret, err) = e.into_parts();
if let Some(err) = err {
self.error = Err(err.into());
}
Err(ret)
}
}
}
}