This repository was archived by the owner on Jul 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 270
Spec for opcode EXTCODESIZE
#341
Merged
icemelon
merged 6 commits into
privacy-ethereum:master
from
scroll-tech:feat/opcode-extcodesize
Dec 15, 2022
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
b4e9073
Implement opcode `EXTCODESIZE`.
silathdiir 34ee922
Update src/zkevm_specs/evm/execution/extcodesize.py
silathdiir bc47011
Update src/zkevm_specs/evm/execution/extcodesize.py
silathdiir 4843ffe
Delete unused import `RLC`.
silathdiir f688e52
Revert to set `code_size` to `RLC(0)` if `exists = 0`.
silathdiir 3fd1387
Update two `StepState` with `reversible_write_counter`.
silathdiir File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # EXTCODESIZE opcode | ||
|
|
||
| ## Procedure | ||
|
|
||
| The `EXTCODESIZE` opcode gets code size of the given account. | ||
|
|
||
| ## EVM behaviour | ||
|
|
||
| The `EXTCODESIZE` opcode pops `address` (20 bytes of data) off the stack and | ||
| pushes the code size of the corresponding account onto the stack. If the given | ||
| account doesn't exist (by checking non existing flag), then it will push 0 onto | ||
| the stack instead. | ||
|
|
||
| ## Circuit behaviour | ||
|
|
||
| 1. Construct call context table in rw table. | ||
| 2. Do bus-mapping lookup for stack read, call context read and account read | ||
| operations. | ||
| 3. Do bus-mapping lookup for transaction access list write and stack write | ||
| operations. | ||
|
|
||
| ## Constraints | ||
|
|
||
| 1. opId = 0x3b | ||
| 2. State transition: | ||
| - gc + 7 (1 stack read, 1 stack write, 3 call context reads, 1 account read, | ||
| 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 bus-mapping lookups | ||
| - `address` is popped from the stack. | ||
| - 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. | ||
| - If witness value `exists == 1`, lookup account `code_hash`, then get | ||
| `code_size`. Otherwise only lookup the account non-existing proof. | ||
| - The EXTCODESIZE result is at the new top of the stack. | ||
| 4. Additional Constraints | ||
| - value `is_warm` matches the gas cost for this opcode. | ||
|
|
||
| ## 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/extcodesize.py`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| from ...util import ( | ||
| EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, | ||
| FQ, | ||
| N_BYTES_ACCOUNT_ADDRESS, | ||
| N_BYTES_U64, | ||
| RLC, | ||
| ) | ||
| from ..instruction import Instruction, Transition | ||
| from ..opcode import Opcode | ||
| from ..table import AccountFieldTag, CallContextFieldTag | ||
|
|
||
|
|
||
| def extcodesize(instruction: Instruction): | ||
| opcode = instruction.opcode_lookup(True) | ||
| instruction.constrain_equal(opcode, Opcode.EXTCODESIZE) | ||
|
|
||
| address = instruction.rlc_to_fq(instruction.stack_pop(), N_BYTES_ACCOUNT_ADDRESS) | ||
|
|
||
| tx_id = instruction.call_context_lookup(CallContextFieldTag.TxId) | ||
| is_warm = instruction.add_account_to_access_list(tx_id, address, instruction.reversion_info()) | ||
|
|
||
| # Load account `exists` value from auxilary witness data. | ||
| exists = instruction.curr.aux_data | ||
|
|
||
| if exists == 1: | ||
| code_hash = instruction.account_read(address, AccountFieldTag.CodeHash) | ||
| code_size = instruction.bytecode_length(code_hash) | ||
| else: # exists == 0 | ||
| instruction.account_read(address, AccountFieldTag.NonExisting) | ||
| code_size = RLC(0) | ||
|
|
||
| instruction.constrain_equal( | ||
| instruction.select(exists, code_size, FQ(0)), | ||
| instruction.rlc_to_fq(instruction.stack_push(), N_BYTES_U64), | ||
| ) | ||
|
|
||
| instruction.step_state_transition_in_same_context( | ||
| opcode, | ||
| rw_counter=Transition.delta(7), | ||
| program_counter=Transition.delta(1), | ||
| stack_pointer=Transition.same(), | ||
| dynamic_gas_cost=instruction.select(is_warm, FQ(0), FQ(EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS)), | ||
| reversible_write_counter=Transition.delta(1), | ||
| ) | ||
|
silathdiir marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| import pytest | ||
| from itertools import chain | ||
| from zkevm_specs.evm import ( | ||
| AccountFieldTag, | ||
| Block, | ||
| Bytecode, | ||
| CallContextFieldTag, | ||
| ExecutionState, | ||
| RWDictionary, | ||
| StepState, | ||
| Tables, | ||
| verify_steps, | ||
| ) | ||
| from zkevm_specs.util import ( | ||
| EMPTY_CODE_HASH, | ||
| EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, | ||
| GAS_COST_WARM_ACCESS, | ||
| RLC, | ||
| U160, | ||
| U256, | ||
| keccak256, | ||
| rand_address, | ||
| rand_bytes, | ||
| rand_fq, | ||
| rand_range, | ||
| rand_word, | ||
| ) | ||
|
|
||
| TESTING_DATA = [ | ||
| (0x30000, bytes(), 0, True, True), # warm empty account | ||
| (0x30000, bytes(), 0, False, True), # cold empty account | ||
| (0x30000, bytes([10, 40]), 1, True, True), # warm non-empty account | ||
| (0x30000, bytes([10, 10, 40]), 1, False, True), # cold non-empty account | ||
| (0x30000, bytes(), 1, False, True), # non-empty account with empty code | ||
| ( | ||
| rand_address(), | ||
| rand_bytes(100), | ||
| rand_range(2), | ||
| rand_range(2), | ||
| True, # persistent call | ||
| ), | ||
| ( | ||
| rand_address(), | ||
| rand_bytes(100), | ||
| rand_range(2), | ||
| rand_range(2), | ||
| False, # reverted call | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("address, code, exists, is_warm, is_persistent", TESTING_DATA) | ||
| def test_extcodesize(address: U160, code: bytes, exists: int, is_warm: bool, is_persistent: bool): | ||
| randomness = rand_fq() | ||
|
|
||
| code_hash = int.from_bytes(keccak256(code), "big") | ||
| code_size = len(code) if exists == 1 else 0 | ||
|
|
||
| tx_id = 1 | ||
| call_id = 1 | ||
|
|
||
| rw_counter_end_of_reversion = 0 | ||
| reversible_write_counter = 0 | ||
|
|
||
| rw_dictionary = ( | ||
| RWDictionary(1) | ||
| .stack_read(call_id, 1023, RLC(address, randomness)) | ||
| .call_context_read(tx_id, CallContextFieldTag.TxId, tx_id) | ||
| .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 - reversible_write_counter, | ||
| ) | ||
| ) | ||
| if exists == 1: | ||
| rw_dictionary.account_read(address, AccountFieldTag.CodeHash, RLC(code_hash, randomness)) | ||
| else: | ||
| rw_dictionary.account_read( | ||
| address, AccountFieldTag.NonExisting, RLC(1 - exists, randomness) | ||
| ) | ||
|
|
||
| rw_table = set(rw_dictionary.stack_write(call_id, 1023, RLC(code_size, randomness)).rws) | ||
|
|
||
| bytecode = Bytecode().extcodesize() | ||
| tables = Tables( | ||
| block_table=Block(), | ||
| tx_table=set(), | ||
| bytecode_table=set( | ||
| chain( | ||
| bytecode.table_assignments(randomness), | ||
| Bytecode(code).table_assignments(randomness), | ||
| ) | ||
| ), | ||
| rw_table=rw_table, | ||
| ) | ||
|
|
||
| bytecode_hash = RLC(bytecode.hash(), randomness) | ||
| verify_steps( | ||
| randomness=randomness, | ||
| tables=tables, | ||
| steps=[ | ||
| StepState( | ||
| execution_state=ExecutionState.EXTCODESIZE, | ||
| rw_counter=1, | ||
| call_id=1, | ||
| is_root=True, | ||
| is_create=False, | ||
| code_hash=bytecode_hash, | ||
| program_counter=0, | ||
| stack_pointer=1023, | ||
| gas_left=GAS_COST_WARM_ACCESS + (not is_warm) * EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, | ||
| aux_data=exists, | ||
| reversible_write_counter=reversible_write_counter, | ||
| ), | ||
| StepState( | ||
| execution_state=ExecutionState.STOP if is_persistent else ExecutionState.REVERT, | ||
| rw_counter=8, | ||
| call_id=1, | ||
| is_root=True, | ||
| is_create=False, | ||
| code_hash=bytecode_hash, | ||
| program_counter=1, | ||
| stack_pointer=1023, | ||
| gas_left=0, | ||
| reversible_write_counter=reversible_write_counter + 1, | ||
| ), | ||
| ], | ||
| ) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.