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(l1): implement EIP-2935 #1876

Merged
merged 12 commits into from
Feb 11, 2025
12 changes: 5 additions & 7 deletions cmd/ef_tests/blockchain/tests/prague.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ fn parse_and_execute(path: &Path) -> datatest_stable::Result<()> {
Ok(())
}

// TODO: Delete main function and uncomment the following line to allow prague tests to be parsed

// datatest_stable::harness!(parse_and_execute, "vectors/prague/", r".*/.*/.*\.json");

fn main() {
//Do nothing
}
datatest_stable::harness!(
parse_and_execute,
"vectors/prague/eip2935_historical_block_hashes_from_state",
r".*/.*\.json"
);
48 changes: 37 additions & 11 deletions crates/blockchain/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ pub fn build_payload(
debug!("Building payload");
let mut evm_state = evm_state(store.clone(), payload.header.parent_hash);
let mut context = PayloadBuildContext::new(payload, &mut evm_state)?;
make_beacon_root_call(&mut context)?;
apply_system_operations(&mut context)?;
apply_withdrawals(&mut context)?;
fill_transactions(&mut context)?;
finalize_payload(&mut context)?;
Expand Down Expand Up @@ -301,7 +301,10 @@ pub fn apply_withdrawals(context: &mut PayloadBuildContext) -> Result<(), EvmErr
Ok(())
}

pub fn make_beacon_root_call(context: &mut PayloadBuildContext) -> Result<(), EvmError> {
// This function applies system level operations:
// - Call beacon root contract, and obtain the new state root
// - Call block hash process contract, and store parent block hash
pub fn apply_system_operations(context: &mut PayloadBuildContext) -> Result<(), EvmError> {
match EVM_BACKEND.get() {
Some(EVM::LEVM) => {
let fork = context
Expand All @@ -312,6 +315,7 @@ pub fn make_beacon_root_call(context: &mut PayloadBuildContext) -> Result<(), Ev
.get_fork_blob_schedule(context.payload.header.timestamp)
.unwrap_or(EVMConfig::canonical_values(fork));
let config = EVMConfig::new(fork, blob_schedule);
let mut new_state = HashMap::new();

if context.payload.header.parent_beacon_block_root.is_some() && fork >= Fork::Cancun {
let store_wrapper = Arc::new(StoreWrapper {
Expand All @@ -324,18 +328,32 @@ pub fn make_beacon_root_call(context: &mut PayloadBuildContext) -> Result<(), Ev
config,
)?;

let mut new_state = report.new_state.clone();
new_state.extend(report.new_state.clone());
}

// Now original_value is going to be the same as the current_value, for the next transaction.
// It should have only one value but it is convenient to keep on using our CacheDB structure
for account in new_state.values_mut() {
for storage_slot in account.storage.values_mut() {
storage_slot.original_value = storage_slot.current_value;
}
}
if fork >= Fork::Prague {
let store_wrapper = Arc::new(StoreWrapper {
store: context.evm_state.database().unwrap().clone(),
block_hash: context.payload.header.parent_hash,
});
let report = backends::levm::process_block_hash_history(
store_wrapper.clone(),
&context.payload.header,
config,
)?;

context.block_cache.extend(new_state);
new_state.extend(report.new_state.clone());
}

// Now original_value is going to be the same as the current_value, for the next transaction.
// It should have only one value but it is convenient to keep on using our CacheDB structure
for account in new_state.values_mut() {
for storage_slot in account.storage.values_mut() {
storage_slot.original_value = storage_slot.current_value;
}
}

context.block_cache.extend(new_state);
}
// This means we are using REVM as default for tests
Some(EVM::REVM) | None => {
Expand All @@ -350,6 +368,14 @@ pub fn make_beacon_root_call(context: &mut PayloadBuildContext) -> Result<(), Ev
spec_id,
)?;
}

if spec_id >= SpecId::PRAGUE {
backends::revm::process_block_hash_history(
context.evm_state,
&context.payload.header,
spec_id,
)?;
}
}
}
Ok(())
Expand Down
3 changes: 3 additions & 0 deletions crates/vm/backends/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub const SYSTEM_ADDRESS_STR: &str = "fffffffffffffffffffffffffffffffffffffffe";
pub const BEACON_ROOTS_ADDRESS_STR: &str = "000F3df6D732807Ef1319fB7B8bB8522d0Beac02";
pub const HISTORY_STORAGE_ADDRESS_STR: &str = "0000F90827F1C53a10cb7A02335B175320002935";
67 changes: 64 additions & 3 deletions crates/vm/backends/levm.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::constants::{BEACON_ROOTS_ADDRESS_STR, HISTORY_STORAGE_ADDRESS_STR, SYSTEM_ADDRESS_STR};
use crate::db::StoreWrapper;
use crate::EvmError;
use crate::EvmState;
#[cfg(not(feature = "l2"))]
use ethrex_core::types::Fork;
use ethrex_core::{
types::{
Expand Down Expand Up @@ -50,6 +50,12 @@ pub fn execute_block(
}
}

if fork >= Fork::Prague {
//eip 2935: stores parent block hash in system contract
let report = process_block_hash_history(store_wrapper.clone(), block_header, config)?;
block_cache.extend(report.new_state);
}

// Account updates are initialized like this because of the beacon_root_contract_call, it is going to be empty if it wasn't called.
let mut account_updates = crate::get_state_transitions(state);

Expand Down Expand Up @@ -259,9 +265,9 @@ pub fn beacon_root_contract_call_levm(
) -> Result<ExecutionReport, EvmError> {
lazy_static! {
static ref SYSTEM_ADDRESS: Address =
Address::from_slice(&hex::decode("fffffffffffffffffffffffffffffffffffffffe").unwrap());
Address::from_slice(&hex::decode(SYSTEM_ADDRESS_STR).unwrap());
static ref CONTRACT_ADDRESS: Address =
Address::from_slice(&hex::decode("000F3df6D732807Ef1319fB7B8bB8522d0Beac02").unwrap(),);
Address::from_slice(&hex::decode(BEACON_ROOTS_ADDRESS_STR).unwrap());
};
// This is OK
let beacon_root = match block_header.parent_beacon_block_root {
Expand Down Expand Up @@ -312,3 +318,58 @@ pub fn beacon_root_contract_call_levm(

Ok(report)
}

/// Calls the EIP-2935 process block hashes history system call contract
/// NOTE: This was implemented by making use of an EVM system contract, but can be changed to a
/// direct state trie update after the verkle fork, as explained in https://eips.ethereum.org/EIPS/eip-2935
pub fn process_block_hash_history(
store_wrapper: Arc<StoreWrapper>,
block_header: &BlockHeader,
config: EVMConfig,
) -> Result<ExecutionReport, EvmError> {
lazy_static! {
static ref SYSTEM_ADDRESS: Address =
Address::from_slice(&hex::decode(SYSTEM_ADDRESS_STR).unwrap());
static ref CONTRACT_ADDRESS: Address =
Address::from_slice(&hex::decode(HISTORY_STORAGE_ADDRESS_STR).unwrap(),);
};

let env = Environment {
origin: *SYSTEM_ADDRESS,
gas_limit: 30_000_000,
block_number: block_header.number.into(),
coinbase: block_header.coinbase,
timestamp: block_header.timestamp.into(),
prev_randao: Some(block_header.prev_randao),
base_fee_per_gas: U256::zero(),
gas_price: U256::zero(),
block_excess_blob_gas: block_header.excess_blob_gas.map(U256::from),
block_blob_gas_used: block_header.blob_gas_used.map(U256::from),
block_gas_limit: 30_000_000,
transient_storage: HashMap::new(),
config,
..Default::default()
};

let calldata = Bytes::copy_from_slice(block_header.parent_hash.as_bytes()).into();

// Here execute with LEVM but just return transaction report. And I will handle it in the calling place.

let mut vm = VM::new(
TxKind::Call(*CONTRACT_ADDRESS),
env,
U256::zero(),
calldata,
store_wrapper,
CacheDB::new(),
vec![],
None,
)
.map_err(EvmError::from)?;

let mut report = vm.execute().map_err(EvmError::from)?;

report.new_state.remove(&*SYSTEM_ADDRESS);

Ok(report)
}
1 change: 1 addition & 0 deletions crates/vm/backends/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod constants;
pub mod levm;
pub mod revm;

Expand Down
75 changes: 69 additions & 6 deletions crates/vm/backends/revm.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::constants::{BEACON_ROOTS_ADDRESS_STR, HISTORY_STORAGE_ADDRESS_STR, SYSTEM_ADDRESS_STR};
use crate::spec_id;
use crate::EvmError;
use crate::EvmState;
Expand Down Expand Up @@ -42,6 +43,12 @@ pub fn execute_block(block: &Block, state: &mut EvmState) -> Result<Vec<Receipt>
}
}
}

//eip 2935: stores parent block hash in system contract
if spec_id >= SpecId::PRAGUE {
process_block_hash_history(state, block_header, spec_id)?;
}

let mut receipts = Vec::new();
let mut cumulative_gas_used = 0;

Expand Down Expand Up @@ -437,12 +444,10 @@ pub fn beacon_root_contract_call(
spec_id: SpecId,
) -> Result<ExecutionResult, EvmError> {
lazy_static! {
static ref SYSTEM_ADDRESS: RevmAddress = RevmAddress::from_slice(
&hex::decode("fffffffffffffffffffffffffffffffffffffffe").unwrap()
);
static ref CONTRACT_ADDRESS: RevmAddress = RevmAddress::from_slice(
&hex::decode("000F3df6D732807Ef1319fB7B8bB8522d0Beac02").unwrap(),
);
static ref SYSTEM_ADDRESS: RevmAddress =
RevmAddress::from_slice(&hex::decode(SYSTEM_ADDRESS_STR).unwrap());
static ref CONTRACT_ADDRESS: RevmAddress =
RevmAddress::from_slice(&hex::decode(BEACON_ROOTS_ADDRESS_STR).unwrap());
};
let beacon_root = match header.parent_beacon_block_root {
None => {
Expand Down Expand Up @@ -496,3 +501,61 @@ pub fn beacon_root_contract_call(
}
}
}

/// Calls the EIP-2935 process block hashes history system call contract
/// NOTE: This was implemented by making use of an EVM system contract, but can be changed to a
/// direct state trie update after the verkle fork, as explained in https://eips.ethereum.org/EIPS/eip-2935
pub fn process_block_hash_history(
state: &mut EvmState,
header: &BlockHeader,
spec_id: SpecId,
) -> Result<ExecutionResult, EvmError> {
lazy_static! {
static ref SYSTEM_ADDRESS: RevmAddress =
RevmAddress::from_slice(&hex::decode(SYSTEM_ADDRESS_STR).unwrap());
static ref CONTRACT_ADDRESS: RevmAddress =
RevmAddress::from_slice(&hex::decode(HISTORY_STORAGE_ADDRESS_STR).unwrap(),);
};
let tx_env = TxEnv {
caller: *SYSTEM_ADDRESS,
transact_to: RevmTxKind::Call(*CONTRACT_ADDRESS),
gas_limit: 30_000_000,
data: revm::primitives::Bytes::copy_from_slice(header.parent_hash.as_bytes()),
..Default::default()
};
let mut block_env = block_env(header);
block_env.basefee = RevmU256::ZERO;
block_env.gas_limit = RevmU256::from(30_000_000);

match state {
EvmState::Store(db) => {
let mut evm = Evm::builder()
.with_db(db)
.with_block_env(block_env)
.with_tx_env(tx_env)
.with_spec_id(spec_id)
.build();

let transaction_result = evm.transact()?;
let mut result_state = transaction_result.state;
result_state.remove(&*SYSTEM_ADDRESS);
result_state.remove(&evm.block().coinbase);

evm.context.evm.db.commit(result_state);

Ok(transaction_result.result.into())
}
EvmState::Execution(db) => {
let mut evm = Evm::builder()
.with_db(db)
.with_block_env(block_env)
.with_tx_env(tx_env)
.with_spec_id(spec_id)
.build();

// Not necessary to commit to DB
let transaction_result = evm.transact()?;
Ok(transaction_result.result.into())
}
}
}
5 changes: 5 additions & 0 deletions test_data/genesis-execution-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"balance": "0x0",
"nonce": "0x1"
},
"000f3df6d732807ef1319fb7b8bb8522d0beac02": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
"balance": "0x2a"
Expand Down
5 changes: 5 additions & 0 deletions test_data/genesis-l1.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
"pragueTime": 1718232101
},
"alloc": {
"0x0F792be4B0c0cb4DAE440Ef133E90C0eCD48CCCC": {
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"balance": "0",
"nonce": "0x1"
},
"0x4e59b44847b379578588920cA78FbF26c0B4956C": {
"balance": "0",
"code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"
Expand Down
Loading