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 BALANCE
#248
Merged
ed255
merged 16 commits into
privacy-ethereum:master
from
scroll-tech:feat/opcode-balance
Nov 3, 2022
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
7353337
Implement opcode `BALANCE`.
silathdiir b0ee8b7
Add Markdown doc of opcode `BALANCE`.
silathdiir a4263b0
Merge remote-tracking branch 'upstream/master' into feat/opcode-balance
silathdiir 5071cbe
Update specs/opcode/31BALANCE.md
silathdiir 270b261
Update `bus-mapping lookups` in Markdown.
silathdiir 931bb88
Fix to get `nonce`, `balance` and `code_hash` to check account
silathdiir 4ddc636
Fix failed test cases.
silathdiir 55a486e
Merge remote-tracking branch 'upstream/master' into feat/opcode-balance
silathdiir e1571d4
Fix to check account existence by `AccountFieldTag.NonExisting`.
silathdiir 6bf2c5c
Revert `extcodehash` fix.
silathdiir 21aa597
Fix to pass witness value `exists` as `aux_data`, and only do `balanc…
silathdiir a84d4ae
Fix reversible_write_counter to `10` with a comment.
silathdiir eeb57ba
Update Markdown doc.
silathdiir c3a6026
Update tests/evm/test_balance.py
silathdiir 2b8f73d
Update src/zkevm_specs/evm/execution/balance.py
silathdiir b8be627
Update specs/opcode/31BALANCE.md
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 @@ | ||
| # BALANCE opcode | ||
|
|
||
| ## Procedure | ||
|
|
||
| The `BALANCE` opcode gets balance of the given account. | ||
|
|
||
| ## EVM behaviour | ||
|
|
||
| The `BALANCE` opcode pops `address` (20 bytes of data) off the stack and pushes | ||
| the balance 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 = 0x31 | ||
| 2. State transition: | ||
| - gc + 7 (1 stack read, 1 stack write, 3 call context reads, 1 account reads, | ||
| 1 transaction access list write) | ||
| - stack_pointer + 0 (one pop and one push) | ||
| - pc + 1 | ||
| - gas: | ||
| - the accessed `address` is warm: GAS_COST_WARM_ACCESS | ||
| - the accessed `address` is cold: GAS_COST_ACCOUNT_COLD_ACCESS | ||
| 3. Lookups: 7 busmapping lookups | ||
| - `address` is at top of the stack. | ||
| - 3 reads 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 `balance`. Otherwise lookup | ||
| account non-existing proof. | ||
| - The BALANCE 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/balance.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,34 @@ | ||
| from ...util import EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, FQ, N_BYTES_ACCOUNT_ADDRESS, RLC | ||
| from ..instruction import Instruction, Transition | ||
| from ..opcode import Opcode | ||
| from ..table import AccountFieldTag, CallContextFieldTag | ||
|
|
||
|
|
||
| def balance(instruction: Instruction): | ||
| opcode = instruction.opcode_lookup(True) | ||
| instruction.constrain_equal(opcode, Opcode.BALANCE) | ||
|
|
||
| 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 == 0: # 1 - exists == 1 | ||
| instruction.account_read(address, AccountFieldTag.NonExisting) | ||
|
|
||
| balance = instruction.account_read(address, AccountFieldTag.Balance) if exists == 1 else RLC(0) | ||
| instruction.constrain_equal( | ||
| instruction.select(exists, balance.expr(), FQ(0)), | ||
| instruction.stack_push(), | ||
| ) | ||
|
|
||
| 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)), | ||
| ) |
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,124 @@ | ||
| import pytest | ||
|
|
||
| from zkevm_specs.evm import ( | ||
| AccountFieldTag, | ||
| Block, | ||
| Bytecode, | ||
| CallContextFieldTag, | ||
| ExecutionState, | ||
| RWDictionary, | ||
| StepState, | ||
| Tables, | ||
| verify_steps, | ||
| ) | ||
| from zkevm_specs.util import ( | ||
| EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, | ||
| GAS_COST_WARM_ACCESS, | ||
| RLC, | ||
| U160, | ||
| U256, | ||
| rand_address, | ||
| rand_bytes, | ||
| rand_fq, | ||
| rand_range, | ||
| rand_word, | ||
| ) | ||
|
|
||
| TESTING_DATA = [ | ||
| (0x30000, 0, 0, True, True), | ||
| (0x30000, 0, 0, False, True), | ||
| (0x30000, 200, 1, True, True), | ||
| (0x30000, 200, 1, False, True), | ||
| ( | ||
| rand_address(), | ||
| rand_word(), | ||
| rand_range(2), | ||
| rand_range(2) == 0, | ||
| True, # persistent call | ||
| ), | ||
| ( | ||
| rand_address(), | ||
| rand_word(), | ||
| rand_range(2), | ||
| rand_range(2) == 0, | ||
| False, # reverted call | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("address, balance, exists, is_warm, is_persistent", TESTING_DATA) | ||
| def test_balance(address: U160, balance: U256, exists: int, is_warm: bool, is_persistent: bool): | ||
| randomness = rand_fq() | ||
|
|
||
| result = balance if exists == 1 else 0 | ||
|
|
||
| tx_id = 1 | ||
| call_id = 1 | ||
|
|
||
| # 9 + 1 reversible operation (the account access list write) | ||
| rw_counter_end_of_reversion = 0 if is_persistent else 10 | ||
| 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.Balance, RLC(balance, randomness)) | ||
| else: | ||
| rw_dictionary.account_read( | ||
| address, AccountFieldTag.NonExisting, RLC(1 - exists, randomness) | ||
| ) | ||
|
|
||
| rw_table = set(rw_dictionary.stack_write(call_id, 1023, RLC(result, randomness)).rws) | ||
|
|
||
| bytecode = Bytecode().balance() | ||
| 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.BALANCE, | ||
| 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, | ||
| ), | ||
| 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, | ||
| ), | ||
| ], | ||
| ) | ||
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.
Is
reversible_write_counteralways zero?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 tried to review reversible-write-reversion doc. It seems that
reversible_write_countershould be initialized to0.And I referenced the code of test_extcodecopy.py and test_extcodehash.py.
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 readed the document for first time, and, ups, it's tricky!
BTW I changed with random values this lines in
SLOADand test are passinghttps://github.com/privacy-scaling-explorations/zkevm-specs/blob/947e012c6d8d1566b295f8bfd717a82eeb475e18/tests/evm/test_sload.py#L56-L57
so are we missing something here? (cc/ @ed255 @ChihChengLiang )
unsure about this revertion parameters, but for the rest, LGTM
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 in this test the
rw_counter_end_of_reversionandreversible_write_counterare not super relevant because we're only checking the constraints of theBALANCEstep with theSTOP/REVERTstep as next. But we're not checking the constraints of theSTOP/REVERTstep.The constraints related to
rw_counter_end_of_reversionandreversible_write_counterare checked during theREVERTstep, so even if we have wrong values here the test would pass.As @silathdiir
reversible_write_countershould start at 0 when a call begins. I thinkrw_counter_end_of_reversionshould be 9 + the count of reversible operations that happened (which I think should be 1 for the account access list write).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.
Fixed
rw_counter_end_of_reversionto10with comment: