diff --git a/RIPS/rip-7728.md b/RIPS/rip-7728.md index 464bb0d..55cfb07 100644 --- a/RIPS/rip-7728.md +++ b/RIPS/rip-7728.md @@ -61,7 +61,8 @@ The introduction of the `L1SLOAD` precompile may increase the requirement for th **Prerequisite 2**: The L2 sequencer has a notion of the *latest seen L1 block*, which is deterministic over all L2 nodes, i.e. it is part of the L2 state machine. The exact mechanism is not in scope for this RIP. -**Implementation**: When the L2 node encounters a call to the `L1SLOAD` precompiled contract, it first verifies that its input is well-formed. It then retrieves its latest seen L1 block number `l1BlockNumber` and sends an RPC query `eth_getStorageAt(address, storageKey, l1BlockNumber)` to the L1 node. Finally, it writes the received storage value to the designated output buffer. +**Implementation**: When the L2 sequencer encounters a call to the `L1SLOAD` precompiled contract, it first verifies that its input is well-formed. It then retrieves its latest seen L1 block number `l1BlockNumber` and sends an RPC query `eth_getStorageAt(address, storageKey, l1BlockNumber)` to the L1 node, and writes the received storage value to the designated output buffer. Finally, it writes all the received storage values of all `L1SLOAD` it encountered in the block, in order, to the end of the L2 batch it submits on L1. \ +During batch validation, when the L2 node encounters a call to the `L1SLOAD` precompile, it retrieves the value from the `L1SLOAD` values array in end of the batch from its respective position in the array, and writes it to the designated output buffer. ### Errors @@ -98,13 +99,10 @@ function loadFromL1(address l1Address, uint256 key1, uint256 key2) public view r ### Which L1 block does `L1SLOAD` read the storage value at? -According to the specification defined above, `L1SLOAD` returns the storage value at the latest L1 block known to the L2 sequencer. There are two related issues: -- How to guarantee the consistent return value of `L1SLOAD` -- The risk of loading the storage value from a very stale L1 state. +According to the specification defined above, `L1SLOAD` returns the storage value at the latest L1 block known to the L2 sequencer. \ +There is one related issue - The risk of loading the storage value from a very stale L1 state. -First, to ensure the return value is consistent during transaction replay, L2 chains should provide a system contract that stores the information of the latest L1 block known to L2 sequencer. Optimism already provides a predeployed contract [`L1Block`](https://docs.optimism.io/stack/protocol/rollup/smart-contracts#l1block). Scroll has a new system contract [design](https://www.notion.so/scrollzkp/L1Blocks-System-Contract-b1a137eacea74819a3fa57d7d6e52498?pvs=4) that trustlessly imports the L1 block information and also stores other header fields such as state root, timestamp, RANDAO, etc. - -Second, L2 protocols determine the L1 block import delay at their own discretion. To make `L1SLOAD` more useful and reduce the risk of reading stale L1 storage states, we argue that the import delay should not be too long, e.g., waiting for finalized state that usually takes about 18-19 minutes. We suggest to wait for around 10 L1 block confirmations that has low risk of Ethereum chain re-organization while the import delay is fairly short (around 2 min). To adopt this, the L2 sequencers need to be capable of handling the situation when there is a long L1 chain re-organization. Furthermore, if the application is sensitive to stale storage reads, developers can limit the difference between the L1 block number retrieved from the system contract and the latest L1 block number per application requirement. +L2 protocols determine the L1 block import delay at their own discretion. To make `L1SLOAD` more useful and reduce the risk of reading stale L1 storage states, we argue that the import delay should not be too long, e.g., waiting for finalized state that usually takes about 18-19 minutes. We suggest to wait for around 10 L1 block confirmations that has low risk of Ethereum chain re-organization while the import delay is fairly short (around 2 min). To adopt this, the L2 sequencers need to be capable of handling the situation when there is a long L1 chain re-organization. Furthermore, if the application is sensitive to stale storage reads, developers can limit the difference between the L1 block number retrieved from the system contract and the latest L1 block number per application requirement. ### The overhead to L2 sequencers from additional RPC latency The `L1SLOAD` precompile introduces risks of additional RPC latency and intermittent RPC errors. Both risks can be mitigated by running a L1 node in the same cluster as the L2 sequencer. It is preferrable for a L2 operator to run their own L1 node instead of using third party to get better security and reliability. We will perform more benchmarks to quantify the latency overhead in such setting.