-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
SystemConfig.sol
371 lines (319 loc) · 17.1 KB
/
SystemConfig.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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { ISemver } from "src/universal/ISemver.sol";
import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { Storage } from "src/libraries/Storage.sol";
import { Constants } from "src/libraries/Constants.sol";
/// @title SystemConfig
/// @notice The SystemConfig contract is used to manage configuration of an Optimism network.
/// All configuration is stored on L1 and picked up by L2 as part of the derviation of
/// the L2 chain.
contract SystemConfig is OwnableUpgradeable, ISemver {
/// @notice Enum representing different types of updates.
/// @custom:value BATCHER Represents an update to the batcher hash.
/// @custom:value GAS_CONFIG Represents an update to txn fee config on L2.
/// @custom:value GAS_LIMIT Represents an update to gas limit on L2.
/// @custom:value UNSAFE_BLOCK_SIGNER Represents an update to the signer key for unsafe
/// block distrubution.
enum UpdateType {
BATCHER,
GAS_CONFIG,
GAS_LIMIT,
UNSAFE_BLOCK_SIGNER
}
/// @notice Struct representing the addresses of L1 system contracts. These should be the
/// proxies and will differ for each OP Stack chain.
struct Addresses {
address l1CrossDomainMessenger;
address l1ERC721Bridge;
address l1StandardBridge;
address l2OutputOracle;
address optimismPortal;
address optimismMintableERC20Factory;
}
/// @notice Version identifier, used for upgrades.
uint256 public constant VERSION = 0;
/// @notice Storage slot that the unsafe block signer is stored at.
/// Storing it at this deterministic storage slot allows for decoupling the storage
/// layout from the way that `solc` lays out storage. The `op-node` uses a storage
/// proof to fetch this value.
/// @dev NOTE: this value will be migrated to another storage slot in a future version.
/// User input should not be placed in storage in this contract until this migration
/// happens. It is unlikely that keccak second preimage resistance will be broken,
/// but it is better to be safe than sorry.
bytes32 public constant UNSAFE_BLOCK_SIGNER_SLOT = keccak256("systemconfig.unsafeblocksigner");
/// @notice Storage slot that the L1CrossDomainMessenger address is stored at.
bytes32 public constant L1_CROSS_DOMAIN_MESSENGER_SLOT =
bytes32(uint256(keccak256("systemconfig.l1crossdomainmessenger")) - 1);
/// @notice Storage slot that the L1ERC721Bridge address is stored at.
bytes32 public constant L1_ERC_721_BRIDGE_SLOT = bytes32(uint256(keccak256("systemconfig.l1erc721bridge")) - 1);
/// @notice Storage slot that the L1StandardBridge address is stored at.
bytes32 public constant L1_STANDARD_BRIDGE_SLOT = bytes32(uint256(keccak256("systemconfig.l1standardbridge")) - 1);
/// @notice Storage slot that the L2OutputOracle address is stored at.
bytes32 public constant L2_OUTPUT_ORACLE_SLOT = bytes32(uint256(keccak256("systemconfig.l2outputoracle")) - 1);
/// @notice Storage slot that the OptimismPortal address is stored at.
bytes32 public constant OPTIMISM_PORTAL_SLOT = bytes32(uint256(keccak256("systemconfig.optimismportal")) - 1);
/// @notice Storage slot that the OptimismMintableERC20Factory address is stored at.
bytes32 public constant OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT =
bytes32(uint256(keccak256("systemconfig.optimismmintableerc20factory")) - 1);
/// @notice Storage slot that the batch inbox address is stored at.
bytes32 public constant BATCH_INBOX_SLOT = bytes32(uint256(keccak256("systemconfig.batchinbox")) - 1);
/// @notice Fixed L2 gas overhead. Used as part of the L2 fee calculation.
uint256 public overhead;
/// @notice Dynamic L2 gas overhead. Used as part of the L2 fee calculation.
uint256 public scalar;
/// @notice Identifier for the batcher.
/// For version 1 of this configuration, this is represented as an address left-padded
/// with zeros to 32 bytes.
bytes32 public batcherHash;
/// @notice L2 block gas limit.
uint64 public gasLimit;
/// @notice The configuration for the deposit fee market.
/// Used by the OptimismPortal to meter the cost of buying L2 gas on L1.
/// Set as internal with a getter so that the struct is returned instead of a tuple.
ResourceMetering.ResourceConfig internal _resourceConfig;
/// @notice Emitted when configuration is updated.
/// @param version SystemConfig version.
/// @param updateType Type of update.
/// @param data Encoded update data.
event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data);
/// @notice The block at which the op-node can start searching for logs from.
uint256 public startBlock;
/// @notice Semantic version.
/// @custom:semver 1.10.0
string public constant version = "1.10.0";
/// @notice Constructs the SystemConfig contract. Cannot set
/// the owner to `address(0)` due to the Ownable contract's
/// implementation, so set it to `address(0xdEaD)`
constructor() {
initialize({
_owner: address(0xdEaD),
_overhead: 0,
_scalar: 0,
_batcherHash: bytes32(0),
_gasLimit: 1,
_unsafeBlockSigner: address(0),
_config: ResourceMetering.ResourceConfig({
maxResourceLimit: 1,
elasticityMultiplier: 1,
baseFeeMaxChangeDenominator: 2,
minimumBaseFee: 0,
systemTxMaxGas: 0,
maximumBaseFee: 0
}),
_startBlock: type(uint256).max,
_batchInbox: address(0),
_addresses: SystemConfig.Addresses({
l1CrossDomainMessenger: address(0),
l1ERC721Bridge: address(0),
l1StandardBridge: address(0),
l2OutputOracle: address(0),
optimismPortal: address(0),
optimismMintableERC20Factory: address(0)
})
});
}
/// @notice Initializer.
/// The resource config must be set before the require check.
/// @param _owner Initial owner of the contract.
/// @param _overhead Initial overhead value.
/// @param _scalar Initial scalar value.
/// @param _batcherHash Initial batcher hash.
/// @param _gasLimit Initial gas limit.
/// @param _unsafeBlockSigner Initial unsafe block signer address.
/// @param _config Initial ResourceConfig.
/// @param _startBlock Starting block for the op-node to search for logs from.
/// Contracts that were deployed before this field existed
/// need to have this field set manually via an override.
/// Newly deployed contracts should set this value to uint256(0).
/// @param _batchInbox Batch inbox address. An identifier for the op-node to find
/// canonical data.
/// @param _addresses Set of L1 contract addresses. These should be the proxies.
function initialize(
address _owner,
uint256 _overhead,
uint256 _scalar,
bytes32 _batcherHash,
uint64 _gasLimit,
address _unsafeBlockSigner,
ResourceMetering.ResourceConfig memory _config,
uint256 _startBlock,
address _batchInbox,
SystemConfig.Addresses memory _addresses
)
public
reinitializer(Constants.INITIALIZER)
{
__Ownable_init();
transferOwnership(_owner);
// These are set in ascending order of their UpdateTypes.
_setBatcherHash(_batcherHash);
_setGasConfig({ _overhead: _overhead, _scalar: _scalar });
_setGasLimit(_gasLimit);
_setUnsafeBlockSigner(_unsafeBlockSigner);
Storage.setAddress(BATCH_INBOX_SLOT, _batchInbox);
Storage.setAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT, _addresses.l1CrossDomainMessenger);
Storage.setAddress(L1_ERC_721_BRIDGE_SLOT, _addresses.l1ERC721Bridge);
Storage.setAddress(L1_STANDARD_BRIDGE_SLOT, _addresses.l1StandardBridge);
Storage.setAddress(L2_OUTPUT_ORACLE_SLOT, _addresses.l2OutputOracle);
Storage.setAddress(OPTIMISM_PORTAL_SLOT, _addresses.optimismPortal);
Storage.setAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT, _addresses.optimismMintableERC20Factory);
_setStartBlock(_startBlock);
_setResourceConfig(_config);
require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low");
}
/// @notice Returns the minimum L2 gas limit that can be safely set for the system to
/// operate. The L2 gas limit must be larger than or equal to the amount of
/// gas that is allocated for deposits per block plus the amount of gas that
/// is allocated for the system transaction.
/// This function is used to determine if changes to parameters are safe.
/// @return uint64 Minimum gas limit.
function minimumGasLimit() public view returns (uint64) {
return uint64(_resourceConfig.maxResourceLimit) + uint64(_resourceConfig.systemTxMaxGas);
}
/// @notice High level getter for the unsafe block signer address.
/// Unsafe blocks can be propagated across the p2p network if they are signed by the
/// key corresponding to this address.
/// @return addr_ Address of the unsafe block signer.
// solhint-disable-next-line ordering
function unsafeBlockSigner() public view returns (address addr_) {
addr_ = Storage.getAddress(UNSAFE_BLOCK_SIGNER_SLOT);
}
/// @notice Getter for the L1CrossDomainMessenger address.
function l1CrossDomainMessenger() external view returns (address addr_) {
addr_ = Storage.getAddress(L1_CROSS_DOMAIN_MESSENGER_SLOT);
}
/// @notice Getter for the L1ERC721Bridge address.
function l1ERC721Bridge() external view returns (address addr_) {
addr_ = Storage.getAddress(L1_ERC_721_BRIDGE_SLOT);
}
/// @notice Getter for the L1StandardBridge address.
function l1StandardBridge() external view returns (address addr_) {
addr_ = Storage.getAddress(L1_STANDARD_BRIDGE_SLOT);
}
/// @notice Getter for the L2OutputOracle address.
function l2OutputOracle() external view returns (address addr_) {
addr_ = Storage.getAddress(L2_OUTPUT_ORACLE_SLOT);
}
/// @notice Getter for the OptimismPortal address.
function optimismPortal() external view returns (address addr_) {
addr_ = Storage.getAddress(OPTIMISM_PORTAL_SLOT);
}
/// @notice Getter for the OptimismMintableERC20Factory address.
function optimismMintableERC20Factory() external view returns (address addr_) {
addr_ = Storage.getAddress(OPTIMISM_MINTABLE_ERC20_FACTORY_SLOT);
}
/// @notice Getter for the BatchInbox address.
function batchInbox() external view returns (address addr_) {
addr_ = Storage.getAddress(BATCH_INBOX_SLOT);
}
/// @notice Sets the start block in a backwards compatible way. Proxies
/// that were initialized before the startBlock existed in storage
/// can have their start block set by a user provided override.
/// A start block of 0 indicates that there is no override and the
/// start block will be set by `block.number`.
/// @dev This logic is used to patch legacy deployments with new storage values.
/// Use the override if it is provided as a non zero value and the value
/// has not already been set in storage. Use `block.number` if the value
/// has already been set in storage
/// @param _startBlock The start block override to set in storage.
function _setStartBlock(uint256 _startBlock) internal {
if (_startBlock != 0 && startBlock == 0) {
// There is an override and it is not already set, this is for legacy chains.
startBlock = _startBlock;
} else if (startBlock == 0) {
// There is no override and it is not set in storage. Set it to the block number.
// This is for newly deployed chains.
startBlock = block.number;
}
}
/// @notice Updates the unsafe block signer address. Can only be called by the owner.
/// @param _unsafeBlockSigner New unsafe block signer address.
function setUnsafeBlockSigner(address _unsafeBlockSigner) external onlyOwner {
_setUnsafeBlockSigner(_unsafeBlockSigner);
}
/// @notice Updates the unsafe block signer address.
/// @param _unsafeBlockSigner New unsafe block signer address.
function _setUnsafeBlockSigner(address _unsafeBlockSigner) internal {
Storage.setAddress(UNSAFE_BLOCK_SIGNER_SLOT, _unsafeBlockSigner);
bytes memory data = abi.encode(_unsafeBlockSigner);
emit ConfigUpdate(VERSION, UpdateType.UNSAFE_BLOCK_SIGNER, data);
}
/// @notice Updates the batcher hash. Can only be called by the owner.
/// @param _batcherHash New batcher hash.
function setBatcherHash(bytes32 _batcherHash) external onlyOwner {
_setBatcherHash(_batcherHash);
}
/// @notice Internal function for updating the batcher hash.
/// @param _batcherHash New batcher hash.
function _setBatcherHash(bytes32 _batcherHash) internal {
batcherHash = _batcherHash;
bytes memory data = abi.encode(_batcherHash);
emit ConfigUpdate(VERSION, UpdateType.BATCHER, data);
}
/// @notice Updates gas config. Can only be called by the owner.
/// @param _overhead New overhead value.
/// @param _scalar New scalar value.
function setGasConfig(uint256 _overhead, uint256 _scalar) external onlyOwner {
_setGasConfig(_overhead, _scalar);
}
/// @notice Internal function for updating the gas config.
/// @param _overhead New overhead value.
/// @param _scalar New scalar value.
function _setGasConfig(uint256 _overhead, uint256 _scalar) internal {
overhead = _overhead;
scalar = _scalar;
bytes memory data = abi.encode(_overhead, _scalar);
emit ConfigUpdate(VERSION, UpdateType.GAS_CONFIG, data);
}
/// @notice Updates the L2 gas limit. Can only be called by the owner.
/// @param _gasLimit New gas limit.
function setGasLimit(uint64 _gasLimit) external onlyOwner {
_setGasLimit(_gasLimit);
}
/// @notice Internal function for updating the L2 gas limit.
/// @param _gasLimit New gas limit.
function _setGasLimit(uint64 _gasLimit) internal {
require(_gasLimit >= minimumGasLimit(), "SystemConfig: gas limit too low");
gasLimit = _gasLimit;
bytes memory data = abi.encode(_gasLimit);
emit ConfigUpdate(VERSION, UpdateType.GAS_LIMIT, data);
}
/// @notice A getter for the resource config.
/// Ensures that the struct is returned instead of a tuple.
/// @return ResourceConfig
function resourceConfig() external view returns (ResourceMetering.ResourceConfig memory) {
return _resourceConfig;
}
/// @notice An external setter for the resource config.
/// In the future, this method may emit an event that the `op-node` picks up
/// for when the resource config is changed.
/// @param _config The new resource config values.
function setResourceConfig(ResourceMetering.ResourceConfig memory _config) external onlyOwner {
_setResourceConfig(_config);
}
/// @notice An internal setter for the resource config.
/// Ensures that the config is sane before storing it by checking for invariants.
/// @param _config The new resource config.
function _setResourceConfig(ResourceMetering.ResourceConfig memory _config) internal {
// Min base fee must be less than or equal to max base fee.
require(
_config.minimumBaseFee <= _config.maximumBaseFee, "SystemConfig: min base fee must be less than max base"
);
// Base fee change denominator must be greater than 1.
require(_config.baseFeeMaxChangeDenominator > 1, "SystemConfig: denominator must be larger than 1");
// Max resource limit plus system tx gas must be less than or equal to the L2 gas limit.
// The gas limit must be increased before these values can be increased.
require(_config.maxResourceLimit + _config.systemTxMaxGas <= gasLimit, "SystemConfig: gas limit too low");
// Elasticity multiplier must be greater than 0.
require(_config.elasticityMultiplier > 0, "SystemConfig: elasticity multiplier cannot be 0");
// No precision loss when computing target resource limit.
require(
((_config.maxResourceLimit / _config.elasticityMultiplier) * _config.elasticityMultiplier)
== _config.maxResourceLimit,
"SystemConfig: precision loss with target resource limit"
);
_resourceConfig = _config;
}
}