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

sepolia-019: Holocene FP upgrade #374

Merged
merged 8 commits into from
Dec 4, 2024
Merged
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
2 changes: 2 additions & 0 deletions NESTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ is ready".

Make sure your ledger is still unlocked and run the following commands depending on which Safe you are signing on behalf of.

Note: during development of the Solidity script, simulations can be run without any Ledger by exporting `SIMULATE_WITHOUT_LEDGER=1` in your shell, or by adding it to the `.env` file.
sebastianst marked this conversation as resolved.
Show resolved Hide resolved

**Note:** Remember that by default the script will assume the derivation path of your address is `m/44'/60'/0'/0/0`.
If you wish to use a different account, append an `X` to the command to set the derivation path of the address that you want to use. For example by adding a `1` to the end, it will derive the address using `m/44'/60'/1'/0/0` instead.

Expand Down
5 changes: 5 additions & 0 deletions tasks/sep/019-fp-holocene-upgrade/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ETH_RPC_URL="https://ethereum-sepolia.publicnode.com"
COUNCIL_SAFE=0xf64bc17485f0B4Ea5F06A96514182FC4cB561977
FOUNDATION_SAFE=0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B
OWNER_SAFE=0x1Eb2fFc903729a0F03966B917003800b145F56E2
SAFE_NONCE=""
130 changes: 130 additions & 0 deletions tasks/sep/019-fp-holocene-upgrade/NestedSignFromJson.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {NestedSignFromJson as OriginalNestedSignFromJson} from "script/NestedSignFromJson.s.sol";
import {Simulation} from "@base-contracts/script/universal/Simulation.sol";
import {Types} from "@eth-optimism-bedrock/scripts/Types.sol";
import {console2 as console} from "forge-std/console2.sol";
import {stdJson} from "forge-std/StdJson.sol";
import {LibString} from "solady/utils/LibString.sol";
import {GnosisSafe} from "safe-contracts/GnosisSafe.sol";
import {Vm, VmSafe} from "forge-std/Vm.sol";
import "@eth-optimism-bedrock/src/dispute/lib/Types.sol";
import {DisputeGameFactory} from "@eth-optimism-bedrock/src/dispute/DisputeGameFactory.sol";
import {FaultDisputeGame} from "@eth-optimism-bedrock/src/dispute/FaultDisputeGame.sol";
import {PermissionedDisputeGame} from "@eth-optimism-bedrock/src/dispute/PermissionedDisputeGame.sol";
import {MIPS} from "@eth-optimism-bedrock/src/cannon/MIPS.sol";
import {ISemver} from "@eth-optimism-bedrock/src/universal/ISemver.sol";

contract NestedSignFromJson is OriginalNestedSignFromJson {
using LibString for string;

DisputeGameFactory constant dgfProxy = DisputeGameFactory(0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1);
sebastianst marked this conversation as resolved.
Show resolved Hide resolved
address constant livenessGuard = 0xc26977310bC89DAee5823C2e2a73195E85382cC7;
sebastianst marked this conversation as resolved.
Show resolved Hide resolved
bytes32 constant absolutePrestate = 0x03925193e3e89f87835bbdf3a813f60b2aa818a36bbe71cd5d8fd7e79f5e8afe;
sebastianst marked this conversation as resolved.
Show resolved Hide resolved
address constant newMips = 0x69470D6970Cd2A006b84B1d4d70179c892cFCE01;
address constant oracle = 0x92240135b46fc1142dA181f550aE8f595B858854;
sebastianst marked this conversation as resolved.
Show resolved Hide resolved
string constant gameVersion = "1.3.1";
uint256 constant chainId = 11155420;

// Safe contract for this task.
GnosisSafe securityCouncilSafe = GnosisSafe(payable(vm.envAddress("COUNCIL_SAFE")));
GnosisSafe fndSafe = GnosisSafe(payable(vm.envAddress("FOUNDATION_SAFE")));
GnosisSafe ownerSafe = GnosisSafe(payable(vm.envAddress("OWNER_SAFE")));

FaultDisputeGame faultDisputeGame;
PermissionedDisputeGame permissionedDisputeGame;

function setUp() public {
string memory inputJson;
string memory path = "/tasks/sep/019-fp-holocene-upgrade/input.json";
try vm.readFile(string.concat(vm.projectRoot(), path)) returns (string memory data) {
inputJson = data;
} catch {
revert(string.concat("Failed to read ", path));
}

permissionedDisputeGame = PermissionedDisputeGame(stdJson.readAddress(inputJson, "$.transactions[0].contractInputsValues._impl"));
faultDisputeGame = FaultDisputeGame(stdJson.readAddress(inputJson, "$.transactions[1].contractInputsValues._impl"));
}

function getCodeExceptions() internal view override returns (address[] memory) {
// Safe owners will appear in storage in the LivenessGuard when added, and they are allowed
// to have code AND to have no code.
address[] memory securityCouncilSafeOwners = securityCouncilSafe.getOwners();

// To make sure we probably handle all signers whether or not they have code, first we count
// the number of signers that have no code.
uint256 numberOfSafeSignersWithNoCode;
for (uint256 i = 0; i < securityCouncilSafeOwners.length; i++) {
if (securityCouncilSafeOwners[i].code.length == 0) {
numberOfSafeSignersWithNoCode++;
}
}

// Then we extract those EOA addresses into a dedicated array.
uint256 trackedSignersWithNoCode;
address[] memory safeSignersWithNoCode = new address[](numberOfSafeSignersWithNoCode);
for (uint256 i = 0; i < securityCouncilSafeOwners.length; i++) {
if (securityCouncilSafeOwners[i].code.length == 0) {
safeSignersWithNoCode[trackedSignersWithNoCode] = securityCouncilSafeOwners[i];
trackedSignersWithNoCode++;
}
}

// Here we add the standard (non Safe signer) exceptions.
address[] memory shouldHaveCodeExceptions = new address[](0 + numberOfSafeSignersWithNoCode);


// And finally, we append the Safe signer exceptions.
for (uint256 i = 0; i < safeSignersWithNoCode.length; i++) {
shouldHaveCodeExceptions[0 + i] = safeSignersWithNoCode[i];
}

return shouldHaveCodeExceptions;
}

function getAllowedStorageAccess() internal view override returns (address[] memory allowed) {
allowed = new address[](5);
allowed[0] = address(dgfProxy);
allowed[1] = address(ownerSafe);
allowed[2] = address(securityCouncilSafe);
allowed[3] = address(fndSafe);
allowed[4] = livenessGuard;
}

function _postCheck(Vm.AccountAccess[] memory accesses, Simulation.Payload memory) internal view override {
console.log("Running post-deploy assertions");
checkStateDiff(accesses);
checkDGFProxyAndGames();
checkMips();
console.log("All assertions passed!");
}

function checkDGFProxyAndGames() internal view {
console.log("check dispute game implementations");
require(address(faultDisputeGame) == address(dgfProxy.gameImpls(GameTypes.CANNON)), "dgf-100");
require(address(permissionedDisputeGame) == address(dgfProxy.gameImpls(GameTypes.PERMISSIONED_CANNON)), "dgf-200");

require(faultDisputeGame.version().eq(gameVersion), "game-100");
require(permissionedDisputeGame.version().eq(gameVersion), "game-200");

require(faultDisputeGame.absolutePrestate().raw() == absolutePrestate, "game-300");
require(permissionedDisputeGame.absolutePrestate().raw() == absolutePrestate, "game-400");

require(address(faultDisputeGame.vm()) == newMips, "game-500");
require(address(permissionedDisputeGame.vm()) == newMips, "game-600");

require(faultDisputeGame.l2ChainId() == chainId, "game-700");
require(permissionedDisputeGame.l2ChainId() == chainId, "game-800");
}

function checkMips() internal view{
console.log("check MIPS");

require(newMips.code.length != 0, "MIPS-100");
vm.assertEq(ISemver(newMips).version(), "1.2.1");
require(address(MIPS(newMips).oracle()) == oracle, "MIPS-200");
}

}
37 changes: 37 additions & 0 deletions tasks/sep/019-fp-holocene-upgrade/OVERVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Holocene Hardfork - Proof Contract Upgrades
Upgrades the `MIPS.sol`, `FaultDisputeGame.sol`, and `PermissionedDisputeGame.sol` contracts for Holocene.

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

## Tx #1: Upgrade `PERMISSIONED_CANNON` game type in `DisputeGameFactory`
Upgrades the `PERMISSIONED_CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash.

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

**To:** `0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1`

**Value:** `0 WEI`

**Raw Input Data:** `0x14f6b1a300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004ed046e66c96600dae1a4ec39267bb0ce476e8cc`

### Inputs
**_gameType:** `1`

**_impl:** `0x4Ed046e66c96600DaE1a4ec39267bB0cE476E8cc`

## Tx #2: Upgrade `CANNON` game type in `DisputeGameFactory`
Upgrades the `CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash.

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

**To:** `0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1`

**Value:** `0 WEI`

**Raw Input Data:** `0x14f6b1a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e0877a8f6692ed470013e651c4357d0c4941e6c`

### Inputs
**_gameType:** `0`

**_impl:** `0x5e0877a8F6692eD470013e651c4357d0C4941e6C`

37 changes: 37 additions & 0 deletions tasks/sep/019-fp-holocene-upgrade/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Holocene Hardfork Upgrade - OP Sepolia

Status: [EXECUTED](https://sepolia.etherscan.io/tx/0x88b0adbcb78aa77d86b0c7d02305a4e8dfcef5d50adb340c2dcd714f3678b2e0)

## Objective

Upgrades the Fault Proof contracts of OP Sepolia for the Holocene hardfork.

The proposal is soon posted to the governance forum. This is just the testnet upgrade.

This upgrades the Fault Proof contracts in the
[op-contracts/v1.8.0-rc.3](https://github.com/ethereum-optimism/optimism/tree/op-contracts/v1.8.0-rc.3) release.

## Pre-deployments

- `MIPS` - `0x69470D6970Cd2A006b84B1d4d70179c892cFCE01`
- `FaultDisputeGame` - `0x5e0877a8F6692eD470013e651c4357d0C4941e6C`
- `PermissionedDisputeGame` - `0x4Ed046e66c96600DaE1a4ec39267bB0cE476E8cc`
mds1 marked this conversation as resolved.
Show resolved Hide resolved

## 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/<path>/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.
24 changes: 24 additions & 0 deletions tasks/sep/019-fp-holocene-upgrade/VALIDATION.md
Original file line number Diff line number Diff line change
@@ -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.

sebastianst marked this conversation as resolved.
Show resolved Hide resolved
## State Changes
sebastianst marked this conversation as resolved.
Show resolved Hide resolved

sebastianst marked this conversation as resolved.
Show resolved Hide resolved
### `0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1` (`DisputeGameFactoryProxy`)

- **Key**: `0xffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b` <br/>
**Before**: `0x000000000000000000000000d9d616e4a03a8e7cc962396c9f8d4e3d306097d3` <br/>
**After**: `0x0000000000000000000000005e0877a8f6692ed470013e651c4357d0c4941e6c` <br/>
**Meaning**: Updates the CANNON game type implementation. Verify that the new implementation is set using `cast call 0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1 "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). Upon executing this call you will see the returned address is `0xD9d616E4a03a8e7cC962396C9f8D4e3d306097D3`, matching the "Before" value of this slot, demonstrating this slot storing the address of the cannon implementation

- **Key**: `0x4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e` <br/>
**Before**: `0x00000000000000000000000098e3f752c7224f8322afa935a4caec3832bb25c9` <br/>
**After**: `0x0000000000000000000000004ed046e66c96600dae1a4ec39267bb0ce476e8cc` <br/>
**Meaning**: Updates the PERMISSIONED_CANNON game type implementation. Verify that the new implementation is set using `cast call 0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1 "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).
67 changes: 67 additions & 0 deletions tasks/sep/019-fp-holocene-upgrade/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"chainId": 11155111,
"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": "0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1",
"value": "0x0",
"data": "0x14f6b1a300000000000000000000000000000000000000000000000000000000000000010000000000000000000000004ed046e66c96600dae1a4ec39267bb0ce476e8cc",
"contractMethod": {
"type": "function",
"name": "setImplementation",
"inputs": [
{
"name": "_gameType",
"type": "uint32"
},
{
"name": "_impl",
"type": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
"contractInputsValues": {
"_gameType": "1",
"_impl": "0x4Ed046e66c96600DaE1a4ec39267bB0cE476E8cc"
}
},
{
"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": "0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1",
"value": "0x0",
"data": "0x14f6b1a300000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e0877a8f6692ed470013e651c4357d0c4941e6c",
"contractMethod": {
"type": "function",
"name": "setImplementation",
"inputs": [
{
"name": "_gameType",
"type": "uint32"
},
{
"name": "_impl",
"type": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
"contractInputsValues": {
"_gameType": "0",
"_impl": "0x5e0877a8F6692eD470013e651c4357d0C4941e6C"
}
}
]
}