owned this note
owned this note
Published
Linked with GitHub
# Call_context
## Intro
Call_context is used to track the correct call context that the memory, storage and stack ops are applied to.
## Data elements
```rust
struct CallContext {
msg_sender: EthAddress
gas_available: u256
gas_used_before_this_call: u256
// tells us if this transaction will throw or revert in the future
is_persistant: bool
is_static_call: bool
// a count of the number of storage updates that need to be undone.
revert_todo: u256
// This is the global counter minimum global counter that needs to be reverted.
gc_to_revert_to: u256
call_depth: u256
}
impl Commitable for CallContext {
fn commit(){
return (
msg_sender +
gas_available * r +
gas_used_before_this_call * r**2 +
is_persistant * r**3 +
is_static_call * r**4 +
revert_todo * r**5 +
gc_to_revert_to * r**6 +
call_depth * r**7
)
}
}
```
The call_context is a random linear combination of
1. msg.sender
2. gas_available
3. gas_used_before_this_call
4. is_persistant
5. is_static_call
6. revert_todo
7. gc_to_revert_to
8. last_call_context
r = hash(msg.sender .. last_call_context)
$call\_context = msg.sender + gas\_available * r .. last\_call\_context *r ^5$
## Terms
### is_presistant
Is a binary flag that tells us if this transaction will throw or revert in the future
### revert_todo
Is a count of the number of storage updates that need to be undone.
### gc_to_revert_to
This is the global counter minimum global counter that needs to be reverted.
## Reverts/throws
When we reach reverts/throws we need to do two things
1. Find how far back we need to revert
2. revert all storage operations until that point
> When we reach op `REVERT`, it is actually a special error case we should not only do above two things, but also refund left gas (other errors consume all left gas). See Q4 for all other errors.
> [name=han]
### How far do we revert
1. If gas_passed = gas available the two transactions are in the same revert_context and both will be reverted together.
TODO: Confirm this is all we need to check how far to revert ?
### Storage
When we are applying storage updates if the is_persitant flag = 1 nothing happens. We apply them as is.
If it is_persistant == 0 we need to preapare to revert every storage write we do. This is done by counting every storage update that is done. We excute all sotrage update as they are defined in the smart contract. When we get to the point where the revert happens we undo all these updates.
We basically force the prover to show us `todo_revert` storage writes that happened between `gc` and `gc_to_revert_to` .
To make this easier storage write bus mapping will contain the value before it was written and the value that was written. For each element in this list we set that storage value to the value_before for `todo_revert` elements with `gc` < = `gc_to_revert_to`
## Questions:
1. Does static call throw on state writes or does it let you make writes as long as they are not persistant ? Assuming it does not let you make writes if you do it throws. Would be good to confirm this
> In `STATICCALL`, it **reverts to snapshot** and **consumes all `gas_available`** when:
> 1. `op` are able to modify state (`SSTORE`, `LOGX`, `CREATEX`)
> 2. `CALL` with value
> [Reference](https://github.com/ethereum/go-ethereum/blob/f05419f0fb8c5328dca92ea9fb184d082300344a/core/vm/interpreter.go#L208-L217)
2. How does reverts get stopped does it just revert the current call or can it revert others ?
> It reverts others too. See this [example](https://github.com/ChihChengLiang/play-contracts/blob/main/contracts/RevertChild.sol). The parent delegate calls to child, who later revert a call to grandchild. The child's revert reverts the grandchild's state but not the parent's.
3. How does throw get stopped ? Does it just revert everything until there is a tx with more gas left ?
> `throw` is just `revert` without keeping gas left, which is due to compiler's behavior (and `throw` is removed in solidity 0.5), they both use `op` `REVERT` in the end.
> When any [errors](https://github.com/ethereum/go-ethereum/blob/master/core/vm/errors.go) happen, it always [reverts to the snapshot](https://github.com/ethereum/go-ethereum/blob/master/core/vm/evm.go#L224-L225) before `CALL`, `DELEGATECALL`, `STATICCALL`, and only error due to `REVERT` will keep the `gas_available`, others just consume all given gas.
4. Should storage be seperated by only address instead of call cotext?
If we do multiple call to a child contract and change its storage, we need their storage to be continue, for example, contract `A` tries to trigger `B.counter++` multiple times, if we seperated them by different call context, the result won't be correct.
> Yes
> [name=barryWhiteHat]
5. If call context is only for seperatation (or namespacing) in state circuit, why not just use the `gc` at `CALL`?
In the call context now, I can see only `gas_used_before_this_call` and `gc_to_revert_to` are unique, others are easily to collide with other calls. For example, I can have two identical call to same child contract.
> Call context is also used as a digest of parent call and last child call, so all the things we need to memorize should be inside call context.
6. When a call is going to end, we have an awkward situation to handle: callee doesn't explicitly halt (opcodes doesn't ended with `STOP`, `RETURN`, `REVERT`), then evm will pad it with an explicit `STOP`.
In each `op_lookup(addr, pc, op)` (not sure if it's the function signature in you idea), we need prover to explicitly tell us if we should do the lookup or not. If we do, we lookup for correct op, otherwise we know it's padded `STOP` and switch to parent context.