Commit 290dde27 authored by Matt Solomon's avatar Matt Solomon Committed by GitHub

OPSM: DeployOPChain assertions (#11943)

* chore: add BaseDeployIO for the deploy input and output contracts

* test: add assertions to DeployOPChain

* chore: semver lock

* pr feedback
parent 110a31db
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Script } from "forge-std/Script.sol"; import { Script } from "forge-std/Script.sol";
import { CommonBase } from "forge-std/Base.sol";
import { LibString } from "@solady/utils/LibString.sol"; import { LibString } from "@solady/utils/LibString.sol";
...@@ -36,9 +35,10 @@ import { Blueprint } from "src/libraries/Blueprint.sol"; ...@@ -36,9 +35,10 @@ import { Blueprint } from "src/libraries/Blueprint.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { Solarray } from "scripts/libraries/Solarray.sol"; import { Solarray } from "scripts/libraries/Solarray.sol";
import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol";
// See DeploySuperchain.s.sol for detailed comments on the script architecture used here. // See DeploySuperchain.s.sol for detailed comments on the script architecture used here.
contract DeployImplementationsInput is CommonBase { contract DeployImplementationsInput is BaseDeployIO {
bytes32 internal _salt; bytes32 internal _salt;
uint256 internal _withdrawalDelaySeconds; uint256 internal _withdrawalDelaySeconds;
uint256 internal _minProposalSizeBytes; uint256 internal _minProposalSizeBytes;
...@@ -153,7 +153,7 @@ contract DeployImplementationsInput is CommonBase { ...@@ -153,7 +153,7 @@ contract DeployImplementationsInput is CommonBase {
} }
} }
contract DeployImplementationsOutput { contract DeployImplementationsOutput is BaseDeployIO {
OPStackManager internal _opsm; OPStackManager internal _opsm;
DelayedWETH internal _delayedWETHImpl; DelayedWETH internal _delayedWETHImpl;
OptimismPortal2 internal _optimismPortalImpl; OptimismPortal2 internal _optimismPortalImpl;
...@@ -205,6 +205,8 @@ contract DeployImplementationsOutput { ...@@ -205,6 +205,8 @@ contract DeployImplementationsOutput {
address(this.disputeGameFactoryImpl()) address(this.disputeGameFactoryImpl())
); );
DeployUtils.assertValidContractAddresses(addrs); DeployUtils.assertValidContractAddresses(addrs);
// TODO Also add the assertions for the implementation contracts from ChainAssertions.sol
} }
function opsm() public returns (OPStackManager) { function opsm() public returns (OPStackManager) {
......
...@@ -7,9 +7,12 @@ import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; ...@@ -7,9 +7,12 @@ import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { Solarray } from "scripts/libraries/Solarray.sol"; import { Solarray } from "scripts/libraries/Solarray.sol";
import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol";
import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol";
import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol";
import { Constants } from "src/libraries/Constants.sol"; import { Constants } from "src/libraries/Constants.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
...@@ -28,7 +31,7 @@ import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; ...@@ -28,7 +31,7 @@ import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";
import { L1StandardBridge } from "src/L1/L1StandardBridge.sol"; import { L1StandardBridge } from "src/L1/L1StandardBridge.sol";
import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol"; import { OptimismMintableERC20Factory } from "src/universal/OptimismMintableERC20Factory.sol";
contract DeployOPChainInput { contract DeployOPChainInput is BaseDeployIO {
address internal _opChainProxyAdminOwner; address internal _opChainProxyAdminOwner;
address internal _systemConfigOwner; address internal _systemConfigOwner;
address internal _batcher; address internal _batcher;
...@@ -125,7 +128,7 @@ contract DeployOPChainInput { ...@@ -125,7 +128,7 @@ contract DeployOPChainInput {
} }
} }
contract DeployOPChainOutput { contract DeployOPChainOutput is BaseDeployIO {
ProxyAdmin internal _opChainProxyAdmin; ProxyAdmin internal _opChainProxyAdmin;
AddressManager internal _addressManager; AddressManager internal _addressManager;
L1ERC721Bridge internal _l1ERC721BridgeProxy; L1ERC721Bridge internal _l1ERC721BridgeProxy;
...@@ -282,41 +285,125 @@ contract DeployOPChainOutput { ...@@ -282,41 +285,125 @@ contract DeployOPChainOutput {
// -------- Assertions on chain architecture -------- // -------- Assertions on chain architecture --------
function assertValidDeploy(DeployOPChainInput _doi) internal view { function assertValidDeploy(DeployOPChainInput _doi) internal view {
// TODO Add other assertions from ChainAssertions.sol here
assertValidSystemConfig(_doi); assertValidSystemConfig(_doi);
assertValidL1CrossDomainMessenger(_doi);
assertValidL1StandardBridge(_doi);
assertValidOptimismMintableERC20Factory(_doi);
assertValidOptimismPortal(_doi);
assertValidDisputeGameFactory(_doi);
assertValidDelayedWETHs(_doi);
// TODO Other FP assertions like the dispute games, anchor state registry, etc.
// TODO add initialization assertions
} }
function assertValidSystemConfig(DeployOPChainInput _doi) internal view { function assertValidSystemConfig(DeployOPChainInput _doi) internal view {
// forgefmt: disable-start SystemConfig systemConfig = systemConfigProxy();
require(systemConfigProxy().owner() == _doi.systemConfigOwner(), "SC10");
require(systemConfigProxy().basefeeScalar() == _doi.basefeeScalar(), "SC20"); DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _slot: 0, _offset: 0 });
require(systemConfigProxy().blobbasefeeScalar() == _doi.blobBaseFeeScalar(), "SC30");
require(systemConfigProxy().batcherHash() == bytes32(uint256(uint160(_doi.batcher()))), "SC40"); require(systemConfig.owner() == _doi.systemConfigOwner(), "SC-10");
require(systemConfigProxy().gasLimit() == uint64(30000000), "SC50");// TODO allow other gas limits? require(systemConfig.basefeeScalar() == _doi.basefeeScalar(), "SC-20");
require(systemConfigProxy().unsafeBlockSigner() == _doi.unsafeBlockSigner(), "SC60"); require(systemConfig.blobbasefeeScalar() == _doi.blobBaseFeeScalar(), "SC-30");
require(systemConfigProxy().scalar() >> 248 == 1, "SC70"); require(systemConfig.batcherHash() == bytes32(uint256(uint160(_doi.batcher()))), "SC-40");
require(systemConfig.gasLimit() == uint64(30000000), "SC-50"); // TODO allow other gas limits?
IResourceMetering.ResourceConfig memory rconfig = Constants.DEFAULT_RESOURCE_CONFIG(); require(systemConfig.unsafeBlockSigner() == _doi.unsafeBlockSigner(), "SC-60");
IResourceMetering.ResourceConfig memory outputConfig = systemConfigProxy().resourceConfig(); require(systemConfig.scalar() >> 248 == 1, "SC-70");
require(outputConfig.maxResourceLimit == rconfig.maxResourceLimit, "SC80");
require(outputConfig.elasticityMultiplier == rconfig.elasticityMultiplier, "SC90"); IResourceMetering.ResourceConfig memory rConfig = Constants.DEFAULT_RESOURCE_CONFIG();
require(outputConfig.baseFeeMaxChangeDenominator == rconfig.baseFeeMaxChangeDenominator, "SC100"); IResourceMetering.ResourceConfig memory outputConfig = systemConfig.resourceConfig();
require(outputConfig.systemTxMaxGas == rconfig.systemTxMaxGas, "SC110"); require(outputConfig.maxResourceLimit == rConfig.maxResourceLimit, "SC-80");
require(outputConfig.minimumBaseFee == rconfig.minimumBaseFee, "SC120"); require(outputConfig.elasticityMultiplier == rConfig.elasticityMultiplier, "SC-90");
require(outputConfig.maximumBaseFee == rconfig.maximumBaseFee, "SC130"); require(outputConfig.baseFeeMaxChangeDenominator == rConfig.baseFeeMaxChangeDenominator, "SC-100");
require(outputConfig.systemTxMaxGas == rConfig.systemTxMaxGas, "SC-110");
require(systemConfigProxy().startBlock() == block.number, "SC140"); require(outputConfig.minimumBaseFee == rConfig.minimumBaseFee, "SC-120");
require(systemConfigProxy().batchInbox() == _doi.opsm().chainIdToBatchInboxAddress(_doi.l2ChainId()), "SC150"); require(outputConfig.maximumBaseFee == rConfig.maximumBaseFee, "SC-130");
require(systemConfigProxy().l1CrossDomainMessenger() == address(l1CrossDomainMessengerProxy()), "SC160"); require(systemConfig.startBlock() == block.number, "SC-140");
require(systemConfigProxy().l1ERC721Bridge() == address(l1ERC721BridgeProxy()), "SC170"); require(systemConfig.batchInbox() == _doi.opsm().chainIdToBatchInboxAddress(_doi.l2ChainId()), "SC-150");
require(systemConfigProxy().l1StandardBridge() == address(l1StandardBridgeProxy()), "SC180");
require(systemConfigProxy().disputeGameFactory() == address(disputeGameFactoryProxy()), "SC190"); require(systemConfig.l1CrossDomainMessenger() == address(l1CrossDomainMessengerProxy()), "SC-160");
require(systemConfigProxy().optimismPortal() == address(optimismPortalProxy()), "SC200"); require(systemConfig.l1ERC721Bridge() == address(l1ERC721BridgeProxy()), "SC-170");
require(systemConfigProxy().optimismMintableERC20Factory() == address(optimismMintableERC20FactoryProxy()), "SC210"); require(systemConfig.l1StandardBridge() == address(l1StandardBridgeProxy()), "SC-180");
(address gasPayingToken,) = systemConfigProxy().gasPayingToken(); require(systemConfig.disputeGameFactory() == address(disputeGameFactoryProxy()), "SC-190");
require(gasPayingToken == Constants.ETHER, "SC220"); require(systemConfig.optimismPortal() == address(optimismPortalProxy()), "SC-200");
// forgefmt: disable-end require(systemConfig.optimismMintableERC20Factory() == address(optimismMintableERC20FactoryProxy()), "SC-210");
(address gasPayingToken,) = systemConfig.gasPayingToken();
require(gasPayingToken == Constants.ETHER, "SC-220");
}
function assertValidL1CrossDomainMessenger(DeployOPChainInput _doi) internal view {
L1CrossDomainMessenger messenger = l1CrossDomainMessengerProxy();
DeployUtils.assertInitialized({ _contractAddress: address(messenger), _slot: 0, _offset: 20 });
require(address(messenger.OTHER_MESSENGER()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-10");
require(address(messenger.otherMessenger()) == Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L1xDM-20");
require(address(messenger.PORTAL()) == address(optimismPortalProxy()), "L1xDM-30");
require(address(messenger.portal()) == address(optimismPortalProxy()), "L1xDM-40");
require(address(messenger.superchainConfig()) == address(_doi.opsm().superchainConfig()), "L1xDM-50");
bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204)));
require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "L1xDM-60");
}
function assertValidL1StandardBridge(DeployOPChainInput _doi) internal view {
L1StandardBridge bridge = l1StandardBridgeProxy();
L1CrossDomainMessenger messenger = l1CrossDomainMessengerProxy();
DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 });
require(address(bridge.MESSENGER()) == address(messenger), "L1SB-10");
require(address(bridge.messenger()) == address(messenger), "L1SB-20");
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_STANDARD_BRIDGE, "L1SB-30");
require(address(bridge.otherBridge()) == Predeploys.L2_STANDARD_BRIDGE, "L1SB-40");
require(address(bridge.superchainConfig()) == address(_doi.opsm().superchainConfig()), "L1SB-50");
}
function assertValidOptimismMintableERC20Factory(DeployOPChainInput) internal view {
OptimismMintableERC20Factory factory = optimismMintableERC20FactoryProxy();
DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 });
require(factory.BRIDGE() == address(l1StandardBridgeProxy()), "OMEF-10");
require(factory.bridge() == address(l1StandardBridgeProxy()), "OMEF-20");
}
function assertValidL1ERC721Bridge(DeployOPChainInput _doi) internal view {
L1ERC721Bridge bridge = l1ERC721BridgeProxy();
DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 });
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "LEB-10");
require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "LEB-20");
require(address(bridge.MESSENGER()) == address(l1CrossDomainMessengerProxy()), "LEB-30");
require(address(bridge.messenger()) == address(l1CrossDomainMessengerProxy()), "LEB-40");
require(address(bridge.superchainConfig()) == address(_doi.opsm().superchainConfig()), "LEB-50");
}
function assertValidOptimismPortal(DeployOPChainInput _doi) internal view {
OptimismPortal2 portal = optimismPortalProxy();
ISuperchainConfig superchainConfig = ISuperchainConfig(address(_doi.opsm().superchainConfig()));
require(address(portal.disputeGameFactory()) == address(disputeGameFactoryProxy()), "OP-10");
require(address(portal.systemConfig()) == address(systemConfigProxy()), "OP-20");
require(address(portal.superchainConfig()) == address(superchainConfig), "OP-30");
require(portal.guardian() == superchainConfig.guardian(), "OP-40");
require(portal.paused() == superchainConfig.paused(), "OP-50");
require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER, "OP-60");
// This slot is the custom gas token _balance and this check ensures
// that it stays unset for forwards compatibility with custom gas token.
require(vm.load(address(portal), bytes32(uint256(61))) == bytes32(0));
}
function assertValidDisputeGameFactory(DeployOPChainInput) internal view {
// TODO add in once FP support is added.
}
function assertValidDelayedWETHs(DeployOPChainInput) internal view {
// TODO add in once FP support is added.
} }
} }
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Script } from "forge-std/Script.sol"; import { Script } from "forge-std/Script.sol";
import { CommonBase } from "forge-std/Base.sol";
import { stdToml } from "forge-std/StdToml.sol"; import { stdToml } from "forge-std/StdToml.sol";
import { SuperchainConfig } from "src/L1/SuperchainConfig.sol"; import { SuperchainConfig } from "src/L1/SuperchainConfig.sol";
...@@ -12,6 +11,7 @@ import { Proxy } from "src/universal/Proxy.sol"; ...@@ -12,6 +11,7 @@ import { Proxy } from "src/universal/Proxy.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { Solarray } from "scripts/libraries/Solarray.sol"; import { Solarray } from "scripts/libraries/Solarray.sol";
import { BaseDeployIO } from "scripts/utils/BaseDeployIO.sol";
// This comment block defines the requirements and rationale for the architecture used in this forge // This comment block defines the requirements and rationale for the architecture used in this forge
// script, along with other scripts that are being written as new Superchain-first deploy scripts to // script, along with other scripts that are being written as new Superchain-first deploy scripts to
...@@ -65,7 +65,10 @@ import { Solarray } from "scripts/libraries/Solarray.sol"; ...@@ -65,7 +65,10 @@ import { Solarray } from "scripts/libraries/Solarray.sol";
// - `doo` for DeployOPChainInput // - `doo` for DeployOPChainInput
// - `doo` for DeployOPChainOutput // - `doo` for DeployOPChainOutput
// - etc. // - etc.
contract DeploySuperchainInput is CommonBase {
// All contracts of the form `Deploy<X>Input` should inherit from `BaseDeployIO`, as it provides
// shared functionality for all deploy scripts, such as access to cheat codes.
contract DeploySuperchainInput is BaseDeployIO {
using stdToml for string; using stdToml for string;
// All inputs are set in storage individually. We put any roles first, followed by the remaining // All inputs are set in storage individually. We put any roles first, followed by the remaining
...@@ -169,7 +172,9 @@ contract DeploySuperchainInput is CommonBase { ...@@ -169,7 +172,9 @@ contract DeploySuperchainInput is CommonBase {
} }
} }
contract DeploySuperchainOutput is CommonBase { // All contracts of the form `Deploy<X>Output` should inherit from `BaseDeployIO`, as it provides
// shared functionality for all deploy scripts, such as access to cheat codes.
contract DeploySuperchainOutput is BaseDeployIO {
// All outputs are stored in storage individually, with the same rationale as doing so for // All outputs are stored in storage individually, with the same rationale as doing so for
// inputs, and the same pattern is used below to expose the outputs. // inputs, and the same pattern is used below to expose the outputs.
ProtocolVersions internal _protocolVersionsImpl; ProtocolVersions internal _protocolVersionsImpl;
...@@ -223,6 +228,8 @@ contract DeploySuperchainOutput is CommonBase { ...@@ -223,6 +228,8 @@ contract DeploySuperchainOutput is CommonBase {
require(actualSuperchainConfigImpl == address(_superchainConfigImpl), "100"); require(actualSuperchainConfigImpl == address(_superchainConfigImpl), "100");
require(actualProtocolVersionsImpl == address(_protocolVersionsImpl), "200"); require(actualProtocolVersionsImpl == address(_protocolVersionsImpl), "200");
// TODO Also add the assertions for the implementation contracts from ChainAssertions.sol
} }
function superchainProxyAdmin() public view returns (ProxyAdmin) { function superchainProxyAdmin() public view returns (ProxyAdmin) {
......
...@@ -47,4 +47,14 @@ library DeployUtils { ...@@ -47,4 +47,14 @@ library DeployUtils {
} }
} }
} }
// Asserts that for a given contract the value of a storage slot at an offset is 1. This
// is used to assert that a contract is initialized.
function assertInitialized(address _contractAddress, uint256 _slot, uint256 _offset) internal view {
bytes32 slotVal = vm.load(_contractAddress, bytes32(_slot));
require(
uint8((uint256(slotVal) >> (_offset * 8)) & 0xFF) == uint8(1),
"Storage value is not 1 at the given slot and offset"
);
}
} }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { CommonBase } from "forge-std/Base.sol";
/// @notice All contracts of the form `Deploy<X>Input` and `Deploy<X>Output` should inherit from this contract.
/// It provides a base set of functionality, such as access to cheat codes, that these scripts may need.
/// See the comments in `DeploySuperchain.s.sol` for more information on this pattern.
abstract contract BaseDeployIO is CommonBase { }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment