/
SystemDictator.sol
484 lines (429 loc) · 17.8 KB
/
SystemDictator.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { L1StandardBridge } from "../L1/L1StandardBridge.sol";
import { L1ChugSplashProxy } from "../legacy/L1ChugSplashProxy.sol";
import { AddressManager } from "../legacy/AddressManager.sol";
import { Proxy } from "../universal/Proxy.sol";
import { ProxyAdmin } from "../universal/ProxyAdmin.sol";
import { OptimismMintableERC20Factory } from "../universal/OptimismMintableERC20Factory.sol";
import { PortalSender } from "./PortalSender.sol";
import { SystemConfig } from "../L1/SystemConfig.sol";
import { ResourceMetering } from "../L1/ResourceMetering.sol";
import { Constants } from "../libraries/Constants.sol";
/**
* @title SystemDictator
* @notice The SystemDictator is responsible for coordinating the deployment of a full Bedrock
* system. The SystemDictator is designed to support both fresh network deployments and
* upgrades to existing pre-Bedrock systems.
*/
contract SystemDictator is OwnableUpgradeable {
/**
* @notice Basic system configuration.
*/
struct GlobalConfig {
AddressManager addressManager;
ProxyAdmin proxyAdmin;
address controller;
address finalOwner;
}
/**
* @notice Set of proxy addresses.
*/
struct ProxyAddressConfig {
address l2OutputOracleProxy;
address optimismPortalProxy;
address l1CrossDomainMessengerProxy;
address l1StandardBridgeProxy;
address optimismMintableERC20FactoryProxy;
address l1ERC721BridgeProxy;
address systemConfigProxy;
}
/**
* @notice Set of implementation addresses.
*/
struct ImplementationAddressConfig {
L2OutputOracle l2OutputOracleImpl;
OptimismPortal optimismPortalImpl;
L1CrossDomainMessenger l1CrossDomainMessengerImpl;
L1StandardBridge l1StandardBridgeImpl;
OptimismMintableERC20Factory optimismMintableERC20FactoryImpl;
L1ERC721Bridge l1ERC721BridgeImpl;
PortalSender portalSenderImpl;
SystemConfig systemConfigImpl;
}
/**
* @notice Dynamic L2OutputOracle config.
*/
struct L2OutputOracleDynamicConfig {
uint256 l2OutputOracleStartingBlockNumber;
uint256 l2OutputOracleStartingTimestamp;
}
/**
* @notice Values for the system config contract.
*/
struct SystemConfigConfig {
address owner;
uint256 overhead;
uint256 scalar;
bytes32 batcherHash;
uint64 gasLimit;
uint256 baseFee;
address unsafeBlockSigner;
ResourceMetering.ResourceConfig resourceConfig;
}
/**
* @notice Combined system configuration.
*/
struct DeployConfig {
GlobalConfig globalConfig;
ProxyAddressConfig proxyAddressConfig;
ImplementationAddressConfig implementationAddressConfig;
SystemConfigConfig systemConfigConfig;
}
/**
* @notice Step after which exit 1 can no longer be used.
*/
uint8 public constant EXIT_1_NO_RETURN_STEP = 3;
/**
* @notice Step where proxy ownership is transferred.
*/
uint8 public constant PROXY_TRANSFER_STEP = 4;
/**
* @notice System configuration.
*/
DeployConfig public config;
/**
* @notice Dynamic configuration for the L2OutputOracle.
*/
L2OutputOracleDynamicConfig public l2OutputOracleDynamicConfig;
/**
* @notice Dynamic configuration for the OptimismPortal. Determines
* if the system should be paused when initialized.
*/
bool public optimismPortalDynamicConfig;
/**
* @notice Current step;
*/
uint8 public currentStep;
/**
* @notice Whether or not dynamic config has been set.
*/
bool public dynamicConfigSet;
/**
* @notice Whether or not the deployment is finalized.
*/
bool public finalized;
/**
* @notice Whether or not the deployment has been exited.
*/
bool public exited;
/**
* @notice Address of the old L1CrossDomainMessenger implementation.
*/
address public oldL1CrossDomainMessenger;
/**
* @notice Checks that the current step is the expected step, then bumps the current step.
*
* @param _step Current step.
*/
modifier step(uint8 _step) {
require(!finalized, "SystemDictator: already finalized");
require(!exited, "SystemDictator: already exited");
require(currentStep == _step, "SystemDictator: incorrect step");
_;
currentStep++;
}
/**
* @notice Constructor required to ensure that the implementation of the SystemDictator is
* initialized upon deployment.
*/
constructor() {
ResourceMetering.ResourceConfig memory rcfg = Constants.DEFAULT_RESOURCE_CONFIG();
// Using this shorter variable as an alias for address(0) just prevents us from having to
// to use a new line for every single parameter.
address zero = address(0);
initialize(
DeployConfig(
GlobalConfig(AddressManager(zero), ProxyAdmin(zero), zero, zero),
ProxyAddressConfig(zero, zero, zero, zero, zero, zero, zero),
ImplementationAddressConfig(
L2OutputOracle(zero),
OptimismPortal(payable(zero)),
L1CrossDomainMessenger(zero),
L1StandardBridge(payable(zero)),
OptimismMintableERC20Factory(zero),
L1ERC721Bridge(zero),
PortalSender(zero),
SystemConfig(zero)
),
SystemConfigConfig(zero, 0, 0, bytes32(0), 0, 0, zero, rcfg)
)
);
}
/**
* @param _config System configuration.
*/
function initialize(DeployConfig memory _config) public initializer {
config = _config;
currentStep = 1;
__Ownable_init();
_transferOwnership(config.globalConfig.controller);
}
/**
* @notice Allows the owner to update dynamic config.
*
* @param _l2OutputOracleDynamicConfig Dynamic L2OutputOracle config.
* @param _optimismPortalDynamicConfig Dynamic OptimismPortal config.
*/
function updateDynamicConfig(
L2OutputOracleDynamicConfig memory _l2OutputOracleDynamicConfig,
bool _optimismPortalDynamicConfig
) external onlyOwner {
l2OutputOracleDynamicConfig = _l2OutputOracleDynamicConfig;
optimismPortalDynamicConfig = _optimismPortalDynamicConfig;
dynamicConfigSet = true;
}
/**
* @notice Configures the ProxyAdmin contract.
*/
function step1() public onlyOwner step(1) {
// Set the AddressManager in the ProxyAdmin.
config.globalConfig.proxyAdmin.setAddressManager(config.globalConfig.addressManager);
// Set the L1CrossDomainMessenger to the RESOLVED proxy type.
config.globalConfig.proxyAdmin.setProxyType(
config.proxyAddressConfig.l1CrossDomainMessengerProxy,
ProxyAdmin.ProxyType.RESOLVED
);
// Set the implementation name for the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.setImplementationName(
config.proxyAddressConfig.l1CrossDomainMessengerProxy,
"BVM_L1CrossDomainMessenger"
);
// Set the L1StandardBridge to the CHUGSPLASH proxy type.
config.globalConfig.proxyAdmin.setProxyType(
config.proxyAddressConfig.l1StandardBridgeProxy,
ProxyAdmin.ProxyType.CHUGSPLASH
);
// Upgrade and initialize the SystemConfig so the Sequencer can start up.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.systemConfigProxy),
address(config.implementationAddressConfig.systemConfigImpl),
abi.encodeCall(
SystemConfig.initialize,
(
config.systemConfigConfig.owner,
config.systemConfigConfig.overhead,
config.systemConfigConfig.scalar,
config.systemConfigConfig.batcherHash,
config.systemConfigConfig.gasLimit,
config.systemConfigConfig.baseFee,
config.systemConfigConfig.unsafeBlockSigner,
config.systemConfigConfig.resourceConfig
)
)
);
}
/**
* @notice Pauses the system by shutting down the L1CrossDomainMessenger and setting the
* deposit halt flag to tell the Sequencer's DTL to stop accepting deposits.
*/
function step2() public onlyOwner step(2) {
// Store the address of the old L1CrossDomainMessenger implementation. We will need this
// address in the case that we have to exit early.
oldL1CrossDomainMessenger = config.globalConfig.addressManager.getAddress(
"BVM_L1CrossDomainMessenger"
);
// Temporarily brick the L1CrossDomainMessenger by setting its implementation address to
// address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing
// the L1CrossDomainMessenger via pause() because it can be easily reverted.
config.globalConfig.addressManager.setAddress("BVM_L1CrossDomainMessenger", address(0));
// Set the DTL shutoff block, which will tell the DTL to stop syncing new deposits from the
// CanonicalTransactionChain. We do this by setting an address in the AddressManager
// because the DTL already has a reference to the AddressManager and this way we don't also
// need to give it a reference to the SystemDictator.
config.globalConfig.addressManager.setAddress(
"DTL_SHUTOFF_BLOCK",
address(uint160(block.number))
);
}
/**
* @notice Removes deprecated addresses from the AddressManager.
*/
function step3() public onlyOwner step(EXIT_1_NO_RETURN_STEP) {
// Remove all deprecated addresses from the AddressManager
string[17] memory deprecated = [
"BVM_CanonicalTransactionChain",
"BVM_L2CrossDomainMessenger",
"BVM_DecompressionPrecompileAddress",
"BVM_Sequencer",
"BVM_Proposer",
"BVM_ChainStorageContainer-CTC-batches",
"BVM_ChainStorageContainer-CTC-queue",
"BVM_CanonicalTransactionChain",
"BVM_StateCommitmentChain",
"BVM_BondManager",
"BVM_ExecutionManager",
"BVM_FraudVerifier",
"BVM_StateManagerFactory",
"BVM_StateTransitionerFactory",
"BVM_SafetyChecker",
"BVM_L1MultiMessageRelayer",
"BondManager"
];
for (uint256 i = 0; i < deprecated.length; i++) {
config.globalConfig.addressManager.setAddress(deprecated[i], address(0));
}
}
/**
* @notice Transfers system ownership to the ProxyAdmin.
*/
function step4() public onlyOwner step(PROXY_TRANSFER_STEP) {
// Transfer ownership of the AddressManager to the ProxyAdmin.
config.globalConfig.addressManager.transferOwnership(
address(config.globalConfig.proxyAdmin)
);
// Transfer ownership of the L1StandardBridge to the ProxyAdmin.
L1ChugSplashProxy(payable(config.proxyAddressConfig.l1StandardBridgeProxy)).setOwner(
address(config.globalConfig.proxyAdmin)
);
// Transfer ownership of the L1ERC721Bridge to the ProxyAdmin.
Proxy(payable(config.proxyAddressConfig.l1ERC721BridgeProxy)).changeAdmin(
address(config.globalConfig.proxyAdmin)
);
}
/**
* @notice Upgrades and initializes proxy contracts.
*/
function step5() public onlyOwner step(5) {
// Dynamic config must be set before we can initialize the L2OutputOracle.
require(dynamicConfigSet, "SystemDictator: dynamic oracle config is not yet initialized");
// Upgrade and initialize the L2OutputOracle.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l2OutputOracleProxy),
address(config.implementationAddressConfig.l2OutputOracleImpl),
abi.encodeCall(
L2OutputOracle.initialize,
(
l2OutputOracleDynamicConfig.l2OutputOracleStartingBlockNumber,
l2OutputOracleDynamicConfig.l2OutputOracleStartingTimestamp
)
)
);
// Upgrade and initialize the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.optimismPortalProxy),
address(config.implementationAddressConfig.optimismPortalImpl),
abi.encodeCall(OptimismPortal.initialize, (optimismPortalDynamicConfig))
);
// Upgrade the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1CrossDomainMessengerProxy),
address(config.implementationAddressConfig.l1CrossDomainMessengerImpl)
);
// Try to initialize the L1CrossDomainMessenger, only fail if it's already been initialized.
try
L1CrossDomainMessenger(config.proxyAddressConfig.l1CrossDomainMessengerProxy)
.initialize()
{
// L1CrossDomainMessenger is the one annoying edge case difference between existing
// networks and fresh networks because in existing networks it'll already be
// initialized but in fresh networks it won't be. Try/catch is the easiest and most
// consistent way to handle this because initialized() is not exposed publicly.
} catch Error(string memory reason) {
require(
keccak256(abi.encodePacked(reason)) ==
keccak256("Initializable: contract is already initialized"),
string.concat("SystemDictator: unexpected error initializing L1XDM: ", reason)
);
} catch {
revert("SystemDictator: unexpected error initializing L1XDM (no reason)");
}
// Transfer ETH from the L1StandardBridge to the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
address(config.implementationAddressConfig.portalSenderImpl),
abi.encodeCall(PortalSender.donate, ())
);
// Upgrade the L1StandardBridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
address(config.implementationAddressConfig.l1StandardBridgeImpl)
);
// Upgrade the OptimismMintableERC20Factory (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.optimismMintableERC20FactoryProxy),
address(config.implementationAddressConfig.optimismMintableERC20FactoryImpl)
);
// Upgrade the L1ERC721Bridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1ERC721BridgeProxy),
address(config.implementationAddressConfig.l1ERC721BridgeImpl)
);
}
/**
* @notice Calls the first 2 steps of the migration process.
*/
function phase1() external onlyOwner {
step1();
step2();
}
/**
* @notice Calls the remaining steps of the migration process, and finalizes.
*/
function phase2() external onlyOwner {
step3();
step4();
step5();
finalize();
}
/**
* @notice Tranfers admin ownership to the final owner.
*/
function finalize() public onlyOwner {
// Transfer ownership of the ProxyAdmin to the final owner.
config.globalConfig.proxyAdmin.transferOwnership(config.globalConfig.finalOwner);
// Optionally also transfer AddressManager and L1StandardBridge if we still own it. Might
// happen if we're exiting early.
if (currentStep <= PROXY_TRANSFER_STEP) {
// Transfer ownership of the AddressManager to the final owner.
config.globalConfig.addressManager.transferOwnership(
address(config.globalConfig.finalOwner)
);
// Transfer ownership of the L1StandardBridge to the final owner.
L1ChugSplashProxy(payable(config.proxyAddressConfig.l1StandardBridgeProxy)).setOwner(
address(config.globalConfig.finalOwner)
);
// Transfer ownership of the L1ERC721Bridge to the final owner.
Proxy(payable(config.proxyAddressConfig.l1ERC721BridgeProxy)).changeAdmin(
address(config.globalConfig.finalOwner)
);
}
// Mark the deployment as finalized.
finalized = true;
}
/**
* @notice First exit point, can only be called before step 3 is executed.
*/
function exit1() external onlyOwner {
require(
currentStep == EXIT_1_NO_RETURN_STEP,
"SystemDictator: can only exit1 before step 3 is executed"
);
// Reset the L1CrossDomainMessenger to the old implementation.
config.globalConfig.addressManager.setAddress(
"BVM_L1CrossDomainMessenger",
oldL1CrossDomainMessenger
);
// Unset the DTL shutoff block which will allow the DTL to sync again.
config.globalConfig.addressManager.setAddress("DTL_SHUTOFF_BLOCK", address(0));
// Mark the deployment as exited.
exited = true;
}
}