Skip to content

Commit

Permalink
fix(levm): change in extcodehash (#1503)
Browse files Browse the repository at this point in the history
**Motivation**

The `extcodehash` opcode was not handling certain edge cases correctly.

**Description**

- Return `U256::zero` for non-existent accounts and empty accounts.
Right now we are checking if the account is empty.
- Properly compute code hashes for accounts with bytecode.

**References**
[EIP-161](https://eips.ethereum.org/EIPS/eip-161): _An account is
considered empty when it has no code and zero nonce and zero balance._
[EIP-1052](https://eips.ethereum.org/EIPS/eip-1052): _In case the
account does not exist or is empty, 0 is pushed to the stack._
  • Loading branch information
damiramirez authored Dec 16, 2024
1 parent be73e39 commit 378d9b5
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 21 deletions.
10 changes: 7 additions & 3 deletions crates/vm/levm/src/opcode_handlers/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,10 +424,14 @@ impl VM {

self.increase_consumed_gas(current_call_frame, gas_cost::extcodehash(address_was_cold)?)?;

current_call_frame.stack.push(U256::from_big_endian(
keccak(account_info.bytecode).as_fixed_bytes(),
))?;
// 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(OpcodeSuccess::Continue);
}

let hash = U256::from_big_endian(keccak(account_info.bytecode).as_fixed_bytes());
current_call_frame.stack.push(hash)?;
Ok(OpcodeSuccess::Continue)
}
}
38 changes: 20 additions & 18 deletions crates/vm/levm/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3873,7 +3873,7 @@ fn create_happy_path() {
#[test]
fn caller_op() {
let caller = Address::from_low_u64_be(0x100);
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(213);

let operations = [Operation::Caller, Operation::Stop];

Expand Down Expand Up @@ -3915,7 +3915,7 @@ fn caller_op() {

#[test]
fn origin_op() {
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(213);
let msg_sender = Address::from_low_u64_be(0x999);

let operations = [Operation::Origin, Operation::Stop];
Expand Down Expand Up @@ -3986,7 +3986,7 @@ fn balance_op() {

#[test]
fn address_op() {
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(123);

let operations = [Operation::Address, Operation::Stop];

Expand Down Expand Up @@ -4028,7 +4028,7 @@ fn address_op() {

#[test]
fn selfbalance_op() {
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(123);
let balance = U256::from(999);

let operations = [Operation::SelfBalance, Operation::Stop];
Expand Down Expand Up @@ -4075,7 +4075,7 @@ fn selfbalance_op() {

#[test]
fn callvalue_op() {
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(123);
let value = U256::from(0x1234);

let operations = [Operation::Callvalue, Operation::Stop];
Expand Down Expand Up @@ -4119,7 +4119,7 @@ fn callvalue_op() {

#[test]
fn codesize_op() {
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(123);

let operations = [Operation::Codesize, Operation::Stop];

Expand Down Expand Up @@ -4161,7 +4161,7 @@ fn codesize_op() {

#[test]
fn gasprice_op() {
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(123);
let operations = [Operation::Gasprice, Operation::Stop];

let mut db = Db::default();
Expand Down Expand Up @@ -4204,7 +4204,7 @@ fn gasprice_op() {
#[test]
fn codecopy_op() {
// Copies two bytes of the code, with offset 2, and loads them beginning at offset 3 in memory.
let address_that_has_the_code = Address::from_low_u64_be(0x42);
let address_that_has_the_code = Address::from_low_u64_be(123);
// https://www.evm.codes/playground?fork=cancun&unit=Wei&codeType=Mnemonic&code=%27~2z~2z~3zCODECOPY%27~PUSH1%200x0z%5Cn%01z~_
let operations = [
Operation::Push((1, 0x02.into())), // size
Expand Down Expand Up @@ -4269,7 +4269,7 @@ fn codecopy_op() {

#[test]
fn extcodesize_existing_account() {
let address_with_code = Address::from_low_u64_be(0x42);
let address_with_code = Address::from_low_u64_be(123);
let operations = [
Operation::Push((20, address_with_code.as_bytes().into())),
Operation::ExtcodeSize,
Expand Down Expand Up @@ -4297,7 +4297,7 @@ fn extcodesize_existing_account() {
fn extcodesize_non_existing_account() {
// EVM Playground: https://www.evm.codes/playground?fork=cancun&unit=Wei&codeType=Mnemonic&code='PUSH20%200x42%5CnEXTCODESIZE%5CnSTOP'_
let operations = [
Operation::Push((20, "0x42".into())),
Operation::Push((20, U256::from(0xABCD))),
Operation::ExtcodeSize,
Operation::Stop,
];
Expand All @@ -4315,7 +4315,7 @@ fn extcodesize_non_existing_account() {

#[test]
fn extcodecopy_existing_account() {
let address_with_code = Address::from_low_u64_be(0x42);
let address_with_code = Address::from_low_u64_be(213);
let size: usize = 1;

let operations = [
Expand Down Expand Up @@ -4358,7 +4358,7 @@ fn extcodecopy_non_existing_account() {
Operation::Push((1, size.into())),
Operation::Push0, // offset
Operation::Push0, // destOffset
Operation::Push((20, "0x42".into())),
Operation::Push((20, U256::from(213))),
Operation::ExtcodeCopy,
Operation::Stop,
];
Expand All @@ -4380,16 +4380,17 @@ fn extcodecopy_non_existing_account() {
}

#[test]
fn extcodehash_account_with_empty_code() {
let address_with_code = Address::from_low_u64_be(0x42);
fn extcodehash_account_with_zero_bytecode_but_not_empty() {
let address = Address::from_low_u64_be(213);
let operations = [
Operation::Push((20, address_with_code.as_bytes().into())),
Operation::Push((20, address.as_bytes().into())),
Operation::ExtcodeHash,
Operation::Stop,
];

let mut db = Db::default();
db.add_accounts(vec![(address_with_code, Account::default())]);
let account = Account::default().with_balance(U256::one()); // Add balance to avoid empty account
db.add_accounts(vec![(address, account)]);

let mut vm = new_vm_with_ops_db(&operations, db).unwrap();

Expand All @@ -4405,8 +4406,9 @@ fn extcodehash_account_with_empty_code() {
#[test]
fn extcodehash_non_existing_account() {
// EVM Playground: https://www.evm.codes/playground?fork=cancun&unit=Wei&codeType=Mnemonic&code='PUSH20%200x42%5CnEXTCODEHASH%5CnSTOP'_
let random_address = Address::from_low_u64_be(12345);
let operations = [
Operation::Push((20, "0x42".into())),
Operation::Push((20, random_address.as_bytes().into())),
Operation::ExtcodeHash,
Operation::Stop,
];
Expand All @@ -4417,7 +4419,7 @@ fn extcodehash_non_existing_account() {
vm.execute(&mut current_call_frame).unwrap();
assert_eq!(
vm.current_call_frame_mut().unwrap().stack.pop().unwrap(),
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470".into()
U256::zero()
);
assert_eq!(current_call_frame.gas_used, 2603.into());
}
Expand Down

0 comments on commit 378d9b5

Please sign in to comment.