Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sep): fp-recovery template for game retirement #494

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions tasks/sep/fp-recovery/006-retire-games/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ETH_RPC_URL="https://ethereum-sepolia.publicnode.com"
SUPERCHAIN_CONFIG_ADDR=0xC2Be75506d5724086DEB7245bd260Cc9753911Be
OWNER_SAFE=0x837DE453AD5F21E89771e3c06239d8236c0EFd5E
SAFE_NONCE=""
L2_CHAIN_NAME=""
75 changes: 75 additions & 0 deletions tasks/sep/fp-recovery/006-retire-games/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Deputy Guardian - Retire Games

Status: CONTINGENCY TASK, SIGN AS NEEDED

## Objective

This batch retires all games by calling the setRespectedGameType function in the OptimismPortal with a value of type(uint32).max. This action requires all in-progress withdrawals to be re-proven against a new game that was created after this update occurs.

The batch will be executed on chain ID `11155111`, and contains `1` transactions.

## Tx #1: Call `setRespectedGameType` in the `OptimismPortalProxy`

Calls `setRespectedGameType` with the reserved value of `type(uint32).max` to trigger the game retirement mechanism.

**Function Signature:** `setRespectedGameType(address,uint32)`

**To:** `0x4220C5deD9dC2C8a8366e684B098094790C72d3c`

**Value:** `0 WEI`

**Raw Input Data:** `0xa1155ed9000000000000000000000000<OptimismPortalProxyAddress------------>00000000000000000000000000000000000000000000000000000000ffffffff`

### Inputs

**\_gameType:** `4294967295` (reserved for retirement mechanism)

**\_portal:** `<user-input>`

## Preparing the Operation

1. Locate the address of the `OptimismPortalProxy` to change the respected game type of.

2. Generate the batch with `just generate-input <OptimismPortalProxyAddress>`.

3. Set the `L2_CHAIN_NAME` configuration to the appropriate chain in the `.env` file.

4. Collect signatures and execute the action according to the instructions in [SINGLE.md](../../../../SINGLE.md).

### State Validations

The two state modifications that are made by this action are:

1. An update to the nonce of the Gnosis safe owner of the `DeputyGuardianModule`.
2. An update to the shared slot between the `respectedGameType` and `respectedGameTypeUpdatedAt` variables.

Slot [`0x000000000000000000000000000000000000000000000000000000000000003b`](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.4.0-rc.4/packages/contracts-bedrock/snapshots/storageLayout/OptimismPortal2.json#L100C3-L113C5) in the `OptimismPortalProxy` has the following packed layout:

| Offset | Description |
| ---------- | ------------------------------------------------------------ |
| `[0, 20)` | Unused; Should be zero'd out. |
| `[20, 28)` | `respectedGameTypeUpdatedAt` timestamp (64 bits, big-endian) |
| `[28, 32)` | `respectedGameType` (32 bits, big-endian) |

Note that the offsets in the above table refer to the slot value's big-endian representation. You can compute the offset values with chisel:
```
➜ uint256 x = 0x000000000000000000000000000000000000000000000000669eeed200000001
➜ uint64 respectedGameTypeUpdatedAt = uint64(x >> 32)
➜ respectedGameTypeUpdatedAt
Type: uint64
├ Hex: 0x
├ Hex (full word): 0x669eeed2
└ Decimal: 1721691858
➜ uint32 respectedGameType = uint32(x & 0xFFFFFFFF)
➜ respectedGameType
Type: uint32
├ Hex: 0x
├ Hex (full word): 0x1
└ Decimal: 1
```

To verify the diff:

1. Check that the only modification to state belongs to the `OptimismPortalProxy` at slot `0x000000000000000000000000000000000000000000000000000000000000003b`
1. Check that the lower 4 bytes are the current unchanged game type when read as a big-endian 32-bit uint.
1. Check that bytes `[20, 28]` equal the timestamp of the transaction's submission when read as a big-endian 64-bit uint.
110 changes: 110 additions & 0 deletions tasks/sep/fp-recovery/006-retire-games/SignFromJson.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {SignFromJson as OriginalSignFromJson} from "script/SignFromJson.s.sol";
import {Simulation} from "@base-contracts/script/universal/Simulation.sol";
import {OptimismPortal2, IDisputeGame} from "@eth-optimism-bedrock/src/L1/OptimismPortal2.sol";
import {Types} from "@eth-optimism-bedrock/scripts/Types.sol";
import {Vm, VmSafe} from "forge-std/Vm.sol";
import {console2 as console} from "forge-std/console2.sol";
import {stdToml} from "forge-std/StdToml.sol";
import {LibString} from "solady/utils/LibString.sol";
import {GnosisSafe} from "safe-contracts/GnosisSafe.sol";
import "@eth-optimism-bedrock/src/dispute/lib/Types.sol";

contract SignFromJson is OriginalSignFromJson {
using LibString for string;

// Chains for this task.
string constant l1ChainName = "sepolia";
string l2ChainName = vm.envString("L2_CHAIN_NAME");

// Safe contract for this task.
GnosisSafe securityCouncilSafe = GnosisSafe(payable(0xf64bc17485f0B4Ea5F06A96514182FC4cB561977));
GnosisSafe foundationSafe = GnosisSafe(payable(0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B));

// Known EOAs to exclude from safety checks.
address l2OutputOracleProposer; // cast call $L2OO "PROPOSER()(address)"
address l2OutputOracleChallenger; // In registry addresses.
address systemConfigOwner; // In registry addresses.
address batchSenderAddress; // In registry genesis-system-configs
address p2pSequencerAddress; // cast call $SystemConfig "unsafeBlockSigner()(address)"
address batchInboxAddress; // In registry yaml.

Types.ContractSet proxies;

/// @notice Sets up the contract
function setUp() public {
proxies = _getContractSet();
}

function checkRespectedGameType() internal view {
OptimismPortal2 portal = OptimismPortal2(payable(proxies.OptimismPortal));
require(portal.respectedGameType().raw() != type(uint32).max);
}

function getCodeExceptions() internal view override returns (address[] memory) {
// Safe owners will appear in storage in the LivenessGuard when added
address[] memory securityCouncilSafeOwners = securityCouncilSafe.getOwners();
address[] memory shouldHaveCodeExceptions = new address[](6 + securityCouncilSafeOwners.length);

shouldHaveCodeExceptions[0] = l2OutputOracleProposer;
shouldHaveCodeExceptions[1] = l2OutputOracleChallenger;
shouldHaveCodeExceptions[2] = systemConfigOwner;
shouldHaveCodeExceptions[3] = batchSenderAddress;
shouldHaveCodeExceptions[4] = p2pSequencerAddress;
shouldHaveCodeExceptions[5] = batchInboxAddress;

for (uint256 i = 0; i < securityCouncilSafeOwners.length; i++) {
shouldHaveCodeExceptions[6 + i] = securityCouncilSafeOwners[i];
}

return shouldHaveCodeExceptions;
}

function getAllowedStorageAccess() internal view override returns (address[] memory allowed) {
allowed = new address[](2);
allowed[0] = proxies.OptimismPortal;
allowed[1] = vm.envAddress("OWNER_SAFE");
}

/// @notice Checks the correctness of the deployment
function _postCheck(Vm.AccountAccess[] memory accesses, Simulation.Payload memory /* simPayload */ )
internal
view
override
{
console.log("Running post-deploy assertions");

checkStateDiff(accesses);
checkRespectedGameType();

console.log("All assertions passed!");
}

/// @notice Reads the contract addresses from lib/superchain-registry/superchain/configs/${l1ChainName}/${l2ChainName}.toml
function _getContractSet() internal returns (Types.ContractSet memory _proxies) {
string memory chainConfig;

// Read chain-specific config toml file
string memory path = string.concat(
"/lib/superchain-registry/superchain/configs/", l1ChainName, "/", l2ChainName, ".toml"
);
try vm.readFile(string.concat(vm.projectRoot(), path)) returns (string memory data) {
chainConfig = data;
} catch {
revert(string.concat("Failed to read ", path));
}

// Read the known EOAs out of the config toml file
l2OutputOracleProposer = stdToml.readAddress(chainConfig, "$.addresses.Proposer");
l2OutputOracleChallenger = stdToml.readAddress(chainConfig, "$.addresses.Challenger");
systemConfigOwner = stdToml.readAddress(chainConfig, "$.addresses.SystemConfigOwner");
batchSenderAddress = stdToml.readAddress(chainConfig, "$.addresses.BatchSubmitter");
p2pSequencerAddress = stdToml.readAddress(chainConfig, "$.addresses.UnsafeBlockSigner");
batchInboxAddress = stdToml.readAddress(chainConfig, "$.batch_inbox_addr");

// Read the chain-specific OptimismPortalProxy address
_proxies.OptimismPortal = stdToml.readAddress(chainConfig, "$.addresses.OptimismPortalProxy");
}
}
38 changes: 38 additions & 0 deletions tasks/sep/fp-recovery/006-retire-games/input-template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"chainId": 11155111,
"metadata": {
"name": "Deputy Guardian - Retire Games",
"description": "This batch retires all games by calling the setRespectedGameType function in the OptimismPortal with a value of type(uint32).max"
},
"transactions": [
{
"metadata": {
"name": "Retire Games",
"description": "Retires all games by calling the setRespectedGameType function in the OptimismPortal with a value of type(uint32).max"
},
"to": "0xfd7E6Ef1f6c9e4cC34F54065Bf8496cE41A4e2e8",
"value": "0x0",
"data": "0xa1155ed9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff",
"contractMethod": {
"type": "function",
"name": "setRespectedGameType",
"inputs": [
{
"name": "_portal",
"type": "address"
},
{
"name": "_gameType",
"type": "uint32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
"contractInputsValues": {
"_portal": "0x0000000000000000000000000000000000000000",
"_gameType": "4294967295"
}
}
]
}
17 changes: 17 additions & 0 deletions tasks/sep/fp-recovery/006-retire-games/justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
set positional-arguments

# default recipe to display help information
default:
@just --list

# Generate the `input.json` with a dispute game address to blacklist
generate-input *args='':
#!/usr/bin/env bash
RESERVED_GAME_TYPE="4294967295"
SET_GAME_TYPE_SIG="setRespectedGameType(address,uint32)"
ENCODED_CALL=$(cast calldata $SET_GAME_TYPE_SIG $1 $RESERVED_GAME_TYPE)

cp ./input-template.json ./input.json
jq "(.transactions[0].data = \"$ENCODED_CALL\") |
(.transactions[0].contractInputsValues._portal = \"$1\")" ./input.json > ./input.tmp.json
mv ./input.tmp.json ./input.json