Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eip7702): apply latest EIP-7702 changes, backport from v52 #1969

Merged
merged 2 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/bytecode/src/eip7702.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use core::fmt;
use primitives::{bytes, Address, Bytes};
use primitives::{b256, bytes, Address, Bytes, B256};

/// Hash of EF01 bytes that is used for EXTCODEHASH when called from legacy bytecode.
pub const EIP7702_MAGIC_HASH: B256 =
b256!("eadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329");

/// EIP-7702 Version Magic in u16 form
pub const EIP7702_MAGIC: u16 = 0xEF01;
Expand Down
11 changes: 4 additions & 7 deletions crates/context/interface/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ mod dummy;
pub use crate::journaled_state::StateLoad;
pub use dummy::DummyHost;

use crate::{
journaled_state::{AccountLoad, Eip7702CodeLoad},
BlockGetter, CfgGetter, TransactionGetter,
};
use crate::{journaled_state::AccountLoad, BlockGetter, CfgGetter, TransactionGetter};
use auto_impl::auto_impl;
use primitives::{Address, Bytes, Log, B256, U256};

Expand All @@ -15,7 +12,7 @@ use primitives::{Address, Bytes, Log, B256, U256};
#[auto_impl(&mut, Box)]
pub trait Host: TransactionGetter + BlockGetter + CfgGetter {
/// Load an account code.
fn load_account_delegated(&mut self, address: Address) -> Option<AccountLoad>;
fn load_account_delegated(&mut self, address: Address) -> Option<StateLoad<AccountLoad>>;

/// Gets the block hash of the given block `number`.
fn block_hash(&mut self, number: u64) -> Option<B256>;
Expand All @@ -24,10 +21,10 @@ pub trait Host: TransactionGetter + BlockGetter + CfgGetter {
fn balance(&mut self, address: Address) -> Option<StateLoad<U256>>;

/// Gets code of `address` and if the account is cold.
fn code(&mut self, address: Address) -> Option<Eip7702CodeLoad<Bytes>>;
fn code(&mut self, address: Address) -> Option<StateLoad<Bytes>>;

/// Gets code hash of `address` and if the account is cold.
fn code_hash(&mut self, address: Address) -> Option<Eip7702CodeLoad<B256>>;
fn code_hash(&mut self, address: Address) -> Option<StateLoad<B256>>;

/// Gets storage value of `address` at `index` and if the account is cold.
fn sload(&mut self, address: Address, index: U256) -> Option<StateLoad<U256>>;
Expand Down
12 changes: 6 additions & 6 deletions crates/context/interface/src/host/dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{Block, BlockGetter, Cfg, CfgGetter, Transaction, TransactionGetter};
use primitives::{hash_map::Entry, Address, Bytes, HashMap, Log, B256, KECCAK_EMPTY, U256};
use std::vec::Vec;

use super::{AccountLoad, Eip7702CodeLoad, StateLoad};
use super::{AccountLoad, StateLoad};

/// A dummy [Host] implementation.
#[derive(Clone, Debug, Default)]
Expand Down Expand Up @@ -74,8 +74,8 @@ impl<BLOCK: Block, TX: Transaction, CFG: Cfg> CfgGetter for DummyHost<BLOCK, TX,

impl<TX: Transaction, BLOCK: Block, CFG: Cfg> Host for DummyHost<BLOCK, TX, CFG> {
#[inline]
fn load_account_delegated(&mut self, _address: Address) -> Option<AccountLoad> {
Some(AccountLoad::default())
fn load_account_delegated(&mut self, _address: Address) -> Option<StateLoad<AccountLoad>> {
Some(StateLoad::new(AccountLoad::default(), false))
}

#[inline]
Expand All @@ -89,13 +89,13 @@ impl<TX: Transaction, BLOCK: Block, CFG: Cfg> Host for DummyHost<BLOCK, TX, CFG>
}

#[inline]
fn code(&mut self, _address: Address) -> Option<Eip7702CodeLoad<Bytes>> {
fn code(&mut self, _address: Address) -> Option<StateLoad<Bytes>> {
Some(Default::default())
}

#[inline]
fn code_hash(&mut self, _address: Address) -> Option<Eip7702CodeLoad<B256>> {
Some(Eip7702CodeLoad::new_not_delegated(KECCAK_EMPTY, false))
fn code_hash(&mut self, _address: Address) -> Option<StateLoad<B256>> {
Some(StateLoad::new(KECCAK_EMPTY, false))
}

#[inline]
Expand Down
90 changes: 3 additions & 87 deletions crates/context/interface/src/journaled_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ pub trait Journal {
fn load_account_delegated(
&mut self,
address: Address,
) -> Result<AccountLoad, <Self::Database as Database>::Error>;
) -> Result<StateLoad<AccountLoad>, <Self::Database as Database>::Error>;

/// Sets bytecode with hash. Assume that account is warm.
fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256);
Expand Down Expand Up @@ -200,94 +200,10 @@ impl<T> StateLoad<T> {
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AccountLoad {
/// Is account and delegate code are loaded
pub load: Eip7702CodeLoad<()>,
/// Is account empty, if true account is not created
pub is_empty: bool,
}

impl Deref for AccountLoad {
type Target = Eip7702CodeLoad<()>;

fn deref(&self) -> &Self::Target {
&self.load
}
}

impl DerefMut for AccountLoad {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.load
}
}

/// EIP-7702 code load result that contains optional delegation is_cold information
///
/// [`is_delegate_account_cold`][Self::is_delegate_account_cold] will be [`Some`] if account has delegation.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Eip7702CodeLoad<T> {
/// Returned data
pub state_load: StateLoad<T>,
/// Does account have delegate code and delegated account is cold loaded
pub is_delegate_account_cold: Option<bool>,
}

impl<T> Deref for Eip7702CodeLoad<T> {
type Target = StateLoad<T>;

fn deref(&self) -> &Self::Target {
&self.state_load
}
}

impl<T> DerefMut for Eip7702CodeLoad<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.state_load
}
}

impl<T> Eip7702CodeLoad<T> {
/// Returns a new [`Eip7702CodeLoad`] with the given data and without delegation.
pub fn new_state_load(state_load: StateLoad<T>) -> Self {
Self {
state_load,
is_delegate_account_cold: None,
}
}

/// Returns a new [`Eip7702CodeLoad`] with the given data and without delegation.
pub fn new_not_delegated(data: T, is_cold: bool) -> Self {
Self {
state_load: StateLoad::new(data, is_cold),
is_delegate_account_cold: None,
}
}

/// Deconstructs the [`Eip7702CodeLoad`] by extracting data and
/// returning a new [`Eip7702CodeLoad`] with empty data.
pub fn into_components(self) -> (T, Eip7702CodeLoad<()>) {
let is_cold = self.is_cold;
(
self.state_load.data,
Eip7702CodeLoad {
state_load: StateLoad::new((), is_cold),
is_delegate_account_cold: self.is_delegate_account_cold,
},
)
}

/// Sets the delegation cold load status.
pub fn set_delegate_load(&mut self, is_delegate_account_cold: bool) {
self.is_delegate_account_cold = Some(is_delegate_account_cold);
}

/// Returns a new [`Eip7702CodeLoad`] with the given data and delegation cold load status.
pub fn new(state_load: StateLoad<T>, is_delegate_account_cold: bool) -> Self {
Self {
state_load,
is_delegate_account_cold: Some(is_delegate_account_cold),
}
}
/// Is account empty, if `true` account is not created
pub is_empty: bool,
}

/// Helper that extracts database error from [`JournalGetter`].
Expand Down
117 changes: 37 additions & 80 deletions crates/context/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
pub mod performant_access;

use crate::{block::BlockEnv, cfg::CfgEnv, journaled_state::JournaledState, tx::TxEnv};
use bytecode::{Bytecode, EOF_MAGIC_BYTES, EOF_MAGIC_HASH};
use bytecode::{
eip7702::{EIP7702_MAGIC_BYTES, EIP7702_MAGIC_HASH},
EOF_MAGIC_BYTES, EOF_MAGIC_HASH,
};
use context_interface::{
block::BlockSetter,
journaled_state::{AccountLoad, Eip7702CodeLoad},
transaction::TransactionSetter,
Block, BlockGetter, Cfg, CfgGetter, DatabaseGetter, ErrorGetter, Journal, JournalGetter,
Transaction, TransactionGetter,
block::BlockSetter, journaled_state::AccountLoad, transaction::TransactionSetter, Block,
BlockGetter, Cfg, CfgGetter, DatabaseGetter, ErrorGetter, Journal, JournalGetter, Transaction,
TransactionGetter,
};
use database_interface::{Database, EmptyDB};
use derive_where::derive_where;
Expand Down Expand Up @@ -84,53 +85,6 @@ where
DB: Database,
JOURNAL: Journal<Database = DB>,
{
/// Returns account code bytes and if address is cold loaded.
///
/// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code.
///
// TODO : Move this in Journaled state
#[inline]
pub fn code(
&mut self,
address: Address,
) -> Result<Eip7702CodeLoad<Bytes>, <DB as Database>::Error> {
let a = self.journaled_state.load_account_code(address)?;
// SAFETY: Safe to unwrap as load_code will insert code if it is empty.
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_account_code(address)?;

// SAFETY: Safe to unwrap as load_code will insert code if it is empty.
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_new_journal<OJOURNAL: Journal<Database = DB>>(
self,
mut journal: OJOURNAL,
Expand Down Expand Up @@ -324,6 +278,28 @@ where
f(&mut self.journaled_state);
}

/// Returns account code bytes and if address is cold loaded.
///
/// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code.
///
// TODO : Move this in Journaled state
#[inline]
pub fn code(&mut self, address: Address) -> Result<StateLoad<Bytes>, <DB as Database>::Error> {
let a = self.journaled_state.load_account_code(address)?;
// SAFETY: Safe to unwrap as load_code will insert code if it is empty.
let code = a.info.code.as_ref().unwrap();

let code = if code.is_eof() {
EOF_MAGIC_BYTES.clone()
} else if code.is_eip7702() {
EIP7702_MAGIC_BYTES.clone()
} else {
code.original_bytes()
};

Ok(StateLoad::new(code, a.is_cold))
}

/// Gets code hash of address.
///
/// In case of EOF account it will return `EOF_MAGIC_HASH`
Expand All @@ -334,42 +310,23 @@ where
pub fn code_hash(
&mut self,
address: Address,
) -> Result<Eip7702CodeLoad<B256>, <DB as Database>::Error> {
) -> Result<StateLoad<B256>, <DB as Database>::Error> {
let acc = self.journaled_state.load_account_code(address)?;
if acc.is_empty() {
return Ok(Eip7702CodeLoad::new_not_delegated(B256::ZERO, acc.is_cold));
return Ok(StateLoad::new(B256::ZERO, acc.is_cold));
}
// SAFETY: Safe to unwrap as load_code will insert code if it is empty.
let code = acc.info.code.as_ref().unwrap();

// If bytecode is EIP-7702 then we need to load the delegated account.
if let Bytecode::Eip7702(code) = code {
let address = code.address();
let is_cold = acc.is_cold;

let delegated_account = self.journaled_state.load_account_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 if code.is_eip7702() {
EIP7702_MAGIC_HASH
} else {
acc.info.code_hash
};

Ok(Eip7702CodeLoad::new_not_delegated(hash, acc.is_cold))
Ok(StateLoad::new(hash, acc.is_cold))
}
}

Expand Down Expand Up @@ -405,7 +362,7 @@ where
Some(B256::ZERO)
}

fn load_account_delegated(&mut self, address: Address) -> Option<AccountLoad> {
fn load_account_delegated(&mut self, address: Address) -> Option<StateLoad<AccountLoad>> {
self.journaled_state
.load_account_delegated(address)
.map_err(|e| self.error = Err(e))
Expand All @@ -420,11 +377,11 @@ where
.ok()
}

fn code(&mut self, address: Address) -> Option<Eip7702CodeLoad<Bytes>> {
fn code(&mut self, address: Address) -> Option<StateLoad<Bytes>> {
self.code(address).map_err(|e| self.error = Err(e)).ok()
}

fn code_hash(&mut self, address: Address) -> Option<Eip7702CodeLoad<B256>> {
fn code_hash(&mut self, address: Address) -> Option<StateLoad<B256>> {
self.code_hash(address)
.map_err(|e| self.error = Err(e))
.ok()
Expand Down
Loading
Loading