-
Notifications
You must be signed in to change notification settings - Fork 77
Spec for opcode BALANCE
#48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7353337
b0ee8b7
44d188d
4ce96c6
a4263b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # ADDRESS opcode | ||
|
|
||
| ## Procedure | ||
|
|
||
| The `ADDRESS` opcode gets the address of currently executing account. | ||
|
|
||
| ## EVM behaviour | ||
|
|
||
| The `ADDRESS` opcode loads the callee address (20 bytes of data) from call | ||
| context, then pushes this address to the stack. | ||
|
|
||
| ## Circuit behaviour | ||
|
|
||
| 1. Construct call context table in rw table | ||
| 2. Do busmapping lookup for call context callee read operation | ||
| 3. Do busmapping lookup for stack write operation | ||
|
|
||
| ## Constraints | ||
|
|
||
| 1. opId = 0x30 | ||
| 2. State transition: | ||
| - gc + 2 (1 stack write, 1 call context read) | ||
| - stack_pointer - 1 | ||
| - pc + 1 | ||
| - gas - 2 | ||
| 3. Lookups: 2 | ||
| - `address` is in the rw table {call context, call ID, callee} | ||
| - `address` is on top of stack | ||
|
|
||
| ## Exceptions | ||
|
|
||
| 1. stack overflow: stack is full, stack pointer = 0 | ||
| 2. out of gas: remaining gas is not enough | ||
|
|
||
| ## Code | ||
|
|
||
| Please refer to `src/zkevm_specs/evm/execution/address.py`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # 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, 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 | ||
| - `address` is popped from 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. | ||
| - `balance` is read from the given account. Set to 0 if it doesn't exist. | ||
| - the `balance` result is pushed onto 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`. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| from ...util import N_BYTES_ACCOUNT_ADDRESS | ||
| from ..instruction import Instruction, Transition | ||
| from ..table import CallContextFieldTag | ||
| from ..opcode import Opcode | ||
|
|
||
|
|
||
| def address(instruction: Instruction): | ||
| opcode = instruction.opcode_lookup(True) | ||
| instruction.constrain_equal(opcode, Opcode.ADDRESS) | ||
|
|
||
| # Get callee address from call context and compare with stack top after push. | ||
| instruction.constrain_equal( | ||
| instruction.call_context_lookup(CallContextFieldTag.CalleeAddress), | ||
| # NOTE: We can replace this with N_BYTES_WORD if we reuse the 32 byte RLC constraint in | ||
| # all places. See: https://github.com/privacy-scaling-explorations/zkevm-specs/issues/101 | ||
| instruction.rlc_to_fq(instruction.stack_push(), N_BYTES_ACCOUNT_ADDRESS), | ||
| ) | ||
|
|
||
| instruction.step_state_transition_in_same_context( | ||
| opcode, | ||
| rw_counter=Transition.delta(2), | ||
| program_counter=Transition.delta(1), | ||
| stack_pointer=Transition.delta(-1), | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| from ...util import EXTRA_GAS_COST_ACCOUNT_COLD_ACCESS, FQ, N_BYTES_ACCOUNT_ADDRESS | ||
| 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()) | ||
|
|
||
| balance = instruction.account_read(address, AccountFieldTag.Balance) | ||
|
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. not sure if need to constrain "If the given account
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. It seems that the balance is constrained to be equal with If the given account doesn't exist,
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. After discussing with @DreamWuGit , will send PR to upstream with this question in comment. |
||
| instruction.constrain_equal(instruction.stack_push(), balance) | ||
|
|
||
| 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)), | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| import pytest | ||
|
|
||
| from zkevm_specs.evm import ( | ||
| Bytecode, | ||
| CallContextFieldTag, | ||
| ExecutionState, | ||
| RWDictionary, | ||
| StepState, | ||
| Tables, | ||
| verify_steps, | ||
| ) | ||
| from zkevm_specs.util import RLC, U160, rand_address, rand_fq | ||
|
|
||
|
|
||
| TESTING_DATA = ( | ||
| 0x00, | ||
| 0x10, | ||
| 0x030201, | ||
| 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, | ||
| rand_address(), | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("address", TESTING_DATA) | ||
| def test_address(address: U160): | ||
| randomness = rand_fq() | ||
|
|
||
| bytecode = Bytecode().address() | ||
| bytecode_hash = RLC(bytecode.hash(), randomness) | ||
|
|
||
| tables = Tables( | ||
| block_table=set(), | ||
| tx_table=set(), | ||
| bytecode_table=set(bytecode.table_assignments(randomness)), | ||
| rw_table=set( | ||
| RWDictionary(9) | ||
| .call_context_read(1, CallContextFieldTag.CalleeAddress, address) | ||
| .stack_write(1, 1023, RLC(address, randomness)) | ||
| .rws | ||
| ), | ||
| ) | ||
|
|
||
| verify_steps( | ||
| randomness=randomness, | ||
| tables=tables, | ||
| steps=[ | ||
| StepState( | ||
| execution_state=ExecutionState.ADDRESS, | ||
| rw_counter=9, | ||
| call_id=1, | ||
| is_root=True, | ||
| is_create=False, | ||
| code_hash=bytecode_hash, | ||
| program_counter=0, | ||
| stack_pointer=1024, | ||
| gas_left=2, | ||
| ), | ||
| StepState( | ||
| execution_state=ExecutionState.STOP, | ||
| rw_counter=11, | ||
| call_id=1, | ||
| is_root=True, | ||
| is_create=False, | ||
| code_hash=bytecode_hash, | ||
| program_counter=1, | ||
| stack_pointer=1023, | ||
| gas_left=0, | ||
| ), | ||
| ], | ||
| ) |
Uh oh!
There was an error while loading. Please reload this page.