Hi guys, Z from LimeChain.
Over the past 2 months we’ve encountered two use cases that are almost possible with the Bedrock update but not quite - both related to the L1 block hash part of the L1Block contract. We think that solving this problem would be valuable for the developer community. This post aims to outline two use-cases that call for the existence of historical L1 block hash as part of the L1Block.
Use case #1: Cross-rollup communication
One popular approach of the notoriously hard field of cross-rollup communication offers is proving that State X (represented in Storage A) has a certain value in another rollup. This happens by proxy of the L1 state.
For any L1 Block where the source rollup has already anchored a certain value in a storage slot, a prover can prove inside Optimism that the value exists and is exactly that.
The L1Block contract nicely offers the block hash of the latest L1 block and this allows access to the state tree root. This is enough to run trustless Merkle-Patricia Trie (MPT) verification of the source rollup storage slot value.
While in theory, this is correct, in practice any prover has just 15 seconds time window to gather all the necessary information and submit OP transaction - in 15 seconds the block hash present in the L1Block will change and the MPT will no longer be correct.
If there was a historical mapping of L1 block number to block hash (both of those are present in the L1Block at a certain time) the use-case would be practical as anyone can prove against any historical block.
Use Case #2: Proving Transactions or Receipts
In somewhat similar vain to the cross rollup communication via storage slot proofs, we can find another use-case - proving that a certain transaction happened inside L1, either by proving the transaction and its data, or its receipt.
While in use case #1, you can at least recalculate your MPT proof for the new block hash, in this use-case once your targeted block hash is gone, you no longer can trustlessly prove the transaction or the receipt. Remember that unlike storage that has “memory” passed in the next blocks, transactions and receipts are only stored in their block and not the subsequent ones.
This makes these 15 seconds critical, and any infra failure unrecoverable.
Solution A: The Ugly Workaround
The ugly solution one can create is “pinning” service. Every time the block hash updates, an automated script sends OP transaction that reads and saves the L1 block hash in a mapping against its L1 block number.
While this is a possible solution, this requires trust in the pinning service liveness, and has the potential for huge wast and somewhat unbounded costs.
Solution B: The Neat Solution
A neat solution would be for the historical block hashes to be part of the L1Block contract - they are already going through it. Instead of replacing them, just store them in a mapping.
Any feedback is deeply appreciated. If this discussion garners popularity, the team at LimeChain will be happy to contribute this to the OP stack.