Commit 57f9fbf8 authored by Matt Solomon's avatar Matt Solomon Committed by GitHub

OPSM: More assertions (#11994)

* refactor: rename var for clarity

* test: add more assertions from ChainAssertions, and some new ones

* chore: ensure unique, clear, consistent revert string IDs

also sorts methods alphabetically for clarity

* fix: var name after rebase

* style: forge fmt

* revert proxyAdmin -> superchainProxyAdmin name change for now

* Update packages/contracts-bedrock/scripts/DeployImplementations.s.sol
Co-authored-by: default avatarMaurelian <john@oplabs.co>

* chore: small tweaks from pr feedback

---------
Co-authored-by: default avatarMaurelian <john@oplabs.co>
parent a080bd23
...@@ -5,6 +5,12 @@ import { Script } from "forge-std/Script.sol"; ...@@ -5,6 +5,12 @@ import { Script } from "forge-std/Script.sol";
import { LibString } from "@solady/utils/LibString.sol"; import { LibString } from "@solady/utils/LibString.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 { Predeploys } from "src/libraries/Predeploys.sol";
import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { ProxyAdmin } from "src/universal/ProxyAdmin.sol";
import { Proxy } from "src/universal/Proxy.sol"; import { Proxy } from "src/universal/Proxy.sol";
import { L1ChugSplashProxy } from "src/legacy/L1ChugSplashProxy.sol"; import { L1ChugSplashProxy } from "src/legacy/L1ChugSplashProxy.sol";
...@@ -191,7 +197,7 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -191,7 +197,7 @@ contract DeployImplementationsOutput is BaseDeployIO {
require(false, "DeployImplementationsOutput: not implemented"); require(false, "DeployImplementationsOutput: not implemented");
} }
function checkOutput(DeployImplementationsInput) public { function checkOutput(DeployImplementationsInput _dii) public {
address[] memory addrs = Solarray.addresses( address[] memory addrs = Solarray.addresses(
address(this.opsmProxy()), address(this.opsmProxy()),
address(this.optimismPortalImpl()), address(this.optimismPortalImpl()),
...@@ -207,7 +213,7 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -207,7 +213,7 @@ contract DeployImplementationsOutput is BaseDeployIO {
); );
DeployUtils.assertValidContractAddresses(addrs); DeployUtils.assertValidContractAddresses(addrs);
// TODO Also add the assertions for the implementation contracts from ChainAssertions.sol assertValidDeploy(_dii);
} }
function opsmProxy() public returns (OPStackManager) { function opsmProxy() public returns (OPStackManager) {
...@@ -265,6 +271,171 @@ contract DeployImplementationsOutput is BaseDeployIO { ...@@ -265,6 +271,171 @@ contract DeployImplementationsOutput is BaseDeployIO {
DeployUtils.assertValidContractAddress(address(_disputeGameFactoryImpl)); DeployUtils.assertValidContractAddress(address(_disputeGameFactoryImpl));
return _disputeGameFactoryImpl; return _disputeGameFactoryImpl;
} }
// -------- Deployment Assertions --------
function assertValidDeploy(DeployImplementationsInput _dii) public {
assertValidDelayedWETHImpl(_dii);
assertValidDisputeGameFactoryImpl(_dii);
assertValidL1CrossDomainMessengerImpl(_dii);
assertValidL1ERC721BridgeImpl(_dii);
assertValidL1StandardBridgeImpl(_dii);
assertValidMipsSingleton(_dii);
assertValidOpsmProxy(_dii);
assertValidOpsmImpl(_dii);
assertValidOptimismMintableERC20FactoryImpl(_dii);
assertValidOptimismPortalImpl(_dii);
assertValidPreimageOracleSingleton(_dii);
assertValidSystemConfigImpl(_dii);
}
function assertValidOpsmProxy(DeployImplementationsInput _dii) internal {
// First we check the proxy as itself.
Proxy proxy = Proxy(payable(address(opsmProxy())));
vm.prank(address(0));
address admin = proxy.admin();
require(admin == address(_dii.superchainProxyAdmin()), "OPSMP-10");
// Then we check the proxy as OPSM.
DeployUtils.assertInitialized({ _contractAddress: address(opsmProxy()), _slot: 0, _offset: 0 });
require(address(opsmProxy().superchainConfig()) == address(_dii.superchainConfigProxy()), "OPSMP-20");
require(address(opsmProxy().protocolVersions()) == address(_dii.protocolVersionsProxy()), "OPSMP-30");
require(LibString.eq(opsmProxy().latestRelease(), _dii.release()), "OPSMP-50"); // Initial release is latest.
}
function assertValidOpsmImpl(DeployImplementationsInput _dii) internal {
Proxy proxy = Proxy(payable(address(opsmProxy())));
vm.prank(address(0));
OPStackManager impl = OPStackManager(proxy.implementation());
DeployUtils.assertInitialized({ _contractAddress: address(impl), _slot: 0, _offset: 0 });
require(address(impl.superchainConfig()) == address(_dii.superchainConfigProxy()), "OPSMI-10");
require(address(impl.protocolVersions()) == address(_dii.protocolVersionsProxy()), "OPSMI-20");
}
function assertValidOptimismPortalImpl(DeployImplementationsInput) internal view {
OptimismPortal2 portal = optimismPortalImpl();
DeployUtils.assertInitialized({ _contractAddress: address(portal), _slot: 0, _offset: 0 });
require(address(portal.disputeGameFactory()) == address(0), "PORTAL-10");
require(address(portal.systemConfig()) == address(0), "PORTAL-20");
require(address(portal.superchainConfig()) == address(0), "PORTAL-30");
require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER, "PORTAL-40");
// 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), "PORTAL-50");
}
function assertValidDelayedWETHImpl(DeployImplementationsInput _dii) internal view {
DelayedWETH delayedWETH = delayedWETHImpl();
DeployUtils.assertInitialized({ _contractAddress: address(delayedWETH), _slot: 0, _offset: 0 });
require(delayedWETH.owner() == address(0), "DW-10");
require(delayedWETH.delay() == _dii.withdrawalDelaySeconds(), "DW-20");
require(delayedWETH.config() == ISuperchainConfig(address(0)), "DW-30");
}
function assertValidPreimageOracleSingleton(DeployImplementationsInput _dii) internal view {
PreimageOracle oracle = preimageOracleSingleton();
require(oracle.minProposalSize() == _dii.minProposalSizeBytes(), "PO-10");
require(oracle.challengePeriod() == _dii.challengePeriodSeconds(), "PO-20");
}
function assertValidMipsSingleton(DeployImplementationsInput) internal view {
MIPS mips = mipsSingleton();
require(address(mips.oracle()) == address(preimageOracleSingleton()), "MIPS-10");
}
function assertValidSystemConfigImpl(DeployImplementationsInput) internal view {
SystemConfig systemConfig = systemConfigImpl();
DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _slot: 0, _offset: 0 });
require(systemConfig.owner() == address(0xdead), "SYSCON-10");
require(systemConfig.overhead() == 0, "SYSCON-20");
require(systemConfig.scalar() == uint256(0x01) << 248, "SYSCON-30");
require(systemConfig.basefeeScalar() == 0, "SYSCON-40");
require(systemConfig.blobbasefeeScalar() == 0, "SYSCON-50");
require(systemConfig.batcherHash() == bytes32(0), "SYSCON-60");
require(systemConfig.gasLimit() == 1, "SYSCON-70");
require(systemConfig.unsafeBlockSigner() == address(0), "SYSCON-80");
IResourceMetering.ResourceConfig memory resourceConfig = systemConfig.resourceConfig();
require(resourceConfig.maxResourceLimit == 1, "SYSCON-90");
require(resourceConfig.elasticityMultiplier == 1, "SYSCON-100");
require(resourceConfig.baseFeeMaxChangeDenominator == 2, "SYSCON-110");
require(resourceConfig.systemTxMaxGas == 0, "SYSCON-120");
require(resourceConfig.minimumBaseFee == 0, "SYSCON-130");
require(resourceConfig.maximumBaseFee == 0, "SYSCON-140");
require(systemConfig.startBlock() == type(uint256).max, "SYSCON-150");
require(systemConfig.batchInbox() == address(0), "SYSCON-160");
require(systemConfig.l1CrossDomainMessenger() == address(0), "SYSCON-170");
require(systemConfig.l1ERC721Bridge() == address(0), "SYSCON-180");
require(systemConfig.l1StandardBridge() == address(0), "SYSCON-190");
require(systemConfig.disputeGameFactory() == address(0), "SYSCON-200");
require(systemConfig.optimismPortal() == address(0), "SYSCON-210");
require(systemConfig.optimismMintableERC20Factory() == address(0), "SYSCON-220");
}
function assertValidL1CrossDomainMessengerImpl(DeployImplementationsInput) internal view {
L1CrossDomainMessenger messenger = l1CrossDomainMessengerImpl();
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(0), "L1xDM-30");
require(address(messenger.portal()) == address(0), "L1xDM-40");
require(address(messenger.superchainConfig()) == address(0), "L1xDM-50");
bytes32 xdmSenderSlot = vm.load(address(messenger), bytes32(uint256(204)));
require(address(uint160(uint256(xdmSenderSlot))) == Constants.DEFAULT_L2_SENDER, "L1xDM-60");
}
function assertValidL1ERC721BridgeImpl(DeployImplementationsInput) internal view {
L1ERC721Bridge bridge = l1ERC721BridgeImpl();
DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 });
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "L721B-10");
require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "L721B-20");
require(address(bridge.MESSENGER()) == address(0), "L721B-30");
require(address(bridge.messenger()) == address(0), "L721B-40");
require(address(bridge.superchainConfig()) == address(0), "L721B-50");
}
function assertValidL1StandardBridgeImpl(DeployImplementationsInput) internal view {
L1StandardBridge bridge = l1StandardBridgeImpl();
DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 });
require(address(bridge.MESSENGER()) == address(0), "L1SB-10");
require(address(bridge.messenger()) == address(0), "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(0), "L1SB-50");
}
function assertValidOptimismMintableERC20FactoryImpl(DeployImplementationsInput) internal view {
OptimismMintableERC20Factory factory = optimismMintableERC20FactoryImpl();
DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 });
require(address(factory.BRIDGE()) == address(0), "MERC20F-10");
require(address(factory.bridge()) == address(0), "MERC20F-20");
}
function assertValidDisputeGameFactoryImpl(DeployImplementationsInput) internal view {
DisputeGameFactory factory = disputeGameFactoryImpl();
DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 });
require(address(factory.owner()) == address(0), "DG-10");
}
} }
contract DeployImplementations is Script { contract DeployImplementations is Script {
......
...@@ -274,16 +274,17 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -274,16 +274,17 @@ contract DeployOPChainOutput is BaseDeployIO {
return _delayedWETHPermissionlessGameProxy; return _delayedWETHPermissionlessGameProxy;
} }
// -------- Assertions on chain architecture -------- // -------- Deployment Assertions --------
function assertValidDeploy(DeployOPChainInput _doi) internal view { function assertValidDeploy(DeployOPChainInput _doi) internal view {
assertValidSystemConfig(_doi); assertValidDelayedWETHs(_doi);
assertValidDisputeGameFactory(_doi);
assertValidL1CrossDomainMessenger(_doi); assertValidL1CrossDomainMessenger(_doi);
assertValidL1ERC721Bridge(_doi);
assertValidL1StandardBridge(_doi); assertValidL1StandardBridge(_doi);
assertValidOptimismMintableERC20Factory(_doi); assertValidOptimismMintableERC20Factory(_doi);
assertValidOptimismPortal(_doi); assertValidOptimismPortal(_doi);
assertValidDisputeGameFactory(_doi); assertValidSystemConfig(_doi);
assertValidDelayedWETHs(_doi);
// TODO Other FP assertions like the dispute games, anchor state registry, etc. // TODO Other FP assertions like the dispute games, anchor state registry, etc.
// TODO add initialization assertions // TODO add initialization assertions
} }
...@@ -293,34 +294,38 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -293,34 +294,38 @@ contract DeployOPChainOutput is BaseDeployIO {
DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _slot: 0, _offset: 0 }); DeployUtils.assertInitialized({ _contractAddress: address(systemConfig), _slot: 0, _offset: 0 });
require(systemConfig.owner() == _doi.systemConfigOwner(), "SC-10"); require(systemConfig.owner() == _doi.systemConfigOwner(), "SYSCON-10");
require(systemConfig.basefeeScalar() == _doi.basefeeScalar(), "SC-20"); require(systemConfig.basefeeScalar() == _doi.basefeeScalar(), "SYSCON-20");
require(systemConfig.blobbasefeeScalar() == _doi.blobBaseFeeScalar(), "SC-30"); require(systemConfig.blobbasefeeScalar() == _doi.blobBaseFeeScalar(), "SYSCON-30");
require(systemConfig.batcherHash() == bytes32(uint256(uint160(_doi.batcher()))), "SC-40"); require(systemConfig.batcherHash() == bytes32(uint256(uint160(_doi.batcher()))), "SYSCON-40");
require(systemConfig.gasLimit() == uint64(30000000), "SC-50"); // TODO allow other gas limits? require(systemConfig.gasLimit() == uint64(30000000), "SYSCON-50"); // TODO allow other gas limits?
require(systemConfig.unsafeBlockSigner() == _doi.unsafeBlockSigner(), "SC-60"); require(systemConfig.unsafeBlockSigner() == _doi.unsafeBlockSigner(), "SYSCON-60");
require(systemConfig.scalar() >> 248 == 1, "SC-70"); require(systemConfig.scalar() >> 248 == 1, "SYSCON-70");
IResourceMetering.ResourceConfig memory rConfig = Constants.DEFAULT_RESOURCE_CONFIG(); IResourceMetering.ResourceConfig memory rConfig = Constants.DEFAULT_RESOURCE_CONFIG();
IResourceMetering.ResourceConfig memory outputConfig = systemConfig.resourceConfig(); IResourceMetering.ResourceConfig memory outputConfig = systemConfig.resourceConfig();
require(outputConfig.maxResourceLimit == rConfig.maxResourceLimit, "SC-80"); require(outputConfig.maxResourceLimit == rConfig.maxResourceLimit, "SYSCON-80");
require(outputConfig.elasticityMultiplier == rConfig.elasticityMultiplier, "SC-90"); require(outputConfig.elasticityMultiplier == rConfig.elasticityMultiplier, "SYSCON-90");
require(outputConfig.baseFeeMaxChangeDenominator == rConfig.baseFeeMaxChangeDenominator, "SC-100"); require(outputConfig.baseFeeMaxChangeDenominator == rConfig.baseFeeMaxChangeDenominator, "SYSCON-100");
require(outputConfig.systemTxMaxGas == rConfig.systemTxMaxGas, "SC-110"); require(outputConfig.systemTxMaxGas == rConfig.systemTxMaxGas, "SYSCON-110");
require(outputConfig.minimumBaseFee == rConfig.minimumBaseFee, "SC-120"); require(outputConfig.minimumBaseFee == rConfig.minimumBaseFee, "SYSCON-120");
require(outputConfig.maximumBaseFee == rConfig.maximumBaseFee, "SC-130"); require(outputConfig.maximumBaseFee == rConfig.maximumBaseFee, "SYSCON-130");
require(systemConfig.startBlock() == block.number, "SC-140"); require(systemConfig.startBlock() == block.number, "SYSCON-140");
require(systemConfig.batchInbox() == _doi.opsmProxy().chainIdToBatchInboxAddress(_doi.l2ChainId()), "SC-150"); require(
systemConfig.batchInbox() == _doi.opsmProxy().chainIdToBatchInboxAddress(_doi.l2ChainId()), "SYSCON-150"
require(systemConfig.l1CrossDomainMessenger() == address(l1CrossDomainMessengerProxy()), "SC-160"); );
require(systemConfig.l1ERC721Bridge() == address(l1ERC721BridgeProxy()), "SC-170");
require(systemConfig.l1StandardBridge() == address(l1StandardBridgeProxy()), "SC-180"); require(systemConfig.l1CrossDomainMessenger() == address(l1CrossDomainMessengerProxy()), "SYSCON-160");
require(systemConfig.disputeGameFactory() == address(disputeGameFactoryProxy()), "SC-190"); require(systemConfig.l1ERC721Bridge() == address(l1ERC721BridgeProxy()), "SYSCON-170");
require(systemConfig.optimismPortal() == address(optimismPortalProxy()), "SC-200"); require(systemConfig.l1StandardBridge() == address(l1StandardBridgeProxy()), "SYSCON-180");
require(systemConfig.optimismMintableERC20Factory() == address(optimismMintableERC20FactoryProxy()), "SC-210"); require(systemConfig.disputeGameFactory() == address(disputeGameFactoryProxy()), "SYSCON-190");
require(systemConfig.optimismPortal() == address(optimismPortalProxy()), "SYSCON-200");
require(
systemConfig.optimismMintableERC20Factory() == address(optimismMintableERC20FactoryProxy()), "SYSCON-210"
);
(address gasPayingToken,) = systemConfig.gasPayingToken(); (address gasPayingToken,) = systemConfig.gasPayingToken();
require(gasPayingToken == Constants.ETHER, "SC-220"); require(gasPayingToken == Constants.ETHER, "SYSCON-220");
} }
function assertValidL1CrossDomainMessenger(DeployOPChainInput _doi) internal view { function assertValidL1CrossDomainMessenger(DeployOPChainInput _doi) internal view {
...@@ -357,8 +362,8 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -357,8 +362,8 @@ contract DeployOPChainOutput is BaseDeployIO {
DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 }); DeployUtils.assertInitialized({ _contractAddress: address(factory), _slot: 0, _offset: 0 });
require(factory.BRIDGE() == address(l1StandardBridgeProxy()), "OMEF-10"); require(factory.BRIDGE() == address(l1StandardBridgeProxy()), "MERC20F-10");
require(factory.bridge() == address(l1StandardBridgeProxy()), "OMEF-20"); require(factory.bridge() == address(l1StandardBridgeProxy()), "MERC20F-20");
} }
function assertValidL1ERC721Bridge(DeployOPChainInput _doi) internal view { function assertValidL1ERC721Bridge(DeployOPChainInput _doi) internal view {
...@@ -366,24 +371,24 @@ contract DeployOPChainOutput is BaseDeployIO { ...@@ -366,24 +371,24 @@ contract DeployOPChainOutput is BaseDeployIO {
DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 }); DeployUtils.assertInitialized({ _contractAddress: address(bridge), _slot: 0, _offset: 0 });
require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "LEB-10"); require(address(bridge.OTHER_BRIDGE()) == Predeploys.L2_ERC721_BRIDGE, "L721B-10");
require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "LEB-20"); require(address(bridge.otherBridge()) == Predeploys.L2_ERC721_BRIDGE, "L721B-20");
require(address(bridge.MESSENGER()) == address(l1CrossDomainMessengerProxy()), "LEB-30"); require(address(bridge.MESSENGER()) == address(l1CrossDomainMessengerProxy()), "L721B-30");
require(address(bridge.messenger()) == address(l1CrossDomainMessengerProxy()), "LEB-40"); require(address(bridge.messenger()) == address(l1CrossDomainMessengerProxy()), "L721B-40");
require(address(bridge.superchainConfig()) == address(_doi.opsmProxy().superchainConfig()), "LEB-50"); require(address(bridge.superchainConfig()) == address(_doi.opsmProxy().superchainConfig()), "L721B-50");
} }
function assertValidOptimismPortal(DeployOPChainInput _doi) internal view { function assertValidOptimismPortal(DeployOPChainInput _doi) internal view {
OptimismPortal2 portal = optimismPortalProxy(); OptimismPortal2 portal = optimismPortalProxy();
ISuperchainConfig superchainConfig = ISuperchainConfig(address(_doi.opsmProxy().superchainConfig())); ISuperchainConfig superchainConfig = ISuperchainConfig(address(_doi.opsmProxy().superchainConfig()));
require(address(portal.disputeGameFactory()) == address(disputeGameFactoryProxy()), "OP-10"); require(address(portal.disputeGameFactory()) == address(disputeGameFactoryProxy()), "PORTAL-10");
require(address(portal.systemConfig()) == address(systemConfigProxy()), "OP-20"); require(address(portal.systemConfig()) == address(systemConfigProxy()), "PORTAL-20");
require(address(portal.superchainConfig()) == address(superchainConfig), "OP-30"); require(address(portal.superchainConfig()) == address(superchainConfig), "PORTAL-30");
require(portal.guardian() == superchainConfig.guardian(), "OP-40"); require(portal.guardian() == superchainConfig.guardian(), "PORTAL-40");
require(portal.paused() == superchainConfig.paused(), "OP-50"); require(portal.paused() == superchainConfig.paused(), "PORTAL-50");
require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER, "OP-60"); require(portal.l2Sender() == Constants.DEFAULT_L2_SENDER, "PORTAL-60");
// This slot is the custom gas token _balance and this check ensures // This slot is the custom gas token _balance and this check ensures
// that it stays unset for forwards compatibility with custom gas token. // that it stays unset for forwards compatibility with custom gas token.
......
...@@ -210,7 +210,7 @@ contract DeploySuperchainOutput is BaseDeployIO { ...@@ -210,7 +210,7 @@ contract DeploySuperchainOutput is BaseDeployIO {
// This function can be called to ensure all outputs are correct. Similar to `writeOutputFile`, // This function can be called to ensure all outputs are correct. Similar to `writeOutputFile`,
// it fetches the output values using external calls to the getter methods for safety. // it fetches the output values using external calls to the getter methods for safety.
function checkOutput(DeploySuperchainInput) public { function checkOutput(DeploySuperchainInput _dsi) public {
address[] memory addrs = Solarray.addresses( address[] memory addrs = Solarray.addresses(
address(this.superchainProxyAdmin()), address(this.superchainProxyAdmin()),
address(this.superchainConfigImpl()), address(this.superchainConfigImpl()),
...@@ -230,6 +230,7 @@ contract DeploySuperchainOutput is BaseDeployIO { ...@@ -230,6 +230,7 @@ contract DeploySuperchainOutput is BaseDeployIO {
require(actualProtocolVersionsImpl == address(_protocolVersionsImpl), "200"); require(actualProtocolVersionsImpl == address(_protocolVersionsImpl), "200");
// TODO Also add the assertions for the implementation contracts from ChainAssertions.sol // TODO Also add the assertions for the implementation contracts from ChainAssertions.sol
assertValidDeploy(_dsi);
} }
function superchainProxyAdmin() public view returns (ProxyAdmin) { function superchainProxyAdmin() public view returns (ProxyAdmin) {
...@@ -256,6 +257,62 @@ contract DeploySuperchainOutput is BaseDeployIO { ...@@ -256,6 +257,62 @@ contract DeploySuperchainOutput is BaseDeployIO {
DeployUtils.assertValidContractAddress(address(_protocolVersionsProxy)); DeployUtils.assertValidContractAddress(address(_protocolVersionsProxy));
return _protocolVersionsProxy; return _protocolVersionsProxy;
} }
// -------- Deployment Assertions --------
function assertValidDeploy(DeploySuperchainInput _dsi) public {
assertValidSuperchainProxyAdmin(_dsi);
assertValidSuperchainConfig(_dsi);
assertValidProtocolVersions(_dsi);
}
function assertValidSuperchainProxyAdmin(DeploySuperchainInput _dsi) internal view {
require(superchainProxyAdmin().owner() == _dsi.proxyAdminOwner(), "SPA-10");
}
function assertValidSuperchainConfig(DeploySuperchainInput _dsi) internal {
// Proxy checks.
SuperchainConfig superchainConfig = superchainConfigProxy();
DeployUtils.assertInitialized({ _contractAddress: address(superchainConfig), _slot: 0, _offset: 0 });
require(superchainConfig.guardian() == _dsi.guardian(), "SUPCON-10");
require(superchainConfig.paused() == _dsi.paused(), "SUPCON-20");
vm.startPrank(address(0));
require(
Proxy(payable(address(superchainConfig))).implementation() == address(superchainConfigImpl()), "SUPCON-30"
);
require(Proxy(payable(address(superchainConfig))).admin() == address(superchainProxyAdmin()), "SUPCON-40");
vm.stopPrank();
// Implementation checks
superchainConfig = superchainConfigImpl();
require(superchainConfig.guardian() == address(0), "SUPCON-50");
require(superchainConfig.paused() == false, "SUPCON-60");
}
function assertValidProtocolVersions(DeploySuperchainInput _dsi) internal {
// Proxy checks.
ProtocolVersions pv = protocolVersionsProxy();
DeployUtils.assertInitialized({ _contractAddress: address(pv), _slot: 0, _offset: 0 });
require(pv.owner() == _dsi.protocolVersionsOwner(), "PV-10");
require(
ProtocolVersion.unwrap(pv.required()) == ProtocolVersion.unwrap(_dsi.requiredProtocolVersion()), "PV-20"
);
require(
ProtocolVersion.unwrap(pv.recommended()) == ProtocolVersion.unwrap(_dsi.recommendedProtocolVersion()),
"PV-30"
);
vm.startPrank(address(0));
require(Proxy(payable(address(pv))).implementation() == address(protocolVersionsImpl()), "PV-40");
require(Proxy(payable(address(pv))).admin() == address(superchainProxyAdmin()), "PV-50");
vm.stopPrank();
// Implementation checks.
pv = protocolVersionsImpl();
require(pv.owner() == address(0xdead), "PV-60");
require(ProtocolVersion.unwrap(pv.required()) == 0, "PV-70");
require(ProtocolVersion.unwrap(pv.recommended()) == 0, "PV-80");
}
} }
// For all broadcasts in this script we explicitly specify the deployer as `msg.sender` because for // For all broadcasts in this script we explicitly specify the deployer as `msg.sender` because for
......
...@@ -48,13 +48,15 @@ library DeployUtils { ...@@ -48,13 +48,15 @@ library DeployUtils {
} }
} }
// Asserts that for a given contract the value of a storage slot at an offset is 1. This // Asserts that for a given contract the value of a storage slot at an offset is 1 or
// is used to assert that a contract is initialized. // `type(uint8).max`. The value is set to 1 when a contract is initialized, and set to
// `type(uint8).max` when `_disableInitializers` is called.
function assertInitialized(address _contractAddress, uint256 _slot, uint256 _offset) internal view { function assertInitialized(address _contractAddress, uint256 _slot, uint256 _offset) internal view {
bytes32 slotVal = vm.load(_contractAddress, bytes32(_slot)); bytes32 slotVal = vm.load(_contractAddress, bytes32(_slot));
uint8 value = uint8((uint256(slotVal) >> (_offset * 8)) & 0xFF);
require( require(
uint8((uint256(slotVal) >> (_offset * 8)) & 0xFF) == uint8(1), value == 1 || value == type(uint8).max,
"Storage value is not 1 at the given slot and offset" "Value at the given slot and offset does not indicate initialization"
); );
} }
} }
...@@ -284,8 +284,8 @@ contract DeploySuperchain_Test is Test { ...@@ -284,8 +284,8 @@ contract DeploySuperchain_Test is Test {
uint256 slot = zeroOutSlotForSelector(dsi.proxyAdminOwner.selector); uint256 slot = zeroOutSlotForSelector(dsi.proxyAdminOwner.selector);
vm.expectRevert("DeploySuperchainInput: proxyAdminOwner not set"); vm.expectRevert("DeploySuperchainInput: proxyAdminOwner not set");
deploySuperchain.run(dsi, dso); deploySuperchain.run(dsi, dso);
vm.store(address(dsi), bytes32(slot), bytes32(uint256(uint160(defaultProxyAdminOwner)))); // Restore the value // Restore the value we just tested.
// we just tested. vm.store(address(dsi), bytes32(slot), bytes32(uint256(uint160(defaultProxyAdminOwner))));
slot = zeroOutSlotForSelector(dsi.protocolVersionsOwner.selector); slot = zeroOutSlotForSelector(dsi.protocolVersionsOwner.selector);
vm.expectRevert("DeploySuperchainInput: protocolVersionsOwner not set"); vm.expectRevert("DeploySuperchainInput: protocolVersionsOwner not set");
......
...@@ -5,4 +5,4 @@ recommendedProtocolVersion = 2 ...@@ -5,4 +5,4 @@ recommendedProtocolVersion = 2
[roles] [roles]
proxyAdminOwner = "0x51f0348a9fA2aAbaB45E82825Fbd13d406e04497" proxyAdminOwner = "0x51f0348a9fA2aAbaB45E82825Fbd13d406e04497"
protocolVersionsOwner = "0xeEB4cc05dC0dE43c465f97cfc703D165418CA93A" protocolVersionsOwner = "0xeEB4cc05dC0dE43c465f97cfc703D165418CA93A"
guardian = "0xE5DbA98c65F4B9EB0aeEBb3674fE64f88509a1eC" guardian = "0xE5DbA98c65F4B9EB0aeEBb3674fE64f88509a1eC"
\ No newline at end of file
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