Overview presentation: https://docs.google.com/presentation/d/1nbxJ3_xaD1QHaRBQaa4Q8BOjAn-UAV-1NN9D3Qzx28U/edit?usp=sharing
Main goal for the node is to run both an L1 node and an L2 node in the same program. This allows same latency reading/writing for both the L1 state and the L2 state.
Additionally, it needs to be possible to support multiple L2s as well. This way the node can be configured to run a full node for all selected chains, which will be very helpful for builders running beefy machines that want to validate/build cross L2 transactions.
It also allows us to be flexible about which chains nodes are required to run as these support direct state access without any additional onchain data. For now, all nodes would be required to run an L1 node and only the L2 they are interested in.
There is (for now) a single main precompile being introduced: XCALLOPTIONS(version: uint16, chainID: uint, sandbox: bool, txOrigin: address, msgSender: address, blockHash: bytes32, proof: bytes)
This call context modifies the behavior of the existing CALL
/DELEGATECALL
/STATICCALL
opcodes. This allows writing standard solidity code to do these special calls instead of having to manually encode the input data and decode the return data which is very inconvenient and tedious because it’s not possible to make wrappers for these in a general way that make them as easy to use as the standard solidity code
This precompile sets additional CALL parameters in the current frame of execution which stick around until the next CALL
(basically extending the CALL
opcode without changing the inputs). When that call has processed, the values are reset.
version
: A version to make the precompile upgradeable, currently only 1
is acceptedchainID
: The target chainID of the callsandbox
: If this is a sandbox call or not. When the call is sandboxed, all changes done in the call are reverted, even if the call was successful (and so the return value still needs to be returned as expected!).txOrigin
: If sandbox is true, this will be the tx.origin
used in the new call contextmsgSender
: If sandbox is true, this will be the msg.sender
used in the new call contextblockHash
:
0x0
, will use the data in proof
to be able to execute the call, with the proofs being validated against the specified blockhash0x0
: For supported chainID
s, will execute the call without requiring external proof
data (as the state is directly accessible and stored in the node)proof
: Will be used for calls for chainIDs that do not support witness generation from the stored state in the node. Required to contain all accessed state tree data with accompanying Merkle proofs, contract code, previous block hashes. This is the same data required for chains with supported internal witness generation in a light execution client, so ideally just uses the same format. Proof format: See the proof data section what to do here.tx.origin
and msg.sender
can be specified for sandbox calls to emulate behavior depending on those values to have specific values. In sandbox mode, msg.value
can be any value, the balance check is skipped.
The gas limit is respected for the call these options will be used for, because gas is still used like usual when doing these called (well, for the stateless calls in the future a zk proof may be used, but for now that’s not the case). Specifying a fixed gas limit is also a great way to make a stateless call completely deterministic (if that would be a problem for the call being done).
Events can be emitted during these calls. However, events are conceptually linked to state changes. And so events emitted in sandboxed calls should NOT be part of the L2 transaction receipt. Only in calls that can actually modify state should the event be logged.
If blockhash == 0 and chainID is in the set of supported direct state access chains, proof
can be empty and the node needs to support the generation of its own Merkle proofs for all accessed state. Otherwise, proof
needs to contain proofs required for all state accesses (storage, code, accounts, blockhashes etc…)