Skip to content

Commit

Permalink
refactor(l1): add enum that specifies EVM (#1949)
Browse files Browse the repository at this point in the history
Alternative to #1897
**Motivation**

When doing the `EVM` trait in #1897, i've ended up needing an enum to
wrap the structs that implement such trait. This approach ended up being
quite complex, and the raw use of an enum was discussed.

**Description**
- Implement an `enum` as unique entrypoint for the crate `ethrex-vm`.

Closes #1661
  • Loading branch information
fborello-lambda authored Feb 20, 2025
1 parent 47d90d5 commit 4d9ab5a
Show file tree
Hide file tree
Showing 18 changed files with 1,100 additions and 1,034 deletions.
15 changes: 10 additions & 5 deletions .github/workflows/common_hive_reports.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ jobs:
- { name: "Sync tests", file_name: sync, simulation: ethereum/sync }

steps:
- name: Pull image
run: |
docker pull ghcr.io/lambdaclass/ethrex:latest
docker tag ghcr.io/lambdaclass/ethrex:latest ethrex:latest
- name: Checkout sources
if: ${{ inputs.job_type != 'main' }}
uses: actions/checkout@v4
Expand All @@ -52,6 +47,16 @@ jobs:
with:
ref: main

- name: Build image
if: ${{ inputs.job_type != 'main' }}
run: make build-image

- name: Pull image
if: ${{ inputs.job_type == 'main' }}
run: |
docker pull ghcr.io/lambdaclass/ethrex:latest
docker tag ghcr.io/lambdaclass/ethrex:latest ethrex:latest
- name: Setup Go
uses: actions/setup-go@v5

Expand Down
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

118 changes: 17 additions & 101 deletions cmd/ef_tests/state/runner/levm_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ use crate::{
types::{EFTest, TransactionExpectedException},
utils::{self, effective_gas_price},
};
use bytes::Bytes;
use ethrex_common::{
types::{code_hash, tx_fields::*, AccountInfo, Fork},
types::{tx_fields::*, Fork},
H256, U256,
};
use ethrex_levm::{
Expand All @@ -16,7 +15,10 @@ use ethrex_levm::{
Environment,
};
use ethrex_storage::AccountUpdate;
use ethrex_vm::db::{EvmState, StoreWrapper};
use ethrex_vm::{
backends::{self},
db::StoreWrapper,
};
use keccak_hash::keccak;
use std::{collections::HashMap, sync::Arc};

Expand Down Expand Up @@ -313,8 +315,18 @@ pub fn ensure_post_state(
// Execution result was successful and no exception was expected.
None => {
let (initial_state, block_hash) = utils::load_initial_state(test);
let levm_account_updates =
get_state_transitions(&initial_state, block_hash, execution_report, fork);
let levm_account_updates = backends::levm::LEVM::get_state_transitions(
Some(*fork),
&initial_state,
block_hash,
&execution_report.new_state,
)
.map_err(|_| {
InternalError::Custom(
"Error at LEVM::get_state_transitions in ensure_post_state()"
.to_owned(),
)
})?;
let pos_state_root = post_state_root(&levm_account_updates, test);
let expected_post_state_root_hash =
test.post.vector_post_value(vector, *fork).hash;
Expand Down Expand Up @@ -369,102 +381,6 @@ pub fn ensure_post_state(
Ok(())
}

pub fn get_state_transitions(
initial_state: &EvmState,
block_hash: H256,
execution_report: &ExecutionReport,
fork: &Fork,
) -> Vec<AccountUpdate> {
let current_db = match initial_state {
EvmState::Store(state) => state.database.store.clone(),
EvmState::Execution(_cache_db) => unreachable!("Execution state should not be passed here"),
};
let mut account_updates: Vec<AccountUpdate> = vec![];
for (new_state_account_address, new_state_account) in &execution_report.new_state {
let initial_account_state = current_db
.get_account_info_by_hash(block_hash, *new_state_account_address)
.expect("Error getting account info by address")
.unwrap_or_default();
let mut updates = 0;
if initial_account_state.balance != new_state_account.info.balance {
updates += 1;
}
if initial_account_state.nonce != new_state_account.info.nonce {
updates += 1;
}
let code = if new_state_account.info.bytecode.is_empty() {
// The new state account has no code
None
} else {
// Get the code hash of the new state account bytecode
let potential_new_bytecode_hash = code_hash(&new_state_account.info.bytecode);
// Look into the current database to see if the bytecode hash is already present
let current_bytecode = current_db
.get_account_code(potential_new_bytecode_hash)
.expect("Error getting account code by hash");
let code = new_state_account.info.bytecode.clone();
// The code is present in the current database
if let Some(current_bytecode) = current_bytecode {
if current_bytecode != code {
// The code has changed
Some(code)
} else {
// The code has not changed
None
}
} else {
// The new state account code is not present in the current
// database, then it must be new
Some(code)
}
};
if code.is_some() {
updates += 1;
}
let mut added_storage = HashMap::new();
for (key, value) in &new_state_account.storage {
added_storage.insert(*key, value.current_value);
updates += 1;
}

if updates == 0 && !new_state_account.is_empty() {
continue;
}

let account_update = AccountUpdate {
address: *new_state_account_address,
removed: new_state_account.is_empty(),
info: Some(AccountInfo {
code_hash: code_hash(&new_state_account.info.bytecode),
balance: new_state_account.info.balance,
nonce: new_state_account.info.nonce,
}),
code,
added_storage,
};

if let Some(old_info) = current_db
.get_account_info_by_hash(block_hash, account_update.address)
.unwrap()
{
// https://eips.ethereum.org/EIPS/eip-161
// if an account was empty and is now empty, after spurious dragon, it should be removed
if account_update.removed
&& old_info.balance.is_zero()
&& old_info.nonce == 0
&& old_info.code_hash == code_hash(&Bytes::new())
&& *fork < Fork::SpuriousDragon
{
continue;
}
}

account_updates.push(account_update);
}

account_updates
}

pub fn post_state_root(account_updates: &[AccountUpdate], test: &EFTest) -> H256 {
let (initial_state, block_hash) = utils::load_initial_state(test);
initial_state
Expand Down
2 changes: 2 additions & 0 deletions cmd/ef_tests/state/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub enum InternalError {
ReRunInternal(String, TestReRunReport),
#[error("Main runner failed unexpectedly: {0}")]
MainRunnerInternal(String),
#[error("{0}")]
Custom(String),
}

#[derive(Parser)]
Expand Down
25 changes: 15 additions & 10 deletions cmd/ef_tests/state/runner/revm_runner.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
use crate::{
report::{ComparisonReport, EFTestReport, EFTestReportForkResult, TestReRunReport, TestVector},
runner::{
levm_runner::{self, post_state_root},
EFTestRunnerError, InternalError,
},
runner::{levm_runner::post_state_root, EFTestRunnerError, InternalError},
types::EFTest,
utils::{effective_gas_price, load_initial_state},
};
Expand All @@ -18,6 +15,7 @@ use ethrex_levm::{
};
use ethrex_storage::{error::StoreError, AccountUpdate};
use ethrex_vm::{
backends::{self},
db::{EvmState, StoreWrapper},
fork_to_spec_id, RevmAddress, RevmU256,
};
Expand Down Expand Up @@ -330,13 +328,19 @@ pub fn ensure_post_state(
// We only want to compare account updates when no exception is expected.
None => {
let (initial_state, block_hash) = load_initial_state(test);
let levm_account_updates = levm_runner::get_state_transitions(
let levm_account_updates = backends::levm::LEVM::get_state_transitions(
Some(*fork),
&initial_state,
block_hash,
levm_execution_report,
fork,
);
let revm_account_updates = ethrex_vm::get_state_transitions(revm_state);
&levm_execution_report.new_state,
)
.map_err(|_| {
InternalError::Custom("Error at LEVM::get_state_transitions()".to_owned())
})?;
let revm_account_updates = backends::revm_b::REVM::get_state_transitions(revm_state)
.map_err(|_| {
InternalError::Custom("Error at REVM::get_state_transitions()".to_owned())
})?;
let account_updates_report = compare_levm_revm_account_updates(
vector,
test,
Expand Down Expand Up @@ -517,7 +521,8 @@ pub fn _ensure_post_state_revm(
}
// Execution result was successful and no exception was expected.
None => {
let revm_account_updates = ethrex_vm::get_state_transitions(revm_state);
let revm_account_updates =
backends::revm_b::REVM::get_state_transitions(revm_state).unwrap();
let pos_state_root = post_state_root(&revm_account_updates, test);
let expected_post_state_root_hash =
test.post.vector_post_value(vector, *fork).hash;
Expand Down
3 changes: 1 addition & 2 deletions crates/blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ ethrex-rlp.workspace = true
ethrex-common.workspace = true
ethrex-storage.workspace = true
ethrex-vm.workspace = true
ethrex-levm.workspace = true

thiserror.workspace = true
sha3.workspace = true
Expand All @@ -31,5 +30,5 @@ path = "./blockchain.rs"

[features]
default = []
c-kzg = ["ethrex-common/c-kzg", "ethrex-vm/c-kzg", "ethrex-levm/c-kzg"]
c-kzg = ["ethrex-common/c-kzg", "ethrex-vm/c-kzg"]
metrics = ["ethrex-metrics/transactions"]
23 changes: 6 additions & 17 deletions crates/blockchain/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ use ethrex_common::H256;
use ethrex_storage::error::StoreError;
use ethrex_storage::Store;
use ethrex_vm::db::evm_state;

use ethrex_vm::EVM_BACKEND;
use ethrex_vm::{backends, backends::EVM};
use ethrex_vm::{backends::BlockExecutionResult, get_evm_backend_or_default};

//TODO: Implement a struct Chain or BlockChain to encapsulate
//functionality and canonical chain state and config
Expand All @@ -45,20 +43,11 @@ pub fn add_block(block: &Block, storage: &Store) -> Result<(), ChainError> {

// Validate the block pre-execution
validate_block(block, &parent_header, &chain_config)?;
let (receipts, requests, account_updates) = {
match EVM_BACKEND.get() {
Some(EVM::LEVM) => {
let r = backends::levm::execute_block(block, &mut state)?;
(r.receipts, r.requests, r.account_updates)
}
// This means we are using REVM as default for tests
Some(EVM::REVM) | None => {
let (receipts, requests) = backends::revm::execute_block(block, &mut state)?;
let account_updates = ethrex_vm::get_state_transitions(&mut state);
(receipts, requests, account_updates)
}
}
};
let BlockExecutionResult {
receipts,
requests,
account_updates,
} = get_evm_backend_or_default().execute_block(block, &mut state)?;

validate_gas_used(&receipts, &block.header)?;

Expand Down
Loading

0 comments on commit 4d9ab5a

Please sign in to comment.