Skip to content

Commit 4a4e6a4

Browse files
tynesmds1maurelian
authored
feat: standard l2 genesis (#97)
Co-authored-by: Matt Solomon <matt@mattsolomon.dev> Co-authored-by: Maurelian <john@oplabs.co> Co-authored-by: Maurelian <maurelian@protonmail.ch>
1 parent 733d6a7 commit 4a4e6a4

File tree

1 file changed

+268
-0
lines changed

1 file changed

+268
-0
lines changed

protocol/standard-l2-genesis.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# Purpose
2+
3+
<!-- This section is also sometimes called “Motivations” or “Goals”. -->
4+
5+
<!-- It is fine to remove this section from the final document,
6+
but understanding the purpose of the doc when writing is very helpful. -->
7+
8+
# Summary
9+
10+
<!-- Most (if not all) documents should have a summary.
11+
While the length will likely be proportional to the length of the full document,
12+
the summary should be as succinct as possible. -->
13+
14+
The L2 predeploys are refactored in a way such that the network specific configuration
15+
is all sourced from a single location where it is ultimately set from L1 deposit transactions.
16+
Any initializable logic in the L2 predeploys is also removed, to make the deposit transaction
17+
based upgrade scheme simple to reason about.
18+
19+
This will accelerate the ability to ship secure software, as we will be able to get chains
20+
on the same versions of the software and know which versions work very well together.
21+
22+
# Problem Statement + Context
23+
24+
<!-- Describe the specific problem that the document is seeking to address as well
25+
as information needed to understand the problem and design space.
26+
If more information is needed on the costs of the problem,
27+
this is a good place to that information. -->
28+
29+
There is currently no good way to do releases of L2 predeploys. Historically, chains
30+
have launched with an arbitrary commit for their L2 genesis, making the block history integrity
31+
checks that are part of the superchain registry very difficult. We tell chains that are
32+
trying to launch to use governance approved L1 contracts, the same should apply for L2.
33+
34+
Given all of the work for making releases and upgrades nice for the L1 contracts and the client software,
35+
it is all a waste if we cannot also have good releases of the L2 predeploys.
36+
37+
Right now, OP Mainnet is running contracts at various versions of the software. It is actually very difficult
38+
to reproduce the exact OP Mainnet set of contracts being used. It would require cherry picking bytecode from many
39+
different commits. We run no tests against this particular combination of contracts. We believe it is safe
40+
given our development practices, but every time that we do want to do a release it results in a lot of time
41+
being spent trying to make sure that we are upgrading to a compatible set of contracts.
42+
43+
# Proposed Solution
44+
45+
<!-- A high level overview of the proposed solution.
46+
When there are multiple alternatives there should be an explanation
47+
of why one solution was picked over other solutions.
48+
As a rule of thumb, including code snippets (except for defining an external API)
49+
is likely too low level. -->
50+
51+
A WIP implementation can be found [here](https://github.com/ethereum-optimism/optimism/pull/12057).
52+
The specs can be found [here](https://github.com/ethereum-optimism/specs/tree/17ef36cdc3bb9893b206a93464122d56730d30fb/specs/protocol/holocene).
53+
54+
Similar to the L1 MCP project, we move all of the network specific configuration out of the
55+
individual contracts themselves and instead place all of it in a single place. Rather than using
56+
`sload` to read the values, the contracts will make a `CALL` to the L1Block contract. These values
57+
will be sourced from L1 via deposit transactions that come from the `SystemConfig.initialize` call.
58+
We need to make sure that the max deposit gas limit is at least able to fullfill these deposit
59+
transactions.
60+
61+
The general flow is as follows:
62+
63+
```mermaid
64+
graph LR
65+
subgraph L1
66+
SystemConfig -- "setConfig(uint8,bytes)" --> OptimismPortal
67+
end
68+
subgraph L2
69+
L1Block
70+
BaseFeeVault -- "baseFeeVaultConfig()(address,uint256,uint8)" --> L1Block
71+
SequencerFeeVault -- "sequencerFeeVaultConfig()(address,uint256,uint8)" --> L1Block
72+
L1FeeVault -- "l1FeeVaultConfig()(address,uint256,uint8)" --> L1Block
73+
L2CrossDomainMessenger -- "l1CrossDomainMessenger()(address)" --> L1Block
74+
L2StandardBridge -- "l1StandardBridge()(address)" --> L1Block
75+
L2ERC721Bridge -- "l1ERC721Bridge()(address)" --> L1Block
76+
OptimismMintableERC721Factory -- "remoteChainId()(uint256)" --> L1Block
77+
end
78+
OptimismPortal -- "setConfig(uint8,bytes)" --> L1Block
79+
```
80+
81+
This is taken from the [specs](https://github.com/ethereum-optimism/specs/blob/17ef36cdc3bb9893b206a93464122d56730d30fb/specs/protocol/holocene/predeploys.md) and misses the `L2ProxyAdmin`. The `L2ProxyAdmin` must also be deterministic
82+
and is explored in the following issue: https://github.com/ethereum-optimism/specs/issues/388. There is general
83+
consensus on using the `DEPOSITOR_ACCOUNT` as the owner.
84+
85+
When we do a contract release, we commit to that bytecode as part of consensus. That is the bytecode used
86+
with deposit transactions doing upgrades to the network. In the L2 genesis creation script, we could have
87+
a library for each release of the predeploys. The genesis script would take the bytecode from the library if
88+
configured for a specific hardfork at genesis, otherwise it would use the compiled source code. This gives
89+
us a lot of flexibility and simplicity when it comes to being able to recreate an L2 genesis deterministically.
90+
91+
The block history integrity check becomes as simple as observing that a 32 byte state root in the genesis
92+
block matches the expected value.
93+
94+
### Rationale Behind Certain Changes
95+
96+
#### SuperchainConfig "Upgrader" Role
97+
98+
The `upgrader` role can call the `OptimismPortal.upgrade(bytes memory data, uint32 gasLimit)` function
99+
and it emits a deposit tx from the `DEPOSITOR_ACCOUNT` that calls the `L2ProxyAdmin`. Sourcing the auth
100+
from the `SuperchainConfig` allows for simple management of this very important role, given that it impacts
101+
stage 1 status. This is meant to simplify operations by removing the aliased L1 `ProxyAdmin` owner
102+
being set as the L2 `ProxyAdmin`.
103+
104+
Since the the L1 and L2 `ProxyAdmin` contracts are intended to have the same owner, an additional
105+
improvement (which may be excluded to limit scope creep), would be to remove the `Ownable`
106+
dependency on the L1 `ProxyAdmin` contract, and instead have it read the
107+
`SuperchainConfig.upgrader()` role to authorize upgrades. This would also be in alignment with the
108+
Superchain strategy, as the Security Council should not manage the pause of chains which they are
109+
not also responsible for upgrading, and participation in the pause is a benefit that chains get when
110+
they join the Superchain ecosystem.Regardless, in order to preserve the existing auth model we MUST
111+
ensure that the `upgrader` is the same account as the current L1 ProxyAdmin owner.
112+
113+
The `data` and `gasLimit` are allowed to be specified since we don't fully know what sorts of calls we may have to do.
114+
We may only need to do simple `upgradeTo` calls, but we may also need to do `upgradeToAndCall`. To support the
115+
[liquidity migration](https://github.com/ethereum-optimism/design-docs/blob/4b62eb12eceb8e4867ac101134730102c0f5a989/protocol/superchainerc20/liquidity-migration.md), we need to backport storage slots into the `OptimismMintableERC20Factory`
116+
contract.
117+
118+
#### FeeAdmin role
119+
120+
The entity which is authorized to modify the various `FeeVault` configs must be able to vary from chain
121+
122+
to chain. Therefore a new `feeAdmin` role will be added to the `SystemConfig` contract. This role
123+
can call a new `SystemConfig.setFeeConfig()` function which forwards config updates to
124+
`OptimismPortal.setConfig()` with the appropriate `ConfigType`.
125+
126+
This role will be set in `SystemConfig.initialize()`, meaning that it can only be updated by an upgrade.
127+
128+
> [!NOTE]
129+
> We need to guarantee 100% backwards compatibility in the roles during the upgrade, so for example
130+
> the same 2/2 multisig that owns the L2 ProxyAdmin on base should be the FeeAdmin in base's
131+
> SystemConfig.
132+
133+
In summary:
134+
135+
1. The `FeeAdmin` can update the `FeeConfig`.
136+
2. The Upgrade Controller (aka [L1 ProxyAdmin Owner](https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/stage-1.md#configuration-of-safes)) Safe cand update the `FeeAdmin`.
137+
138+
#### L2ProxyAdmin
139+
140+
A new contract exists called the `L2ProxyAdmin`, it simply inherits from the `ProxyAdmin` and overrides the
141+
`owner()(address)` function to return `DEPOSITOR_ACCOUNT`.
142+
143+
Ideally we can remove the need for legacy proxy types since they don't exist on L2 eventually, but
144+
that is considered a bonus when we do get around to it.
145+
146+
#### SystemConfig
147+
148+
The `SystemConfig`'s `initialize()` function will be updated to:
149+
150+
- Accept a new `Roles` struct, composed of the existing `owner` address, and the new
151+
`feeAdmin` role.
152+
- Makes multiple calls to the OptimismPortal's `setConfig()` function to set the config values.
153+
154+
> [!IMPORTANT]
155+
> We should consider if there is a risk associated with 'resetting' these values. Similar to the OptimismPortal's
156+
> `DEFAULT_L2_SENDER` [reinit issue](https://github.com/ethereum-optimism/optimism/pull/8864).
157+
> I do not believe so as they are only modifiable in the `initializer` and so cannot be
158+
> changed in normal operation. However the current design will require that all
159+
> `SystemConfig` upgrades do not unintentionally modify the existing values.
160+
161+
The `SystemConfig` will also get the following new external methods which are only callable by the
162+
`feeAdmin`:
163+
164+
```solidity
165+
function setBaseFeeVaultConfig(address _recipient, uint256 _min, Types.WithdrawalNetwork _network) external;
166+
function setL1FeeVaultConfig(address _recipient, uint256 _min, Types.WithdrawalNetwork _network) external;
167+
function setSequencerFeeVaultConfig(address _recipient, uint256 _min, Types.WithdrawalNetwork _network) external;
168+
```
169+
170+
#### Initializable Predeploys Removed
171+
172+
All predeploys are no longer initializable. This allows for upgrades issued by deposit transactions to be very smooth.
173+
This impacts the following contracts:
174+
175+
- `CrossDomainMessenger`
176+
- `StandardBridge`
177+
- `ERC721Bridge`
178+
179+
#### CrossDomainMessenger
180+
181+
Since the `CrossDomainMessenger` is no longer `initializable` we need to slightly modify the semantics around
182+
the `xDomainMsgSender`. There is actually no need to set the value in storage during `initialize`, we could modify
183+
the semantics such that if its `address(0)` in storage, then return the default value, otherwise return the
184+
actual sender value. This should be safe since there is no way to be a sender from `address(0)`.
185+
186+
Given this insight and the fact that there is reentrancy check on `relayMessage`, it should be safe to use transient
187+
storage without a call depth context. There is an [open PR](https://github.com/ethereum-optimism/optimism/pull/12356) to migrate to solc `0.8.25`.
188+
189+
## Resource Usage
190+
191+
<!-- What is the resource usage of the proposed solution?
192+
Does it consume a large amount of computational resources or time? -->
193+
194+
The additional deposit gas is the only additional resource usage and its covered in the risks section
195+
at the bottom of this document.
196+
197+
This approach expands the ABI of the `L1Block` contract, meaning that automatically generated solidity dispatcher
198+
will binary search over the possible function selectors, consuming a bit more gas.
199+
200+
## Implications for the Predeploy Releases Process
201+
202+
- TODO: get clarity about how to package up L2 contracts releases. How will alternative clients consume the L2 Genesis?
203+
204+
### L2Genesis Generation
205+
206+
When a new predeploy release is created, the bytcode from each predeploy should be placed into a
207+
an new autogenerated library which resembles the following:
208+
209+
```solidity
210+
library HolocenePredeploys {
211+
bytes constant L1Block = hex"...";
212+
...
213+
}
214+
```
215+
216+
The `L2Genesis.s.sol` solidity script will have additional functionality so that it can
217+
optionally generate the L2 state from the current commit as it currently does using
218+
`vm.getDeployedCode()`, or retrieve the code from the specified library.
219+
220+
### Upgrade Process
221+
222+
Note that this design modifies how L2 upgrade auth is managed (moving that management into a single
223+
storage slot on L1), but does not change how upgrades to predeploy contracts are performed.
224+
Predeploy upgrades can still be done either via a `TransactionDeposited()` event, or a [network
225+
upgrade automation
226+
transaction](https://github.com/ethereum-optimism/specs/blob/9f7226be064be0c87f90cbc6be6b0a4b4f58656a/specs/protocol/derivation.md#network-upgrade-automation-transactions).
227+
228+
Routine hardforks should continue to prefer using network upgrade automation transactions, with
229+
deposit transaction upgrades being reserved for incident response or other extenuating circumstances.
230+
231+
# Alternatives Considered
232+
233+
<!-- List out a short summary of each possible solution that was considered.
234+
Comparing the effort of each solution -->
235+
236+
There is a long history of alternatives here.
237+
238+
Another option would be to embed these config values directly into the client software's config and have the client
239+
software create these deposit txs rather than the smart contracts. This is less flexible but comes with the tradeoff
240+
of additional required rollup config and doesn't solve the problem for existing chains. Existing chains would need a way
241+
to source this config, it would likely need to be hardcoded in the binary and that isn't super scalable.
242+
243+
# Risks & Uncertainties
244+
245+
<!-- An overview of what could go wrong.
246+
Also any open questions that need more work to resolve. -->
247+
248+
## Sequenced transactions on a fresh chain
249+
250+
There is a concern that the sequencer can include transactions before these values are set on L2.
251+
If we define the `SystemConfig.startBlock` as the [first block to start derivation in](https://github.com/ethereum-optimism/optimism/blob/d05fb505809717282d5cee7264a09d26002a4ddd/op-node/cmd/genesis/cmd.go#L174C30-L174C40),
252+
which is set on `SystemConfig.initialize` and also the deposit transactions that set these values are
253+
sent in the same block, then we should have the guarantee that no user transactions are included before
254+
the deposit transactions.
255+
256+
## Max Resource Limit on Deposit Transactions
257+
258+
There is a concern around the max deposit gas limit being too small so that the `SystemConfig` cannot
259+
deposit all of these values, we should have logic that reverts if the deposit gas limit is too small
260+
in the `setResourceConfig()` function's [sanity checks](https://github.com/ethereum-optimism/optimism/blob/feat/holocene-contracts/packages/contracts-bedrock/src/L1/SystemConfig.sol#L538-L556). Since the `ResourceConfig` can be modified during
261+
`initialize`, it should be sufficient to include testing to identify when the total cost of
262+
`setConfig()` calls will exceed the resource limit in a single block.
263+
264+
A related concern is that the `useGas()`
265+
[function](https://github.com/ethereum-optimism/optimism/blob/f99424ded3917ddc0c4ef14355d61e50a38d4d0d/packages/contracts-bedrock/src/L1/ResourceMetering.sol#L156)
266+
which is called in the `upgrade()` and `setConfig()` functions does not check that the max resource limit is not exceeded by
267+
the additional `prevBoughtGas`. This is in contrast with
268+
[`_metered()`](https://github.com/ethereum-optimism/optimism/blob/f99424ded3917ddc0c4ef14355d61e50a38d4d0d/packages/contracts-bedrock/src/L1/ResourceMetering.sol#L128C1-L132C10) which does check `prevBoughtGas`. This requires investigation.

0 commit comments

Comments
 (0)