diff --git a/interfaces/legacy/ILegacySuperchainConfig.sol b/interfaces/legacy/ILegacySuperchainConfig.sol deleted file mode 100644 index fbd92af3e..000000000 --- a/interfaces/legacy/ILegacySuperchainConfig.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface ILegacySuperchainConfig { - function pausable(address _identifier) external view returns (bool); -} - -interface ILegacySystemConfig { - function superchainConfig() external view returns (ILegacySuperchainConfig); -} diff --git a/justfile b/justfile index 006ee4831..edd425a42 100644 --- a/justfile +++ b/justfile @@ -108,6 +108,7 @@ print-pinned-block-number: # Prepares the environment for upgrade path variant of contract tests and coverage. # Env Vars: # - ETH_RPC_URL must be set to a production (Sepolia or Mainnet) RPC URL. +# - SUPERCHAIN_OPS_ALLOCS_PATH must point to simulated post-upgrade state from superchain-ops. # - FORK_BLOCK_NUMBER can be set in the env, or else will fallback to the default block number. # Reusing the default block number greatly speeds up the test execution time by caching the # rpc call responses in ~/.foundry/cache/rpc. The default block will need to be updated @@ -136,6 +137,7 @@ anvil-fork: anvil --fork-url $ETH_RPC_URL # Use anvil-fork in a separate terminal before running this command. +# Requires SUPERCHAIN_OPS_ALLOCS_PATH with simulated post-upgrade state from superchain-ops. # Helpful for debugging. test-upgrade-against-anvil *ARGS: build-go-ffi #!/bin/bash diff --git a/scripts/deploy/DeployConfig.s.sol b/scripts/deploy/DeployConfig.s.sol index bc31b424d..6eda44dfc 100644 --- a/scripts/deploy/DeployConfig.s.sol +++ b/scripts/deploy/DeployConfig.s.sol @@ -26,7 +26,6 @@ contract DeployConfig is Script { address public teeProposer; bool public fundDevAccounts; - bool public useUpgradedFork; bytes32 public multiproofConfigHash; bytes32 public multiproofGenesisOutputRoot; @@ -152,15 +151,4 @@ contract DeployConfig is Script { _protocolVersionsInitialSchedule.push(uint64(schedule[i])); } } - - /// @notice Allow the `useUpgradedFork` config to be overridden in testing environments - /// @dev When true, the forked system WILL be upgraded in setUp(). - /// When false, the forked system WILL NOT be upgraded in setUp(). - /// This function does nothing when not testing in a forked environment. - /// Generally the only time you should call this function is if you want to - /// call the upgrade script in the test itself, rather than have the upgraded - /// system be deployed in setUp(). - function setUseUpgradedFork(bool _useUpgradedFork) public { - useUpgradedFork = _useUpgradedFork; - } } diff --git a/scripts/deploy/SystemDeploy.s.sol b/scripts/deploy/SystemDeploy.s.sol index b33caba8f..867725561 100644 --- a/scripts/deploy/SystemDeploy.s.sol +++ b/scripts/deploy/SystemDeploy.s.sol @@ -19,7 +19,6 @@ import { IOptimismPortal2 as IOptimismPortal } from "interfaces/L1/IOptimismPort import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; import { IAddressManager } from "interfaces/legacy/IAddressManager.sol"; -import { ILegacySuperchainConfig, ILegacySystemConfig } from "interfaces/legacy/ILegacySuperchainConfig.sol"; import { IL1ChugSplashProxy } from "interfaces/legacy/IL1ChugSplashProxy.sol"; import { IResolvedDelegateProxy } from "interfaces/legacy/IResolvedDelegateProxy.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; @@ -41,10 +40,9 @@ import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; import { ZKVerifier } from "src/L1/proofs/zk/ZKVerifier.sol"; import { Constants } from "src/libraries/Constants.sol"; import { GameType, GameTypes, Hash, Proposal } from "src/libraries/bridge/Types.sol"; -import { Claim } from "src/libraries/bridge/LibUDT.sol"; /// @title SystemDeploy -/// @notice Script-level API for deploying or upgrading a complete OP Stack L1 system. +/// @notice Script-level API for deploying a complete OP Stack L1 system. contract SystemDeploy is Script { DeployConfig public constant cfg = DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); @@ -89,17 +87,6 @@ contract SystemDeploy is Script { Types.DeployOutput opChain; } - struct UpgradeInput { - bool saveArtifacts; - Types.Implementations implementations; - ISystemConfig systemConfigProxy; - IProtocolVersions protocolVersionsProxy; - } - - struct UpgradeOutput { - bool chainUpgraded; - } - struct AggregateVerifierInput { GameType multiproofGameType; IAnchorStateRegistry anchorStateRegistry; @@ -127,13 +114,10 @@ contract SystemDeploy is Script { } event Deployed(uint256 indexed l2ChainId, address indexed deployer, bytes deployOutput); - event Upgraded(uint256 indexed l2ChainId, ISystemConfig indexed systemConfig, address indexed upgrader); error InvalidChainId(); error InvalidRoleAddress(string role); error InvalidStartingAnchorRoot(); - error LegacySuperchainConfigPaused(); - error MissingImplementations(); /// @notice Sets up the shared deployment config and artifact registry. function setUp() public virtual { @@ -182,32 +166,6 @@ contract SystemDeploy is Script { _runConfigured(); } - /// @notice Deploys implementation contracts from the active deploy config and saves their artifact names. - function deployImplementations() public returns (Types.Implementations memory output_) { - output_ = _deployImplementations(_configuredImplementationsInput()); - _saveUpgradeArtifacts(output_); - } - - /// @notice Returns the latest implementation set saved by `deployImplementations` or `run`. - function getImplementations() public view returns (Types.Implementations memory) { - return Types.Implementations({ - l1ERC721BridgeImpl: artifacts.mustGetAddress("L1ERC721BridgeImpl"), - optimismPortalImpl: artifacts.mustGetAddress("OptimismPortalImpl"), - systemConfigImpl: artifacts.mustGetAddress("SystemConfigImpl"), - optimismMintableERC20FactoryImpl: artifacts.mustGetAddress("OptimismMintableERC20FactoryImpl"), - l1CrossDomainMessengerImpl: artifacts.mustGetAddress("L1CrossDomainMessengerImpl"), - l1StandardBridgeImpl: artifacts.mustGetAddress("L1StandardBridgeImpl"), - disputeGameFactoryImpl: artifacts.mustGetAddress("DisputeGameFactoryImpl"), - anchorStateRegistryImpl: artifacts.mustGetAddress("AnchorStateRegistryImpl"), - delayedWETHImpl: artifacts.mustGetAddress("DelayedWETHImpl"), - protocolVersionsImpl: artifacts.mustGetAddress("ProtocolVersionsImpl"), - aggregateVerifierImpl: artifacts.getAddress("AggregateVerifier"), - teeProverRegistryImpl: artifacts.getAddress("TEEProverRegistryImpl"), - teeVerifierImpl: artifacts.getAddress("TEEVerifier"), - zkVerifierImpl: artifacts.getAddress("ZKVerifier") - }); - } - function _runConfigured() internal returns (DeployOutput memory output_) { output_ = deploy(_deployInput()); @@ -315,27 +273,6 @@ contract SystemDeploy is Script { emit Deployed(_input.opChainInput.l2ChainId, msg.sender, abi.encode(output_.opChain)); } - function upgrade(UpgradeInput memory _input) public returns (UpgradeOutput memory output_) { - _assertValidImplementations(_input.implementations); - - if (address(_input.systemConfigProxy) != address(0)) { - ISystemConfig systemConfigProxy = _input.systemConfigProxy; - DeployUtils.assertValidContractAddress(address(systemConfigProxy)); - - IProtocolVersions protocolVersionsProxy = _input.protocolVersionsProxy; - if (address(protocolVersionsProxy) == address(0) && address(artifacts).code.length != 0) { - protocolVersionsProxy = IProtocolVersions(artifacts.getAddress("ProtocolVersionsProxy")); - } - - _upgradeOPChain(systemConfigProxy, _input.implementations, protocolVersionsProxy); - output_.chainUpgraded = true; - } - - if (_input.saveArtifacts) { - _saveUpgradeArtifacts(_input.implementations); - } - } - function _deployImplementations(ImplementationInput memory _input) internal returns (Types.Implementations memory output_) @@ -528,91 +465,6 @@ contract SystemDeploy is Script { ); } - function _upgradeOPChain( - ISystemConfig _systemConfigProxy, - Types.Implementations memory _impls, - IProtocolVersions _protocolVersionsProxy - ) - internal - { - IProxyAdmin proxyAdmin = _systemConfigProxy.proxyAdmin(); - uint256 l2ChainId = _systemConfigProxy.l2ChainId(); - - _assertLegacySuperchainConfigNotPaused(_systemConfigProxy); - - _upgradeTo(proxyAdmin, address(_systemConfigProxy), _impls.systemConfigImpl); - - IOptimismPortal optimismPortal = IOptimismPortal(payable(_systemConfigProxy.optimismPortal())); - _upgradeTo(proxyAdmin, address(optimismPortal), _impls.optimismPortalImpl); - _upgradeTo(proxyAdmin, address(optimismPortal.anchorStateRegistry()), _impls.anchorStateRegistryImpl); - _upgradeTo( - proxyAdmin, _systemConfigProxy.optimismMintableERC20Factory(), _impls.optimismMintableERC20FactoryImpl - ); - - IDisputeGameFactory disputeGameFactory = IDisputeGameFactory(_systemConfigProxy.disputeGameFactory()); - _upgradeTo(proxyAdmin, address(disputeGameFactory), _impls.disputeGameFactoryImpl); - _upgradeMultiproofContracts(_systemConfigProxy, disputeGameFactory, _impls); - - ISystemConfig.Addresses memory opChainAddrs = _systemConfigProxy.getAddresses(); - _upgradeTo(proxyAdmin, opChainAddrs.l1CrossDomainMessenger, _impls.l1CrossDomainMessengerImpl); - _upgradeTo(proxyAdmin, opChainAddrs.l1StandardBridge, _impls.l1StandardBridgeImpl); - _upgradeTo(proxyAdmin, opChainAddrs.l1ERC721Bridge, _impls.l1ERC721BridgeImpl); - if (opChainAddrs.delayedWETH != address(0)) { - _upgradeTo(proxyAdmin, opChainAddrs.delayedWETH, _impls.delayedWETHImpl); - } - - if (address(_protocolVersionsProxy) != address(0)) { - _upgradeTo(proxyAdmin, address(_protocolVersionsProxy), _impls.protocolVersionsImpl); - } - - emit Upgraded(l2ChainId, _systemConfigProxy, msg.sender); - } - - /// @notice Rejects a migration from SuperchainConfig until its global and chain-specific pause records are cleared. - /// @dev A pause submitted after this check and before the upgrade can be lost. Either the Guardian or the - /// Incident Responder can pause during that window, so coordinate migrations with both. - function _assertLegacySuperchainConfigNotPaused(ISystemConfig _systemConfigProxy) internal view { - (bool success, bytes memory returndata) = - address(_systemConfigProxy).staticcall(abi.encodeCall(ILegacySystemConfig.superchainConfig, ())); - if (!success || returndata.length != 32) return; - - ILegacySuperchainConfig superchainConfig = ILegacySuperchainConfig(abi.decode(returndata, (address))); - if (!superchainConfig.pausable(address(0)) || !superchainConfig.pausable(_systemConfigProxy.optimismPortal())) { - revert LegacySuperchainConfigPaused(); - } - } - - function _upgradeMultiproofContracts( - ISystemConfig _systemConfigProxy, - IDisputeGameFactory _disputeGameFactory, - Types.Implementations memory _impls - ) - internal - { - IDisputeGame currentGameImpl = _disputeGameFactory.gameImpls(GameTypes.AGGREGATE_VERIFIER); - if (address(currentGameImpl) == address(0)) return; - - if (_impls.teeProverRegistryImpl != address(0)) { - TEEProverRegistry newTEEProverRegistry = TEEProverRegistry(_impls.teeProverRegistryImpl); - require( - address(newTEEProverRegistry.DISPUTE_GAME_FACTORY()) == address(_disputeGameFactory), - "SystemDeploy: TEEProverRegistry factory mismatch" - ); - AggregateVerifier currentAggregateVerifier = AggregateVerifier(address(currentGameImpl)); - TEEProverRegistry teeProverRegistry = - TEEVerifier(address(currentAggregateVerifier.TEE_VERIFIER())).TEE_PROVER_REGISTRY(); - _upgradeTo(_systemConfigProxy.proxyAdmin(), address(teeProverRegistry), _impls.teeProverRegistryImpl); - } - - if (_impls.aggregateVerifierImpl != address(0) && address(currentGameImpl) != _impls.aggregateVerifierImpl) { - DeployUtils.assertValidContractAddress(_impls.aggregateVerifierImpl); - vm.broadcast(msg.sender); - _disputeGameFactory.setImplementation( - GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(_impls.aggregateVerifierImpl)) - ); - } - } - function _encodeSystemConfigInitializer( Types.DeployInput memory _input, Types.DeployOutput memory _output @@ -709,12 +561,6 @@ contract SystemDeploy is Script { _proxyAdmin.upgradeAndCall(payable(_target), _implementation, _data); } - function _upgradeTo(IProxyAdmin _proxyAdmin, address _target, address _implementation) internal { - DeployUtils.assertValidContractAddress(_implementation); - vm.broadcast(msg.sender); - _proxyAdmin.upgrade(payable(_target), _implementation); - } - function _deployAddressManager( Types.DeployInput memory _input, IProxyAdmin _proxyAdmin @@ -1004,7 +850,6 @@ contract SystemDeploy is Script { } function _assertValidImplementations(Types.Implementations memory _impls) internal view { - if (_implementationsEmpty(_impls)) revert MissingImplementations(); DeployUtils.assertValidContractAddress(_impls.l1ERC721BridgeImpl); DeployUtils.assertValidContractAddress(_impls.optimismPortalImpl); DeployUtils.assertValidContractAddress(_impls.systemConfigImpl); @@ -1041,7 +886,7 @@ contract SystemDeploy is Script { } function _saveDeployArtifacts(DeployOutput memory _output) internal { - _saveUpgradeArtifacts(_output.impls); + _saveImplementationArtifacts(_output.impls); Types.DeployOutput memory chain = _output.opChain; artifacts.save("ProxyAdmin", address(chain.opChainProxyAdmin)); @@ -1069,7 +914,7 @@ contract SystemDeploy is Script { } } - function _saveUpgradeArtifacts(Types.Implementations memory _impls) internal { + function _saveImplementationArtifacts(Types.Implementations memory _impls) internal { artifacts.save("L1ERC721BridgeImpl", _impls.l1ERC721BridgeImpl); artifacts.save("OptimismPortalImpl", _impls.optimismPortalImpl); artifacts.save("SystemConfigImpl", _impls.systemConfigImpl); diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 76fc7ef0f..5505ad2ef 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -21,7 +21,7 @@ }, "src/L1/SystemConfig.sol:SystemConfig": { "initCodeHash": "0x4a83d1725257aee27f58f9874c543e55b89c3112e190c62ab4858e3c4dbf5bc3", - "sourceCodeHash": "0x04fee741d6628c4b74ec5315e252e5f5e62bdd820740e9ba7bd98525820043da" + "sourceCodeHash": "0x51ee250a08a99b08e366b04fc621bc62918743a263cb3f380732b60677da1957" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { "initCodeHash": "0x91a51808e37fa282a974cb5028e0ba517e04c903ce594b393cfda9dcc5408188", diff --git a/src/L1/SystemConfig.sol b/src/L1/SystemConfig.sol index 8b17024f7..2a0bb697f 100644 --- a/src/L1/SystemConfig.sol +++ b/src/L1/SystemConfig.sol @@ -22,7 +22,7 @@ import { IOptimismPortal2 } from "interfaces/L1/IOptimismPortal2.sol"; /// the L2 chain. It also owns the pause state for the network, which the Guardian and /// Incident Responder use to halt withdrawals and message relaying. /// @dev Upgrading a pre-merge deployment moves pause management from the legacy SuperchainConfig. -/// Use SystemDeploy for that upgrade, which requires legacy pause records to be cleared. +/// Clear legacy pause records before that migration so no active pause is lost. contract SystemConfig is ProxyAdminOwnedBase, OwnableUpgradeable, ReinitializableBase, ISemver { /// @notice Enum representing different types of updates. /// @custom:value BATCHER Represents an update to the batcher hash. diff --git a/test/deploy/SystemDeploy.t.sol b/test/deploy/SystemDeploy.t.sol index 282d20359..b7f9e5315 100644 --- a/test/deploy/SystemDeploy.t.sol +++ b/test/deploy/SystemDeploy.t.sol @@ -9,11 +9,9 @@ import { Types } from "scripts/libraries/Types.sol"; import { SystemDeployAssertions } from "test/deploy/SystemDeployAssertions.sol"; import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; -import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroValidator } from "interfaces/L1/proofs/tee/INitroValidator.sol"; import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { ProtocolVersions } from "src/L1/ProtocolVersions.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; @@ -21,46 +19,16 @@ import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; import { ZKVerifier } from "src/L1/proofs/zk/ZKVerifier.sol"; import { GameType, Hash, Proposal } from "src/libraries/bridge/Types.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; -import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { MockNitroValidator } from "test/mocks/MockNitroValidator.sol"; -contract MockLegacyTEEProverRegistry is DevTEEProverRegistry { - constructor( - INitroValidator nitroValidator, - IDisputeGameFactory factory - ) - DevTEEProverRegistry(nitroValidator, factory) - { } - - function version() public pure override returns (string memory) { - return "0.5.0"; - } -} - -contract MockSP1Verifier { - function verifyProof(bytes32, bytes calldata, bytes calldata) external pure { } -} - contract MockInvalidTEEProverRegistry { function NITRO_VALIDATOR() external pure returns (INitroValidator) { return INitroValidator(address(0)); } } -contract MockLegacySuperchainConfig { - address internal immutable UNPAUSABLE_IDENTIFIER; - - constructor(address _unpausableIdentifier) { - UNPAUSABLE_IDENTIFIER = _unpausableIdentifier; - } - - function pausable(address _identifier) external view returns (bool) { - return _identifier != UNPAUSABLE_IDENTIFIER; - } - - function paused(address) external pure returns (bool) { - return false; - } +contract MockSP1Verifier { + function verifyProof(bytes32, bytes calldata, bytes calldata) external pure { } } contract SystemDeploy_Test is Test, SystemDeployAssertions { @@ -86,12 +54,7 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { sp1Verifier = new MockSP1Verifier(); } - function testFuzz_deployImplementations_bindsGuardianRoles_succeeds( - address _guardian, - address _incidentResponder - ) - public - { + function testFuzz_deploy_bindsGuardianRoles_succeeds(address _guardian, address _incidentResponder) public { vm.assume(_guardian != address(0)); SystemDeploy.DeployInput memory input = _defaultDeployInput(); @@ -115,7 +78,7 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { ); } - function test_deployImplementations_nullGuardian_reverts() public { + function test_deploy_nullGuardian_reverts() public { SystemDeploy.DeployInput memory input = _defaultDeployInput(); input.implementationsInput.guardian = address(0); @@ -306,149 +269,12 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { systemDeploy.deploy(input); } - function test_upgrade_registryWithInvalidNitroValidator_reverts() public { - SystemDeploy.DeployInput memory input = _defaultDeployInput(); - SystemDeploy.DeployOutput memory output = systemDeploy.deploy(input); - Types.Implementations memory implementations = output.impls; - implementations.teeProverRegistryImpl = address(new MockInvalidTEEProverRegistry()); + function test_deploy_suppliedRegistryWithInvalidNitroValidator_reverts() public { + SystemDeploy.DeployInput memory input = _reuseImplementationsInput(); + input.implementations.teeProverRegistryImpl = address(new MockInvalidTEEProverRegistry()); vm.expectRevert("DeployUtils: zero address"); - systemDeploy.upgrade( - SystemDeploy.UpgradeInput({ - saveArtifacts: false, - implementations: implementations, - systemConfigProxy: output.opChain.systemConfigProxy, - protocolVersionsProxy: output.opChain.protocolVersionsProxy - }) - ); - } - - function testFuzz_upgrade_expiredLegacyPause_reverts(bool _globalPause) public { - SystemDeploy.DeployInput memory input = _defaultDeployInput(); - SystemDeploy.DeployOutput memory output = systemDeploy.deploy(input); - address systemConfigProxy = address(output.opChain.systemConfigProxy); - address pausedIdentifier = _globalPause ? address(0) : address(output.opChain.optimismPortalProxy); - MockLegacySuperchainConfig legacySuperchainConfig = new MockLegacySuperchainConfig(pausedIdentifier); - address systemConfigImpl = output.opChain.opChainProxyAdmin.getProxyImplementation(systemConfigProxy); - - assertFalse(legacySuperchainConfig.paused(pausedIdentifier), "legacy pause inactive"); - assertFalse(legacySuperchainConfig.pausable(pausedIdentifier), "legacy pause record remains"); - - vm.mockCall( - systemConfigProxy, - abi.encodeWithSelector(bytes4(keccak256("superchainConfig()"))), - abi.encode(address(legacySuperchainConfig)) - ); - - vm.expectRevert(SystemDeploy.LegacySuperchainConfigPaused.selector); - systemDeploy.upgrade( - SystemDeploy.UpgradeInput({ - saveArtifacts: false, - implementations: output.impls, - systemConfigProxy: output.opChain.systemConfigProxy, - protocolVersionsProxy: output.opChain.protocolVersionsProxy - }) - ); - - assertEq( - output.opChain.opChainProxyAdmin.getProxyImplementation(systemConfigProxy), - systemConfigImpl, - "system config impl" - ); - } - - function test_upgrade_withoutManagerDelegatecall_succeeds() public { - SystemDeploy.DeployInput memory input = _defaultDeployInput(); - SystemDeploy.DeployOutput memory output = systemDeploy.deploy(input); - Types.Implementations memory implementations = output.impls; - ProtocolVersions protocolVersionsImpl = new ProtocolVersions(); - implementations.protocolVersionsImpl = address(protocolVersionsImpl); - - SystemDeploy.UpgradeOutput memory upgradeOutput = systemDeploy.upgrade( - SystemDeploy.UpgradeInput({ - saveArtifacts: false, - implementations: implementations, - systemConfigProxy: output.opChain.systemConfigProxy, - protocolVersionsProxy: output.opChain.protocolVersionsProxy - }) - ); - - assertTrue(upgradeOutput.chainUpgraded, "chain upgraded"); - assertEq( - output.opChain.opChainProxyAdmin.getProxyImplementation(address(output.opChain.protocolVersionsProxy)), - address(protocolVersionsImpl), - "protocol versions impl" - ); - assertValidStandardSystem(_expected(output, input)); - } - - function test_upgrade_predeployedTEEProverRegistryImplementation_succeeds() public { - SystemDeploy.DeployInput memory input = _defaultDeployInput(); - SystemDeploy.DeployOutput memory output = systemDeploy.deploy(input); - TEEProverRegistry registry = TEEProverRegistry(address(output.opChain.teeProverRegistryProxy)); - MockLegacyTEEProverRegistry legacyImpl = new MockLegacyTEEProverRegistry({ - nitroValidator: INitroValidator(address(nitroValidator)), factory: registry.DISPUTE_GAME_FACTORY() - }); - output.opChain.opChainProxyAdmin.upgrade(payable(address(registry)), address(legacyImpl)); - - address signer = makeAddr("existing-signer"); - bytes32 imageHash = keccak256("existing-image"); - DevTEEProverRegistry(address(registry)).addDevSigner(signer, imageHash); - - TEEProverRegistry newImpl = new TEEProverRegistry({ - nitroValidator: INitroValidator(address(nitroValidator)), factory: registry.DISPUTE_GAME_FACTORY() - }); - Types.Implementations memory implementations = output.impls; - implementations.teeProverRegistryImpl = address(newImpl); - systemDeploy.upgrade( - SystemDeploy.UpgradeInput({ - saveArtifacts: false, - implementations: implementations, - systemConfigProxy: output.opChain.systemConfigProxy, - protocolVersionsProxy: output.opChain.protocolVersionsProxy - }) - ); - - assertEq( - output.opChain.opChainProxyAdmin.getProxyImplementation(address(registry)), - address(newImpl), - "tee registry impl" - ); - assertEq(registry.version(), "0.6.1"); - assertEq(registry.owner(), owner); - assertEq(registry.manager(), owner); - assertEq(GameType.unwrap(registry.gameType()), uint32(input.implementationsInput.multiproofGameType)); - assertTrue(registry.isValidProposer(proposer)); - assertTrue(registry.isValidProposer(challenger)); - assertTrue(registry.isRegisteredSigner(signer)); - assertEq(registry.signerImageHash(signer), imageHash); - assertEq(address(registry.NITRO_VALIDATOR()), address(nitroValidator)); - } - - function test_upgrade_discoversProtocolVersionsProxyFromArtifacts_succeeds() public { - SystemDeploy.DeployInput memory input = _defaultDeployInput(); - SystemDeploy.DeployOutput memory output = systemDeploy.deploy(input); - Types.Implementations memory implementations = output.impls; - ProtocolVersions protocolVersionsImpl = new ProtocolVersions(); - implementations.protocolVersionsImpl = address(protocolVersionsImpl); - - _saveArtifact("ProtocolVersionsProxy", address(output.opChain.protocolVersionsProxy)); - - SystemDeploy.UpgradeOutput memory upgradeOutput = systemDeploy.upgrade( - SystemDeploy.UpgradeInput({ - saveArtifacts: false, - implementations: implementations, - systemConfigProxy: output.opChain.systemConfigProxy, - protocolVersionsProxy: IProtocolVersions(address(0)) - }) - ); - - assertTrue(upgradeOutput.chainUpgraded, "chain upgraded"); - assertEq( - output.opChain.opChainProxyAdmin.getProxyImplementation(address(output.opChain.protocolVersionsProxy)), - address(protocolVersionsImpl), - "protocol versions impl" - ); + systemDeploy.deploy(input); } function test_deploy_reusingImplementations_doesNotSaveZeroImplementationOnlyArtifacts() public { @@ -607,13 +433,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { ); } - function _saveArtifact(string memory _name, address _addr) internal { - vm.etch(address(artifacts), vm.getDeployedCode("Artifacts.s.sol:Artifacts")); - bytes32 slot = keccak256(abi.encodePacked(_name, uint256(0))); - vm.store(address(artifacts), slot, bytes32(uint256(uint160(_addr)))); - assertEq(artifacts.getAddress(_name), _addr, "artifact saved"); - } - function _expected( SystemDeploy.DeployOutput memory _output, SystemDeploy.DeployInput memory _input diff --git a/test/setup/CommonTest.sol b/test/setup/CommonTest.sol index 3ee0a0fe8..0e201cf0a 100644 --- a/test/setup/CommonTest.sol +++ b/test/setup/CommonTest.sol @@ -12,8 +12,6 @@ import { FFIInterface } from "test/setup/FFIInterface.sol"; // Contracts import { ERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; -import { console2 as console } from "lib/forge-std/src/console2.sol"; - // Interfaces import { IOptimismMintableERC20Full } from "interfaces/universal/IOptimismMintableERC20Full.sol"; @@ -27,12 +25,6 @@ abstract contract CommonTest is Test, Setup, Events { FFIInterface constant ffi = FFIInterface(address(uint160(uint256(keccak256(abi.encode("optimism.ffi")))))); - /// @dev This value is only used in forked tests. During forked tests, the default is to perform the upgrade before - /// running the tests. - /// This value should only be set to false in forked tests which are specifically testing the upgrade path - /// itself, rather than simply ensuring that the tests pass after the upgrade. - bool useUpgradedFork = true; - ERC20 L1Token; ERC20 BadL1Token; IOptimismMintableERC20Full L2Token; @@ -55,11 +47,6 @@ abstract contract CommonTest is Test, Setup, Events { vm.deal(alice, DEFAULT_TEST_BALANCE); vm.deal(bob, DEFAULT_TEST_BALANCE); - // Override the config after the deploy script initialized the config - if (useUpgradedFork) { - deploy.cfg().setUseUpgradedFork(true); - } - if (!isForkTest()) { // Modifying these values on a fork test causes issues. vm.warp(deploy.cfg().l2OutputOracleStartingTimestamp() + 1); @@ -128,24 +115,4 @@ abstract contract CommonTest is Test, Setup, Events { { emit TransactionDeposited(_from, _to, 0, abi.encodePacked(_mint, _value, _gasLimit, _isCreation, _data)); } - - /// @dev Checks if the system has already been deployed, based off of the heuristic that alice and bob have not been - /// set by the `setUp` function yet. - function _checkNotDeployed(string memory _feature) internal view { - if (alice != address(0) && bob != address(0)) { - revert( - string.concat("CommonTest: Cannot enable ", _feature, " after deployment. Consider overriding `setUp`.") - ); - } - console.log("CommonTest: enabling", _feature); - } - - /// @dev Disables upgrade mode for testing. By default the fork testing env will be upgraded to the latest - /// implementation. This can be used to disable the upgrade, which is useful for tests targeting the upgrade - /// process itself. - function disableUpgradedFork() internal { - _checkNotDeployed("non-upgraded fork"); - - useUpgradedFork = false; - } } diff --git a/test/setup/ForkLive.s.sol b/test/setup/ForkLive.s.sol index 8d2aa610f..09e2185ca 100644 --- a/test/setup/ForkLive.s.sol +++ b/test/setup/ForkLive.s.sol @@ -6,22 +6,17 @@ import { Script } from "lib/forge-std/src/Script.sol"; // Scripts import { Artifacts } from "scripts/Artifacts.s.sol"; -import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; -import { SystemDeploy } from "scripts/deploy/SystemDeploy.s.sol"; import { Config } from "scripts/libraries/Config.sol"; // Libraries import { GameTypes } from "src/libraries/bridge/Types.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; -import { Types } from "scripts/libraries/Types.sol"; // Interfaces import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { IAggregateVerifier } from "interfaces/L1/proofs/IAggregateVerifier.sol"; import { IAddressManager } from "interfaces/legacy/IAddressManager.sol"; import { ISystemConfig } from "interfaces/L1/ISystemConfig.sol"; -import { IProtocolVersions } from "interfaces/L1/IProtocolVersions.sol"; -import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; /// @title ForkLive /// @notice This script is called by Setup.sol as a preparation step for the foundry test suite, and is run as an @@ -34,15 +29,9 @@ import { IProxyAdmin } from "interfaces/universal/IProxyAdmin.sol"; /// This contract must not have constructor logic because it is set into state using `etch`. contract ForkLive is Script { - DeployConfig internal constant cfg = - DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); - Artifacts internal constant artifacts = Artifacts(address(uint160(uint256(keccak256(abi.encode("optimism.artifacts")))))); - SystemDeploy internal constant deploy = - SystemDeploy(address(uint160(uint256(keccak256(abi.encode("optimism.deploy")))))); - bool public useOpsRepo; struct SystemAddresses { @@ -70,39 +59,20 @@ contract ForkLive is Script { } } - /// @dev This function sets up the system to test it as follows: - /// 1. Check if the SUPERCHAIN_OPS_ALLOCS_PATH environment variable was set from superchain ops. - /// 2. If set, load the state from the given path. - /// 3. Derive the live system addresses from the configured fork entrypoints. - /// 4. If the environment variable wasn't set, deploy the updated implementations of the contracts. - /// 5. Upgrade the system using SystemDeploy.upgrade() if useUpgradedFork is true. + /// @notice Loads simulated superchain-ops upgrade state and derives the system addresses from it. function run() public { string memory superchainOpsAllocsPath = Config.superchainOpsAllocsPath(); - useOpsRepo = bytes(superchainOpsAllocsPath).length > 0; - if (useOpsRepo) { - console.log("ForkLive: loading state from %s", superchainOpsAllocsPath); - // Load the simulated post-upgrade state before deriving implementation addresses. - vm.loadAllocs(superchainOpsAllocsPath); - } + require(bytes(superchainOpsAllocsPath).length > 0, "ForkLive: SUPERCHAIN_OPS_ALLOCS_PATH must be set"); + console.log("ForkLive: loading state from %s", superchainOpsAllocsPath); + // Load the simulated post-upgrade state before deriving implementation addresses. + vm.loadAllocs(superchainOpsAllocsPath); + useOpsRepo = true; _readForkAddresses(); - if (!useOpsRepo) { - _deployNewImplementations(); - } - - if (useOpsRepo) { - console.log("ForkLive: using ops repo to upgrade"); - } else if (cfg.useUpgradedFork()) { - console.log("ForkLive: upgrading"); - _upgrade(); - } } - /// @notice Reads the live fork system and saves the addresses to disk. - /// @dev During development of an upgrade which adds a new contract, the contract will not yet be derivable from - /// the live fork. In this case, the contract will be deployed by the upgrade process, and will need to be - /// stored by artifacts.save() after the call to SystemDeploy.upgrade(). + /// @notice Reads the loaded fork system and saves its addresses to the artifact registry. function _readForkAddresses() internal { SystemAddresses memory system = forkSystemAddresses(); ISystemConfig systemConfig = ISystemConfig(system.systemConfig); @@ -144,72 +114,12 @@ contract ForkLive is Script { artifacts.save("DelayedWETHImpl", EIP1967Helper.getImplementation(gameAddresses.weth)); } - /// @notice Calls to the SystemDeploy.s.sol contract etched by Setup.sol to a deterministic address, sets up the - /// environment, and deploys new implementations. - function _deployNewImplementations() internal { - deploy.deployImplementations(); - } - - /// @notice Performs a script-level upgrade without a manager delegatecall. - /// @param _upgrader The address of the OP Chain ProxyAdmin owner to use for the chain upgrade. - /// @param _systemConfigProxy The OP Chain SystemConfig proxy to upgrade. - function _doUpgrade(address _upgrader, ISystemConfig _systemConfigProxy) internal { - SystemDeploy systemDeploy = new SystemDeploy(); - Types.Implementations memory implementations = _latestImplementations(); - IProtocolVersions protocolVersionsProxy = IProtocolVersions(artifacts.getAddress("ProtocolVersionsProxy")); - - // Run the per-chain upgrade as the OP Chain ProxyAdmin owner. - vm.prank(_upgrader); - systemDeploy.upgrade( - SystemDeploy.UpgradeInput({ - saveArtifacts: false, - implementations: implementations, - systemConfigProxy: _systemConfigProxy, - protocolVersionsProxy: protocolVersionsProxy - }) - ); - } - - /// @notice Upgrades the contracts using the script-level upgrade API. - function _upgrade() internal { - ISystemConfig systemConfig = ISystemConfig(artifacts.mustGetAddress("SystemConfigProxy")); - IProxyAdmin proxyAdmin = IProxyAdmin(EIP1967Helper.getAdmin(address(systemConfig))); - - address upgrader = proxyAdmin.owner(); - vm.label(upgrader, "ProxyAdmin Owner"); - - if (block.chainid != 1) { - revert UnsupportedChainId(); - } - - _doUpgrade(upgrader, systemConfig); - - console.log("ForkLive: Saving newly deployed contracts"); - - // A new ASR and new dispute games were deployed, so we need to update them - IDisputeGameFactory disputeGameFactory = - IDisputeGameFactory(artifacts.mustGetAddress("DisputeGameFactoryProxy")); - IAggregateVerifier aggregateVerifier = - IAggregateVerifier(address(disputeGameFactory.gameImpls(GameTypes.AGGREGATE_VERIFIER))); - artifacts.save("AggregateVerifier", address(aggregateVerifier)); - - GameAddresses memory gameAddresses = _aggregateVerifierAddresses(aggregateVerifier); - artifacts.save("AnchorStateRegistryProxy", gameAddresses.anchorStateRegistry); - artifacts.save("DelayedWETHProxy", gameAddresses.weth); - artifacts.save("DelayedWETHImpl", EIP1967Helper.getImplementation(gameAddresses.weth)); - } - - /// @notice Returns the latest implementation set saved by deployImplementations. - function _latestImplementations() internal view returns (Types.Implementations memory) { - return deploy.getImplementations(); - } - /// @notice Saves the proxy and implementation addresses for a contract name. function _saveProxyAndImpl(string memory _contractName, address _proxy) internal { artifacts.save(string.concat(_contractName, "Proxy"), _proxy); address impl = EIP1967Helper.getImplementation(_proxy); - require(impl != address(0), "Upgrade: Implementation address is zero"); + require(impl != address(0), "ForkLive: Implementation address is zero"); artifacts.save(string.concat(_contractName, "Impl"), impl); } diff --git a/test/setup/ForkLive.t.sol b/test/setup/ForkLive.t.sol new file mode 100644 index 000000000..931a78985 --- /dev/null +++ b/test/setup/ForkLive.t.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; +import { GameTypes } from "src/libraries/bridge/Types.sol"; +import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; +import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; +import { CommonTest } from "test/setup/CommonTest.sol"; +import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; + +contract ForkLive_Run_Test is CommonTest { + address internal constant ENTRYPOINT = 0x73a79Fab69143498Ed3712e519A88a918e1f4072; + string internal constant ALLOCS_PATH = "deployments/fork-live-test.json"; + + function setUp() public override { + super.setUp(); + AggregateVerifier gameImpl = new AggregateVerifier( + GameTypes.AGGREGATE_VERIFIER, + anchorStateRegistry, + delayedWeth, + IVerifier(address(0)), + IVerifier(address(0)), + bytes32(0), + AggregateVerifier.ZkHashes(bytes32(0), bytes32(0)), + bytes32(0), + systemConfig.l2ChainId(), + AggregateVerifier.IntervalConfig({ + slowBlockInterval: 100, + slowIntermediateBlockInterval: 10, + fastBlockInterval: 1000, + fastIntermediateBlockInterval: 100 + }), + AggregateVerifier.ScheduleConfig({ + protocolVersions: protocolVersions, genesisBlockNumber: 0, genesisTimestamp: 1, blockTime: 2 + }) + ); + vm.prank(disputeGameFactory.owner()); + disputeGameFactory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(gameImpl))); + vm.chainId(1); + vm.etch(ENTRYPOINT, address(systemConfig).code); + vm.copyStorage(address(systemConfig), ENTRYPOINT); + // dumpState needs the initialized storage still cached in the setup transaction. + vm.dumpState(ALLOCS_PATH); + } + + // vm.setEnv is process-wide, so these paths must not run in parallel tests. + function test_run_requiresAndLoadsOpsAllocs_succeeds() public { + string memory previousPath = vm.envOr("SUPERCHAIN_OPS_ALLOCS_PATH", string("")); + vm.setEnv("SUPERCHAIN_OPS_ALLOCS_PATH", ""); + vm.expectRevert("ForkLive: SUPERCHAIN_OPS_ALLOCS_PATH must be set"); + forkLive.run(); + + address systemConfigImpl = EIP1967Helper.getImplementation(address(systemConfig)); + address delayedWethImpl = EIP1967Helper.getImplementation(address(delayedWeth)); + vm.etch(ENTRYPOINT, hex""); + EIP1967Helper.setImplementation(address(delayedWeth), address(0)); + vm.setEnv("SUPERCHAIN_OPS_ALLOCS_PATH", ALLOCS_PATH); + + forkLive.run(); + + assertEq(artifacts.mustGetAddress("SystemConfigProxy"), ENTRYPOINT); + assertEq(artifacts.mustGetAddress("SystemConfigImpl"), systemConfigImpl); + assertEq(artifacts.mustGetAddress("DelayedWETHImpl"), delayedWethImpl); + assertTrue(forkLive.useOpsRepo()); + vm.setEnv("SUPERCHAIN_OPS_ALLOCS_PATH", previousPath); + vm.removeFile(ALLOCS_PATH); + } +}