diff --git a/.circleci/config.yml b/.circleci/config.yml index cbddd563fce9c..05c1a77a767fd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1489,6 +1489,21 @@ workflows: fork_op_chain: op fork_base_chain: mainnet fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io + - contracts-bedrock-tests-upgrade: + name: contracts-bedrock-tests-upgrade base-mainnet + fork_op_chain: base + fork_base_chain: mainnet + fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io + - contracts-bedrock-tests-upgrade: + name: contracts-bedrock-tests-upgrade ink-mainnet + fork_op_chain: ink + fork_base_chain: mainnet + fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io + - contracts-bedrock-tests-upgrade: + name: contracts-bedrock-tests-upgrade unichain-mainnet + fork_op_chain: unichain + fork_base_chain: mainnet + fork_base_rpc: https://ci-mainnet-l1-archive.optimism.io - contracts-bedrock-checks: requires: - contracts-bedrock-build diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index e256ab97e995d..ab85d8853f5d9 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -66,6 +66,7 @@ fs_permissions = [ { access='read', path='./kout-deployment' }, { access='read', path='./test/fixtures' }, { access='read', path='./lib/superchain-registry/superchain/configs/' }, + { access='read', path='./lib/superchain-registry/validation/standard/' }, ] # 5159 error code is selfdestruct error code diff --git a/packages/contracts-bedrock/lib/superchain-registry b/packages/contracts-bedrock/lib/superchain-registry index b9383cc1d0e97..f5d8c509f0241 160000 --- a/packages/contracts-bedrock/lib/superchain-registry +++ b/packages/contracts-bedrock/lib/superchain-registry @@ -1 +1 @@ -Subproject commit b9383cc1d0e97c6699e78dda06465aca182647c5 +Subproject commit f5d8c509f0241cb2c59f508a889db1980a60ad89 diff --git a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol index abdc2bb7c5c33..c57425f439101 100644 --- a/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol +++ b/packages/contracts-bedrock/test/L1/OPContractsManager.t.sol @@ -245,6 +245,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { address upgrader; IOPContractsManager.OpChainConfig[] opChainConfigs; Claim absolutePrestate; + string public opChain = vm.envOr("FORK_OP_CHAIN", string("op")); function setUp() public virtual override { super.disableUpgradedFork(); @@ -293,10 +294,25 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { function runUpgrade13UpgradeAndChecks(address _delegateCaller) public { // The address below corresponds with the address of the v2.0.0-rc.1 OPCM on mainnet. - IOPContractsManager deployedOPCM = IOPContractsManager(address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76)); + address OPCM_ADDRESS = 0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76; + + IOPContractsManager deployedOPCM = IOPContractsManager(OPCM_ADDRESS); IOPCMImplementationsWithoutLockbox.Implementations memory impls = IOPCMImplementationsWithoutLockbox(address(deployedOPCM)).implementations(); + address mainnetPAO = artifacts.mustGetAddress("SuperchainConfigProxy"); + + // If the delegate caller is not the mainnet PAO, we need to call upgrade as the mainnet PAO first. + if (_delegateCaller != mainnetPAO) { + IOPContractsManager.OpChainConfig[] memory opmChain = new IOPContractsManager.OpChainConfig[](0); + ISuperchainConfig superchainConfig = ISuperchainConfig(mainnetPAO); + + address opmUpgrader = IProxyAdmin(EIP1967Helper.getAdmin(address(superchainConfig))).owner(); + vm.etch(opmUpgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); + + DelegateCaller(opmUpgrader).dcForward(OPCM_ADDRESS, abi.encodeCall(IOPContractsManager.upgrade, (opmChain))); + } + // Cache the old L1xDM address so we can look for it in the AddressManager's event address oldL1CrossDomainMessenger = addressManager.getAddress("OVM_L1CrossDomainMessenger"); @@ -344,6 +360,7 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { vm.expectEmit(false, true, true, true, address(disputeGameFactory)); emit ImplementationSet(address(0), GameTypes.CANNON); } + vm.expectEmit(address(_delegateCaller)); emit Upgraded(l2ChainId, opChainConfigs[0].systemConfigProxy, address(_delegateCaller)); @@ -400,10 +417,25 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { } function runUpgrade14UpgradeAndChecks(address _delegateCaller) public { - IOPContractsManager deployedOPCM = IOPContractsManager(address(0x3A1f523a4bc09cd344A2745a108Bb0398288094F)); + address OPCM_ADDRESS = 0x3A1f523a4bc09cd344A2745a108Bb0398288094F; + + IOPContractsManager deployedOPCM = IOPContractsManager(OPCM_ADDRESS); IOPCMImplementationsWithoutLockbox.Implementations memory impls = IOPCMImplementationsWithoutLockbox(address(deployedOPCM)).implementations(); + address mainnetPAO = artifacts.mustGetAddress("SuperchainConfigProxy"); + + // If the delegate caller is not the mainnet PAO, we need to call upgrade as the mainnet PAO first. + if (_delegateCaller != mainnetPAO) { + IOPContractsManager.OpChainConfig[] memory opmChain = new IOPContractsManager.OpChainConfig[](0); + ISuperchainConfig superchainConfig = ISuperchainConfig(mainnetPAO); + + address opmUpgrader = IProxyAdmin(EIP1967Helper.getAdmin(address(superchainConfig))).owner(); + vm.etch(opmUpgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); + + DelegateCaller(opmUpgrader).dcForward(OPCM_ADDRESS, abi.encodeCall(IOPContractsManager.upgrade, (opmChain))); + } + // sanity check IPermissionedDisputeGame oldPDG = IPermissionedDisputeGame(address(disputeGameFactory.gameImpls(GameTypes.PERMISSIONED_CANNON))); @@ -589,11 +621,13 @@ contract OPContractsManager_Upgrade_Harness is CommonTest { contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { function test_upgradeOPChainOnly_succeeds() public { + skipIfNotOpFork("test_upgradeOPChainOnly_succeeds"); // Run the upgrade test and checks runUpgradeTestAndChecks(upgrader); } function test_isRcFalseAfterCalledByUpgrader_works() public { + skipIfNotOpFork("test_isRcFalseAfterCalledByUpgrader_works"); assertTrue(opcm.isRC()); bytes memory releaseBytes = bytes(opcm.l1ContractsRelease()); assertEq(Bytes.slice(releaseBytes, releaseBytes.length - 3, 3), "-rc", "release should end with '-rc'"); @@ -610,6 +644,7 @@ contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { ) public { + skipIfNotOpFork("testFuzz_upgrade_nonUpgradeControllerDelegatecallerShouldNotSetIsRCToFalse_works"); if ( _nonUpgradeController == upgrader || _nonUpgradeController == address(0) || _nonUpgradeController < address(0x4200000000000000000000000000000000000000) @@ -638,6 +673,8 @@ contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { } function test_upgrade_duplicateL2ChainId_succeeds() public { + skipIfNotOpFork("test_upgrade_duplicateL2ChainId_succeeds"); + // Deploy a new OPChain with the same L2 chain ID as the current OPChain Deploy deploy = Deploy(address(uint160(uint256(keccak256(abi.encode("optimism.deploy")))))); IOPContractsManager.DeployInput memory deployInput = deploy.getDeployInput(); @@ -726,6 +763,7 @@ contract OPContractsManager_Upgrade_Test is OPContractsManager_Upgrade_Harness { contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harness { // Upgrade to U14 first function setUp() public override { + skipIfNotOpFork("test_upgrade_notDelegateCalled_reverts"); super.setUp(); runUpgrade13UpgradeAndChecks(upgrader); } @@ -775,6 +813,8 @@ contract OPContractsManager_Upgrade_TestFails is OPContractsManager_Upgrade_Harn contract OPContractsManager_SetRC_Test is OPContractsManager_Upgrade_Harness { /// @notice Tests the setRC function can be set by the upgrade controller. function test_setRC_succeeds(bool _isRC) public { + skipIfNotOpFork("test_setRC_succeeds"); + vm.prank(upgrader); opcm.setRC(_isRC); diff --git a/packages/contracts-bedrock/test/setup/ForkLive.s.sol b/packages/contracts-bedrock/test/setup/ForkLive.s.sol index 70839e22bbfe4..40e9c94e33148 100644 --- a/packages/contracts-bedrock/test/setup/ForkLive.s.sol +++ b/packages/contracts-bedrock/test/setup/ForkLive.s.sol @@ -14,6 +14,7 @@ import { Deploy } from "scripts/deploy/Deploy.s.sol"; // Libraries import { GameTypes, Claim } from "src/dispute/lib/Types.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; +import { LibString } from "solady/src/utils/LibString.sol"; // Interfaces import { IFaultDisputeGame } from "interfaces/dispute/IFaultDisputeGame.sol"; @@ -21,6 +22,7 @@ import { IPermissionedDisputeGame } from "interfaces/dispute/IPermissionedDisput import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { IAddressManager } from "interfaces/legacy/IAddressManager.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; +import { ISuperchainConfig } from "interfaces/L1/ISuperchainConfig.sol"; import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; import { IOPContractsManager } from "interfaces/L1/IOPContractsManager.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; @@ -39,6 +41,7 @@ import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; contract ForkLive is Deployer { using stdToml for string; + using LibString for string; bool public useOpsRepo; @@ -99,16 +102,24 @@ contract ForkLive is Deployer { /// using either the `saveProxyAndImpl` or `artifacts.save()` functions. function _readSuperchainRegistry() internal { string memory superchainBasePath = "./lib/superchain-registry/superchain/configs/"; + string memory validationBasePath = "./lib/superchain-registry/validation/standard/"; string memory superchainToml = vm.readFile(string.concat(superchainBasePath, baseChain(), "/superchain.toml")); string memory opToml = vm.readFile(string.concat(superchainBasePath, baseChain(), "/", opChain(), ".toml")); + string memory standardVersionsToml = + vm.readFile(string.concat(validationBasePath, "standard-versions-", baseChain(), ".toml")); + + standardVersionsToml = standardVersionsToml.replace('"op-contracts/v2.0.0-rc.1"', "RELEASE"); + // Slightly hacky, we encode the uint chainId as an address to save it in Artifacts artifacts.save("L2ChainId", address(uint160(vm.parseTomlUint(opToml, ".chain_id")))); // Superchain shared contracts saveProxyAndImpl("SuperchainConfig", superchainToml, ".superchain_config_addr"); saveProxyAndImpl("ProtocolVersions", superchainToml, ".protocol_versions_addr"); - artifacts.save("OPContractsManager", vm.parseTomlAddress(superchainToml, ".op_contracts_manager_proxy_addr")); + artifacts.save( + "OPContractsManager", vm.parseTomlAddress(standardVersionsToml, "$.RELEASE.op_contracts_manager.address") + ); // Core contracts artifacts.save("ProxyAdmin", vm.parseTomlAddress(opToml, ".addresses.ProxyAdmin")); @@ -192,12 +203,33 @@ contract ForkLive is Deployer { absolutePrestate: Claim.wrap(bytes32(keccak256("absolutePrestate"))) }); + IOPContractsManager.OpChainConfig[] memory opmChain = new IOPContractsManager.OpChainConfig[](0); // Temporarily replace the upgrader with a DelegateCaller so we can test the upgrade, // then reset its code to the original code. + bytes memory upgraderCode = address(upgrader).code; vm.etch(upgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); + // The 2.0.0 OPCM requires that the SuperchainConfig and ProtocolVersions contracts + // have been upgraded before it will upgrade other contracts. + // Those contracts can only be upgrade by the OP Mainnet ProxyAdminOwner. So for chains which have a different + // ProxyAdminOwner, we first need to call opcm.upgrade from the OP Mainnet PAO. + address mainnetPAO = artifacts.mustGetAddress("SuperchainConfigProxy"); + + if (upgrader != mainnetPAO) { + ISuperchainConfig superchainConfig = ISuperchainConfig(mainnetPAO); + + address opmUpgrader = IProxyAdmin(EIP1967Helper.getAdmin(address(superchainConfig))).owner(); + vm.etch(opmUpgrader, vm.getDeployedCode("test/mocks/Callers.sol:DelegateCaller")); + + DelegateCaller(opmUpgrader).dcForward( + address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), + abi.encodeCall(IOPContractsManager.upgrade, (opmChain)) + ); + } + // Start by doing Upgrade 13. + DelegateCaller(upgrader).dcForward( address(0x026b2F158255Beac46c1E7c6b8BbF29A4b6A7B76), abi.encodeCall(IOPContractsManager.upgrade, (opChains)) ); diff --git a/packages/contracts-bedrock/test/setup/Setup.sol b/packages/contracts-bedrock/test/setup/Setup.sol index a650d23ee3405..f014acf8eda20 100644 --- a/packages/contracts-bedrock/test/setup/Setup.sol +++ b/packages/contracts-bedrock/test/setup/Setup.sol @@ -140,6 +140,12 @@ contract Setup { return vm.envOr("FORK_TEST", false); } + /// @notice Indicates whether a test is running against a forked network that is OP. + function isOpFork() public view returns (bool) { + string memory opChain = vm.envOr("FORK_OP_CHAIN", string("op")); + return keccak256(bytes(opChain)) == keccak256(bytes("op")); + } + /// @dev Deploys either the Deploy.s.sol or Fork.s.sol contract, by fetching the bytecode dynamically using /// `vm.getDeployedCode()` and etching it into the state. /// This enables us to avoid including the bytecode of those contracts in the bytecode of this contract. @@ -195,6 +201,14 @@ contract Setup { } } + /// @dev Skips tests when running against a forked production network that is not OP. + function skipIfNotOpFork(string memory message) public { + if (isForkTest() && !isOpFork()) { + vm.skip(true); + console.log(string.concat("Skipping non-OP fork test: ", message)); + } + } + /// @dev Skips tests when running against a forked production network using the superchain ops repo. function skipIfOpsRepoTest(string memory message) public { if (forkLive.useOpsRepo()) {