use crate::{
cfg::GasParams,
context::{SStoreResult, SelfDestructResult, StateLoad},
journaled_state::{AccountInfoLoad, AccountLoad},
};
use auto_impl::auto_impl;
use primitives::{hardfork::SpecId, Address, Bytes, Log, StorageKey, StorageValue, B256, U256};
use state::Bytecode;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum LoadError {
ColdLoadSkipped,
DBError,
}
#[auto_impl(&mut, Box)]
pub trait Host {
fn basefee(&self) -> U256;
fn blob_gasprice(&self) -> U256;
fn gas_limit(&self) -> U256;
fn difficulty(&self) -> U256;
fn prevrandao(&self) -> Option<U256>;
fn block_number(&self) -> U256;
fn timestamp(&self) -> U256;
fn beneficiary(&self) -> Address;
fn slot_num(&self) -> U256;
fn chain_id(&self) -> U256;
fn effective_gas_price(&self) -> U256;
fn caller(&self) -> Address;
fn blob_hash(&self, number: usize) -> Option<U256>;
fn max_initcode_size(&self) -> usize;
fn gas_params(&self) -> &GasParams;
fn is_amsterdam_eip8037_enabled(&self) -> bool;
fn block_hash(&mut self, number: u64) -> Option<B256>;
fn selfdestruct(
&mut self,
address: Address,
target: Address,
skip_cold_load: bool,
) -> Result<StateLoad<SelfDestructResult>, LoadError>;
fn log(&mut self, log: Log);
fn sstore_skip_cold_load(
&mut self,
address: Address,
key: StorageKey,
value: StorageValue,
skip_cold_load: bool,
) -> Result<StateLoad<SStoreResult>, LoadError>;
fn sstore(
&mut self,
address: Address,
key: StorageKey,
value: StorageValue,
) -> Option<StateLoad<SStoreResult>> {
self.sstore_skip_cold_load(address, key, value, false).ok()
}
fn sload_skip_cold_load(
&mut self,
address: Address,
key: StorageKey,
skip_cold_load: bool,
) -> Result<StateLoad<StorageValue>, LoadError>;
fn sload(&mut self, address: Address, key: StorageKey) -> Option<StateLoad<StorageValue>> {
self.sload_skip_cold_load(address, key, false).ok()
}
fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue);
fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue;
fn load_account_info_skip_cold_load(
&mut self,
address: Address,
load_code: bool,
skip_cold_load: bool,
) -> Result<AccountInfoLoad<'_>, LoadError>;
#[inline]
fn balance(&mut self, address: Address) -> Option<StateLoad<U256>> {
self.load_account_info_skip_cold_load(address, false, false)
.ok()
.map(|load| load.into_state_load(|i| i.balance))
}
#[inline]
fn load_account_delegated(&mut self, address: Address) -> Option<StateLoad<AccountLoad>> {
let account = self
.load_account_info_skip_cold_load(address, true, false)
.ok()?;
let mut account_load = StateLoad::new(
AccountLoad {
is_delegate_account_cold: None,
is_empty: account.is_empty,
},
account.is_cold,
);
if let Some(address) = account.code.as_ref().and_then(Bytecode::eip7702_address) {
let delegate_account = self
.load_account_info_skip_cold_load(address, true, false)
.ok()?;
account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
}
Some(account_load)
}
#[inline]
fn load_account_code(&mut self, address: Address) -> Option<StateLoad<Bytes>> {
self.load_account_info_skip_cold_load(address, true, false)
.ok()
.map(|load| {
load.into_state_load(|i| {
i.code
.as_ref()
.map(|b| b.original_bytes())
.unwrap_or_default()
})
})
}
#[inline]
fn load_account_code_hash(&mut self, address: Address) -> Option<StateLoad<B256>> {
self.load_account_info_skip_cold_load(address, false, false)
.ok()
.map(|load| {
load.into_state_load(|i| {
if i.is_empty() {
B256::ZERO
} else {
i.code_hash
}
})
})
}
}
#[derive(Default, Debug)]
pub struct DummyHost {
gas_params: GasParams,
}
impl DummyHost {
pub fn new(spec: SpecId) -> Self {
Self {
gas_params: GasParams::new_spec(spec),
}
}
}
impl Host for DummyHost {
fn basefee(&self) -> U256 {
U256::ZERO
}
fn blob_gasprice(&self) -> U256 {
U256::ZERO
}
fn gas_limit(&self) -> U256 {
U256::ZERO
}
fn gas_params(&self) -> &GasParams {
&self.gas_params
}
fn is_amsterdam_eip8037_enabled(&self) -> bool {
false
}
fn difficulty(&self) -> U256 {
U256::ZERO
}
fn prevrandao(&self) -> Option<U256> {
None
}
fn block_number(&self) -> U256 {
U256::ZERO
}
fn timestamp(&self) -> U256 {
U256::ZERO
}
fn beneficiary(&self) -> Address {
Address::ZERO
}
fn slot_num(&self) -> U256 {
U256::ZERO
}
fn chain_id(&self) -> U256 {
U256::ZERO
}
fn effective_gas_price(&self) -> U256 {
U256::ZERO
}
fn caller(&self) -> Address {
Address::ZERO
}
fn blob_hash(&self, _number: usize) -> Option<U256> {
None
}
fn max_initcode_size(&self) -> usize {
0
}
fn block_hash(&mut self, _number: u64) -> Option<B256> {
None
}
fn selfdestruct(
&mut self,
_address: Address,
_target: Address,
_skip_cold_load: bool,
) -> Result<StateLoad<SelfDestructResult>, LoadError> {
Ok(Default::default())
}
fn log(&mut self, _log: Log) {}
fn tstore(&mut self, _address: Address, _key: StorageKey, _value: StorageValue) {}
fn tload(&mut self, _address: Address, _key: StorageKey) -> StorageValue {
StorageValue::ZERO
}
fn load_account_info_skip_cold_load(
&mut self,
_address: Address,
_load_code: bool,
_skip_cold_load: bool,
) -> Result<AccountInfoLoad<'_>, LoadError> {
Ok(Default::default())
}
fn sstore_skip_cold_load(
&mut self,
_address: Address,
_key: StorageKey,
_value: StorageValue,
_skip_cold_load: bool,
) -> Result<StateLoad<SStoreResult>, LoadError> {
Ok(Default::default())
}
fn sload_skip_cold_load(
&mut self,
_address: Address,
_key: StorageKey,
_skip_cold_load: bool,
) -> Result<StateLoad<StorageValue>, LoadError> {
Ok(Default::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
use primitives::{hardfork::SpecId, Address, U256};
use state::{AccountInfo, Bytecode};
use std::borrow::Cow;
struct Eip7702Host {
dummy: DummyHost,
delegated: Address,
delegate: Address,
delegated_info: AccountInfo,
}
impl Eip7702Host {
fn new() -> Self {
let delegated = Address::repeat_byte(0x11);
let delegate = Address::repeat_byte(0x22);
let delegated_info = AccountInfo::new(
U256::from(1),
1,
B256::ZERO,
Bytecode::new_eip7702(delegate),
);
Self {
dummy: DummyHost::new(SpecId::PRAGUE),
delegated,
delegate,
delegated_info,
}
}
}
impl Host for Eip7702Host {
fn basefee(&self) -> U256 {
self.dummy.basefee()
}
fn blob_gasprice(&self) -> U256 {
self.dummy.blob_gasprice()
}
fn gas_limit(&self) -> U256 {
self.dummy.gas_limit()
}
fn gas_params(&self) -> &GasParams {
self.dummy.gas_params()
}
fn is_amsterdam_eip8037_enabled(&self) -> bool {
self.dummy.is_amsterdam_eip8037_enabled()
}
fn difficulty(&self) -> U256 {
self.dummy.difficulty()
}
fn prevrandao(&self) -> Option<U256> {
self.dummy.prevrandao()
}
fn block_number(&self) -> U256 {
self.dummy.block_number()
}
fn timestamp(&self) -> U256 {
self.dummy.timestamp()
}
fn beneficiary(&self) -> Address {
self.dummy.beneficiary()
}
fn slot_num(&self) -> U256 {
self.dummy.slot_num()
}
fn chain_id(&self) -> U256 {
self.dummy.chain_id()
}
fn effective_gas_price(&self) -> U256 {
self.dummy.effective_gas_price()
}
fn caller(&self) -> Address {
self.dummy.caller()
}
fn blob_hash(&self, number: usize) -> Option<U256> {
self.dummy.blob_hash(number)
}
fn max_initcode_size(&self) -> usize {
self.dummy.max_initcode_size()
}
fn block_hash(&mut self, number: u64) -> Option<B256> {
self.dummy.block_hash(number)
}
fn selfdestruct(
&mut self,
address: Address,
target: Address,
skip_cold_load: bool,
) -> Result<StateLoad<SelfDestructResult>, LoadError> {
self.dummy.selfdestruct(address, target, skip_cold_load)
}
fn log(&mut self, log: Log) {
self.dummy.log(log)
}
fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue) {
self.dummy.tstore(address, key, value)
}
fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
self.dummy.tload(address, key)
}
fn sstore_skip_cold_load(
&mut self,
address: Address,
key: StorageKey,
value: StorageValue,
skip_cold_load: bool,
) -> Result<StateLoad<SStoreResult>, LoadError> {
self.dummy
.sstore_skip_cold_load(address, key, value, skip_cold_load)
}
fn sload_skip_cold_load(
&mut self,
address: Address,
key: StorageKey,
skip_cold_load: bool,
) -> Result<StateLoad<StorageValue>, LoadError> {
self.dummy
.sload_skip_cold_load(address, key, skip_cold_load)
}
fn load_account_info_skip_cold_load(
&mut self,
address: Address,
_load_code: bool,
_skip_cold_load: bool,
) -> Result<AccountInfoLoad<'_>, LoadError> {
if address == self.delegated {
Ok(AccountInfoLoad {
account: Cow::Owned(self.delegated_info.clone()),
is_cold: false,
is_empty: false,
})
} else if address == self.delegate {
Ok(AccountInfoLoad {
account: Cow::Owned(AccountInfo::default()),
is_cold: true,
is_empty: true,
})
} else {
Ok(Default::default())
}
}
}
#[test]
fn load_account_delegated_keeps_caller_is_empty_not_delegate() {
let mut host = Eip7702Host::new();
let load = host
.load_account_delegated(host.delegated)
.expect("delegated account loads");
assert!(
load.data.is_delegate_account_cold.is_some(),
"delegate account must be loaded"
);
assert!(
!load.data.is_empty,
"is_empty must stay false for the non-empty EIP-7702 account"
);
}
}