diff --git a/Makefile b/Makefile index 0deb3e0e..cff3ba4c 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ SAFE_MANAGEMENT_DIR = $(network)/$(shell date +'%Y-%m-%d')-safe-swap-owner FUNDING_DIR = $(network)/$(shell date +'%Y-%m-%d')-funding SET_BASE_BRIDGE_PARTNER_THRESHOLD_DIR = $(network)/$(shell date +'%Y-%m-%d')-pause-bridge-base PAUSE_BRIDGE_BASE_DIR = $(network)/$(shell date +'%Y-%m-%d')-pause-bridge-base +SWITCH_TO_PERMISSIONED_GAME_DIR=$(network)/$(shell date +'%Y-%m-%d')-switch-to-permissioned-game TEMPLATE_GENERIC = setup-templates/template-generic TEMPLATE_GAS_INCREASE = setup-templates/template-gas-increase @@ -15,6 +16,7 @@ TEMPLATE_SAFE_MANAGEMENT = setup-templates/template-safe-management TEMPLATE_FUNDING = setup-templates/template-funding TEMPLATE_SET_BASE_BRIDGE_PARTNER_THRESHOLD = setup-templates/template-set-bridge-partner-threshold TEMPLATE_PAUSE_BRIDGE_BASE = setup-templates/template-pause-bridge-base +TEMPLATE_SWITCH_TO_PERMISSIONED_GAME = setup-templates/template-switch-to-permissioned-game ifndef $(GOPATH) GOPATH=$(shell go env GOPATH) @@ -63,6 +65,10 @@ setup-bridge-pause: rm -rf $(TEMPLATE_PAUSE_BRIDGE_BASE)/cache $(TEMPLATE_PAUSE_BRIDGE_BASE)/lib $(TEMPLATE_PAUSE_BRIDGE_BASE)/out cp -r $(TEMPLATE_PAUSE_BRIDGE_BASE) $(PAUSE_BRIDGE_BASE_DIR) +setup-switch-to-permissioned-game: + rm -rf $(TEMPLATE_SWITCH_TO_PERMISSIONED_GAME)/cache $(TEMPLATE_SWITCH_TO_PERMISSIONED_GAME)/lib $(TEMPLATE_SWITCH_TO_PERMISSIONED_GAME)/out + cp -r $(TEMPLATE_SWITCH_TO_PERMISSIONED_GAME) $(SWITCH_TO_PERMISSIONED_GAME_DIR) + ## # Solidity Setup ## diff --git a/setup-templates/template-switch-to-permissioned-game/.env b/setup-templates/template-switch-to-permissioned-game/.env new file mode 100644 index 00000000..9cb2e0ad --- /dev/null +++ b/setup-templates/template-switch-to-permissioned-game/.env @@ -0,0 +1,16 @@ +OP_COMMIT= +BASE_CONTRACTS_COMMIT= + +CURRENT_RETIREMENT_TIMESTAMP= + +# Mainnet Config +SYSTEM_CONFIG=0x73a79Fab69143498Ed3712e519A88a918e1f4072 + +OWNER_SAFE=0x09f7150D8c019BeF34450d6920f6B3608ceFdAf2 # Optimism Guardian Multisig (controls Anchor State Registry) +OP_SECURITY_COUNCIL_SAFE=0xc2819DC788505Aac350142A7A707BF9D03E3Bd03 # Owner of Optimism Guardian Multisig + +# # Sepolia Config +# SYSTEM_CONFIG=0xf272670eb55e895584501d564AfEB048bEd26194 + +# OWNER_SAFE=0x7a50f00e8D05b95F98fE38d8BeE366a7324dCf7E # Optimism Guardian Multisig (controls Anchor State Registry) +# OP_SECURITY_COUNCIL_SAFE=0xf64bc17485f0B4Ea5F06A96514182FC4cB561977 # Owner of Optimism Guardian Multisig diff --git a/setup-templates/template-switch-to-permissioned-game/Makefile b/setup-templates/template-switch-to-permissioned-game/Makefile new file mode 100644 index 00000000..12f0b7b0 --- /dev/null +++ b/setup-templates/template-switch-to-permissioned-game/Makefile @@ -0,0 +1,35 @@ +include ../../Makefile +include ../../Multisig.mk + +include ../.env +include .env + +ifndef LEDGER_ACCOUNT +override LEDGER_ACCOUNT = 0 +endif + +RPC_URL = $(L1_RPC_URL) +SCRIPT_NAME = SwitchToPermissionedGame + +# OP +.PHONY: sign-op +sign-op: + $(call MULTISIG_SIGN,$(OP_SECURITY_COUNCIL_SAFE)) + +.PHONY: approve-op +approve-op: + $(call MULTISIG_APPROVE,$(OP_SECURITY_COUNCIL_SAFE),$(SIGNATURES)) + +# Execute +.PHONY: execute +execute: + $(call MULTISIG_EXECUTE,0x) + +# Base -- used for simulating the transcations as op-signers by base engineers +.PHONY: sign-base-sepolia +sign-base-mainnet: + $(call MULTISIG_SIGN,$(OP_SECURITY_COUNCIL_SAFE)) --sender 0x1822b35B09f5ce1C78ecbC06AC0A4e17885b925e + +.PHONY: sign-base-sepolia +sign-base-sepolia: + $(call MULTISIG_SIGN,$(OP_SECURITY_COUNCIL_SAFE)) --sender 0x1084092Ac2f04c866806CF3d4a385Afa4F6A6C97 diff --git a/setup-templates/template-switch-to-permissioned-game/README.md b/setup-templates/template-switch-to-permissioned-game/README.md new file mode 100644 index 00000000..9a6ba8f9 --- /dev/null +++ b/setup-templates/template-switch-to-permissioned-game/README.md @@ -0,0 +1,168 @@ +# Upgrade Fault Proofs + +Status: PENDING + +## Description + +This task contains a single script that will update the `respectedGameType` and `retirementTimestamp` in the AnchorStateRegistry. This can only be done by the +"Optimism Guardian Multisig" which is a single-nested multisig controlled by the OP Security Council. + +## Procedure + +### 1. Update repo: + +```bash +cd contract-deployments +git pull +cd /-switch-to-permissioned-game +make deps +``` + +### 2. Setup Ledger + +Your Ledger needs to be connected and unlocked. The Ethereum +application needs to be opened on Ledger with the message "Application +is ready". + +### 3. Run relevant script(s) + +#### 3.1 Sign the transaction + +**If on mainnet**: + +Op signer: + +```bash +make sign-op +``` + +You will see a "Simulation link" from the output. + +Paste this URL in your browser. A prompt may ask you to choose a +project, any project will do. You can create one if necessary. + +Click "Simulate Transaction". + +We will be performing 3 validations and extract the domain hash and message hash to approve on your Ledger: + +1. Validate integrity of the simulation. +2. Validate correctness of the state diff. +3. Validate and extract domain hash and message hash to approve. + +##### 3.2.1 Validate integrity of the simulation. + +Make sure you are on the "Summary" tab of the tenderly simulation, to +validate integrity of the simulation, we need to check the following: + +1. "Network": Check the network is Sepolia or Mainnet. +2. "Timestamp": Check the simulation is performed on a block with a + recent timestamp (i.e. close to when you run the script). +3. "Sender": Check the address shown is your signer account. If not see the derivation path Note above. + +##### 3.2.2. Validate correctness of the state diff. + +Now click on the "State" tab, and refer to the [State Validations](./VALIDATION.md) instructions for the transaction you are signing. +Once complete return to this document to complete the signing. + +##### 3.2.3. Extract the domain hash and the message hash to approve. + +Now that we have verified the transaction performs the right +operation, we need to extract the domain hash and the message hash to +approve. + +Go back to the "Summary" tab, and find the +`GnosisSafe.checkSignatures` (for OP signers) or `Safe.checkSignatures` (for Coinbase signers) call. +This call's `data` parameter contains both the domain hash and the +message hash that will show up in your Ledger. + +It will be a concatenation of `0x1901`, the domain hash, and the +message hash: `0x1901[domain hash][message hash]`. + +Note down this value. You will need to compare it with the ones +displayed on the Ledger screen at signing. + +Once the validations are done, it's time to actually sign the +transaction. + +> [!WARNING] +> This is the most security critical part of the playbook: make sure the +> domain hash and message hash in the following two places match: +> +> 1. On your Ledger screen. +> 2. In the Tenderly simulation. You should use the same Tenderly +> simulation as the one you used to verify the state diffs, instead +> of opening the new one printed in the console. +> +> There is no need to verify anything printed in the console. There is +> no need to open the new Tenderly simulation link either. + +After verification, sign the transaction. You will see the `Data`, +`Signer` and `Signature` printed in the console. Format should be +something like this: + +```shell +Data: +Signer:
+Signature: +``` + +Double check the signer address is the right one. + +##### 3.2.4 Send the output to Facilitator(s) + +Nothing has occurred onchain - these are offchain signatures which +will be collected by Facilitators for execution. Execution can occur +by anyone once a threshold of signatures are collected, so a +Facilitator will do the final execution for convenience. + +Share the `Data`, `Signer` and `Signature` with the Facilitator, and +congrats, you are done! + +### [For Facilitator ONLY] How to execute + +#### Execute the transaction + +1. IMPORTANT: Ensure op-challenger has been updated before executing. +1. Collect outputs from all participating signers. +1. Concatenate all signatures and export it as the `SIGNATURES` + environment variable, i.e. `export +SIGNATURES="[SIGNATURE1][SIGNATURE2]..."`. +1. Run the `make execute` or `make approve` command as described below to execute the transaction. + +For example, if the quorum is 2 and you get the following outputs: + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE01 +Signature: AAAA +``` + +```shell +Data: 0xDEADBEEF +Signer: 0xC0FFEE02 +Signature: BBBB +``` + +If on testnet, then you should run: + +Coinbase facilitator: + +```bash +SIGNATURES=AAAABBBB make execute +``` + +If on mainnet, then you should run: + +Optimism facilitator: + +```bash +SIGNATURES=AAAABBBB make approve-op +``` + +#### If on mainnet, execute the transaction + +Once the signatures have been submitted approving the transaction for all nested Safes run: + +```bash +make execute +``` diff --git a/setup-templates/template-switch-to-permissioned-game/foundry.toml b/setup-templates/template-switch-to-permissioned-game/foundry.toml new file mode 100644 index 00000000..7a443d45 --- /dev/null +++ b/setup-templates/template-switch-to-permissioned-game/foundry.toml @@ -0,0 +1,20 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +broadcast = 'records' +fs_permissions = [{ access = "read-write", path = "./" }] +optimizer = true +optimizer_runs = 200 +via-ir = false +remappings = [ + '@eth-optimism-bedrock/=lib/optimism/packages/contracts-bedrock/', + '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts', + '@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts', + '@rari-capital/solmate/=lib/solmate/', + '@base-contracts/=lib/base-contracts', + 'solady/=lib/solady/src/', + '@lib-keccak/=lib/lib-keccak/contracts/lib', +] + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config diff --git a/setup-templates/template-switch-to-permissioned-game/script/SwitchToPermissionedGame.sol b/setup-templates/template-switch-to-permissioned-game/script/SwitchToPermissionedGame.sol new file mode 100644 index 00000000..fe3b79b6 --- /dev/null +++ b/setup-templates/template-switch-to-permissioned-game/script/SwitchToPermissionedGame.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; +import {stdJson} from "forge-std/StdJson.sol"; +import {IMulticall3} from "forge-std/interfaces/IMulticall3.sol"; +import {console} from "forge-std/console.sol"; +import {IAnchorStateRegistry} from "@eth-optimism-bedrock/src/dispute/FaultDisputeGame.sol"; +import {SystemConfig} from "@eth-optimism-bedrock/src/L1/SystemConfig.sol"; +import {DisputeGameFactory} from "@eth-optimism-bedrock/src/dispute/DisputeGameFactory.sol"; +import {FaultDisputeGame} from "@eth-optimism-bedrock/src/dispute/PermissionedDisputeGame.sol"; +import {GameTypes, GameType} from "@eth-optimism-bedrock/src/dispute/lib/Types.sol"; +import {MultisigScript} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; + +/// @notice This script updates the FaultDisputeGame and PermissionedDisputeGame implementations in the +/// DisputeGameFactory contract. +contract SwitchToPermissionedGame is MultisigScript { + using stdJson for string; + + // TODO: Confirm expected version + string public constant EXPECTED_VERSION = "1.4.1"; + + address public immutable OWNER_SAFE; + uint64 public immutable CURRENT_RETIREMENT_TIMESTAMP; + + SystemConfig internal _SYSTEM_CONFIG = SystemConfig(vm.envAddress("SYSTEM_CONFIG")); + + IAnchorStateRegistry anchorStateRegistry; + + constructor() { + OWNER_SAFE = vm.envAddress("OWNER_SAFE"); + CURRENT_RETIREMENT_TIMESTAMP = uint64(vm.envUint("CURRENT_RETIREMENT_TIMESTAMP")); + } + + function setUp() public { + DisputeGameFactory dgfProxy = DisputeGameFactory(_SYSTEM_CONFIG.disputeGameFactory()); + FaultDisputeGame currentFdg = FaultDisputeGame(address(dgfProxy.gameImpls(GameTypes.CANNON))); + anchorStateRegistry = currentFdg.anchorStateRegistry(); + + _precheckRetirementTimestamp(); + } + + // Checks that the current state matches the CURRENT_RETIREMENT_TIMESTAMP + function _precheckRetirementTimestamp() internal view { + require(anchorStateRegistry.retirementTimestamp() == CURRENT_RETIREMENT_TIMESTAMP, "00"); + } + + // Confirm the CURRENT_RETIREMENT_TIMESTAMP is updated to the block time. + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + require(anchorStateRegistry.retirementTimestamp() == block.timestamp, "post-110"); + require(GameType.unwrap(anchorStateRegistry.respectedGameType()) == GameType.unwrap(GameTypes.PERMISSIONED_CANNON), "post-111"); + } + + + function _buildCalls() internal view override returns (IMulticall3.Call3Value[] memory) { + IMulticall3.Call3Value[] memory calls = new IMulticall3.Call3Value[](2); + + calls[0] = IMulticall3.Call3Value({ + target: address(anchorStateRegistry), + allowFailure: false, + callData: abi.encodeCall(IAnchorStateRegistry.setRespectedGameType, (GameTypes.PERMISSIONED_CANNON)), + value: 0 + }); + + calls[1] = IMulticall3.Call3Value({ + target: address(anchorStateRegistry), + allowFailure: false, + callData: abi.encodeCall(IAnchorStateRegistry.updateRetirementTimestamp, ()), + value: 0 + }); + + return calls; + } + + function _ownerSafe() internal view override returns (address) { + return OWNER_SAFE; + } +}