diff --git a/specs/opcode/3fEXTCODEHASH.md b/specs/opcode/3fEXTCODEHASH.md index 11a633260..3329b35f8 100644 --- a/specs/opcode/3fEXTCODEHASH.md +++ b/specs/opcode/3fEXTCODEHASH.md @@ -11,16 +11,17 @@ the stack instead. 1. opId = 0x3f 2. State transition: - - gc + 7 (1 stack read, 1 stack write, 1 call context read, 3 account reads, + - gc + 9 (1 stack read, 1 stack write, 3 call context reads, 3 account reads, 1 transaction access list write) - stack_pointer + 0 (one pop and one push) - pc + 1 - gas: - the accessed `address` is warm: WARM_STORAGE_READ_COST - the accessed `address` is cold: COLD_ACCOUNT_ACCESS_COST -3. Lookups: 7 +3. Lookups: 9 - `address` is popped from the stack - - tx id of call context is `tx_id` + - 3 from call context for `tx_id`, `rw_counter_end_of_reversion`, and + `is_persistent`. - `address` is added to the transaction access list if not already present - nonce of account is `nonce` - balance of account is `balance` diff --git a/src/zkevm_specs/evm/execution/extcodehash.py b/src/zkevm_specs/evm/execution/extcodehash.py index 5422a6ddd..ced422074 100644 --- a/src/zkevm_specs/evm/execution/extcodehash.py +++ b/src/zkevm_specs/evm/execution/extcodehash.py @@ -12,7 +12,7 @@ def extcodehash(instruction: Instruction): address = instruction.rlc_to_fq_exact(instruction.stack_pop(), 20) tx_id = instruction.call_context_lookup(CallContextFieldTag.TxId) - is_warm = instruction.add_account_to_access_list(tx_id, address) + is_warm = instruction.add_account_to_access_list(tx_id, address, instruction.reversion_info()) nonce = instruction.account_read(address, AccountFieldTag.Nonce) balance = instruction.account_read(address, AccountFieldTag.Balance) @@ -33,7 +33,7 @@ def extcodehash(instruction: Instruction): instruction.step_state_transition_in_same_context( opcode, - rw_counter=Transition.delta(7), + rw_counter=Transition.delta(9), program_counter=Transition.delta(1), stack_pointer=Transition.delta(0), dynamic_gas_cost=instruction.select(is_warm, FQ(0), FQ(EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS)), diff --git a/tests/evm/test_extcodehash.py b/tests/evm/test_extcodehash.py index 3e2543291..f22904592 100644 --- a/tests/evm/test_extcodehash.py +++ b/tests/evm/test_extcodehash.py @@ -29,17 +29,34 @@ TESTING_DATA = [ - (0x30000, 0, 0, bytes(), True), # warm empty account - (0x30000, 0, 0, bytes(), False), # cold empty account - (0x30000, 1, 200, bytes([10, 40]), True), # warm non-empty account - (0x30000, 1, 200, bytes([10, 10]), False), # cold non-empty account - (0x30000, 1, 0, bytes(), False), # non-empty account because of nonce - (rand_address(), rand_word(), rand_word(), rand_bytes(100), rand_range(2) == 0), + (0x30000, 0, 0, bytes(), True, True), # warm empty account + (0x30000, 0, 0, bytes(), False, True), # cold empty account + (0x30000, 1, 200, bytes([10, 40]), True, True), # warm non-empty account + (0x30000, 1, 200, bytes([10, 10]), False, True), # cold non-empty account + (0x30000, 1, 0, bytes(), False, True), # non-empty account because of nonce + ( + rand_address(), + rand_word(), + rand_word(), + rand_bytes(100), + rand_range(2) == 0, + True, # persistent call + ), + ( + rand_address(), + rand_word(), + rand_word(), + rand_bytes(100), + rand_range(2) == 0, + False, # reverted call + ), ] -@pytest.mark.parametrize("address, nonce, balance, code, is_warm", TESTING_DATA) -def test_extcodehash(address: U160, nonce: U256, balance: U256, code: bytes, is_warm: bool): +@pytest.mark.parametrize("address, nonce, balance, code, is_warm, is_persistent", TESTING_DATA) +def test_extcodehash( + address: U160, nonce: U256, balance: U256, code: bytes, is_warm: bool, is_persistent: bool +): randomness = rand_fq() code_hash = int.from_bytes(keccak256(code), "big") @@ -48,11 +65,24 @@ def test_extcodehash(address: U160, nonce: U256, balance: U256, code: bytes, is_ tx_id = 1 call_id = 1 + rw_counter_end_of_reversion = 0 if is_persistent else 9 + state_write_counter = 0 + rw_table = set( - RWDictionary(0) + RWDictionary(1) .stack_read(call_id, 1023, RLC(address, randomness)) .call_context_read(tx_id, CallContextFieldTag.TxId, tx_id) - .tx_access_list_account_write(tx_id, address, True, is_warm) + .call_context_read( + tx_id, CallContextFieldTag.RwCounterEndOfReversion, rw_counter_end_of_reversion + ) + .call_context_read(tx_id, CallContextFieldTag.IsPersistent, is_persistent) + .tx_access_list_account_write( + tx_id, + address, + True, + is_warm, + rw_counter_of_reversion=rw_counter_end_of_reversion - state_write_counter, + ) .account_read(address, AccountFieldTag.Nonce, RLC(nonce, randomness)) .account_read(address, AccountFieldTag.Balance, RLC(balance, randomness)) .account_read(address, AccountFieldTag.CodeHash, RLC(code_hash, randomness)) @@ -75,7 +105,7 @@ def test_extcodehash(address: U160, nonce: U256, balance: U256, code: bytes, is_ steps=[ StepState( execution_state=ExecutionState.EXTCODEHASH, - rw_counter=0, + rw_counter=1, call_id=1, is_root=True, is_create=False, @@ -85,8 +115,8 @@ def test_extcodehash(address: U160, nonce: U256, balance: U256, code: bytes, is_ gas_left=GAS_COST_WARM_ACCESS + (not is_warm) * EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, ), StepState( - execution_state=ExecutionState.STOP, - rw_counter=7, + execution_state=ExecutionState.STOP if is_persistent else ExecutionState.REVERT, + rw_counter=10, call_id=1, is_root=True, is_create=False,