github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/scripts/ChainAssertions.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity ^0.8.0; 3 4 import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; 5 import { ResourceMetering } from "src/L1/ResourceMetering.sol"; 6 import { DeployConfig } from "scripts/DeployConfig.s.sol"; 7 import { Deployer } from "scripts/Deployer.sol"; 8 import { SystemConfig } from "src/L1/SystemConfig.sol"; 9 import { Constants } from "src/libraries/Constants.sol"; 10 import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; 11 import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; 12 import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; 13 import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol"; 14 import { ProtocolVersion, ProtocolVersions } from "src/L1/ProtocolVersions.sol"; 15 import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; 16 import { OptimismPortal } from "src/L1/OptimismPortal.sol"; 17 import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; 18 import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; 19 import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; 20 import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; 21 import { Predeploys } from "src/libraries/Predeploys.sol"; 22 import { Types } from "scripts/Types.sol"; 23 import { Vm } from "forge-std/Vm.sol"; 24 import { ISystemConfigV0 } from "scripts/interfaces/ISystemConfigV0.sol"; 25 import { console2 as console } from "forge-std/console2.sol"; 26 27 library ChainAssertions { 28 Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); 29 30 /// @notice Asserts the correctness of an L1 deployment. This function expects that all contracts 31 /// within the `prox` ContractSet are proxies that have been setup and initialized. 32 function postDeployAssertions( 33 Types.ContractSet memory _prox, 34 DeployConfig _cfg, 35 uint256 _l2OutputOracleStartingTimestamp, 36 Vm _vm 37 ) 38 internal 39 view 40 { 41 console.log("Running post-deploy assertions"); 42 ResourceMetering.ResourceConfig memory rcfg = SystemConfig(_prox.SystemConfig).resourceConfig(); 43 ResourceMetering.ResourceConfig memory dflt = Constants.DEFAULT_RESOURCE_CONFIG(); 44 require(keccak256(abi.encode(rcfg)) == keccak256(abi.encode(dflt))); 45 46 checkSystemConfig({ _contracts: _prox, _cfg: _cfg, _isProxy: true }); 47 checkL1CrossDomainMessenger({ _contracts: _prox, _vm: _vm, _isProxy: true }); 48 checkL1StandardBridge({ _contracts: _prox, _isProxy: true }); 49 checkL2OutputOracle({ 50 _contracts: _prox, 51 _cfg: _cfg, 52 _l2OutputOracleStartingTimestamp: _l2OutputOracleStartingTimestamp, 53 _isProxy: true 54 }); 55 checkOptimismMintableERC20Factory({ _contracts: _prox, _isProxy: true }); 56 checkL1ERC721Bridge({ _contracts: _prox, _isProxy: true }); 57 checkOptimismPortal({ _contracts: _prox, _cfg: _cfg, _isProxy: true }); 58 checkOptimismPortal2({ _contracts: _prox, _cfg: _cfg, _isProxy: true }); 59 checkProtocolVersions({ _contracts: _prox, _cfg: _cfg, _isProxy: true }); 60 } 61 62 /// @notice Asserts that the SystemConfig is setup correctly 63 function checkSystemConfig(Types.ContractSet memory _contracts, DeployConfig _cfg, bool _isProxy) internal view { 64 console.log("Running chain assertions on the SystemConfig"); 65 SystemConfig config = SystemConfig(_contracts.SystemConfig); 66 67 // Check that the contract is initialized 68 assertSlotValueIsOne({ _contractAddress: address(config), _slot: 0, _offset: 0 }); 69 70 ResourceMetering.ResourceConfig memory resourceConfig = config.resourceConfig(); 71 72 if (_isProxy) { 73 require(config.owner() == _cfg.finalSystemOwner()); 74 require(config.overhead() == _cfg.gasPriceOracleOverhead()); 75 require(config.scalar() == _cfg.gasPriceOracleScalar()); 76 require(config.batcherHash() == bytes32(uint256(uint160(_cfg.batchSenderAddress())))); 77 require(config.gasLimit() == uint64(_cfg.l2GenesisBlockGasLimit())); 78 require(config.unsafeBlockSigner() == _cfg.p2pSequencerAddress()); 79 // Check _config 80 ResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG(); 81 require(resourceConfig.maxResourceLimit == rconfig.maxResourceLimit); 82 require(resourceConfig.elasticityMultiplier == rconfig.elasticityMultiplier); 83 require(resourceConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator); 84 require(resourceConfig.systemTxMaxGas == rconfig.systemTxMaxGas); 85 require(resourceConfig.minimumBaseFee == rconfig.minimumBaseFee); 86 require(resourceConfig.maximumBaseFee == rconfig.maximumBaseFee); 87 // Depends on start block being set to 0 in `initialize` 88 uint256 cfgStartBlock = _cfg.systemConfigStartBlock(); 89 require(config.startBlock() == (cfgStartBlock == 0 ? block.number : cfgStartBlock)); 90 require(config.batchInbox() == _cfg.batchInboxAddress()); 91 // Check _addresses 92 require(config.l1CrossDomainMessenger() == _contracts.L1CrossDomainMessenger); 93 require(config.l1ERC721Bridge() == _contracts.L1ERC721Bridge); 94 require(config.l1StandardBridge() == _contracts.L1StandardBridge); 95 require(config.l2OutputOracle() == _contracts.L2OutputOracle); 96 require(config.optimismPortal() == _contracts.OptimismPortal); 97 require(config.optimismMintableERC20Factory() == _contracts.OptimismMintableERC20Factory); 98 } else { 99 require(config.owner() == address(0xdead)); 100 require(config.overhead() == 0); 101 require(config.scalar() == 0); 102 require(config.batcherHash() == bytes32(0)); 103 require(config.gasLimit() == 1); 104 require(config.unsafeBlockSigner() == address(0)); 105 // Check _config 106 require(resourceConfig.maxResourceLimit == 1); 107 require(resourceConfig.elasticityMultiplier == 1); 108 require(resourceConfig.baseFeeMaxChangeDenominator == 2); 109 require(resourceConfig.systemTxMaxGas == 0); 110 require(resourceConfig.minimumBaseFee == 0); 111 require(resourceConfig.maximumBaseFee == 0); 112 // Check _addresses 113 require(config.startBlock() == type(uint256).max); 114 require(config.batchInbox() == address(0)); 115 require(config.l1CrossDomainMessenger() == address(0)); 116 require(config.l1ERC721Bridge() == address(0)); 117 require(config.l1StandardBridge() == address(0)); 118 require(config.l2OutputOracle() == address(0)); 119 require(config.optimismPortal() == address(0)); 120 require(config.optimismMintableERC20Factory() == address(0)); 121 } 122 } 123 124 /// @notice Asserts that the L1CrossDomainMessenger is setup correctly 125 function checkL1CrossDomainMessenger(Types.ContractSet memory _contracts, Vm _vm, bool _isProxy) internal view { 126 console.log("Running chain assertions on the L1CrossDomainMessenger"); 127 L1CrossDomainMessenger messenger = L1CrossDomainMessenger(_contracts.L1CrossDomainMessenger); 128 129 // Check that the contract is initialized 130 assertSlotValueIsOne({ _contractAddress: address(messenger), _slot: 0, _offset: 20 }); 131 132 require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER); 133 require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER); 134 135 if (_isProxy) { 136 require(address(messenger.PORTAL()) == _contracts.OptimismPortal); 137 require(address(messenger.portal()) == _contracts.OptimismPortal); 138 require(address(messenger.superchainConfig()) == _contracts.SuperchainConfig); 139 bytes32 xdmSenderSlot = _vm.load(address(messenger), bytes32(uint256(204))); 140 require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER); 141 } else { 142 require(address(messenger.PORTAL()) == address(0)); 143 require(address(messenger.portal()) == address(0)); 144 require(address(messenger.superchainConfig()) == address(0)); 145 } 146 } 147 148 /// @notice Asserts that the L1StandardBridge is setup correctly 149 function checkL1StandardBridge(Types.ContractSet memory _contracts, bool _isProxy) internal view { 150 console.log("Running chain assertions on the L1StandardBridge"); 151 L1StandardBridge bridge = L1StandardBridge(payable(_contracts.L1StandardBridge)); 152 153 // Check that the contract is initialized 154 assertSlotValueIsOne({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); 155 156 if (_isProxy) { 157 require(address(bridge.MESSENGER()) == _contracts.L1CrossDomainMessenger); 158 require(address(bridge.messenger()) == _contracts.L1CrossDomainMessenger); 159 require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE); 160 require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE); 161 require(address(bridge.superchainConfig()) == _contracts.SuperchainConfig); 162 } else { 163 require(address(bridge.MESSENGER()) == address(0)); 164 require(address(bridge.messenger()) == address(0)); 165 require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE); 166 require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE); 167 require(address(bridge.superchainConfig()) == address(0)); 168 } 169 } 170 171 /// @notice Asserts that the DisputeGameFactory is setup correctly 172 function checkDisputeGameFactory(Types.ContractSet memory _contracts, address _expectedOwner) internal view { 173 console.log("Running chain assertions on the DisputeGameFactory"); 174 DisputeGameFactory factory = DisputeGameFactory(_contracts.DisputeGameFactory); 175 176 // Check that the contract is initialized 177 assertSlotValueIsOne({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); 178 179 require(factory.owner() == _expectedOwner); 180 } 181 182 /// @notice Asserts that the DelayedWETH is setup correctly 183 function checkDelayedWETH( 184 Types.ContractSet memory _contracts, 185 DeployConfig _cfg, 186 bool _isProxy, 187 address _expectedOwner 188 ) 189 internal 190 view 191 { 192 console.log("Running chain assertions on the DelayedWETH"); 193 DelayedWETH weth = DelayedWETH(payable(_contracts.DelayedWETH)); 194 195 // Check that the contract is initialized 196 assertSlotValueIsOne({ _contractAddress: address(weth), _slot: 0, _offset: 0 }); 197 198 if (_isProxy) { 199 require(weth.owner() == _expectedOwner); 200 require(weth.delay() == _cfg.faultGameWithdrawalDelay()); 201 require(weth.config() == SuperchainConfig(_contracts.SuperchainConfig)); 202 } else { 203 require(weth.owner() == _expectedOwner); 204 require(weth.delay() == _cfg.faultGameWithdrawalDelay()); 205 } 206 } 207 208 /// @notice Asserts that the L2OutputOracle is setup correctly 209 function checkL2OutputOracle( 210 Types.ContractSet memory _contracts, 211 DeployConfig _cfg, 212 uint256 _l2OutputOracleStartingTimestamp, 213 bool _isProxy 214 ) 215 internal 216 view 217 { 218 console.log("Running chain assertions on the L2OutputOracle"); 219 L2OutputOracle oracle = L2OutputOracle(_contracts.L2OutputOracle); 220 221 // Check that the contract is initialized 222 assertSlotValueIsOne({ _contractAddress: address(oracle), _slot: 0, _offset: 0 }); 223 224 if (_isProxy) { 225 require(oracle.SUBMISSION_INTERVAL() == _cfg.l2OutputOracleSubmissionInterval()); 226 require(oracle.submissionInterval() == _cfg.l2OutputOracleSubmissionInterval()); 227 require(oracle.L2_BLOCK_TIME() == _cfg.l2BlockTime()); 228 require(oracle.l2BlockTime() == _cfg.l2BlockTime()); 229 require(oracle.PROPOSER() == _cfg.l2OutputOracleProposer()); 230 require(oracle.proposer() == _cfg.l2OutputOracleProposer()); 231 require(oracle.CHALLENGER() == _cfg.l2OutputOracleChallenger()); 232 require(oracle.challenger() == _cfg.l2OutputOracleChallenger()); 233 require(oracle.FINALIZATION_PERIOD_SECONDS() == _cfg.finalizationPeriodSeconds()); 234 require(oracle.finalizationPeriodSeconds() == _cfg.finalizationPeriodSeconds()); 235 require(oracle.startingBlockNumber() == _cfg.l2OutputOracleStartingBlockNumber()); 236 require(oracle.startingTimestamp() == _l2OutputOracleStartingTimestamp); 237 } else { 238 require(oracle.SUBMISSION_INTERVAL() == 1); 239 require(oracle.submissionInterval() == 1); 240 require(oracle.L2_BLOCK_TIME() == 1); 241 require(oracle.l2BlockTime() == 1); 242 require(oracle.PROPOSER() == address(0)); 243 require(oracle.proposer() == address(0)); 244 require(oracle.CHALLENGER() == address(0)); 245 require(oracle.challenger() == address(0)); 246 require(oracle.FINALIZATION_PERIOD_SECONDS() == 0); 247 require(oracle.finalizationPeriodSeconds() == 0); 248 require(oracle.startingBlockNumber() == 0); 249 require(oracle.startingTimestamp() == 0); 250 } 251 } 252 253 /// @notice Asserts that the OptimismMintableERC20Factory is setup correctly 254 function checkOptimismMintableERC20Factory(Types.ContractSet memory _contracts, bool _isProxy) internal view { 255 console.log("Running chain assertions on the OptimismMintableERC20Factory"); 256 OptimismMintableERC20Factory factory = OptimismMintableERC20Factory(_contracts.OptimismMintableERC20Factory); 257 258 // Check that the contract is initialized 259 assertSlotValueIsOne({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); 260 261 if (_isProxy) { 262 require(factory.BRIDGE() == _contracts.L1StandardBridge); 263 require(factory.bridge() == _contracts.L1StandardBridge); 264 } else { 265 require(factory.BRIDGE() == address(0)); 266 require(factory.bridge() == address(0)); 267 } 268 } 269 270 /// @notice Asserts that the L1ERC721Bridge is setup correctly 271 function checkL1ERC721Bridge(Types.ContractSet memory _contracts, bool _isProxy) internal view { 272 console.log("Running chain assertions on the L1ERC721Bridge"); 273 L1ERC721Bridge bridge = L1ERC721Bridge(_contracts.L1ERC721Bridge); 274 275 // Check that the contract is initialized 276 assertSlotValueIsOne({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); 277 278 require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE); 279 require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE); 280 281 if (_isProxy) { 282 require(address(bridge.MESSENGER()) == _contracts.L1CrossDomainMessenger); 283 require(address(bridge.messenger()) == _contracts.L1CrossDomainMessenger); 284 require(address(bridge.superchainConfig()) == _contracts.SuperchainConfig); 285 } else { 286 require(address(bridge.MESSENGER()) == address(0)); 287 require(address(bridge.messenger()) == address(0)); 288 require(address(bridge.superchainConfig()) == address(0)); 289 } 290 } 291 292 /// @notice Asserts the OptimismPortal is setup correctly 293 function checkOptimismPortal(Types.ContractSet memory _contracts, DeployConfig _cfg, bool _isProxy) internal view { 294 console.log("Running chain assertions on the OptimismPortal"); 295 296 OptimismPortal portal = OptimismPortal(payable(_contracts.OptimismPortal)); 297 298 // Check that the contract is initialized 299 assertSlotValueIsOne({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); 300 301 address guardian = _cfg.superchainConfigGuardian(); 302 if (guardian.code.length == 0) { 303 console.log("Guardian has no code: %s", guardian); 304 } 305 306 if (_isProxy) { 307 require(address(portal.L2_ORACLE()) == _contracts.L2OutputOracle); 308 require(address(portal.l2Oracle()) == _contracts.L2OutputOracle); 309 require(address(portal.SYSTEM_CONFIG()) == _contracts.SystemConfig); 310 require(address(portal.systemConfig()) == _contracts.SystemConfig); 311 require(portal.GUARDIAN() == guardian); 312 require(portal.guardian() == guardian); 313 require(address(portal.superchainConfig()) == address(_contracts.SuperchainConfig)); 314 require(portal.paused() == SuperchainConfig(_contracts.SuperchainConfig).paused()); 315 require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER); 316 } else { 317 require(address(portal.L2_ORACLE()) == address(0)); 318 require(address(portal.l2Oracle()) == address(0)); 319 require(address(portal.SYSTEM_CONFIG()) == address(0)); 320 require(address(portal.systemConfig()) == address(0)); 321 require(address(portal.superchainConfig()) == address(0)); 322 require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER); 323 } 324 } 325 326 /// @notice Asserts the OptimismPortal2 is setup correctly 327 function checkOptimismPortal2( 328 Types.ContractSet memory _contracts, 329 DeployConfig _cfg, 330 bool _isProxy 331 ) 332 internal 333 view 334 { 335 console.log("Running chain assertions on the OptimismPortal2"); 336 337 OptimismPortal2 portal = OptimismPortal2(payable(_contracts.OptimismPortal2)); 338 339 // Check that the contract is initialized 340 assertSlotValueIsOne({ _contractAddress: address(portal), _slot: 0, _offset: 0 }); 341 342 address guardian = _cfg.superchainConfigGuardian(); 343 if (guardian.code.length == 0) { 344 console.log("Guardian has no code: %s", guardian); 345 } 346 347 if (_isProxy) { 348 require(address(portal.disputeGameFactory()) == _contracts.DisputeGameFactory); 349 require(address(portal.SYSTEM_CONFIG()) == _contracts.SystemConfig); 350 require(address(portal.systemConfig()) == _contracts.SystemConfig); 351 require(portal.GUARDIAN() == guardian); 352 require(portal.guardian() == guardian); 353 require(address(portal.superchainConfig()) == address(_contracts.SuperchainConfig)); 354 require(portal.paused() == SuperchainConfig(_contracts.SuperchainConfig).paused()); 355 require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER); 356 } else { 357 require(address(portal.disputeGameFactory()) == address(0)); 358 require(address(portal.SYSTEM_CONFIG()) == address(0)); 359 require(address(portal.systemConfig()) == address(0)); 360 require(address(portal.superchainConfig()) == address(0)); 361 require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER); 362 } 363 } 364 365 /// @notice Asserts that the ProtocolVersions is setup correctly 366 function checkProtocolVersions( 367 Types.ContractSet memory _contracts, 368 DeployConfig _cfg, 369 bool _isProxy 370 ) 371 internal 372 view 373 { 374 console.log("Running chain assertions on the ProtocolVersions"); 375 ProtocolVersions versions = ProtocolVersions(_contracts.ProtocolVersions); 376 377 // Check that the contract is initialized 378 assertSlotValueIsOne({ _contractAddress: address(versions), _slot: 0, _offset: 0 }); 379 380 if (_isProxy) { 381 require(versions.owner() == _cfg.finalSystemOwner()); 382 require(ProtocolVersion.unwrap(versions.required()) == _cfg.requiredProtocolVersion()); 383 require(ProtocolVersion.unwrap(versions.recommended()) == _cfg.recommendedProtocolVersion()); 384 } else { 385 require(versions.owner() == address(0xdead)); 386 require(ProtocolVersion.unwrap(versions.required()) == 0); 387 require(ProtocolVersion.unwrap(versions.recommended()) == 0); 388 } 389 } 390 391 /// @notice Asserts that the SuperchainConfig is setup correctly 392 function checkSuperchainConfig( 393 Types.ContractSet memory _contracts, 394 DeployConfig _cfg, 395 bool _isPaused 396 ) 397 internal 398 view 399 { 400 console.log("Running chain assertions on the SuperchainConfig"); 401 SuperchainConfig superchainConfig = SuperchainConfig(_contracts.SuperchainConfig); 402 403 // Check that the contract is initialized 404 assertSlotValueIsOne({ _contractAddress: address(superchainConfig), _slot: 0, _offset: 0 }); 405 406 require(superchainConfig.guardian() == _cfg.superchainConfigGuardian()); 407 require(superchainConfig.paused() == _isPaused); 408 } 409 410 /// @dev Asserts that for a given contract the value of a storage slot at an offset is 1. 411 function assertSlotValueIsOne(address _contractAddress, uint256 _slot, uint256 _offset) internal view { 412 bytes32 slotVal = vm.load(_contractAddress, bytes32(_slot)); 413 require( 414 uint8((uint256(slotVal) >> (_offset * 8)) & 0xFF) == uint8(1), 415 "Storage value is not 1 at the given slot and offset" 416 ); 417 } 418 }