From a449b9a16398c4f6f1a9e8b44719a00eea5c05f2 Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 7 Mar 2022 15:31:30 -0500 Subject: [PATCH 01/12] Standardize titles in specs --- specs/opcode/01ADD_03SUB.md | 2 +- specs/opcode/02MUL.md | 2 +- specs/opcode/0bSIGNEXTEND.md | 2 +- specs/opcode/10LT_11GT.md | 2 +- specs/opcode/12SLT_13SGT.md | 2 +- specs/opcode/16AND_17OR_18XOR.md | 2 +- specs/opcode/1aBYTE.md | 2 +- specs/opcode/41COINBASE.md | 2 +- specs/opcode/42TIMESTAMP.md | 2 +- specs/opcode/47SELFBALANCE.md | 2 +- specs/opcode/50POP.md | 2 +- specs/opcode/51MLOAD_52MSTORE_53MSTORE8.md | 2 +- specs/opcode/54SLOAD_55SSTORE.md | 2 +- specs/opcode/56JUMP.md | 2 +- specs/opcode/57JUMPI.md | 2 +- specs/opcode/59MSIZE.md | 2 +- specs/opcode/5BJUMPDEST.md | 2 +- specs/opcode/80DUPX.md | 2 +- specs/opcode/CopyToMemory.md | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/specs/opcode/01ADD_03SUB.md b/specs/opcode/01ADD_03SUB.md index b780c2ba0..dd0f85005 100644 --- a/specs/opcode/01ADD_03SUB.md +++ b/specs/opcode/01ADD_03SUB.md @@ -1,4 +1,4 @@ -# ADD and SUB op code +# ADD and SUB opcodes ## Procedure diff --git a/specs/opcode/02MUL.md b/specs/opcode/02MUL.md index b5b24ee4d..5af5cd2c0 100644 --- a/specs/opcode/02MUL.md +++ b/specs/opcode/02MUL.md @@ -1,4 +1,4 @@ -# MUL op code +# MUL opcode ## Procedure diff --git a/specs/opcode/0bSIGNEXTEND.md b/specs/opcode/0bSIGNEXTEND.md index 67d55332b..d50388002 100644 --- a/specs/opcode/0bSIGNEXTEND.md +++ b/specs/opcode/0bSIGNEXTEND.md @@ -1,4 +1,4 @@ -# SIGNEXTEND op code +# SIGNEXTEND opcode ## Procedure diff --git a/specs/opcode/10LT_11GT.md b/specs/opcode/10LT_11GT.md index b4971504f..75670872b 100644 --- a/specs/opcode/10LT_11GT.md +++ b/specs/opcode/10LT_11GT.md @@ -1,4 +1,4 @@ -# LT & GT opcode +# LT & GT opcodes ## Procedure diff --git a/specs/opcode/12SLT_13SGT.md b/specs/opcode/12SLT_13SGT.md index 1521d678e..727c80563 100644 --- a/specs/opcode/12SLT_13SGT.md +++ b/specs/opcode/12SLT_13SGT.md @@ -1,4 +1,4 @@ -# SLT & SGT opcode +# SLT & SGT opcodes ## Procedure diff --git a/specs/opcode/16AND_17OR_18XOR.md b/specs/opcode/16AND_17OR_18XOR.md index 955b9d9fc..f7b96adf6 100644 --- a/specs/opcode/16AND_17OR_18XOR.md +++ b/specs/opcode/16AND_17OR_18XOR.md @@ -1,4 +1,4 @@ -# AND, OR, XOR op code +# AND, OR, XOR opcodes ## Procedure diff --git a/specs/opcode/1aBYTE.md b/specs/opcode/1aBYTE.md index 588e18c3e..37d8fd931 100644 --- a/specs/opcode/1aBYTE.md +++ b/specs/opcode/1aBYTE.md @@ -1,4 +1,4 @@ -# BYTE op code +# BYTE opcode ## Procedure diff --git a/specs/opcode/41COINBASE.md b/specs/opcode/41COINBASE.md index aa229c1ea..fe08aa541 100644 --- a/specs/opcode/41COINBASE.md +++ b/specs/opcode/41COINBASE.md @@ -1,4 +1,4 @@ -# Coinbase op code +# COINBASE op code ## Procedure diff --git a/specs/opcode/42TIMESTAMP.md b/specs/opcode/42TIMESTAMP.md index 2c37b854e..4126620ca 100644 --- a/specs/opcode/42TIMESTAMP.md +++ b/specs/opcode/42TIMESTAMP.md @@ -1,4 +1,4 @@ -# Timestamp op code +# TIMESTAMP opcode ## Procedure diff --git a/specs/opcode/47SELFBALANCE.md b/specs/opcode/47SELFBALANCE.md index 8df0c7b4e..7e3ab9ce6 100644 --- a/specs/opcode/47SELFBALANCE.md +++ b/specs/opcode/47SELFBALANCE.md @@ -1,4 +1,4 @@ -# Selfbalance op code +# SELFBALANCE opcode ## Procedure diff --git a/specs/opcode/50POP.md b/specs/opcode/50POP.md index 479f7729c..7830fd450 100644 --- a/specs/opcode/50POP.md +++ b/specs/opcode/50POP.md @@ -1,4 +1,4 @@ -# POP op code +# POP opcode ## Procedure diff --git a/specs/opcode/51MLOAD_52MSTORE_53MSTORE8.md b/specs/opcode/51MLOAD_52MSTORE_53MSTORE8.md index c583f1078..b4e182df7 100644 --- a/specs/opcode/51MLOAD_52MSTORE_53MSTORE8.md +++ b/specs/opcode/51MLOAD_52MSTORE_53MSTORE8.md @@ -1,4 +1,4 @@ -# MLOAD & MSTORE & MSTORE8 op code +# MLOAD & MSTORE & MSTORE8 opcodes ## Procedure diff --git a/specs/opcode/54SLOAD_55SSTORE.md b/specs/opcode/54SLOAD_55SSTORE.md index 7fde3c6f6..dc5de4879 100644 --- a/specs/opcode/54SLOAD_55SSTORE.md +++ b/specs/opcode/54SLOAD_55SSTORE.md @@ -1,4 +1,4 @@ -# SLOAD & SSTORE op code +# SLOAD & SSTORE opcodes ## Variables definition diff --git a/specs/opcode/56JUMP.md b/specs/opcode/56JUMP.md index 490a797da..1cd8b66ac 100644 --- a/specs/opcode/56JUMP.md +++ b/specs/opcode/56JUMP.md @@ -1,4 +1,4 @@ -# JUMP code +# JUMP opcode ## Procedure diff --git a/specs/opcode/57JUMPI.md b/specs/opcode/57JUMPI.md index 6de30c84d..493338eb3 100644 --- a/specs/opcode/57JUMPI.md +++ b/specs/opcode/57JUMPI.md @@ -1,4 +1,4 @@ -# JUMPI code +# JUMPI opcode ## Procedure diff --git a/specs/opcode/59MSIZE.md b/specs/opcode/59MSIZE.md index 04c570214..ac83ce67a 100644 --- a/specs/opcode/59MSIZE.md +++ b/specs/opcode/59MSIZE.md @@ -1,4 +1,4 @@ -# MSIZE op code +# MSIZE opcode ## Procedure diff --git a/specs/opcode/5BJUMPDEST.md b/specs/opcode/5BJUMPDEST.md index 6cd53fd65..1bc4ac21f 100644 --- a/specs/opcode/5BJUMPDEST.md +++ b/specs/opcode/5BJUMPDEST.md @@ -1,4 +1,4 @@ -# JUMPDEST op code +# JUMPDEST opcode ## Procedure diff --git a/specs/opcode/80DUPX.md b/specs/opcode/80DUPX.md index 8c7643467..9803279d8 100644 --- a/specs/opcode/80DUPX.md +++ b/specs/opcode/80DUPX.md @@ -1,4 +1,4 @@ -# DUPX op code +# DUPX opcode ## Procedure diff --git a/specs/opcode/CopyToMemory.md b/specs/opcode/CopyToMemory.md index e2ec7d6ba..a314c82ba 100644 --- a/specs/opcode/CopyToMemory.md +++ b/specs/opcode/CopyToMemory.md @@ -1,4 +1,4 @@ -# CopyToMemory +# CopyToMemory opcode ## Circuit behaviour From 8491600cc2fb74d80039beb7a99a1d293ad7fabe Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 7 Mar 2022 21:21:41 -0500 Subject: [PATCH 02/12] Add spec for EXTCODEHASH --- specs/opcode/3fEXTCODEHASH.md | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 specs/opcode/3fEXTCODEHASH.md diff --git a/specs/opcode/3fEXTCODEHASH.md b/specs/opcode/3fEXTCODEHASH.md new file mode 100644 index 000000000..c1a59582f --- /dev/null +++ b/specs/opcode/3fEXTCODEHASH.md @@ -0,0 +1,40 @@ +# EXTCODEHASH opcode + +## Procedure + +The `EXTCODEHASH` opcode pops `address` off the stack and pushes the code hash +of the corresponding account onto the stack. If the corresponding account is +empty (i.e. has nonce = 0, balance = 0 and no code), then it will push 0 onto +the stack instead. + +## Constraints + +1. opId = 0x3f +2. State transition: + - gc + 7 (1 stack read, 1 stack write, 1 call context read, 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 + - `address` is popped from the stack + - tx id of call context is `tx_id` + - `address` is added to the transaction access list if not already present + - nonce of account is `nonce` + - balance of account is `balance` + - code hash of account is `code_hash` + - if the account is (non)empty, (`code_hash`) 0 is pushed onto the stack +4. Additional Constraints + - value `is_warm` matches the gas cost for this opcode + - `is_empty` is boolean and 1 iff the account is empty + +## Exceptions + +1. stack underflow: if the stack starts empty +2. out of gas: remaining gas is not enough + +## Code + +Please refer to `src/zkevm_specs/evm/execution/extcodehash.py`. From 0b301e3b78aeac55bea49667d3b287af073d4c5f Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 7 Mar 2022 21:22:28 -0500 Subject: [PATCH 03/12] Add implementation for EXTCODEHASH --- src/zkevm_specs/evm/execution/__init__.py | 2 ++ src/zkevm_specs/evm/execution/extcodehash.py | 33 ++++++++++++++++++++ src/zkevm_specs/util/param.py | 1 + 3 files changed, 36 insertions(+) create mode 100644 src/zkevm_specs/evm/execution/extcodehash.py diff --git a/src/zkevm_specs/evm/execution/__init__.py b/src/zkevm_specs/evm/execution/__init__.py index a3024210a..0ecb05b2f 100644 --- a/src/zkevm_specs/evm/execution/__init__.py +++ b/src/zkevm_specs/evm/execution/__init__.py @@ -27,6 +27,7 @@ from .gasprice import * from .storage import * from .selfbalance import * +from .extcodehash import * EXECUTION_STATE_IMPL: Dict[ExecutionState, Callable] = { @@ -53,4 +54,5 @@ ExecutionState.SSTORE: sstore, ExecutionState.SELFBALANCE: selfbalance, ExecutionState.GASPRICE: gasprice, + ExecutionState.EXTCODEHASH: extcodehash, } diff --git a/src/zkevm_specs/evm/execution/extcodehash.py b/src/zkevm_specs/evm/execution/extcodehash.py new file mode 100644 index 000000000..0c3bdfd10 --- /dev/null +++ b/src/zkevm_specs/evm/execution/extcodehash.py @@ -0,0 +1,33 @@ +from ..instruction import Instruction, Transition +from ..table import CallContextFieldTag, AccountFieldTag +from ..opcode import Opcode +from ...util.param import N_BYTES_MEMORY_ADDRESS +from ...util import keccak256 + + +def extcodehash(instruction: Instruction): + opcode = instruction.opcode_lookup(True) + + address = instruction.stack_pop() + + call_id = instruction.call_context_lookup(CallContextFieldTag.TxId) + is_warm = instruction.add_account_to_access_list(call_id, address) + + nonce = instruction.account_read(address, AccountFieldTag.Nonce) + balance = instruction.account_read(address, AccountFieldTag.Balance) + code_hash = instruction.account_read(address, AccountFieldTag.CodeHash) + + is_empty = nonce == 0 and balance == 0 and code_hash == int.from_bytes(keccak256(""), "big") + + instruction.constrain_equal( + 0 if is_empty else code_hash, + instruction.stack_push(), + ) + + instruction.step_state_transition_in_same_context( + opcode, + rw_counter=Transition.delta(7), + program_counter=Transition.delta(1), + stack_pointer=Transition.delta(0), + dynamic_gas_cost=0 if is_warm else COLD_ACCOUNT_ACCESS_COST - WARM_STORAGE_READ_COST, + ) diff --git a/src/zkevm_specs/util/param.py b/src/zkevm_specs/util/param.py index 32011d672..843cc44ff 100644 --- a/src/zkevm_specs/util/param.py +++ b/src/zkevm_specs/util/param.py @@ -71,6 +71,7 @@ COLD_SLOAD_COST = 2100 +COLD_ACCOUNT_ACCESS_COST = 100 WARM_STORAGE_READ_COST = 100 SLOAD_GAS = 100 SSTORE_SET_GAS = 20000 From 9c55e1029157e70346a3a5950ddc0a7295d50cd1 Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 7 Mar 2022 21:22:42 -0500 Subject: [PATCH 04/12] Add tests --- tests/evm/test_extcodehash.py | 92 +++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 tests/evm/test_extcodehash.py diff --git a/tests/evm/test_extcodehash.py b/tests/evm/test_extcodehash.py new file mode 100644 index 000000000..c15b86bdc --- /dev/null +++ b/tests/evm/test_extcodehash.py @@ -0,0 +1,92 @@ +import pytest + +from zkevm_specs.evm import ( + ExecutionState, + StepState, + verify_steps, + Tables, + RWTableTag, + RW, + Block, + Bytecode, + CallContextFieldTag, + AccountFieldTag, +) +from zkevm_specs.util import ( + rand_address, + rand_range, + rand_word, + rand_fp, + U256, + U160, + keccak256, + RLC, + rand_bytes, +) +from zkevm_specs.util.param import N_BYTES_WORD + +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 + (rand_address(), rand_word(), rand_word(), rand_bytes(100), rand_range(2) == 0), +] + + +@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): + randomness = rand_fp() + + bytecode = Bytecode().extcodehash() + + code_hash = int.from_bytes(keccak256(code), "big") + result = 0 if (address == 0 and nonce == 0 and code_hash == keccak256("")) else code_hash + rlc_code_hash = RLC(code_hash, randomness) + + rw_table = { + (0, RW.Read, RWTableTag.Stack, 1, 1023, 0, address, 0, 0, 0), + (1, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.TxId, 0, 1, 0, 0, 0), + (2, RW.Write, RWTableTag.TxAccessListAccount, 1, address, 0, 1, int(is_warm), 0, 0), + (3, RW.Read, RWTableTag.Account, address, AccountFieldTag.Nonce, 0, nonce, 0, 0, 0), + (4, RW.Read, RWTableTag.Account, address, AccountFieldTag.Balance, 0, balance, 0, 0, 0), + (5, RW.Read, RWTableTag.Account, address, AccountFieldTag.CodeHash, 0, code_hash, 0, 0, 0), + (6, RW.Write, RWTableTag.Stack, 1, 1023, 0, result, 0, 0, 0), + } + + tables = Tables( + block_table=Block(), + tx_table=set(), + bytecode_table=set(bytecode.table_assignments(randomness)), + rw_table=rw_table, + ) + + bytecode_hash = RLC(bytecode.hash(), randomness) + verify_steps( + randomness=randomness, + tables=tables, + steps=[ + StepState( + execution_state=ExecutionState.EXTCODEHASH, + rw_counter=0, + call_id=1, + is_root=True, + is_create=False, + code_source=bytecode_hash, + program_counter=0, + stack_pointer=1023, + gas_left=100, + ), + StepState( + execution_state=ExecutionState.STOP, + rw_counter=7, + call_id=1, + is_root=True, + is_create=False, + code_source=bytecode_hash, + program_counter=1, + stack_pointer=1023, + gas_left=0, + ), + ], + ) From 5223369fdc3cafd4ab0ee3f839b5ad569bde47dc Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 7 Mar 2022 21:28:35 -0500 Subject: [PATCH 05/12] mdformat --- specs/opcode/3fEXTCODEHASH.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/opcode/3fEXTCODEHASH.md b/specs/opcode/3fEXTCODEHASH.md index c1a59582f..11a633260 100644 --- a/specs/opcode/3fEXTCODEHASH.md +++ b/specs/opcode/3fEXTCODEHASH.md @@ -12,7 +12,7 @@ the stack instead. 1. opId = 0x3f 2. State transition: - gc + 7 (1 stack read, 1 stack write, 1 call context read, 3 account reads, - 1 transaction access list write) + 1 transaction access list write) - stack_pointer + 0 (one pop and one push) - pc + 1 - gas: From 0ed3a519252006f1773ccfe51d2ccb3fdb2f903e Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Thu, 10 Mar 2022 21:44:08 -0500 Subject: [PATCH 06/12] Use instruction methods --- src/zkevm_specs/evm/execution/extcodehash.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/zkevm_specs/evm/execution/extcodehash.py b/src/zkevm_specs/evm/execution/extcodehash.py index 0c3bdfd10..ea2683bbe 100644 --- a/src/zkevm_specs/evm/execution/extcodehash.py +++ b/src/zkevm_specs/evm/execution/extcodehash.py @@ -17,10 +17,14 @@ def extcodehash(instruction: Instruction): balance = instruction.account_read(address, AccountFieldTag.Balance) code_hash = instruction.account_read(address, AccountFieldTag.CodeHash) - is_empty = nonce == 0 and balance == 0 and code_hash == int.from_bytes(keccak256(""), "big") + is_empty = ( + instruction.is_zero(nonce) + * instruction.is_zero(balance) + * instruction.is_zero(code_hash - int.from_bytes(keccak256(""), "big")) + ) instruction.constrain_equal( - 0 if is_empty else code_hash, + instruction.select(is_empty, 0, code_hash), instruction.stack_push(), ) From 829df9110b381527df63aaae0940c21389ff2dd3 Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Fri, 11 Mar 2022 11:53:12 -0500 Subject: [PATCH 07/12] Fix is_empty by checking if balance is 0 and not address --- src/zkevm_specs/evm/execution/extcodehash.py | 11 ++++++----- src/zkevm_specs/util/param.py | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/zkevm_specs/evm/execution/extcodehash.py b/src/zkevm_specs/evm/execution/extcodehash.py index ea2683bbe..1a760e8d2 100644 --- a/src/zkevm_specs/evm/execution/extcodehash.py +++ b/src/zkevm_specs/evm/execution/extcodehash.py @@ -1,8 +1,9 @@ from ..instruction import Instruction, Transition from ..table import CallContextFieldTag, AccountFieldTag from ..opcode import Opcode -from ...util.param import N_BYTES_MEMORY_ADDRESS +from ...util.param import EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, GAS_COST_WARM_ACCESS from ...util import keccak256 +from ...util.hash import EMPTY_CODE_HASH def extcodehash(instruction: Instruction): @@ -11,7 +12,7 @@ def extcodehash(instruction: Instruction): address = instruction.stack_pop() call_id = instruction.call_context_lookup(CallContextFieldTag.TxId) - is_warm = instruction.add_account_to_access_list(call_id, address) + is_cold = instruction.add_account_to_access_list(call_id, address) nonce = instruction.account_read(address, AccountFieldTag.Nonce) balance = instruction.account_read(address, AccountFieldTag.Balance) @@ -20,11 +21,11 @@ def extcodehash(instruction: Instruction): is_empty = ( instruction.is_zero(nonce) * instruction.is_zero(balance) - * instruction.is_zero(code_hash - int.from_bytes(keccak256(""), "big")) + * instruction.is_zero(code_hash - EMPTY_CODE_HASH) ) instruction.constrain_equal( - instruction.select(is_empty, 0, code_hash), + instruction.select(not instruction.is_zero(is_empty), 0, code_hash), instruction.stack_push(), ) @@ -33,5 +34,5 @@ def extcodehash(instruction: Instruction): rw_counter=Transition.delta(7), program_counter=Transition.delta(1), stack_pointer=Transition.delta(0), - dynamic_gas_cost=0 if is_warm else COLD_ACCOUNT_ACCESS_COST - WARM_STORAGE_READ_COST, + dynamic_gas_cost=is_cold * EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, ) diff --git a/src/zkevm_specs/util/param.py b/src/zkevm_specs/util/param.py index 843cc44ff..32011d672 100644 --- a/src/zkevm_specs/util/param.py +++ b/src/zkevm_specs/util/param.py @@ -71,7 +71,6 @@ COLD_SLOAD_COST = 2100 -COLD_ACCOUNT_ACCESS_COST = 100 WARM_STORAGE_READ_COST = 100 SLOAD_GAS = 100 SSTORE_SET_GAS = 20000 From fd3649d4fa6aa04c16ebbed46517d11047378a08 Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Fri, 11 Mar 2022 11:54:08 -0500 Subject: [PATCH 08/12] Fix tests now that is_warm is being checked --- tests/evm/test_extcodehash.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/evm/test_extcodehash.py b/tests/evm/test_extcodehash.py index c15b86bdc..4247139fb 100644 --- a/tests/evm/test_extcodehash.py +++ b/tests/evm/test_extcodehash.py @@ -23,7 +23,9 @@ RLC, rand_bytes, ) -from zkevm_specs.util.param import N_BYTES_WORD +from zkevm_specs.util.param import EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, GAS_COST_WARM_ACCESS +from zkevm_specs.util.hash import EMPTY_CODE_HASH + TESTING_DATA = [ (0x30000, 0, 0, bytes(), True), # warm empty account @@ -38,12 +40,8 @@ def test_extcodehash(address: U160, nonce: U256, balance: U256, code: bytes, is_warm: bool): randomness = rand_fp() - bytecode = Bytecode().extcodehash() - code_hash = int.from_bytes(keccak256(code), "big") - result = 0 if (address == 0 and nonce == 0 and code_hash == keccak256("")) else code_hash - rlc_code_hash = RLC(code_hash, randomness) - + result = 0 if (nonce == 0 and balance == 0 and code_hash == EMPTY_CODE_HASH) else code_hash rw_table = { (0, RW.Read, RWTableTag.Stack, 1, 1023, 0, address, 0, 0, 0), (1, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.TxId, 0, 1, 0, 0, 0), @@ -54,6 +52,7 @@ def test_extcodehash(address: U160, nonce: U256, balance: U256, code: bytes, is_ (6, RW.Write, RWTableTag.Stack, 1, 1023, 0, result, 0, 0, 0), } + bytecode = Bytecode().extcodehash() tables = Tables( block_table=Block(), tx_table=set(), @@ -75,7 +74,7 @@ def test_extcodehash(address: U160, nonce: U256, balance: U256, code: bytes, is_ code_source=bytecode_hash, program_counter=0, stack_pointer=1023, - gas_left=100, + gas_left=GAS_COST_WARM_ACCESS + (not is_warm) * EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, ), StepState( execution_state=ExecutionState.STOP, From 34bc703804eb56bff0ed057e75c02b57a0dcb926 Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 14 Mar 2022 13:05:42 -0400 Subject: [PATCH 09/12] Add rlc_encode method --- src/zkevm_specs/evm/instruction.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/zkevm_specs/evm/instruction.py b/src/zkevm_specs/evm/instruction.py index 246f59281..367000e9c 100644 --- a/src/zkevm_specs/evm/instruction.py +++ b/src/zkevm_specs/evm/instruction.py @@ -301,7 +301,7 @@ def add_words(self, addends: Sequence[RLC]) -> Tuple[RLC, FQ]: sum_bytes = sum_lo.to_bytes(16, "little") + sum_hi.to_bytes(16, "little") - return RLC(sum_bytes, self.randomness), FQ(carry_hi) + return self.rlc_encode(sum_bytes), FQ(carry_hi) def sub_word(self, minuend: RLC, subtrahend: RLC) -> Tuple[RLC, FQ]: minuend_lo, minuend_hi = self.word_to_lo_hi(minuend) @@ -314,7 +314,7 @@ def sub_word(self, minuend: RLC, subtrahend: RLC) -> Tuple[RLC, FQ]: diff_bytes = diff_lo.n.to_bytes(16, "little") + diff_hi.n.to_bytes(16, "little") - return RLC(diff_bytes, self.randomness), FQ(borrow_hi) + return self.rlc_encode(diff_bytes), FQ(borrow_hi) def mul_word_by_u64(self, multiplicand: RLC, multiplier: Expression) -> Tuple[RLC, FQ]: multiplicand_lo, multiplicand_hi = self.word_to_lo_hi(multiplicand) @@ -326,7 +326,7 @@ def mul_word_by_u64(self, multiplicand: RLC, multiplier: Expression) -> Tuple[RL product_bytes = product_lo.to_bytes(16, "little") + product_hi.to_bytes(16, "little") - return RLC(product_bytes, self.randomness), FQ(quotient_hi) + return self.rlc_encode(product_bytes), FQ(quotient_hi) def rlc_to_fq_unchecked(self, word: RLC, n_bytes: int) -> Tuple[FQ, FQ]: return self.bytes_to_fq(word.le_bytes[:n_bytes]), self.is_zero( @@ -347,6 +347,9 @@ def bytes_to_fq(self, value: bytes) -> FQ: assert len(value) <= MAX_N_BYTES, "Too many bytes to composite an integer in field" return FQ(int.from_bytes(value, "little")) + def rlc_encode(self, value: bytes) -> RLC: + return RLC(value, self.randomness) + def range_lookup(self, value: Expression, range: int): self.fixed_lookup(FixedTableTag.range_table_tag(range), value) From 51f750069299e0db90978c0851aa9bbba4e4744e Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 14 Mar 2022 13:06:00 -0400 Subject: [PATCH 10/12] Fix tests --- src/zkevm_specs/evm/execution/extcodehash.py | 12 ++++---- tests/evm/test_extcodehash.py | 30 +++++++++++++------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/zkevm_specs/evm/execution/extcodehash.py b/src/zkevm_specs/evm/execution/extcodehash.py index 1a760e8d2..30263ac16 100644 --- a/src/zkevm_specs/evm/execution/extcodehash.py +++ b/src/zkevm_specs/evm/execution/extcodehash.py @@ -2,17 +2,17 @@ from ..table import CallContextFieldTag, AccountFieldTag from ..opcode import Opcode from ...util.param import EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, GAS_COST_WARM_ACCESS -from ...util import keccak256 from ...util.hash import EMPTY_CODE_HASH +from ...util import FQ def extcodehash(instruction: Instruction): opcode = instruction.opcode_lookup(True) - address = instruction.stack_pop() + address = instruction.rlc_to_fq_exact(instruction.stack_pop(), 20) - call_id = instruction.call_context_lookup(CallContextFieldTag.TxId) - is_cold = instruction.add_account_to_access_list(call_id, address) + tx_id = instruction.call_context_lookup(CallContextFieldTag.TxId) + is_cold = instruction.add_account_to_access_list(tx_id, address) nonce = instruction.account_read(address, AccountFieldTag.Nonce) balance = instruction.account_read(address, AccountFieldTag.Balance) @@ -21,11 +21,11 @@ def extcodehash(instruction: Instruction): is_empty = ( instruction.is_zero(nonce) * instruction.is_zero(balance) - * instruction.is_zero(code_hash - EMPTY_CODE_HASH) + * instruction.is_equal(code_hash, instruction.rlc_encode(EMPTY_CODE_HASH)) ) instruction.constrain_equal( - instruction.select(not instruction.is_zero(is_empty), 0, code_hash), + instruction.select(is_empty, FQ(0), code_hash), instruction.stack_push(), ) diff --git a/tests/evm/test_extcodehash.py b/tests/evm/test_extcodehash.py index 4247139fb..3e2543291 100644 --- a/tests/evm/test_extcodehash.py +++ b/tests/evm/test_extcodehash.py @@ -11,12 +11,13 @@ Bytecode, CallContextFieldTag, AccountFieldTag, + RWDictionary, ) from zkevm_specs.util import ( rand_address, rand_range, rand_word, - rand_fp, + rand_fq, U256, U160, keccak256, @@ -32,25 +33,32 @@ (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), ] @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): - randomness = rand_fp() + randomness = rand_fq() code_hash = int.from_bytes(keccak256(code), "big") result = 0 if (nonce == 0 and balance == 0 and code_hash == EMPTY_CODE_HASH) else code_hash - rw_table = { - (0, RW.Read, RWTableTag.Stack, 1, 1023, 0, address, 0, 0, 0), - (1, RW.Read, RWTableTag.CallContext, 1, CallContextFieldTag.TxId, 0, 1, 0, 0, 0), - (2, RW.Write, RWTableTag.TxAccessListAccount, 1, address, 0, 1, int(is_warm), 0, 0), - (3, RW.Read, RWTableTag.Account, address, AccountFieldTag.Nonce, 0, nonce, 0, 0, 0), - (4, RW.Read, RWTableTag.Account, address, AccountFieldTag.Balance, 0, balance, 0, 0, 0), - (5, RW.Read, RWTableTag.Account, address, AccountFieldTag.CodeHash, 0, code_hash, 0, 0, 0), - (6, RW.Write, RWTableTag.Stack, 1, 1023, 0, result, 0, 0, 0), - } + + tx_id = 1 + call_id = 1 + + rw_table = set( + RWDictionary(0) + .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) + .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)) + .stack_write(call_id, 1023, RLC(result, randomness)) + .rws + ) bytecode = Bytecode().extcodehash() tables = Tables( From c988f334e9e4e5bd285c28cf8b2369dcb2839d62 Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 14 Mar 2022 13:53:50 -0400 Subject: [PATCH 11/12] Fix types --- src/zkevm_specs/evm/execution/extcodehash.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/zkevm_specs/evm/execution/extcodehash.py b/src/zkevm_specs/evm/execution/extcodehash.py index 30263ac16..76764d9ee 100644 --- a/src/zkevm_specs/evm/execution/extcodehash.py +++ b/src/zkevm_specs/evm/execution/extcodehash.py @@ -21,11 +21,13 @@ def extcodehash(instruction: Instruction): is_empty = ( instruction.is_zero(nonce) * instruction.is_zero(balance) - * instruction.is_equal(code_hash, instruction.rlc_encode(EMPTY_CODE_HASH)) + * instruction.is_equal( + code_hash, instruction.rlc_encode(EMPTY_CODE_HASH.to_bytes(64, "little")) + ) ) instruction.constrain_equal( - instruction.select(is_empty, FQ(0), code_hash), + instruction.select(is_empty, FQ(0), code_hash.value), instruction.stack_push(), ) @@ -34,5 +36,5 @@ def extcodehash(instruction: Instruction): rw_counter=Transition.delta(7), program_counter=Transition.delta(1), stack_pointer=Transition.delta(0), - dynamic_gas_cost=is_cold * EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, + dynamic_gas_cost=instruction.select(is_cold, FQ(EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS), FQ(0)), ) From 532872a780609187012e604ca58d14d306e1c426 Mon Sep 17 00:00:00 2001 From: Mason Liang Date: Mon, 14 Mar 2022 13:55:22 -0400 Subject: [PATCH 12/12] Fix for is_cold -> is_warm --- src/zkevm_specs/evm/execution/extcodehash.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zkevm_specs/evm/execution/extcodehash.py b/src/zkevm_specs/evm/execution/extcodehash.py index 76764d9ee..5422a6ddd 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_cold = instruction.add_account_to_access_list(tx_id, address) + is_warm = instruction.add_account_to_access_list(tx_id, address) nonce = instruction.account_read(address, AccountFieldTag.Nonce) balance = instruction.account_read(address, AccountFieldTag.Balance) @@ -36,5 +36,5 @@ def extcodehash(instruction: Instruction): rw_counter=Transition.delta(7), program_counter=Transition.delta(1), stack_pointer=Transition.delta(0), - dynamic_gas_cost=instruction.select(is_cold, FQ(EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS), FQ(0)), + dynamic_gas_cost=instruction.select(is_warm, FQ(0), FQ(EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS)), )