Repository: lambdaclass/ethrex
Pull request #1877: feat(levm): pectra-devnet6 eftests
Author: login: fborello-lambda
Labels: name: testing
description: Hive tests, execution-spec-tests, etc
name: levm
description: Lambda EVM implementation
Created: 2025-02-05T21:55:48Z | Merged: 2025-03-17T14:38:50Z

**Motivation**

The latest test for pectra has been released. [Pectra Devnet 6 ](https://github.com/ethereum/execution-spec-tests/releases/tag/pectra-devnet-6%40v1.0.0)

**Description**

- Download latest tests
- Add eip7702 latest changes: https://github.com/ethereum/EIPs/pull/9248
- Add a new CI rule for EF Tests to be 100% from London to Prague forks
- Fix tests from `set_code_txs_2` and `refunds` belonging to EIP 7702 and EIP 7623


Diff:
diff --git a/.github/workflows/ci_levm.yaml b/.github/workflows/ci_levm.yaml
index 5f94cb7da59..dcdb50e79dd 100644
--- a/.github/workflows/ci_levm.yaml
+++ b/.github/workflows/ci_levm.yaml
@@ -113,6 +113,14 @@ jobs:
           name: main-ef-test-data
           path: crates/vm/levm/test_result_main_short.txt
 
+      # This is for ensuring test from merge to Prague are passing.
+      # After adding the Legacy Test suite, not all are passing.
+      # This is a temporary solution until we fix the legacy tests.
+      - name: Check EF-TESTS from London to Prague is 100%
+        run: |
+          cd crates/vm/levm
+          awk '/Prague:/, /London:/ {print $1, $3}' test_result_main.txt | sed 's/[()]//g' | grep -v '100.00%' && echo "All percentage are not 100%." && exit 1 || echo "All percentage are 100%."
+
       - name: Check EF-TESTS status is 100%
         run: |
           cd crates/vm/levm
diff --git a/crates/vm/levm/Makefile b/crates/vm/levm/Makefile
index 5a8645220df..ec9e80de611 100644
--- a/crates/vm/levm/Makefile
+++ b/crates/vm/levm/Makefile
@@ -24,8 +24,8 @@ ETH_TEST_URL := https://github.com/ethereum/tests.git
 ETH_TEST_TAG := v14.1
 COMMIT_LEGACY_TESTS_FOR_TAG := b2e6c9e
 
-STATETEST_VERSION := pectra-devnet-5%40v1.1.0
-STATETEST_NET := pectra-devnet-5
+STATETEST_VERSION := pectra-devnet-6%40v1.0.0
+STATETEST_NET := pectra-devnet-6
 STATETEST_ARTIFACT := fixtures_$(STATETEST_NET).tar.gz
 STATETEST_URL := https://github.com/ethereum/execution-spec-tests/releases/download/$(STATETEST_VERSION)/fixtures_$(STATETEST_NET).tar.gz
 
diff --git a/crates/vm/levm/src/hooks/default_hook.rs b/crates/vm/levm/src/hooks/default_hook.rs
index 2669192cce8..ffeb1d491ee 100644
--- a/crates/vm/levm/src/hooks/default_hook.rs
+++ b/crates/vm/levm/src/hooks/default_hook.rs
@@ -363,14 +363,14 @@ impl Hook for DefaultHook {
 
         // 2. Return unused gas + gas refunds to the sender.
         let max_gas = vm.env.gas_limit;
-        let consumed_gas = report.gas_used;
+        let mut consumed_gas = report.gas_used;
         // [EIP-3529](https://eips.ethereum.org/EIPS/eip-3529)
         let quotient = if vm.env.config.fork < Fork::London {
             MAX_REFUND_QUOTIENT_PRE_LONDON
         } else {
             MAX_REFUND_QUOTIENT
         };
-        let refunded_gas = report.gas_refunded.min(
+        let mut refunded_gas = report.gas_refunded.min(
             consumed_gas
                 .checked_div(quotient)
                 .ok_or(VMError::Internal(InternalError::UndefinedState(-1)))?,
@@ -378,6 +378,15 @@ impl Hook for DefaultHook {
         // "The max refundable proportion of gas was reduced from one half to one fifth by EIP-3529 by Buterin and Swende [2021] in the London release"
         report.gas_refunded = refunded_gas;
 
+        if vm.env.config.fork >= Fork::Prague {
+            let floor_gas_price = vm.get_floor_gas_price(initial_call_frame)?;
+            let execution_gas_used = consumed_gas.saturating_sub(refunded_gas);
+            if floor_gas_price > execution_gas_used {
+                consumed_gas = floor_gas_price;
+                refunded_gas = 0;
+            }
+        }
+
         let gas_to_return = max_gas
             .checked_sub(consumed_gas)
             .and_then(|gas| gas.checked_add(refunded_gas))
diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs
index 5f296f98ef7..7d1b710c6d7 100644
--- a/crates/vm/levm/src/opcode_handlers/environment.rs
+++ b/crates/vm/levm/src/opcode_handlers/environment.rs
@@ -1,10 +1,9 @@
 use crate::{
     call_frame::CallFrame,
-    constants::SET_CODE_DELEGATION_BYTES,
     errors::{InternalError, OpcodeResult, VMError},
     gas_cost::{self},
     memory::{self, calculate_memory_size},
-    utils::{access_account, has_delegation, word_to_address},
+    utils::{access_account, word_to_address},
     vm::VM,
 };
 use ethrex_common::{types::Fork, U256};
@@ -294,19 +293,14 @@ impl VM {
             address,
         );
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodesize(
             address_was_cold,
             self.env.config.fork,
         )?)?;
 
-        current_call_frame.stack.push(if is_delegation {
-            SET_CODE_DELEGATION_BYTES[..2].len().into()
-        } else {
-            account_info.bytecode.len().into()
-        })?;
+        current_call_frame
+            .stack
+            .push(account_info.bytecode.len().into())?;
 
         Ok(OpcodeResult::Continue { pc_increment: 1 })
     }
@@ -334,9 +328,6 @@ impl VM {
 
         let new_memory_size = calculate_memory_size(dest_offset, size)?;
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodecopy(
             size,
             new_memory_size,
@@ -349,11 +340,9 @@ impl VM {
             return Ok(OpcodeResult::Continue { pc_increment: 1 });
         }
 
-        let bytecode = if is_delegation {
-            SET_CODE_DELEGATION_BYTES[..2].into()
-        } else {
-            account_info.bytecode
-        };
+        // If the bytecode is a delegation designation, it will copy the marker (0xef0100) || address.
+        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
+        let bytecode = account_info.bytecode;
 
         let mut data = vec![0u8; size];
         if offset < bytecode.len().into() {
@@ -467,29 +456,20 @@ impl VM {
             address,
         );
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodehash(
             address_was_cold,
             self.env.config.fork,
         )?)?;
 
-        if is_delegation {
-            let hash =
-                U256::from_big_endian(keccak(&SET_CODE_DELEGATION_BYTES[..2]).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
-        } else {
-            // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
-            if account_info.is_empty() {
-                current_call_frame.stack.push(U256::zero())?;
-                return Ok(OpcodeResult::Continue { pc_increment: 1 });
-            }
-
-            let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
+        // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
+        if account_info.is_empty() {
+            current_call_frame.stack.push(U256::zero())?;
+            return Ok(OpcodeResult::Continue { pc_increment: 1 });
         }
 
+        let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
+        current_call_frame.stack.push(hash)?;
+
         Ok(OpcodeResult::Continue { pc_increment: 1 })
     }
 }
diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs
index d52a0ef60f4..b75b0a08f8a 100644
--- a/crates/vm/levm/src/vm.rs
+++ b/crates/vm/levm/src/vm.rs
@@ -26,7 +26,6 @@ use ethrex_common::{
     Address, H256, U256,
 };
 use std::{
-    cmp::max,
     collections::{HashMap, HashSet},
     fmt::Debug,
     sync::Arc,
@@ -233,9 +232,15 @@ impl VM {
             TxKind::Call(address_to) => {
                 default_touched_accounts.insert(address_to);
 
-                let bytecode = get_account_no_push_cache(&cache, db.clone(), address_to)
-                    .info
-                    .bytecode;
+                let mut substate = Substate {
+                    selfdestruct_set: HashSet::new(),
+                    touched_accounts: default_touched_accounts,
+                    touched_storage_slots: default_touched_storage_slots,
+                    created_accounts: HashSet::new(),
+                };
+
+                let (_is_delegation, _eip7702_gas_consumed, _code_address, bytecode) =
+                    eip7702_get_code(&mut cache, db.clone(), &mut substate, address_to)?;
 
                 // CALL tx
                 let initial_call_frame = CallFrame::new(
@@ -252,13 +257,6 @@ impl VM {
                     false,
                 );
 
-                let substate = Substate {
-                    selfdestruct_set: HashSet::new(),
-                    touched_accounts: default_touched_accounts,
-                    touched_storage_slots: default_touched_storage_slots,
-                    created_accounts: HashSet::new(),
-                };
-
                 Ok(Self {
                     call_frames: vec![initial_call_frame],
                     db,
@@ -361,42 +359,33 @@ impl VM {
         matches!(self.tx_kind, TxKind::Create)
     }
 
-    fn gas_used(
-        &self,
-        initial_call_frame: &CallFrame,
-        report: &ExecutionReport,
-    ) -> Result<u64, VMError> {
-        if self.env.config.fork >= Fork::Prague {
-            // If the transaction is a CREATE transaction, the calldata is emptied and the bytecode is assigned.
-            let calldata = if self.is_create() {
-                &initial_call_frame.bytecode
-            } else {
-                &initial_call_frame.calldata
-            };
-
-            // tokens_in_calldata = nonzero_bytes_in_calldata * 4 + zero_bytes_in_calldata
-            // tx_calldata = nonzero_bytes_in_calldata * 16 + zero_bytes_in_calldata * 4
-            // this is actually tokens_in_calldata * STANDARD_TOKEN_COST
-            // see it in https://eips.ethereum.org/EIPS/eip-7623
-            let tokens_in_calldata: u64 = gas_cost::tx_calldata(calldata, self.env.config.fork)
-                .map_err(VMError::OutOfGas)?
-                .checked_div(STANDARD_TOKEN_COST)
-                .ok_or(VMError::Internal(InternalError::DivisionError))?;
-
-            // floor_gas_price = TX_BASE_COST + TOTAL_COST_FLOOR_PER_TOKEN * tokens_in_calldata
-            let mut floor_gas_price: u64 = tokens_in_calldata
-                .checked_mul(TOTAL_COST_FLOOR_PER_TOKEN)
-                .ok_or(VMError::Internal(InternalError::GasOverflow))?;
-
-            floor_gas_price = floor_gas_price
-                .checked_add(TX_BASE_COST)
-                .ok_or(VMError::Internal(InternalError::GasOverflow))?;
-
-            let gas_used = max(floor_gas_price, report.gas_used);
-            Ok(gas_used)
+    pub fn get_floor_gas_price(&self, initial_call_frame: &CallFrame) -> Result<u64, VMError> {
+        // If the transaction is a CREATE transaction, the calldata is emptied and the bytecode is assigned.
+        let calldata = if self.is_create() {
+            &initial_call_frame.bytecode
         } else {
-            Ok(report.gas_used)
-        }
+            &initial_call_frame.calldata
+        };
+
+        // tokens_in_calldata = nonzero_bytes_in_calldata * 4 + zero_bytes_in_calldata
+        // tx_calldata = nonzero_bytes_in_calldata * 16 + zero_bytes_in_calldata * 4
+        // this is actually tokens_in_calldata * STANDARD_TOKEN_COST
+        // see it in https://eips.ethereum.org/EIPS/eip-7623
+        let tokens_in_calldata: u64 = gas_cost::tx_calldata(calldata, self.env.config.fork)
+            .map_err(VMError::OutOfGas)?
+            .checked_div(STANDARD_TOKEN_COST)
+            .ok_or(VMError::Internal(InternalError::DivisionError))?;
+
+        // floor_gas_price = TX_BASE_COST + TOTAL_COST_FLOOR_PER_TOKEN * tokens_in_calldata
+        let mut floor_gas_price: u64 = tokens_in_calldata
+            .checked_mul(TOTAL_COST_FLOOR_PER_TOKEN)
+            .ok_or(VMError::Internal(InternalError::GasOverflow))?;
+
+        floor_gas_price = floor_gas_price
+            .checked_add(TX_BASE_COST)
+            .ok_or(VMError::Internal(InternalError::GasOverflow))?;
+
+        Ok(floor_gas_price)
     }
 
     pub fn execute(&mut self) -> Result<ExecutionReport, VMError> {
@@ -435,8 +424,6 @@ impl VM {
 
         let mut report = self.run_execution(&mut initial_call_frame)?;
 
-        report.gas_used = self.gas_used(&initial_call_frame, &report)?;
-
         self.finalize_execution(&initial_call_frame, &mut report)?;
 
         report.new_state.clone_from(&self.cache);


Reviews:
user: login: LeanSerra
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T17:50:55Z
user: login: ilitteri
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-14T18:00:34Z
user: login: tomip01
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T18:32:40Z
user: login: fkrause98
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T18:37:47Z
user: login: fkrause98
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T18:38:30Z
user: login: fborello-lambda
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T19:11:34Z
user: login: fborello-lambda
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T19:12:00Z
user: login: ilitteri
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-14T20:04:21Z
user: login: ilitteri
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-14T21:34:19Z
user: login: avilagaston9
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T13:52:28Z
user: login: fkrause98
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T14:25:23Z

Review comments:
diff_hunk: @@ -113,6 +113,11 @@ jobs:
           name: main-ef-test-data
           path: crates/vm/levm/test_result_main_short.txt
 
+      - name: Check EF-TESTS from London to Prague is 100%
+        run: |
+          cd crates/vm/levm
+          awk '/Prague:/, /London:/ {print $1, $3}' test_result_main.txt | sed 's/[()]//g' | grep -v '100.00%' && echo "All percentage are not 100%." && exit 1 || echo "All percentage are 100%."
path: .github/workflows/ci_levm.yaml
user: login: ilitteri
body: Add a comment explaining the idea behind this line.



created_at: 2025-03-14T18:00:34Z
author_association: COLLABORATOR
diff_hunk: @@ -113,6 +113,11 @@ jobs:
           name: main-ef-test-data
           path: crates/vm/levm/test_result_main_short.txt
 
+      - name: Check EF-TESTS from London to Prague is 100%
+        run: |
+          cd crates/vm/levm
+          awk '/Prague:/, /London:/ {print $1, $3}' test_result_main.txt | sed 's/[()]//g' | grep -v '100.00%' && echo "All percentage are not 100%." && exit 1 || echo "All percentage are 100%."
path: .github/workflows/ci_levm.yaml
user: login: tomip01
body: Done, [49b5117](https://github.com/lambdaclass/ethrex/pull/1877/commits/49b51170c4d9ff64c73d4a31f411c1bbeefacec4).

This because after adding the legacy tests, some tests are still not passing. We want to ensure the tests from London to Prague are passing.
created_at: 2025-03-14T18:32:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -467,29 +456,20 @@ impl VM {
             address,
         );
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodehash(
             address_was_cold,
             self.env.config.fork,
         )?)?;
 
-        if is_delegation {
-            let hash =
-                U256::from_big_endian(keccak(&SET_CODE_DELEGATION_BYTES[..2]).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
-        } else {
-            // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
-            if account_info.is_empty() {
-                current_call_frame.stack.push(U256::zero())?;
-                return Ok(OpcodeResult::Continue { pc_increment: 1 });
-            }
-
-            let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
+        // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
+        if account_info.is_empty() {
+            current_call_frame.stack.push(U256::zero())?;
+            return Ok(OpcodeResult::Continue { pc_increment: 1 });
         }
 
+        let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
path: crates/vm/levm/src/opcode_handlers/environment.rs
user: login: fkrause98
body: Let's explicitly make the keccack use `to_le_bytes` instead 
created_at: 2025-03-14T18:37:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -467,29 +456,20 @@ impl VM {
             address,
         );
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodehash(
             address_was_cold,
             self.env.config.fork,
         )?)?;
 
-        if is_delegation {
-            let hash =
-                U256::from_big_endian(keccak(&SET_CODE_DELEGATION_BYTES[..2]).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
-        } else {
-            // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
-            if account_info.is_empty() {
-                current_call_frame.stack.push(U256::zero())?;
-                return Ok(OpcodeResult::Continue { pc_increment: 1 });
-            }
-
-            let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
+        // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
+        if account_info.is_empty() {
+            current_call_frame.stack.push(U256::zero())?;
+            return Ok(OpcodeResult::Continue { pc_increment: 1 });
         }
 
+        let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
path: crates/vm/levm/src/opcode_handlers/environment.rs
user: login: fkrause98
body: Let's explicitly make the keccack use to_le_bytes instead
created_at: 2025-03-14T18:38:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -467,29 +456,20 @@ impl VM {
             address,
         );
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodehash(
             address_was_cold,
             self.env.config.fork,
         )?)?;
 
-        if is_delegation {
-            let hash =
-                U256::from_big_endian(keccak(&SET_CODE_DELEGATION_BYTES[..2]).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
-        } else {
-            // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
-            if account_info.is_empty() {
-                current_call_frame.stack.push(U256::zero())?;
-                return Ok(OpcodeResult::Continue { pc_increment: 1 });
-            }
-
-            let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
+        // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
+        if account_info.is_empty() {
+            current_call_frame.stack.push(U256::zero())?;
+            return Ok(OpcodeResult::Continue { pc_increment: 1 });
         }
 
+        let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
path: crates/vm/levm/src/opcode_handlers/environment.rs
user: login: fborello-lambda
body: There isn't a `to_be_bytes` present, we only have `as_fixed_bytes`. There may be a "clearer" way for this:
```suggestion
        let hash = U256::from_str(&keccak(account_info.bytecode).to_string())
            .map_err(|_| VMError::Internal(InternalError::ConversionError))?;
```
In this case, the `from_str` and `to_str` handle the byte ordering internally without exposing it to the user (us).

created_at: 2025-03-14T19:11:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -467,29 +456,20 @@ impl VM {
             address,
         );
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodehash(
             address_was_cold,
             self.env.config.fork,
         )?)?;
 
-        if is_delegation {
-            let hash =
-                U256::from_big_endian(keccak(&SET_CODE_DELEGATION_BYTES[..2]).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
-        } else {
-            // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
-            if account_info.is_empty() {
-                current_call_frame.stack.push(U256::zero())?;
-                return Ok(OpcodeResult::Continue { pc_increment: 1 });
-            }
-
-            let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
+        // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
+        if account_info.is_empty() {
+            current_call_frame.stack.push(U256::zero())?;
+            return Ok(OpcodeResult::Continue { pc_increment: 1 });
         }
 
+        let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
path: crates/vm/levm/src/opcode_handlers/environment.rs
user: login: fborello-lambda
body: If you think it's a better approach feel free to commit it.
created_at: 2025-03-14T19:11:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -467,29 +456,20 @@ impl VM {
             address,
         );
 
-        // https://eips.ethereum.org/EIPS/eip-7702#delegation-designation
-        let is_delegation = has_delegation(&account_info)?;
-
         current_call_frame.increase_consumed_gas(gas_cost::extcodehash(
             address_was_cold,
             self.env.config.fork,
         )?)?;
 
-        if is_delegation {
-            let hash =
-                U256::from_big_endian(keccak(&SET_CODE_DELEGATION_BYTES[..2]).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
-        } else {
-            // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
-            if account_info.is_empty() {
-                current_call_frame.stack.push(U256::zero())?;
-                return Ok(OpcodeResult::Continue { pc_increment: 1 });
-            }
-
-            let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
-            current_call_frame.stack.push(hash)?;
+        // An account is considered empty when it has no code and zero nonce and zero balance. [EIP-161]
+        if account_info.is_empty() {
+            current_call_frame.stack.push(U256::zero())?;
+            return Ok(OpcodeResult::Continue { pc_increment: 1 });
         }
 
+        let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
path: crates/vm/levm/src/opcode_handlers/environment.rs
user: login: ilitteri
body: > Let's explicitly make the keccack use to_le_bytes instead

I'm pretty sure this is the best way to turn an `H256` into an `U256` (sadly).
created_at: 2025-03-14T20:04:21Z
author_association: COLLABORATOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-02-05T21:58:38Z
body: <h2>Lines of code report</h2>

Total lines added: `8`
Total lines removed: `28`
Total lines changed: `36`

<details>
<summary>Detailed view</summary>

```
+----------------------------------------------------------+-------+------+
| File                                                     | Lines | Diff |
+----------------------------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/hooks/default_hook.rs          | 310   | +8   |
+----------------------------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/opcode_handlers/environment.rs | 359   | -16  |
+----------------------------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/vm.rs                          | 418   | -12  |
+----------------------------------------------------------+-------+------+

```
</details>

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-02-05T22:07:01Z
body: # Benchmark Results Comparison
## PR Results
#### Benchmark Results: Factorial
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_Factorial` | 235.4 ± 0.6 | 234.4 | 236.3 | 1.00 |
| `levm_Factorial` | 913.0 ± 12.3 | 903.0 | 946.7 | 3.88 ± 0.05 |
#### Benchmark Results: Factorial - Recursive
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `revm_FactorialRecursive` | 1.405 ± 0.084 | 1.301 | 1.556 | 1.00 |
| `levm_FactorialRecursive` | 15.678 ± 0.141 | 15.515 | 15.953 | 11.16 ± 0.68 |
#### Benchmark Results: Fibonacci
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_Fibonacci` | 206.3 ± 1.1 | 205.1 | 208.1 | 1.00 |
| `levm_Fibonacci` | 907.0 ± 12.4 | 893.3 | 929.8 | 4.40 ± 0.06 |
#### Benchmark Results: ManyHashes
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ManyHashes` | 8.6 ± 0.1 | 8.5 | 8.7 | 1.00 |
| `levm_ManyHashes` | 18.4 ± 0.3 | 18.2 | 19.1 | 2.15 ± 0.03 |
#### Benchmark Results: BubbleSort
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `revm_BubbleSort` | 3.202 ± 0.025 | 3.170 | 3.240 | 1.00 |
| `levm_BubbleSort` | 6.101 ± 0.044 | 6.056 | 6.182 | 1.91 ± 0.02 |
#### Benchmark Results: ERC20 - Transfer
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ERC20Transfer` | 246.4 ± 3.6 | 242.1 | 253.9 | 1.00 |
| `levm_ERC20Transfer` | 542.1 ± 3.7 | 539.4 | 551.7 | 2.20 ± 0.04 |
#### Benchmark Results: ERC20 - Mint
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ERC20Mint` | 139.6 ± 2.2 | 138.1 | 145.6 | 1.00 |
| `levm_ERC20Mint` | 394.1 ± 34.0 | 354.6 | 451.4 | 2.82 ± 0.25 |
#### Benchmark Results: ERC20 - Approval
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ERC20Approval` | 1.020 ± 0.004 | 1.015 | 1.029 | 1.00 |
| `levm_ERC20Approval` | 2.020 ± 0.020 | 1.999 | 2.054 | 1.98 ± 0.02 |
## Main Results
#### Benchmark Results: Factorial
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_Factorial` | 236.6 ± 2.5 | 234.3 | 240.4 | 1.00 |
| `levm_Factorial` | 906.8 ± 7.5 | 899.5 | 920.8 | 3.83 ± 0.05 |
#### Benchmark Results: Factorial - Recursive
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `revm_FactorialRecursive` | 1.417 ± 0.095 | 1.315 | 1.571 | 1.00 |
| `levm_FactorialRecursive` | 15.614 ± 0.109 | 15.440 | 15.718 | 11.02 ± 0.74 |
#### Benchmark Results: Fibonacci
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_Fibonacci` | 203.2 ± 3.4 | 201.6 | 212.7 | 1.00 |
| `levm_Fibonacci` | 913.3 ± 13.2 | 891.6 | 932.8 | 4.50 ± 0.10 |
#### Benchmark Results: ManyHashes
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ManyHashes` | 8.6 ± 0.1 | 8.6 | 8.7 | 1.00 |
| `levm_ManyHashes` | 18.3 ± 0.1 | 18.0 | 18.4 | 2.12 ± 0.02 |
#### Benchmark Results: BubbleSort
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `revm_BubbleSort` | 3.211 ± 0.022 | 3.186 | 3.248 | 1.00 |
| `levm_BubbleSort` | 6.087 ± 0.031 | 6.043 | 6.128 | 1.90 ± 0.02 |
#### Benchmark Results: ERC20 - Transfer
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ERC20Transfer` | 248.5 ± 4.1 | 244.7 | 257.7 | 1.00 |
| `levm_ERC20Transfer` | 539.8 ± 2.4 | 537.3 | 544.1 | 2.17 ± 0.04 |
#### Benchmark Results: ERC20 - Mint
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ERC20Mint` | 138.6 ± 0.5 | 137.9 | 139.1 | 1.00 |
| `levm_ERC20Mint` | 351.7 ± 2.0 | 348.9 | 355.0 | 2.54 ± 0.02 |
#### Benchmark Results: ERC20 - Approval
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `revm_ERC20Approval` | 1.040 ± 0.010 | 1.029 | 1.060 | 1.00 |
| `levm_ERC20Approval` | 2.015 ± 0.015 | 1.989 | 2.038 | 1.94 ± 0.02 |

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-02-20T19:47:31Z
body: # EF Tests Comparison
|Test Name | MAIN     | PR | DIFF | 
|----------|----------|----|------|
|Summary:|16374/16773 (97.62%) |19689/20129 (97.81%)| ⬆️ 3315 |
|Prague:|2373/2373 (100.00%) |2574/2574 (100.00%)| ➖️ 201 |
|Cancun:|3579/3579 (100.00%) |4983/4983 (100.00%)| ➖️ 1404 |
|Shanghai:|221/221 (100.00%) |614/614 (100.00%)| ➖️ 393 |
|Paris:|62/62 (100.00%) |285/285 (100.00%)| ➖️ 223 |
|London:|39/39 (100.00%) |247/247 (100.00%)| ➖️ 208 |
|Berlin:|35/35 (100.00%) |205/239 (85.77%)| ⬇️️ 170 |
|Istanbul:|35/35 (100.00%) |229/230 (99.57%)| ⬇️️ 194 |
|Petersburg:|2377/2399 (99.08%) |2516/2541 (99.02%)| ⬇️️ 139 |
|Constantinople:|2338/2405 (97.21%) |2338/2405 (97.21%)| ➖️  |
|Byzantium:|2309/2330 (99.10%) |2448/2472 (99.03%)| ⬇️️ 139 |
|SpuriousDragon:|579/579 (100.00%) |579/579 (100.00%)| ➖️  |
|Tangerine:|563/650 (86.62%) |563/650 (86.62%)| ➖️  |
|Homestead:|1223/1324 (92.37%) |1346/1447 (93.02%)| ⬆️ 123 |
|Frontier:|641/742 (86.39%) |762/863 (88.30%)| ⬆️ 121 |

author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-11T14:52:53Z
body: ## Benchmark Block Execution Results Comparison Against Main

| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `base` | 141.205 ± 1.094 | 139.580 | 142.883 | 1.00 |
| `head` | 141.922 ± 0.803 | 140.951 | 143.591 | 1.01 ± 0.01 |

author_association: NONE
user: login: ilitteri
created_at: 2025-03-14T18:02:13Z
body: > Add a new CI rule for EF Tests to be 100% from London to Prague forks

The idea is to enforce that EF tests from London to Prague should be 100% right?
author_association: COLLABORATOR