diff --git a/docker-bake.hcl b/docker-bake.hcl index 5f7f311cefe47..b53c7add0f87a 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -214,6 +214,17 @@ target "proofs-tools" { tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/proofs-tools:${tag}"] } +target "holocene-deployer" { + dockerfile = "./packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile" + context = "./packages/contracts-bedrock/scripts/upgrades/holocene" + args = { + REV = "op-contracts/v1.8.0-rc.1" + } + target="holocene-deployer" + platforms = split(",", PLATFORMS) + tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/holocene-deployer:${tag}"] +} + target "ci-builder" { dockerfile = "./ops/docker/ci-builder/Dockerfile" context = "." diff --git a/ops/scripts/ci-docker-tag-op-stack-release.sh b/ops/scripts/ci-docker-tag-op-stack-release.sh index 45dd920949946..1de86b749d329 100755 --- a/ops/scripts/ci-docker-tag-op-stack-release.sh +++ b/ops/scripts/ci-docker-tag-op-stack-release.sh @@ -6,7 +6,7 @@ DOCKER_REPO=$1 GIT_TAG=$2 GIT_SHA=$3 -IMAGE_NAME=$(echo "$GIT_TAG" | grep -Eow '^(ci-builder(-rust)?|da-server|proofs-tools|cannon|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)' || true) +IMAGE_NAME=$(echo "$GIT_TAG" | grep -Eow '^(ci-builder(-rust)?|da-server|proofs-tools|holocene-deployer|cannon|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)' || true) if [ -z "$IMAGE_NAME" ]; then echo "image name could not be parsed from git tag '$GIT_TAG'" exit 1 diff --git a/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json b/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json index ff87fc4f8cbb4..4239d10c3d071 100644 --- a/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json +++ b/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json @@ -65,7 +65,7 @@ "eip1559Denominator": 250, "eip1559DenominatorCanyon": 250, "systemConfigStartBlock": 4071248, - "faultGameAbsolutePrestate": "0x0385c3f8ee78491001d92b90b07d0cf387b7b52ab9b83b4d87c994e92cf823ba", + "faultGameAbsolutePrestate": "0x03925193e3e89f87835bbdf3a813f60b2aa818a36bbe71cd5d8fd7e79f5e8afe", "faultGameMaxDepth": 73, "faultGameClockExtension": 3600, "faultGameMaxClockDuration": 14400, diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example new file mode 100644 index 0000000000000..02d67a4bad06f --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example @@ -0,0 +1,93 @@ +############################################## +# ↓ Required ↓ # +############################################## + +# The network to deploy the contracts to. +# Must be one of 'mainnet', 'sepolia' +NETWORK= + +# Etherscan API key used to verify contract bytecode +ETHERSCAN_API_KEY= + +# RPC URL for the L1 network that matches $NETWORK +ETH_RPC_URL= + +# Private key used to deploy the new contracts for this upgrade +PRIVATE_KEY= + +# Path to the deploy config JSON file +DEPLOY_CONFIG_PATH= + +# Path to the folder where output artifacts will be stored +OUTPUT_FOLDER_PATH= + +# Address of deployed `PreimageOracle` contract. +PREIMAGE_ORACLE_ADDR= + +# Address of deployed `AnchorStateRegistry` proxy contract. +ANCHOR_STATE_REGISTRY_PROXY_ADDR= + +# Address of the `SuperchainConfig` proxy contract. +SUPERCHAIN_CONFIG_PROXY_ADDR= + +# Address of deployed `ProxyAdmin` contract. +PROXY_ADMIN_ADDR= + +# Address of deployed `SystemConfig` proxy contract. +SYSTEM_CONFIG_PROXY_ADDR= + +# Address of deployed `DisputeGameFactory` proxy contract. +DISPUTE_GAME_FACTORY_PROXY_ADDR= + +# Whether or not to deploy and include any fault proof contracts in the upgrade. +# +# If 'true', the `PermissionedDisputeGame` contract will be deployed and included in the upgrade. +# If 'false', the `PermissionedDisputeGame` contract will not be deployed or included in the upgrade. +# +# Must be one of 'true', 'false' +# Cannot be 'false' if `USE_PERMISSIONLESS_FAULT_PROOFS` is 'true' +USE_FAULT_PROOFS=true + +# Whether or not to deploy and include the `FaultDisputeGame` contract in the upgrade. +# +# If 'true', the `FaultDisputeGame` contract will be deployed and included in the upgrade. +# If 'false', the `FaultDisputeGame` contract will not be deployed or included in the upgrade. +# +# Must be one of 'true', 'false' +# Cannot be 'true' if `USE_FAULT_PROOFS` is 'false' +USE_PERMISSIONLESS_FAULT_PROOFS=true + +################################################### +# ↓ Optional ↓ # +# Do not set if you don't know what you're doing. # +################################################### + +# Address of the deployed `SystemConfig` implementation for Holocene. +# +# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`. +# If you are not the first to deploy Holocene on this L1, this field should be set to +# the existing deployment address. +# +# If this field is not set, the `superchain-registry` will be consulted for the implementation address. +# If this field is set to the zero address, a new `SystemConfig` implementation will be deployed. +SYSTEM_CONFIG_IMPL_ADDR= + +# Address of the deployed `MIPS` implementation for Holocene. +# +# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`. +# If you are not the first to deploy Holocene on this L1, this field should be set to +# the existing deployment address. +# +# If this field is not set, the `superchain-registry` will be consulted for the implementation address. +# If this field is set to the zero address, a new `MIPS` implementation will be deployed. +MIPS_IMPL_ADDR= + +# Address of deployed `DelayedWETH` implementation contract. +# +# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`. +# If you are not the first to deploy permissionless fault proofs on L1, this field should be +# set to the existing deployment address. +# +# If this field is not set, the `superchain-registry` will be consulted for the implementation address. +# If this field is set to the zero address, a new `DelayedWETH` implementation will be deployed. +DELAYED_WETH_IMPL_ADDR= diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore b/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore new file mode 100644 index 0000000000000..442ed87d93042 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore @@ -0,0 +1,2 @@ +# Environment +.env diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol b/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol new file mode 100644 index 0000000000000..8203e42b060c6 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +// Forge +import { console2 as console } from "forge-std/console2.sol"; + +// Scripts +import { Deployer } from "scripts/deploy/Deployer.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +// Utils +import { Claim, GameTypes, Duration } from "src/dispute/lib/Types.sol"; + +// Interfaces +import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; +import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { + IFaultDisputeGame, + IBigStepper, + IAnchorStateRegistry, + IDelayedWETH +} from "src/dispute/interfaces/IFaultDisputeGame.sol"; +import { IPermissionedDisputeGame } from "src/dispute/interfaces/IPermissionedDisputeGame.sol"; +import { IMIPS, IPreimageOracle } from "src/cannon/interfaces/IMIPS.sol"; + +/// @title DeployUpgrade +/// @notice A deployment script for smart contract upgrades surrounding the Holocene hardfork. +contract DeployUpgrade is Deployer { + /// @dev The entrypoint to the deployment script. + function deploy( + address _proxyAdmin, + address _superchainConfig, + address _systemConfigImpl, + address _mipsImpl, + address _delayedWETH, + address _preimageOracle, + address _anchorStateRegistry, + bool _useFaultProofs, + bool _usePermissionlessFaultProofs + ) + public + { + // Shim the existing contracts that this upgrade is dependent on. + shim({ + _proxyAdmin: _proxyAdmin, + _superchainConfig: _superchainConfig, + _systemConfigImpl: _systemConfigImpl, + _mipsImpl: _mipsImpl, + _delayedWETH: _delayedWETH, + _preimageOracle: _preimageOracle, + _anchorStateRegistry: _anchorStateRegistry + }); + + // Deploy conditional implementations. + if (_systemConfigImpl == address(0)) deploySystemConfigImplementation(); + + if (_useFaultProofs) { + if (_mipsImpl == address(0)) deployMIPSImplementation(); + if (_delayedWETH == address(0)) deployDelayedWETH(); + + // Deploy: + // 1. New `DelayedWETH` proxy contracts for the `FaultDisputeGame` and `PermissionedDisputeGame`. + // 2. New `FaultDisputeGame` and `PermissionedDisputeGame` implementation contracts. + deployDelayedWETHProxy("PDG"); + deployPermissionedDisputeGameImplementation(); + if (_usePermissionlessFaultProofs) { + deployDelayedWETHProxy("FDG"); + deployFaultDisputeGameImplementation(); + } + + // Run deployment checks. + checkMIPS(); + checkPermissionedDisputeGame(); + checkDelayedWETH("PDG"); + if (_usePermissionlessFaultProofs) { + checkFaultDisputeGame(); + checkDelayedWETH("FDG"); + } + } + + // Print the deployment summary. + printSummary(); + } + + /// @dev Shims the existing contracts that this upgrade is dependent on. + function shim( + address _proxyAdmin, + address _superchainConfig, + address _systemConfigImpl, + address _mipsImpl, + address _delayedWETH, + address _preimageOracle, + address _anchorStateRegistry + ) + public + { + prankDeployment("ProxyAdmin", _proxyAdmin); + prankDeployment("SuperchainConfig", _superchainConfig); + if (_systemConfigImpl != address(0)) prankDeployment("SystemConfig", _systemConfigImpl); + if (_mipsImpl != address(0)) prankDeployment("MIPS", _mipsImpl); + if (_delayedWETH != address(0)) prankDeployment("DelayedWETH", _delayedWETH); + prankDeployment("PreimageOracle", _preimageOracle); + prankDeployment("AnchorStateRegistry", _anchorStateRegistry); + } + + /// @dev Deploys the Holocene `SystemConfig` implementation contract. + function deploySystemConfigImplementation() public { + vm.broadcast(msg.sender); + address systemConfig = DeployUtils.create1( + "SystemConfig", DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ())) + ); + save("SystemConfig", systemConfig); + } + + /// @dev Deploys the new `MIPS` implementation contract. + function deployMIPSImplementation() public { + vm.broadcast(msg.sender); + address mips = DeployUtils.create1({ + _name: "MIPS", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IMIPS.__constructor__, (IPreimageOracle(mustGetAddress("PreimageOracle")))) + ) + }); + save("MIPS", mips); + } + + /// @dev Checks if the `MIPS` contract is correctly configured. + function checkMIPS() public view { + IMIPS mips = IMIPS(mustGetAddress("MIPS")); + require( + address(mips.oracle()) == mustGetAddress("PreimageOracle"), "DeployHoloceneUpgrade: invalid MIPS oracle" + ); + } + + /// @dev Deploys the Holocene `FaultDisputeGame` implementation contract. + function deployFaultDisputeGameImplementation() public { + bytes memory constructorInput = abi.encodeCall( + IFaultDisputeGame.__constructor__, + ( + GameTypes.CANNON, + Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())), + cfg.faultGameMaxDepth(), + cfg.faultGameSplitDepth(), + Duration.wrap(uint64(cfg.faultGameClockExtension())), + Duration.wrap(uint64(cfg.faultGameMaxClockDuration())), + IBigStepper(mustGetAddress("MIPS")), + IDelayedWETH(payable(mustGetAddress("DelayedWETHProxyFDG"))), + IAnchorStateRegistry(mustGetAddress("AnchorStateRegistry")), + cfg.l2ChainID() + ) + ); + + vm.broadcast(msg.sender); + address fdg = DeployUtils.create1("FaultDisputeGame", DeployUtils.encodeConstructor(constructorInput)); + save("FaultDisputeGame", fdg); + } + + /// @dev Checks if the `FaultDisputeGame` contract is correctly configured. + function checkFaultDisputeGame() public view { + IFaultDisputeGame fdg = IFaultDisputeGame(mustGetAddress("FaultDisputeGame")); + require( + fdg.gameType().raw() == GameTypes.CANNON.raw(), "DeployHoloceneUpgrade: invalid FaultDisputeGame gameType" + ); + require( + fdg.absolutePrestate().raw() == bytes32(cfg.faultGameAbsolutePrestate()), + "DeployHoloceneUpgrade: invalid FaultDisputeGame absolutePrestate" + ); + require( + fdg.maxGameDepth() == cfg.faultGameMaxDepth(), "DeployHoloceneUpgrade: invalid FaultDisputeGame maxDepth" + ); + require( + fdg.splitDepth() == cfg.faultGameSplitDepth(), "DeployHoloceneUpgrade: invalid FaultDisputeGame splitDepth" + ); + require( + fdg.clockExtension().raw() == cfg.faultGameClockExtension(), + "DeployHoloceneUpgrade: invalid FaultDisputeGame clockExtension" + ); + require( + fdg.maxClockDuration().raw() == cfg.faultGameMaxClockDuration(), + "DeployHoloceneUpgrade: invalid FaultDisputeGame maxClockDuration" + ); + require(address(fdg.vm()) == mustGetAddress("MIPS"), "DeployHoloceneUpgrade: invalid FaultDisputeGame MIPS"); + require( + address(fdg.weth()) == mustGetAddress("DelayedWETHProxyFDG"), + "DeployHoloceneUpgrade: invalid FaultDisputeGame DelayedWETH" + ); + require( + address(fdg.anchorStateRegistry()) == mustGetAddress("AnchorStateRegistry"), + "DeployHoloceneUpgrade: invalid FaultDisputeGame AnchorStateRegistry" + ); + require(fdg.l2ChainId() == cfg.l2ChainID(), "DeployHoloceneUpgrade: invalid FaultDisputeGame l2ChainID"); + } + + /// @dev Deploys the Holocene `PermissionedDisputeGame` implementation contract. + function deployPermissionedDisputeGameImplementation() public { + bytes memory constructorInput = abi.encodeCall( + IPermissionedDisputeGame.__constructor__, + ( + GameTypes.PERMISSIONED_CANNON, + Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())), + cfg.faultGameMaxDepth(), + cfg.faultGameSplitDepth(), + Duration.wrap(uint64(cfg.faultGameClockExtension())), + Duration.wrap(uint64(cfg.faultGameMaxClockDuration())), + IBigStepper(mustGetAddress("MIPS")), + IDelayedWETH(payable(mustGetAddress("DelayedWETHProxyPDG"))), + IAnchorStateRegistry(mustGetAddress("AnchorStateRegistry")), + cfg.l2ChainID(), + cfg.l2OutputOracleProposer(), + cfg.l2OutputOracleChallenger() + ) + ); + + vm.broadcast(msg.sender); + address fdg = DeployUtils.create1("PermissionedDisputeGame", DeployUtils.encodeConstructor(constructorInput)); + save("PermissionedDisputeGame", fdg); + } + + /// @dev Checks if the `PermissionedDisputeGame` contract is correctly configured. + function checkPermissionedDisputeGame() public view { + IPermissionedDisputeGame pdg = IPermissionedDisputeGame(mustGetAddress("PermissionedDisputeGame")); + require( + pdg.gameType().raw() == GameTypes.PERMISSIONED_CANNON.raw(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame gameType" + ); + require( + pdg.absolutePrestate().raw() == bytes32(cfg.faultGameAbsolutePrestate()), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame absolutePrestate" + ); + require( + pdg.maxGameDepth() == cfg.faultGameMaxDepth(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame maxDepth" + ); + require( + pdg.splitDepth() == cfg.faultGameSplitDepth(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame splitDepth" + ); + require( + pdg.clockExtension().raw() == cfg.faultGameClockExtension(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame clockExtension" + ); + require( + pdg.maxClockDuration().raw() == cfg.faultGameMaxClockDuration(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame maxClockDuration" + ); + require( + address(pdg.vm()) == mustGetAddress("MIPS"), "DeployHoloceneUpgrade: invalid PermissionedDisputeGame MIPS" + ); + require( + address(pdg.weth()) == mustGetAddress("DelayedWETHProxyPDG"), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame DelayedWETH" + ); + require( + address(pdg.anchorStateRegistry()) == mustGetAddress("AnchorStateRegistry"), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame AnchorStateRegistry" + ); + require(pdg.l2ChainId() == cfg.l2ChainID(), "DeployHoloceneUpgrade: invalid PermissionedDisputeGame l2ChainID"); + require( + pdg.proposer() == cfg.l2OutputOracleProposer(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame proposer" + ); + require( + pdg.challenger() == cfg.l2OutputOracleChallenger(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame challenger" + ); + } + + /// @dev Deploys a new implementation of the `DelayedWETH` contract. + function deployDelayedWETH() public { + uint256 delay = cfg.faultGameWithdrawalDelay(); + + vm.broadcast(msg.sender); + address impl = DeployUtils.create1({ + _name: "DelayedWETH", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IDelayedWETH.__constructor__, (delay))) + }); + + // Save the new implementation address. + save("DelayedWETH", impl); + } + + /// @dev Deploys a new proxy contract with a new `DelayedWETH` implementation. + function deployDelayedWETHProxy(string memory _variant) public { + address delayedWethOwner = cfg.finalSystemOwner(); + address proxyAdmin = mustGetAddress("ProxyAdmin"); + address impl = mustGetAddress("DelayedWETH"); + ISuperchainConfig superchainConfig = ISuperchainConfig(mustGetAddress("SuperchainConfig")); + string memory finalName = string.concat("DelayedWETHProxy", _variant); + + // Deploy the implementation and proxy contracts. + vm.broadcast(msg.sender); + IProxy proxy = IProxy( + DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) + }) + ); + + // Upgrade the proxy to the implementation and initialize it. + vm.broadcast(msg.sender); + proxy.upgradeToAndCall(impl, abi.encodeCall(IDelayedWETH.initialize, (delayedWethOwner, superchainConfig))); + + // Transfer the admin role of the proxy to the ProxyAdmin, now that we've upgraded + // and initialized the proxy. + vm.broadcast(msg.sender); + proxy.changeAdmin(proxyAdmin); + + // Save the proxy address. + save(finalName, address(proxy)); + } + + /// @dev Checks if the `DelayedWETH` contract is correctly configured. + function checkDelayedWETH(string memory _variant) internal { + string memory finalName = string.concat("DelayedWETHProxy", _variant); + IDelayedWETH delayedWeth = IDelayedWETH(mustGetAddress(finalName)); + require( + delayedWeth.delay() == cfg.faultGameWithdrawalDelay(), "DeployHoloceneUpgrade: invalid DelayedWETH delay" + ); + require( + delayedWeth.config() == ISuperchainConfig(mustGetAddress("SuperchainConfig")), + "DeployHoloceneUpgrade: invalid DelayedWETH config" + ); + + vm.prank(mustGetAddress("ProxyAdmin")); + address admin = IProxy(payable(address(delayedWeth))).admin(); + require(admin == mustGetAddress("ProxyAdmin"), "DeployHoloceneUpgrade: invalid DelayedWETH admin"); + } + + /// @dev Prints a summary of the deployment. + function printSummary() internal view { + console.log("1. SystemConfig: %s", mustGetAddress("SystemConfig")); + console.log("2. MIPS: %s", getAddress("MIPS")); + console.log("3. FaultDisputeGame: %s", getAddress("FaultDisputeGame")); + console.log("4. PermissionedDisputeGame: %s", getAddress("PermissionedDisputeGame")); + } +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md new file mode 100644 index 0000000000000..f16127ffebbd0 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md @@ -0,0 +1,41 @@ +# Holocene Upgrade + +This directory contains a repeatable task for: +* upgrading an `op-contracts/v1.6.0` deployment to `op-contracts/v1.8.0`. +* upgrading an `op-contracts/v1.3.0` deployment to `op-contracts/v1.8.0`, while retaining the `L2OutputOracle`. + +## Dependencies + +- [`docker`](https://docs.docker.com/engine/install/) +- [`just`](https://github.com/casey/just) +- [`foundry`](https://getfoundry.sh/) + +## Usage + +This script has several different modes of operation. Namely: +1. Deploy and upgrade `op-contracts/1.6.0` -> `op-contracts/v1.8.0` + - Always upgrade the `SystemConfig` + - FP options: + - With permissionless fault proofs enabled (incl. `FaultDisputeGame`) + - With permissioned fault proofs enabled (excl. `FaultDisputeGame`) +1. Deploy and upgrade `op-contracts/v1.3.0` -> `op-contracts/v1.8.0`, with the `L2OutputOracle` still active. + - Only upgrade the `SystemConfig` + +```sh +# 1. Clone the monorepo and navigate to this directory. +git clone git@github.com:ethereum-optimism/monorepo.git && \ + cd monorepo/packages/contracts-bedrock/scripts/upgrades/holocene + +# 2. Set up the `.env` file +# +# Read the documentation carefully, and when in doubt, reach out to the OP Labs team. +cp .env.example .env && vim .env + +# 3. Run the upgrade task. +# +# This task will: +# - Deploy the new smart contract implementations. +# - Optionally, generate a safe upgrade bundle. +# - Optionally, generate a `superchain-ops` upgrade task. +just run +``` diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/justfile b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile new file mode 100644 index 0000000000000..08cdc771ac92c --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile @@ -0,0 +1,26 @@ +# Default recipe to list help menu. +default: + @just --list + +# Run the deployment / upgrade generation image. If the image is not present locally, +# it will be built. +run *args='': + #!/bin/bash + if [ ! "$(docker images -q op-holocene-upgrade:local 2> /dev/null)" ]; then + just build-image + fi + + # Run the deployment. + docker run \ + --rm \ + -v $OUTPUT_FOLDER_PATH/:/output \ + --env-file=.env \ + op-holocene-upgrade:local {{args}} + +# Build the image locally. +build-image: + docker build \ + -t op-holocene-upgrade:local \ + -f upgrade.dockerfile \ + --build-arg REV=op-contracts/v1.8.0-rc.1 \ + . diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh new file mode 100755 index 0000000000000..0f4e80b29c408 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Associative array to store cached TOML content for different URLs +# Used by fetch_standard_address and fetch_superchain_config_address +declare -A CACHED_TOML_CONTENT + +# error_handler +# +# Basic error handler +error_handler() { + echo "Error occurred in ${BASH_SOURCE[1]} at line: ${BASH_LINENO[0]}" + echo "Error message: $BASH_COMMAND" + exit 1 +} + +# Register the error handler +trap error_handler ERR + +# reqenv +# +# Checks if a specified environment variable is set. +# +# Arguments: +# $1 - The name of the environment variable to check +# +# Exits with status 1 if: +# - The specified environment variable is not set +reqenv() { + if [ -z "$1" ]; then + echo "Error: $1 is not set" + exit 1 + fi +} + +# prompt +# +# Prompts the user for a yes/no response. +# +# Arguments: +# $1 - The prompt message +# +# Exits with status 1 if: +# - The user does not respond with 'y' +# - The process is interrupted +prompt() { + read -p "$1 [Y/n] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + [[ "$0" = "${BASH_SOURCE[0]}" ]] && exit 1 || return 1 + exit 1 + fi +} + +# fetch_standard_address +# +# Fetches the implementation address for a given contract from a TOML file. +# The TOML file is downloaded from a URL specified in ADDRESSES_TOML_URL +# environment variable. Results are cached to avoid repeated downloads. +# +# Arguments: +# $1 - Network name +# $2 - The release version +# $3 - The name of the contract to look up +# +# Returns: +# The implementation address of the specified contract +# +# Exits with status 1 if: +# - Failed to fetch the TOML file +# - The release version is not found in the TOML file +# - The implementation address for the specified contract is not found +fetch_standard_address() { + local network_name="$1" + local release_version="$2" + local contract_name="$3" + + # Determine the correct toml url + local toml_url="https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/refs/heads/main/validation/standard/standard-versions" + if [ "$network_name" = "mainnet" ]; then + toml_url="$toml_url-mainnet.toml" + elif [ "$network_name" = "sepolia" ]; then + toml_url="$toml_url-sepolia.toml" + else + echo "Error: NETWORK must be set to 'mainnet' or 'sepolia'" + exit 1 + fi + + # Fetch the TOML file content from the URL if not already cached for this URL + if [ -z "${CACHED_TOML_CONTENT[$toml_url]:-}" ]; then + CACHED_TOML_CONTENT[$toml_url]=$(curl -s "$toml_url") + # shellcheck disable=SC2181 + if [ $? -ne 0 ]; then + echo "Error: Failed to fetch TOML file from $toml_url" + exit 1 + fi + fi + + # Use the cached content for the current URL + local toml_content="${CACHED_TOML_CONTENT[$toml_url]}" + + # Find the section for v1.6.0 release + # shellcheck disable=SC2155 + local section_content=$(echo "$toml_content" | awk -v version="$release_version" ' + $0 ~ "^\\[releases.\"op-contracts/v" version "\"\\]" { + flag=1; + next + } + flag && /^\[/ { + exit + } + flag { + print + } + ') + if [ -z "$section_content" ]; then + echo "Error: v$release_version release section not found in addresses TOML" + exit 1 + fi + + # Extract the implementation address for the specified contract + local regex="(address|implementation_address) = \"(0x[a-fA-F0-9]{40})\"" + # shellcheck disable=SC2155 + local data=$(echo "$section_content" | grep "${contract_name}") + if [[ $data =~ $regex ]]; then + echo "${BASH_REMATCH[2]}" + else + echo "Error: Implementation address for $contract_name not found in v$release_version release" + exit 1 + fi +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh new file mode 100755 index 0000000000000..d1e15aa63e7f5 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check required environment variables +reqenv "ETH_RPC_URL" +reqenv "PRIVATE_KEY" +reqenv "ETHERSCAN_API_KEY" +reqenv "DEPLOY_CONFIG_PATH" +reqenv "IMPL_SALT" + +# Check required address environment variables +reqenv "PROXY_ADMIN_ADDR" +reqenv "SUPERCHAIN_CONFIG_PROXY_ADDR" +reqenv "PREIMAGE_ORACLE_ADDR" +reqenv "ANCHOR_STATE_REGISTRY_PROXY_ADDR" +reqenv "DELAYED_WETH_IMPL_ADDR" +reqenv "SYSTEM_CONFIG_IMPL_ADDR" +reqenv "MIPS_IMPL_ADDR" +reqenv "USE_FAULT_PROOFS" +reqenv "USE_PERMISSIONLESS_FAULT_PROOFS" + +# Run the upgrade script +forge script DeployUpgrade.s.sol \ + --rpc-url "$ETH_RPC_URL" \ + --private-key "$PRIVATE_KEY" \ + --etherscan-api-key "$ETHERSCAN_API_KEY" \ + --sig "deploy(address,address,address,address,address,address,address,bool,bool)" \ + "$PROXY_ADMIN_ADDR" \ + "$SUPERCHAIN_CONFIG_PROXY_ADDR" \ + "$SYSTEM_CONFIG_IMPL_ADDR" \ + "$MIPS_IMPL_ADDR" \ + "$DELAYED_WETH_IMPL_ADDR" \ + "$PREIMAGE_ORACLE_ADDR" \ + "$ANCHOR_STATE_REGISTRY_PROXY_ADDR" \ + "$USE_FAULT_PROOFS" \ + "$USE_PERMISSIONLESS_FAULT_PROOFS" \ + --broadcast \ + --verify \ + --slow diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh new file mode 100755 index 0000000000000..95fe1a43c231b --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +echo " +⠄⢀⠀⠀⡐⠠⠀⢂⣠⡤⣤⣖⣤⣤⣄⢢⣤⣤⣤⡀⠄⠰⠀⠆⠀⠀⠠⠀⠆⠠⢀⠢⠐⠆⡀ +⠔⢀⠀⠄⡀⠔⢠⣾⣿⣿⣷⣿⣿⣿⣿⣷⣟⣛⣻⣿⣤⡠⢀⠆⢄⠂⠠⢠⠀⠄⠀⢠⠰⢄⠄ +⣈⠀⠐⡀⠀⠈⣾⣿⣿⣿⣿⡿⡿⣿⣿⣿⣿⡿⢿⣿⣿⣿⣦⠀⠂⠀⢈⠀⠁⠂⡀⢀⠠⠈⡀ +⠆⠈⠀⠄⣃⣼⣿⣿⣿⠿⡿⣿⣿⣷⣾⣷⣾⡾⣿⣿⣿⢿⡟⡇⠠⠁⠨⠐⠀⠃⠱⠊⠀⠀⠄ +⠐⣠⣶⣿⣿⣿⣿⣿⣿⣿⣾⣮⣛⢿⣿⡿⠘⣇⢯⣹⣶⣷⣿⣿⡄⠆⢐⠀⢄⠢⡒⠐⠠⢀⠂ +⢴⣿⣿⣿⣿⣿⣿⡿⠿⢿⣿⣿⣟⢿⣶⣾⣿⣧⢻⣿⢿⣿⣿⣿⠂⠈⢀⠈⡀⡁⢀⠉⢀⠀⠀ +⡜⣿⣿⣿⣿⣿⣿⣦⡻⠀⢨⣽⣿⣿⣿⣿⣿⣿⣦⡛⣾⣭⣃⣀⣦⠀⠨⠐⠀⠄⠃⠈⠀⠀⠁ +⣳⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⢛⣭⣿⣿⣿⣿⣿⣿⣿⢢⣿⣿⣿⣿⡇⢀⠲⠂⠄⢀⡐⠠⠐⠂ +⢣⠵⢹⣿⣿⣿⣿⣿⣿⣿⣿⣧⣛⣻⣿⣭⣿⣿⠻⢥⣿⣿⣿⣿⣿⣿⡄⠀⠁⡀⢀⢈⠀⢀⠁ +⡼⣹⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣟⣻⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠁⠉⠐⠀⠂⠈ +⡷⣡⠧⢹⣿⣿⣿⣿⣿⣿⣗⣶⣾⣿⣿⣿⣿⣿⠮⢿⣿⣿⡿⠿⣿⣿⢀⣀⠂⡁⠐⢌⠐⡠⠁ +⢷⠡⠏⢧⢌⢻⣿⣿⣿⣟⣿⣿⣻⣿⡿⠿⣛⣵⢟⣭⡭⠥⠮⠕⣒⣒⣚⣮⣰⢤⣤⣄⣀⡠⠄ +⠁⠠⠀⠄⣏⢦⡙⣿⣿⣽⣟⣿⡟⣬⣾⣿⣿⣿⣾⣆⠭⠽⠶⢶⢶⣖⣶⣹⣖⡿⣿⣿⣿⣿⡆ +⠀⡁⠂⡟⡜⣦⢫⣌⠻⣷⣿⢏⣾⣿⣿⣿⢿⣿⢿⣿⣿⣿⣿⣿⠿⣛⣛⣛⠛⣭⣿⣿⢻⣽⡇ +⣏⠖⣮⢱⣋⢖⣣⢎⡳⢤⡅⣾⣿⢯⣷⡏⡖⣰⣝⣚⣙⣫⢍⡶⠷⣽⢭⣛⡇⠀⢰⣶⣾⣿⠀ +⡮⡝⢦⣓⢎⡳⡜⠎⠡⠁⢸⣿⣿⣿⣿⣁⢠⣿⡿⣿⡟⣎⣯⣽⣋⡷⣾⣹⡃⠀⢸⣿⣿⢿⠀ +⣳⠁⠀⡝⢮⣱⢹⠀⠂⠈⣿⣿⣿⣿⡻⣈⣸⣿⣙⢾⢿⣹⡶⣿⣼⣗⣻⡞⣡⠁⣼⣿⣿⣿⡀ +⡔⢦⢥⡛⣜⠦⣏⡄⡈⣸⢿⣿⡿⣽⢃⠇⣿⣧⡝⣟⡳⢾⣹⣟⡻⣾⣹⢣⠞⣄⣯⣿⣷⣿⡆ + -*~ [ Grug Deployer mk2 ] ~*- + ~*- [ Holocene ] -*~ +" + +# Set variables from environment or error. +export RELEASE="1.8.0" +export NETWORK="${NETWORK:?NETWORK must be set}" +export ETHERSCAN_API_KEY=${ETHERSCAN_API_KEY:?ETHERSCAN_API_KEY must be set} +export ETH_RPC_URL=${ETH_RPC_URL:?ETH_RPC_URL must be set} +export PRIVATE_KEY=${PRIVATE_KEY:?PRIVATE_KEY must be set} +export BASE_DEPLOY_CONFIG_PATH=${DEPLOY_CONFIG_PATH:?DEPLOY_CONFIG_PATH must be set} +export OUTPUT_FOLDER_PATH=${OUTPUT_FOLDER_PATH:?OUTPUT_FOLDER_PATH must be set} +export SYSTEM_CONFIG_IMPL_ADDR=${SYSTEM_CONFIG_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "system_config")} +export MIPS_IMPL_ADDR=${MIPS_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "mips")} +export PREIMAGE_ORACLE_ADDR=${PREIMAGE_ORACLE_ADDR:?PREIMAGE_ORACLE_ADDR must be set} +export ANCHOR_STATE_REGISTRY_PROXY_ADDR=${ANCHOR_STATE_REGISTRY_PROXY_ADDR:?ANCHOR_STATE_REGISTRY_PROXY_ADDR must be set} +export DELAYED_WETH_IMPL_ADDR=${DELAYED_WETH_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "delayed_weth")} +export PROXY_ADMIN_ADDR=${PROXY_ADMIN_ADDR:?PROXY_ADMIN_ADDR must be set} +export SUPERCHAIN_CONFIG_PROXY_ADDR=${SUPERCHAIN_CONFIG_PROXY_ADDR:?SUPERCHAIN_CONFIG_ADDR must be set} +export SYSTEM_CONFIG_PROXY_ADDR=${SYSTEM_CONFIG_PROXY_ADDR:?SYSTEM_CONFIG_PROXY_ADDR must be set} +export DISPUTE_GAME_FACTORY_PROXY_ADDR=${DISPUTE_GAME_FACTORY_PROXY_ADDR:?DISPUTE_GAME_FACTORY_PROXY_ADDR must be set} +export USE_FAULT_PROOFS=${USE_FAULT_PROOFS:?USE_FAULT_PROOFS must be set} +export USE_PERMISSIONLESS_FAULT_PROOFS=${USE_PERMISSIONLESS_FAULT_PROOFS:?USE_PERMISSIONLESS_FAULT_PROOFS must be set} + +# Sanity check FP configuration. +if [[ $USE_PERMISSIONLESS_FAULT_PROOFS == true && $USE_FAULT_PROOFS == false ]]; then + echo "Error: USE_PERMISSIONLESS_FAULT_PROOFS cannot be true if USE_FAULT_PROOFS is false" + exit 1 +fi + +# Make the output folder, if it doesn't exist +mkdir -p "$OUTPUT_FOLDER_PATH" + +# Find the contracts-bedrock directory +CONTRACTS_BEDROCK_DIR=$(pwd) +while [[ "$CONTRACTS_BEDROCK_DIR" != "/" && "${CONTRACTS_BEDROCK_DIR##*/}" != "contracts-bedrock" ]]; do + CONTRACTS_BEDROCK_DIR=$(dirname "$CONTRACTS_BEDROCK_DIR") +done + +# Error out if we couldn't find it for some reason +if [[ "$CONTRACTS_BEDROCK_DIR" == "/" ]]; then + echo "Error: 'contracts-bedrock' directory not found" + exit 1 +fi + +# Set file paths from command-line arguments +export DEPLOY_CONFIG_PATH="$CONTRACTS_BEDROCK_DIR/deploy-config/deploy-config.json" + +# Copy the files into the paths so that the script can actually access it +cp "$BASE_DEPLOY_CONFIG_PATH" "$DEPLOY_CONFIG_PATH" + +# Run deploy.sh +DEPLOY_LOG_PATH="$OUTPUT_FOLDER_PATH/deploy.log" +if ! "$SCRIPT_DIR/deploy.sh" | tee "$DEPLOY_LOG_PATH"; then + echo "Error: deploy.sh failed" + exit 1 +fi + +# Extract the addresses from the deployment logs +# shellcheck disable=2155 +export SYSTEM_CONFIG_IMPL=$(grep "1. SystemConfig:" "$DEPLOY_LOG_PATH" | awk '{print $3}') +# shellcheck disable=2155 +export MIPS_IMPL=$(grep "2. MIPS:" "$DEPLOY_LOG_PATH" | awk '{print $3}') +# shellcheck disable=2155 +export FDG_IMPL=$(grep "3. FaultDisputeGame:" "$DEPLOY_LOG_PATH" | awk '{print $3}') +# shellcheck disable=2155 +export PDG_IMPL=$(grep "4. PermissionedDisputeGame:" "$DEPLOY_LOG_PATH" | awk '{print $3}') + +# Ensure that the addresses were extracted properly +reqenv "SYSTEM_CONFIG_IMPL" +reqenv "MIPS_IMPL" +reqenv "FDG_IMPL" +reqenv "PDG_IMPL" + +# Generate deployments.json with extracted addresses +DEPLOYMENTS_JSON_PATH="$OUTPUT_FOLDER_PATH/deployments.json" +cat << EOF > "$DEPLOYMENTS_JSON_PATH" +{ + "SystemConfig": "$SYSTEM_CONFIG_IMPL", + "MIPS": "$MIPS_IMPL", + "FaultDisputeGame": "$FDG_IMPL", + "PermissionedDisputeGame": "$PDG_IMPL" +} +EOF + +echo "✨ Deployed contracts and saved addresses to \"$DEPLOYMENTS_JSON_PATH\"" + +# Print a message when the script exits +trap 'echo "✨ Done. Artifacts are available in \"$OUTPUT_FOLDER_PATH\""' EXIT + +prompt "Generate safe upgrade bundle for SystemConfig?" + +# Generate the system config upgrade bundle +if ! "$SCRIPT_DIR/sys-cfg-bundle.sh"; then + echo "Error: sys-cfg-bundle.sh failed" + exit 1 +fi + +prompt "Generate superchain-ops upgrade task for SystemConfig upgrade bundle?" + +# Generate the superchain-ops upgrade task +if ! "$SCRIPT_DIR/sc-ops-sys-cfg.sh"; then + echo "Error: sc-ops-sys-cfg.sh failed" + exit 1 +fi + +if [[ $USE_FAULT_PROOFS == true ]]; then + prompt "Generate safe upgrade bundle for proofs contracts?" + + # Generate the proofs contracts' upgrade bundle + if ! "$SCRIPT_DIR/proofs-bundle.sh"; then + echo "Error: proofs-bundle.sh failed" + exit 1 + fi + + prompt "Generate superchain-ops upgrade task for proofs contracts upgrade bundle?" + + # Generate the superchain-ops upgrade task + if ! "$SCRIPT_DIR/sc-ops-proofs.sh"; then + echo "Error: sc-ops-proofs.sh failed" + exit 1 + fi +fi diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh new file mode 100755 index 0000000000000..85295d3040f1f --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check the env +reqenv "ETH_RPC_URL" +reqenv "OUTPUT_FOLDER_PATH" +reqenv "MIPS_IMPL" +reqenv "FDG_IMPL" +reqenv "PDG_IMPL" +reqenv "DISPUTE_GAME_FACTORY_PROXY_ADDR" +reqenv "USE_PERMISSIONLESS_FAULT_PROOFS" + +# Local environment +BUNDLE_PATH="$OUTPUT_FOLDER_PATH/proofs_bundle.json" +L1_CHAIN_ID=$(cast chain-id) + +# Copy the bundle template +cp ./templates/proof_upgrade_bundle_template.json "$BUNDLE_PATH" + +# Tx 1: Upgrade PermissionedDisputeGame implementation +TX_1_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 1 "$PDG_IMPL") + +# Tx 2: Upgrade FaultDisputeGame implementation +TX_2_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 0 "$FDG_IMPL") + +# Replace variables +sed -i '' "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" +sed -i '' "s/\$PDG_IMPL/$PDG_IMPL/g" "$BUNDLE_PATH" +sed -i '' "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" +sed -i '' "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" + +# Conditionally, if the FDG is being deployed, append the bundle extension +if [ "$USE_PERMISSIONLESS_FAULT_PROOFS" == true ]; then + echo "✨ USE_PERMISSIONLESS_FAULT_PROOFS=true | Adding FDG deployment to upgrade bundle." + jq --argjson fdg_extension "$(cat ./templates/fdg_bundle_extension.json)" \ + '.transactions += [$fdg_extension]' \ + "$BUNDLE_PATH" > "$BUNDLE_PATH.tmp" + mv "$BUNDLE_PATH.tmp" "$BUNDLE_PATH" + + # Replace variables + sed -i '' "s/\$FDG_IMPL/$FDG_IMPL/g" "$BUNDLE_PATH" + sed -i '' "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" +fi + +sed -i '' "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$BUNDLE_PATH" + +echo "✨ Generated proof contracts upgrade bundle at \"$BUNDLE_PATH\"" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh new file mode 100755 index 0000000000000..d4ff18db5738e --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check required environment variables +reqenv "OUTPUT_FOLDER_PATH" +reqenv "MIPS_IMPL" +reqenv "FDG_IMPL" +reqenv "PDG_IMPL" +reqenv "DISPUTE_GAME_FACTORY_PROXY_ADDR" + +# Create directory for the task +TASK_DIR="$OUTPUT_FOLDER_PATH/proofs-sc-ops-task" +mkdir -p "$TASK_DIR" + +# Copy the bundle and task template +cp "$OUTPUT_FOLDER_PATH/proofs_bundle.json" "$TASK_DIR/input.json" +cp -R "$SCRIPT_DIR/../templates/proofs-sc-ops-task/." "$TASK_DIR/" + +# Generate the task overview +msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md" + +# Generate the README +sed -i '' "s/\$MIPS_IMPL/$MIPS_IMPL/g" "$TASK_DIR/README.md" +sed -i '' "s/\$FDG_IMPL/$FDG_IMPL/g" "$TASK_DIR/README.md" +sed -i '' "s/\$PDG_IMPL/$PDG_IMPL/g" "$TASK_DIR/README.md" + +# Generate the validation doc +OLD_FDG=$(cast call "$DISPUTE_GAME_FACTORY_PROXY_ADDR" "gameImpls(uint32)" 0) +OLD_PDG=$(cast call "$DISPUTE_GAME_FACTORY_PROXY_ADDR" "gameImpls(uint32)" 1) + +PADDED_OLD_FDG=$(cast 2u "$OLD_FDG") +PADDED_OLD_PDG=$(cast 2u "$OLD_PDG") +PADDED_FDG_IMPL=$(cast 2u "$FDG_IMPL") +PADDED_PDG_IMPL=$(cast 2u "$PDG_IMPL") + +sed -i '' "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" +sed -i '' "s/\$OLD_FDG/$PADDED_OLD_FDG/g" "$TASK_DIR/VALIDATION.md" +sed -i '' "s/\$FDG_IMPL/$PADDED_FDG_IMPL/g" "$TASK_DIR/VALIDATION.md" +sed -i '' "s/\$PDG_IMPL/$PADDED_PDG_IMPL/g" "$TASK_DIR/VALIDATION.md" +sed -i '' "s/\$OLD_PDG/$PADDED_OLD_PDG/g" "$TASK_DIR/VALIDATION.md" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh new file mode 100755 index 0000000000000..b0e4a57b32b70 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check required environment variables +reqenv "OUTPUT_FOLDER_PATH" +reqenv "SYSTEM_CONFIG_IMPL" +reqenv "SYSTEM_CONFIG_PROXY_ADDR" + +# Create directory for the task +TASK_DIR="$OUTPUT_FOLDER_PATH/sys-cfg-sc-ops-task" +mkdir -p "$TASK_DIR" + +# Copy the bundle and task template +cp "$OUTPUT_FOLDER_PATH/sys_cfg_bundle.json" "$TASK_DIR/input.json" +cp -R "$SCRIPT_DIR/../templates/sys-cfg-sc-ops-task/." "$TASK_DIR/" + +# Generate the task overview +msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md" + +# Generate the README +sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$TASK_DIR/README.md" + +# Generate the validation doc +OLD_SYS_CFG=$(cast impl "$SYSTEM_CONFIG_PROXY_ADDR") + +PADDED_OLD_SYS_CFG=$(cast 2u "$OLD_SYS_CFG") +PADDED_SYS_CFG=$(cast 2u "$SYSTEM_CONFIG_IMPL") + +sed -i '' "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" +sed -i '' "s/\$OLD_SYS_CFG/$PADDED_OLD_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" +sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$PADDED_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh new file mode 100755 index 0000000000000..64c34088ebf53 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check the env +reqenv "ETH_RPC_URL" +reqenv "OUTPUT_FOLDER_PATH" +reqenv "PROXY_ADMIN_ADDR" +reqenv "SYSTEM_CONFIG_PROXY_ADDR" +reqenv "SYSTEM_CONFIG_IMPL" + +# Local environment +BUNDLE_PATH="$OUTPUT_FOLDER_PATH/sys_cfg_bundle.json" +L1_CHAIN_ID=$(cast chain-id) + +# Copy the bundle template +cp ./templates/sys_cfg_upgrade_bundle_template.json "$BUNDLE_PATH" + +# Tx 1: Upgrade SystemConfigProxy implementation +TX_1_PAYLOAD=$(cast calldata "upgrade(address,address)" "$SYSTEM_CONFIG_PROXY_ADDR" "$SYSTEM_CONFIG_IMPL") + +# Replace variables +sed -i '' "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" +sed -i '' "s/\$PROXY_ADMIN_ADDR/$PROXY_ADMIN_ADDR/g" "$BUNDLE_PATH" +sed -i '' "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$BUNDLE_PATH" +sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$BUNDLE_PATH" +sed -i '' "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" + +echo "✨ Generated SystemConfig upgrade bundle at \"$BUNDLE_PATH\"" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json new file mode 100644 index 0000000000000..fa6680496bdb8 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json @@ -0,0 +1,29 @@ +{ + "metadata": { + "name": "Upgrade `CANNON` game type in `DisputeGameFactory`", + "description": "Upgrades the `CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash." + }, + "to": "$DISPUTE_GAME_FACTORY_PROXY_ADDR", + "value": "0x0", + "data": "$TX_2_PAYLOAD", + "contractMethod": { + "type": "function", + "name": "setImplementation", + "inputs": [ + { + "name": "_gameType", + "type": "uint32" + }, + { + "name": "_impl", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + "contractInputsValues": { + "_gameType": "0", + "_impl": "$FDG_IMPL" + } +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json new file mode 100644 index 0000000000000..e74360f02a97c --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json @@ -0,0 +1,38 @@ +{ + "chainId": $L1_CHAIN_ID, + "metadata": { + "name": "Holocene Hardfork - Proof Contract Upgrades", + "description": "Upgrades the `MIPS.sol`, `FaultDisputeGame.sol`, and `PermissionedDisputeGame.sol` contracts for Holocene." + }, + "transactions": [ + { + "metadata": { + "name": "Upgrade `PERMISSIONED_CANNON` game type in `DisputeGameFactory`", + "description": "Upgrades the `PERMISSIONED_CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash." + }, + "to": "$DISPUTE_GAME_FACTORY_PROXY_ADDR", + "value": "0x0", + "data": "$TX_1_PAYLOAD", + "contractMethod": { + "type": "function", + "name": "setImplementation", + "inputs": [ + { + "name": "_gameType", + "type": "uint32" + }, + { + "name": "_impl", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + "contractInputsValues": { + "_gameType": "1", + "_impl": "$PDG_IMPL" + } + } + ] +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example new file mode 100644 index 0000000000000..ba2beff7f4291 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example @@ -0,0 +1,6 @@ +ETH_RPC_URL= +SUPERCHAIN_CONFIG_ADDR= +COUNCIL_SAFE= +FOUNDATION_SAFE= +OWNER_SAFE= +SAFE_NONCE= diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md new file mode 100644 index 0000000000000..e2077747dba12 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md @@ -0,0 +1,46 @@ +# Holocene Hardfork Upgrade + +Status: DRAFT, NOT READY TO SIGN + +## Objective + +Upgrades the Fault Proof contracts for the Holocene hardfork. + +The proposal was: + +- [ ] Posted on the governance forum. +- [ ] Approved by Token House voting. +- [ ] Not vetoed by the Citizens' house. +- [ ] Executed on OP Mainnet. + +The governance proposal should be treated as the source of truth and used to verify the correctness of the onchain operations. + +Governance post of the upgrade can be found at . + +This upgrades the Fault Proof contracts in the +[op-contracts/v1.8.0](https://github.com/ethereum-optimism/optimism/tree/op-contracts/v1.8.0-rc.1) release. + +## Pre-deployments + +- `MIPS` - `$MIPS_IMPL` +- `FaultDisputeGame` - `$FDG_IMPL` +- `PermissionedDisputeGame` - `$PDG_IMPL` + +## Simulation + +Please see the "Simulating and Verifying the Transaction" instructions in [NESTED.md](../../../NESTED.md). +When simulating, ensure the logs say `Using script /your/path/to/superchain-ops/tasks//NestedSignFromJson.s.sol`. +This ensures all safety checks are run. If the default `NestedSignFromJson.s.sol` script is shown (without the full path), something is wrong and the safety checks will not run. + +## State Validation + +Please see the instructions for [validation](./VALIDATION.md). + +## Execution + +This upgrade +* Changes dispute game implementation of the `CANNON` and `PERMISSIONED_CANNON` game types to contain a `op-program` release for the Holocene hardfork, which contains + the Holocene fork implementation as well as a `ChainConfig` and `RollupConfig` for the L2 chain being upgraded. +* Upgrades `MIPS.sol` to support the `F_GETFD` syscall, required by the golang 1.22+ runtime. + +See the [overview](./OVERVIEW.md) and `input.json` bundle for more details. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md new file mode 100644 index 0000000000000..50ba3ca106b46 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md @@ -0,0 +1,24 @@ +# Validation + +This document can be used to validate the state diff resulting from the execution of the upgrade +transaction. + +For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: + +- The following state changes (and none others) are made to that contract. This validates that no unexpected state changes occur. +- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. +- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. + +## State Changes + +### `$DISPUTE_GAME_FACTORY_PROXY_ADDR` (`DisputeGameFactoryProxy`) + +- **Key**: `0xffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b`
+ **Before**: `$OLD_FDG`
+ **After**: `$FDG_IMPL`
+ **Meaning**: Updates the CANNON game type implementation. Verify that the new implementation is set using `cast call $DISPUTE_GAME_FACTORY_PROXY_ADDR "gameImpls(uint32)(address)" 0`. Where `0` is the [`CANNON` game type](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.4.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L28). + +- **Key**: `0x4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e`
+ **Before**: `$OLD_PDG`
+ **After**: `$PDG_IMPL`
+ **Meaning**: Updates the PERMISSIONED_CANNON game type implementation. Verify that the new implementation is set using `cast call $DISPUTE_GAME_FACTORY_PROXY_ADDR "gameImpls(uint32)(address)" 1`. Where `1` is the [`PERMISSIONED_CANNON` game type](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.4.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L31). diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example new file mode 100644 index 0000000000000..ba2beff7f4291 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example @@ -0,0 +1,6 @@ +ETH_RPC_URL= +SUPERCHAIN_CONFIG_ADDR= +COUNCIL_SAFE= +FOUNDATION_SAFE= +OWNER_SAFE= +SAFE_NONCE= diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md new file mode 100644 index 0000000000000..740219586b80f --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md @@ -0,0 +1,42 @@ +# Holocene Hardfork Upgrade - `SystemConfig` + +Status: DRAFT, NOT READY TO SIGN + +## Objective + +Upgrades the `SystemConfig` for the Holocene hardfork. + +The proposal was: + +- [ ] Posted on the governance forum. +- [ ] Approved by Token House voting. +- [ ] Not vetoed by the Citizens' house. +- [ ] Executed on OP Mainnet. + +The governance proposal should be treated as the source of truth and used to verify the correctness of the onchain operations. + +Governance post of the upgrade can be found at . + +This upgrades the `SystemConfig` in the +[op-contracts/v1.8.0](https://github.com/ethereum-optimism/optimism/tree/op-contracts/v1.8.0-rc.1) release. + +## Pre-deployments + +- `SystemConfig` - `$SYSTEM_CONFIG_IMPL` + +## Simulation + +Please see the "Simulating and Verifying the Transaction" instructions in [NESTED.md](../../../NESTED.md). +When simulating, ensure the logs say `Using script /your/path/to/superchain-ops/tasks//NestedSignFromJson.s.sol`. +This ensures all safety checks are run. If the default `NestedSignFromJson.s.sol` script is shown (without the full path), something is wrong and the safety checks will not run. + +## State Validation + +Please see the instructions for [validation](./VALIDATION.md). + +## Execution + +This upgrade +* Changes the implementation of the `SystemConfig` to hold EIP-1559 parameters for the + +See the [overview](./OVERVIEW.md) and `input.json` bundle for more details. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md new file mode 100644 index 0000000000000..3a5e7b67595aa --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md @@ -0,0 +1,19 @@ +# Validation + +This document can be used to validate the state diff resulting from the execution of the upgrade +transaction. + +For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: + +- The following state changes (and none others) are made to that contract. This validates that no unexpected state changes occur. +- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. +- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. + +## State Changes + +### `$SYSTEM_CONFIG_PROXY_ADDR` (`SystemConfigProxy`) + +- **Key**: `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + **Before**: `$OLD_SYS_CFG` + **After**: `$SYSTEM_CONFIG_IMPL` + **Meaning**: Updates the `SystemConfig` proxy implementation. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json new file mode 100644 index 0000000000000..62746a51982fe --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json @@ -0,0 +1,38 @@ +{ + "chainId": $L1_CHAIN_ID, + "metadata": { + "name": "Holocene Hardfork - SystemConfig Upgrade", + "description": "Upgrades the `SystemConfig.sol` contract for Holocene." + }, + "transactions": [ + { + "metadata": { + "name": "Upgrade `SystemConfig` proxy", + "description": "Upgrades the `SystemConfig` proxy to the new implementation, featuring configurable EIP-1559 parameters." + }, + "to": "$PROXY_ADMIN_ADDR", + "value": "0x0", + "data": "$TX_1_PAYLOAD", + "contractMethod": { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "_proxy", + "type": "address" + }, + { + "name": "_implementation", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + "contractInputsValues": { + "_proxy": "$SYSTEM_CONFIG_PROXY_ADDR", + "_implementation": "$SYSTEM_CONFIG_IMPL" + } + } + ] +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile new file mode 100644 index 0000000000000..f91ff8dadd83e --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile @@ -0,0 +1,62 @@ +# Use a base image with necessary tools +FROM ubuntu:20.04 + +ARG REV + +# Install required packages +RUN apt-get update && apt-get install -y \ + git \ + bash \ + curl \ + build-essential \ + jq \ + && rm -rf /var/lib/apt/lists/* + +# Install Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install just +RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin + +# Install msup +RUN git clone https://github.com/clabby/msup.git && \ + cd msup && \ + cargo install --path . + +# Install foundryup +RUN curl -L https://foundry.paradigm.xyz | bash +ENV PATH="/root/.foundry/bin:${PATH}" + +# Set the working directory +WORKDIR /app + +# Clone the repository +RUN git clone https://github.com/ethereum-optimism/optimism.git . + +# Check out the target branch +RUN git checkout $REV + +# Set the working directory to the root of the monorepo +WORKDIR /app + +# Install correct foundry version +RUN just update-foundry + +# Set the working directory to the root of the contracts package +WORKDIR /app/packages/contracts-bedrock + +# Install dependencies +RUN just install + +# Build the contracts package +RUN forge build + +# Deliberately run the upgrade script with invalid args to trigger a build +RUN forge script ./scripts/upgrades/holocene/DeployUpgrade.s.sol || true + +# Set the working directory to where upgrade.sh is located +WORKDIR /app/packages/contracts-bedrock/scripts/upgrades/holocene + +# Set the entrypoint to the main.sh script +ENTRYPOINT ["./scripts/main.sh"]