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
Feat/#434 Implement ErrorInvalidCreationCode error state #478
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
ebcfba7
feat: MD spec completed
KimiWu123 8e8ec2d
feat: impl ErrorInvalidCreationCode error state
KimiWu123 581a37c
test: impl test cases for ErrorInvalidCreationCode
KimiWu123 74f01ff
Update src/zkevm_specs/evm_circuit/execution/error_invalid_creation_c…
KimiWu123 6373832
Merge branch 'master' into feat/#434-The-InvalidCreationCode-Error
KimiWu123 3a1ad47
Merge branch 'master' into feat/#434-The-InvalidCreationCode-Error
KimiWu123 4a3594a
doc: refine MD spec
KimiWu123 d699107
Merge branch 'master' into feat/#434-The-InvalidCreationCode-Error
KimiWu123 fc93c3c
Merge branch 'master' into feat/#434-The-InvalidCreationCode-Error
KimiWu123 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
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,38 @@ | ||
| # ErrorInvalidCreationCode state | ||
|
|
||
| ## Procedure | ||
|
|
||
| InvalidCreationCode is an error related with code store. This error only occurs when executing `CREATE` or `CREATE2` opcode or contract creation transaction (`tx.to == null`). | ||
|
|
||
| ### EVM behavior | ||
|
|
||
| When handling either a `CREATE` or a`CREATE2` opcode, the initial bytecode is executed and the current call context is created. The contract bytecode will then be returned through the `RETURN` opcode as the execution result. More particularly, the contract bytecode will be defined as the memory chunk of length `length` starting at offset `offset`, that is the memory located at [`offset`...`offset` + `length`] is stored in the state db. | ||
|
|
||
|
|
||
| In bus-mapping, when the executing opcode is `RETURN` and the current call context is `CREATE` or `CREATE2`, check if the first byte of contract code is `0xEF` to identify this error. | ||
|
|
||
| Even though this error occurs in `CREATE` or `CREATE2`, this error should be constrained in the `RETURN` opcode. Since it is easy to get the available memory offset and construct constraints with it. | ||
|
|
||
| Overall it looks as the following: | ||
|
|
||
| - Pop EVM word `offset` from the stack. | ||
| - Go to `ErrorInvalidCreationCode` state when the current call context is `CREATE` or `CREATE2` and the first byte of the deployed bytecode is `0xEF`. | ||
|
|
||
| ### Constraints | ||
|
|
||
| 1. Current opcode is `RETURN` and `is_create` is True. | ||
| 2. The first byte of contract code is `0xEF`. | ||
| 3. Current call must be failed. | ||
| 4. If it's a root call, it transits to `EndTx`. | ||
| 5. if it is not root call, it restores caller's context by reading to `rw_table`, then does step state transition to it. | ||
|
|
||
| ### Lookups | ||
|
|
||
| - Memory lookup. | ||
| - Stack reads for `offset`. | ||
| - Call context lookups for `is_success` and `rw_counter_end_of_reversion`. | ||
| - Restore context lookups for non-root call. | ||
|
|
||
| ## Code | ||
|
|
||
| Please refer to `src/zkevm_specs/evm/execution/error_invalid_creation_code.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
31 changes: 31 additions & 0 deletions
31
src/zkevm_specs/evm_circuit/execution/error_invalid_creation_code.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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| from zkevm_specs.evm_circuit.table import RW | ||
| from zkevm_specs.util.param import ( | ||
| INVALID_FIRST_BYTE_CONTRACT_CODE, | ||
| N_BYTES_MEMORY_ADDRESS, | ||
| ) | ||
| from ...util import FQ | ||
| from ..instruction import Instruction | ||
| from ..opcode import Opcode | ||
|
|
||
|
|
||
| def error_invalid_creation_code(instruction: Instruction): | ||
| opcode = instruction.opcode_lookup(True) | ||
|
|
||
| # opcode must be `RETURN` | ||
| instruction.constrain_equal(opcode, Opcode.RETURN) | ||
|
|
||
| # the call must be coming from CREATE or CREATE2 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can it also be that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we couldn't add this constraint. There are two routes for this error, a.) it's a |
||
| instruction.constrain_equal(FQ(instruction.curr.is_create), FQ(1)) | ||
|
|
||
| # pop offset from stack only | ||
| return_offset = instruction.word_to_fq(instruction.stack_pop(), N_BYTES_MEMORY_ADDRESS) | ||
|
|
||
| # lookup the first byte of deployed bytecode from memory | ||
| first_byte = instruction.memory_lookup(RW.Read, return_offset) | ||
|
|
||
| # verify if the first byte is `0xEF`, which is introduced in EIP-3541 | ||
| instruction.constrain_equal(first_byte, FQ(INVALID_FIRST_BYTE_CONTRACT_CODE)) | ||
|
|
||
| instruction.constrain_error_state( | ||
| instruction.rw_counter_offset + instruction.curr.reversible_write_counter | ||
| ) | ||
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,105 @@ | ||
| import pytest | ||
|
|
||
| from zkevm_specs.evm_circuit import ( | ||
| Block, | ||
| Bytecode, | ||
| CallContextFieldTag, | ||
| ExecutionState, | ||
| RWDictionary, | ||
| StepState, | ||
| Tables, | ||
| verify_steps, | ||
| ) | ||
| from zkevm_specs.util import Word | ||
| from zkevm_specs.util.param import INVALID_FIRST_BYTE_CONTRACT_CODE | ||
|
|
||
|
|
||
| TESTING_DATA = ( | ||
| (0, True), # is_root | ||
| (0, False), # not is_root | ||
| (200, True), # is_root | ||
| (200, False), # not is_root | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("offset, is_root", TESTING_DATA) | ||
| def test_error_invalid_creation_code(offset: int, is_root: bool): | ||
| bytecode = Bytecode().push32(32).push32(offset).return_() | ||
| bytecode_hash = Word(bytecode.hash()) | ||
|
|
||
| current_call_id = 1 if is_root else 2 | ||
| stack_pointer = 1023 | ||
| pc = 66 | ||
| reversible_write_counter = 2 | ||
| rw_counter = 3 if is_root is True else 15 | ||
|
|
||
| # Only need `size` from RETURN opcode | ||
| rw_table = RWDictionary(rw_counter).stack_read(current_call_id, stack_pointer, Word(offset)) | ||
|
|
||
| # assign invalid byte `0xEF` to the first byte of memory | ||
| rw_table.memory_read(current_call_id, offset, INVALID_FIRST_BYTE_CONTRACT_CODE) | ||
|
|
||
| rw_table.call_context_read(current_call_id, CallContextFieldTag.IsSuccess, 0) | ||
|
|
||
| if not is_root: | ||
| # fmt: off | ||
| rw_table \ | ||
| .call_context_read(current_call_id, CallContextFieldTag.CallerId, 1) \ | ||
| .call_context_read(1, CallContextFieldTag.IsRoot, False) \ | ||
| .call_context_read(1, CallContextFieldTag.IsCreate, True) \ | ||
| .call_context_read(1, CallContextFieldTag.CodeHash, bytecode_hash) \ | ||
| .call_context_read(1, CallContextFieldTag.ProgramCounter, pc + 1) \ | ||
| .call_context_read(1, CallContextFieldTag.StackPointer, 1024) \ | ||
| .call_context_read(1, CallContextFieldTag.GasLeft, 0) \ | ||
| .call_context_read(1, CallContextFieldTag.MemorySize, 0) \ | ||
| .call_context_read(1, CallContextFieldTag.ReversibleWriteCounter, reversible_write_counter) \ | ||
| .call_context_write(1, CallContextFieldTag.LastCalleeId, 2) \ | ||
| .call_context_write(1, CallContextFieldTag.LastCalleeReturnDataOffset, 0) \ | ||
| .call_context_write(1, CallContextFieldTag.LastCalleeReturnDataLength, 0) | ||
| # fmt: on | ||
|
|
||
| tables = Tables( | ||
| block_table=set(Block().table_assignments()), | ||
| tx_table=set(), | ||
| bytecode_table=set(bytecode.table_assignments()), | ||
| rw_table=set(rw_table.rws), | ||
| ) | ||
|
|
||
| verify_steps( | ||
| tables=tables, | ||
| steps=[ | ||
| StepState( | ||
| execution_state=ExecutionState.ErrorInvalidCreationCode, | ||
| rw_counter=rw_counter, | ||
| call_id=current_call_id, | ||
| is_root=is_root, | ||
| is_create=True, | ||
| code_hash=bytecode_hash, | ||
| program_counter=pc, | ||
| stack_pointer=stack_pointer, | ||
| gas_left=100, | ||
| memory_word_size=0, | ||
| reversible_write_counter=reversible_write_counter, | ||
| ), | ||
| StepState( | ||
| execution_state=ExecutionState.EndTx, | ||
| rw_counter=rw_table.rw_counter + reversible_write_counter, | ||
| call_id=1, | ||
| gas_left=0, | ||
| ) | ||
| if is_root is True | ||
| else StepState( | ||
| execution_state=ExecutionState.STOP, | ||
| rw_counter=rw_table.rw_counter + reversible_write_counter, | ||
| call_id=1, | ||
| is_root=is_root, | ||
| is_create=True, | ||
| code_hash=bytecode_hash, | ||
| program_counter=pc + 1, | ||
| stack_pointer=1024, | ||
| gas_left=0, | ||
| memory_word_size=0, | ||
| reversible_write_counter=reversible_write_counter, | ||
| ), | ||
| ], | ||
| ) |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to check that it's not a static call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't. If it's a
CREATEcallcontext and also a static call, it would hitErrorWriteProtectionfirst. Correct me if I misunderstand error states handling mechanism.