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

add task for Holocene System Config upgrade across sepolia/{op,mode,metal,zora} #378

Merged
merged 28 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7fea7f1
add Seb's input from Clabby's tool
geoknee Nov 26, 2024
648c335
move task into subfolder, and populate additional folders for MMZB
geoknee Nov 26, 2024
896e26f
consolidate 5 ops into 1
geoknee Nov 26, 2024
27b7b50
draft solidity validation script
geoknee Nov 26, 2024
e473f7d
get validation script to compile
geoknee Nov 26, 2024
6f682b8
commit env
geoknee Nov 26, 2024
fed0a38
tweaks
geoknee Nov 26, 2024
bbc3bc9
fill out getAllowedStorageAccess
geoknee Nov 26, 2024
0f70519
rename file
geoknee Nov 26, 2024
c9a5f9b
remove base and fixup validation script
geoknee Nov 26, 2024
4e2aa1a
remove unecessary file
geoknee Nov 27, 2024
7e1de44
set status to READY TO SIGN
geoknee Nov 27, 2024
f7080e0
change SystemConfig implementation address
geoknee Nov 27, 2024
5e8733f
update calldata with new sys cfg impl addr
geoknee Nov 27, 2024
04b6e5e
Apply suggestions from code review
geoknee Nov 27, 2024
56557e5
complete sentence with gov proposal wording
geoknee Nov 27, 2024
1c0d836
add state overrides and nonce changes to validation.md
geoknee Nov 28, 2024
7bad130
add approvedHashes sections to validation.md
geoknee Nov 28, 2024
ef832c9
add liveness guard section to validation.md
geoknee Nov 28, 2024
19d9e21
load livenessGuard from storage
geoknee Nov 28, 2024
4413fb5
add checkProxyAdminOwnerSafe fn
geoknee Nov 28, 2024
e95ac21
rename variables for clarity
geoknee Nov 28, 2024
4c735c6
clarify that this is testnet, so no gov proposal etc.
geoknee Nov 28, 2024
4bf34f1
typo
geoknee Nov 28, 2024
0726e80
update section on liveness guard
geoknee Nov 28, 2024
5841212
Apply suggestions from code review
geoknee Nov 28, 2024
9feb929
Add Test CI for the task `020-holocene-system-config-upgrade-multi-ch…
Ethnical Nov 29, 2024
be28707
update liveness guard explanation
geoknee Nov 29, 2024
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
22 changes: 22 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,27 @@ jobs:
just prepare-json
just simulate-council # simulate again to make sure the json is still valid

just_simulate_020-holocene-system-config-upgrade-multi-chain:
docker:
- image: <<pipeline.parameters.ci_builder_image>>
steps:
- checkout
- run:
name: just simulate 020-holocene-system-config-upgrade-multi-chain
command: |
just install
cd tasks/sep/020-holocene-system-config-upgrade-multi-chain
export SIMULATE_WITHOUT_LEDGER=1
just \
--dotenv-path $(pwd)/.env \
--justfile ../../../nested.just \
simulate \
foundation
just \
--dotenv-path $(pwd)/.env \
--justfile ../../../nested.just \
simulate \
council
forge_build:
docker:
- image: <<pipeline.parameters.ci_builder_image>>
Expand Down Expand Up @@ -237,3 +258,4 @@ workflows:
- just_simulate_sc_rehearsal_1
- just_simulate_sc_rehearsal_2
- just_simulate_sc_rehearsal_4
- just_simulate_020-holocene-system-config-upgrade-multi-chain
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ETH_RPC_URL="https://ethereum-sepolia.publicnode.com"
COUNCIL_SAFE=0xf64bc17485f0B4Ea5F06A96514182FC4cB561977
FOUNDATION_SAFE=0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B
OWNER_SAFE=0x1Eb2fFc903729a0F03966B917003800b145F56E2
geoknee marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import {NestedSignFromJson as OriginalNestedSignFromJson} from "script/NestedSignFromJson.s.sol";
import {GnosisSafe} from "safe-contracts/GnosisSafe.sol";
import {Vm, VmSafe} from "forge-std/Vm.sol";
import {Simulation} from "@base-contracts/script/universal/Simulation.sol";
import {console2 as console} from "forge-std/console2.sol";
import {ProxyAdmin} from "@eth-optimism-bedrock/src/universal/ProxyAdmin.sol";
import {stdJson} from "forge-std/StdJson.sol";

/// @title ISemver
/// @notice ISemver is a simple contract for ensuring that contracts are
/// versioned using semantic versioning.
interface ISemver {
/// @notice Getter for the semantic version of the contract. This is not
/// meant to be used onchain but instead meant to be used by offchain
/// tooling.
/// @return Semver contract version as a string.
function version() external view returns (string memory);
}

contract NestedSignFromJson is OriginalNestedSignFromJson {
string[4] l2ChainIds = [
"11155420", // op
"1740", // metal
"919", // mode
"999999999" // zora
];

// The slot used to store the livenessGuard address in GnosisSafe.
// See https://github.com/safe-global/safe-smart-account/blob/186a21a74b327f17fc41217a927dea7064f74604/contracts/base/GuardManager.sol#L30
bytes32 livenessGuardSlot = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;

address newSystemConfigImplAddress = 0x33b83E4C305c908B2Fc181dDa36e230213058d7d;

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

/// @notice Sets up the contract
function setUp() public {}

function checkProxyAdminproxyAdminOwnerSafe(string memory l2ChainId) internal view {
ProxyAdmin proxyAdmin = ProxyAdmin(readAddressFromSuperchainRegistry(l2ChainId, "ProxyAdmin"));

address proxyAdminOwner = proxyAdmin.owner();
require(proxyAdminOwner == address(proxyAdminOwnerSafe), "checkProxyAdminproxyAdminOwnerSafe-260");

address[] memory owners = proxyAdminOwnerSafe.getOwners();
require(owners.length == 2, "checkProxyAdminproxyAdminOwnerSafe-270");
require(proxyAdminOwnerSafe.isOwner(address(fndSafe)), "checkProxyAdminproxyAdminOwnerSafe-300");
require(proxyAdminOwnerSafe.isOwner(address(securityCouncilSafe)), "checkProxyAdminproxyAdminOwnerSafe-400");
}

/// @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);
for (uint256 i = 0; i < l2ChainIds.length; i++) {
Ethnical marked this conversation as resolved.
Show resolved Hide resolved
checkProxyAdminproxyAdminOwnerSafe(l2ChainIds[i]);
ISemver systemConfigProxy = ISemver(readAddressFromSuperchainRegistry(l2ChainIds[i], "SystemConfigProxy"));
ProxyAdmin proxyAdmin = ProxyAdmin(readAddressFromSuperchainRegistry(l2ChainIds[i], "ProxyAdmin"));
require(
proxyAdmin.getProxyImplementation(address(systemConfigProxy)) == newSystemConfigImplAddress,
"SystemConfigProxy implementation not updated"
);
require(
keccak256(abi.encode(systemConfigProxy.version())) == keccak256(abi.encode("2.3.0")),
"Version not updated"
);
}

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

function readAddressFromSuperchainRegistry(string memory chainId, string memory contractName)
internal
view
returns (address)
{
string memory addressesJson;

// Read addresses json
string memory path = "/lib/superchain-registry/superchain/extra/addresses/addresses.json";

try vm.readFile(string.concat(vm.projectRoot(), path)) returns (string memory data) {
addressesJson = data;
} catch {
revert(string.concat("Failed to read ", path));
}

return stdJson.readAddress(addressesJson, string.concat("$.", chainId, ".", contractName));
}

function getAllowedStorageAccess() internal view override returns (address[] memory allowed) {
address livenessGuard = address(uint160(uint256(vm.load(address(securityCouncilSafe), livenessGuardSlot))));

allowed = new address[](9);

for (uint256 i = 0; i < l2ChainIds.length; i++) {
address systemConfigProxy = readAddressFromSuperchainRegistry(l2ChainIds[i], "SystemConfigProxy");
allowed[i] = systemConfigProxy;
}
allowed[5] = address(proxyAdminOwnerSafe);
allowed[6] = address(securityCouncilSafe);
allowed[7] = address(fndSafe);
allowed[8] = livenessGuard;
}

function getCodeExceptions() internal pure override returns (address[] memory) {
address[] memory exceptions = new address[](0);
return exceptions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Holocene Hardfork Upgrade - `SystemConfig`

Status: READY TO SIGN

## Objective

Upgrades the `SystemConfig` for the Holocene hardfork for Sepolia/{OP,Mode,Metal,Zora,Base}.

The proposal was:

- [ ] ~~Posted on the governance forum.~~ (Not applicable, as this is a set of testnet upgrades)
- [ ] ~~Approved by Token House voting.~~ (Not applicable, as this is a set of testnet upgrades)
- [ ] ~~Not vetoed by the Citizens' house.~~ (Not applicable, as this is a set of testnet upgrades)
- [ ] Executed on Sepolia.

This upgrades the [`SystemConfig`](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.8.0-rc.3/packages/contracts-bedrock/src/L1/SystemConfig.sol) 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` - `0x33b83E4C305c908B2Fc181dDa36e230213058d7d`

## 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/sep/020-holocene-system-config-upgrade-multi-chain/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

The SystemConfig L1 contract will get upgraded to version 2.3.0, which is part of the OP Contracts v1.8.0-rc.3 release. The upgrade will happen after the Holocene activation. The upgraded SystemConfig enables chain operators to update the EIP-1559 parameters via a new function setEIP1559Params.

See the `input.json` bundle for more details.
Ethnical marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# 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 Overrides

The following state overrides should be seen:

### `0x1Eb2fFc903729a0F03966B917003800b145F56E2` (The 2/2 `ProxyAdmin` Owner)

Links:
- [Etherscan](https://sepolia.etherscan.io/address/0x1Eb2fFc903729a0F03966B917003800b145F56E2)

Enables the simulation by setting the threshold to 1:

- **Key:** `0x0000000000000000000000000000000000000000000000000000000000000004` <br/>
**Value:** `0x0000000000000000000000000000000000000000000000000000000000000001`

### `0xf64bc17485f0B4Ea5F06A96514182FC4cB561977` (Council Safe) or `0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B` (Foundation Safe)

Links:
- [Etherscan (Council Safe)](https://sepolia.etherscan.io/address/0xf64bc17485f0B4Ea5F06A96514182FC4cB561977). This address is attested to the [Optimism docs](https://docs.optimism.io/chain/security/privileged-roles#l1-proxy-admin), as it's one of the signers of the L1 Proxy Admin owner.
- [Etherscan (Foundation Safe)](https://sepolia.etherscan.io/address/0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B). This address is attested to the [Optimism docs](https://docs.optimism.io/chain/security/privileged-roles#l1-proxy-admin), as it's one of the signers of the L1 Proxy Admin owner.

The Safe you are signing for will have the following overrides which will set the [Multicall](https://sepolia.etherscan.io/address/0xca11bde05977b3631167028862be2a173976ca11#code) contract as the sole owner of the signing safe. This allows simulating both the approve hash and the final tx in a single Tenderly tx.

- **Key:** 0x0000000000000000000000000000000000000000000000000000000000000003 <br/>
**Value:** 0x0000000000000000000000000000000000000000000000000000000000000001 <br/>
**Meaning:** The number of owners is set to 1.

- **Key:** 0x0000000000000000000000000000000000000000000000000000000000000004 <br/>
**Value:** 0x0000000000000000000000000000000000000000000000000000000000000001 <br/>
**Meaning:** The threshold is set to 1.

The following two overrides are modifications to the [`owners` mapping](https://github.com/safe-global/safe-contracts/blob/v1.4.0/contracts/libraries/SafeStorage.sol#L15). For the purpose of calculating the storage, note that this mapping is in slot `2`.
This mapping implements a linked list for iterating through the list of owners. Since we'll only have one owner (Multicall), and the `0x01` address is used as the first and last entry in the linked list, we will see the following overrides:
- `owners[1] -> 0xca11bde05977b3631167028862be2a173976ca11`
- `owners[0xca11bde05977b3631167028862be2a173976ca11] -> 1`

And we do indeed see these entries:

- **Key:** 0x316a0aac0d94f5824f0b66f5bbe94a8c360a17699a1d3a233aafcf7146e9f11c <br/>
**Value:** 0x0000000000000000000000000000000000000000000000000000000000000001 <br/>
**Meaning:** This is `owners[0xca11bde05977b3631167028862be2a173976ca11] -> 1`, so the key can be
derived from `cast index address 0xca11bde05977b3631167028862be2a173976ca11 2`.

- **Key:** 0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0 <br/>
**Value:** 0x000000000000000000000000ca11bde05977b3631167028862be2a173976ca11 <br/>
**Meaning:** This is `owners[1] -> 0xca11bde05977b3631167028862be2a173976ca11`, so the key can be
derived from `cast index address 0x0000000000000000000000000000000000000001 2`.

## State Changes

### `0x034edD2A225f7f429A63E0f1D2084B9E0A93b538` (`SystemConfigProxy`) for OP Sepolia

- **Key**: `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
**Before**: `0x000000000000000000000000ccdd86d581e40fb5a1c77582247bc493b6c8b169`
**After**: `0x00000000000000000000000033b83E4C305c908B2Fc181dDa36e230213058d7d`
**Meaning**: Updates the `SystemConfig` proxy implementation.


### `0x15cd4f6e0CE3B4832B33cB9c6f6Fe6fc246754c2` (`SystemConfigProxy`) for Mode Sepolia

- **Key**: `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
**Before**: `0x000000000000000000000000ba2492e52f45651b60b8b38d4ea5e2390c64ffb1`
**After**: `0x00000000000000000000000033b83E4C305c908B2Fc181dDa36e230213058d7d`
**Meaning**: Updates the `SystemConfig` proxy implementation.


### `0x5D63A8Dc2737cE771aa4a6510D063b6Ba2c4f6F2` (`SystemConfigProxy`) for Metal Sepolia

- **Key**: `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
**Before**: `0x000000000000000000000000ba2492e52f45651b60b8b38d4ea5e2390c64ffb1`
**After**: `0x00000000000000000000000033b83E4C305c908B2Fc181dDa36e230213058d7d`
**Meaning**: Updates the `SystemConfig` proxy implementation.

### `0xB54c7BFC223058773CF9b739cC5bd4095184Fb08` (`SystemConfigProxy`) for Zora Sepolia

- **Key**: `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
**Before**: `0x000000000000000000000000ba2492e52f45651b60b8b38d4ea5e2390c64ffb1`
**After**: `0x00000000000000000000000033b83E4C305c908B2Fc181dDa36e230213058d7d`
**Meaning**: Updates the `SystemConfig` proxy implementation.

### For the Council:

- **Key:** `0x3481a62ac310eecec9b2bcbdfc7f9759c1641b33ec9f302e19c8dc75aa3427bb` <br/>
**Before:** `0x0000000000000000000000000000000000000000000000000000000000000000`<br/>
**After:** `0x0000000000000000000000000000000000000000000000000000000000000001` <br/>
**Meaning:** The GnosisSafe `approvedHashes` mapping is updated to indicate approval of this transaction by the council. The correctness of this slot can be verified as follows:
- Since this is a nested mapping, we need to use `cast index` twice to confirm that this is the correct slot. The inputs needed are:
- The location (`8`) of the `approvedHashes` mapping in the [GnosisSafe storage layout](https://github.com/safe-global/safe-contracts/blob/v1.4.0/contracts/libraries/SafeStorage.sol#L23)
- The address of the Council Safe: `0xf64bc17485f0B4Ea5F06A96514182FC4cB561977`
- The safe hash to approve: `0x7e8055d58462ab08d75766766252966eda91b23097f8d96aca0547fe7aae078a`
- The using `cast index`, we can verify that:
```shell
$ cast index address 0xf64bc17485f0B4Ea5F06A96514182FC4cB561977 8
0x56362ae34e37f50105bd722d564a267a69bbc15ede4cb7136e81afd747b41c4d
```
and
```shell
$ cast index bytes32 0x7e8055d58462ab08d75766766252966eda91b23097f8d96aca0547fe7aae078a 0x56362ae34e37f50105bd722d564a267a69bbc15ede4cb7136e81afd747b41c4d
0x3481a62ac310eecec9b2bcbdfc7f9759c1641b33ec9f302e19c8dc75aa3427bb
```
And so the output of the second command matches the key above.

### For the Foundation:

- **Key:** `0x66833911cd4988ff9068991368a392dfd91753075a1080eee9ac5b6bf6a4815b` <br/>
**Before:** `0x0000000000000000000000000000000000000000000000000000000000000000`<br/>
**After:** `0x0000000000000000000000000000000000000000000000000000000000000001` <br/>
**Meaning:** The GnosisSafe `approvedHashes` mapping is updated to indicate approval of this transaction by the council. The correctness of this slot can be verified as follows:
- Since this is a nested mapping, we need to use `cast index` twice to confirm that this is the correct slot. The inputs needed are:
- The location (`8`) of the `approvedHashes` mapping in the [GnosisSafe storage layout](https://github.com/safe-global/safe-contracts/blob/v1.4.0/contracts/libraries/SafeStorage.sol#L23)
- The address of the Foundation Safe: `0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B`
- The safe hash to approve: `0x7e8055d58462ab08d75766766252966eda91b23097f8d96aca0547fe7aae078a`
- The using `cast index`, we can verify that:
```shell
$ cast index address 0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B 8
0xc18fefc0a6b81265cf06017c3f1f91c040dc3227321d73c608cfbcf1c5253e5c
```
and
```shell
$ cast index bytes32 0x7e8055d58462ab08d75766766252966eda91b23097f8d96aca0547fe7aae078a 0xc18fefc0a6b81265cf06017c3f1f91c040dc3227321d73c608cfbcf1c5253e5c
0x66833911cd4988ff9068991368a392dfd91753075a1080eee9ac5b6bf6a4815b
```
And so the output of the second command matches the key above.

### Liveness Guard
When the Security Council (`0xf64bc17485f0B4Ea5F06A96514182FC4cB561977`) execute a transaction, this is updating the liveness timestamp for each owner that signed the tasks.
This is updating at the moment of the transaction is submitted (`block.timestamp`) into the [`lastLive`](https://github.com/ethereum-optimism/optimism/blob/e84868c27776fd04dc77e95176d55c8f6b1cc9a3/packages/contracts-bedrock/src/safe/LivenessGuard.sol#L41) mapping located at the slot `0`.

### Nonce increments

The only other state change are two nonce increments:

- One on the Council or Foundation safe (`0xDEe57160aAfCF04c34C887B5962D0a69676d3C8B` for Foundation and `0xf64bc17485f0B4Ea5F06A96514182FC4cB561977` for Council). If this is not decoded, it corresponds to key `0x05` on a `GnosisSafeProxy`.
- One on the ProxyAdminOwner (2/2) with address`0x1Eb2fFc903729a0F03966B917003800b145F56E2`.




Loading