Skip to content
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
4 changes: 2 additions & 2 deletions .github/workflows/validate-common-scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ concurrency:
cancel-in-progress: true

env:
# base-contracts v8.2.1 — the version the active/evm shared Foundry project
# base/contracts releases/v8.3.0 — the version the active/evm shared Foundry project
# (active/evm/foundry.toml) and script/common/ are written against. Bump this
# together with the task .env pins when moving to a newer base-contracts.
BASE_CONTRACTS_COMMIT: f3a33c8577c8ca1e037b45e822bfcb75f099270b
BASE_CONTRACTS_COMMIT: 385f21a41f277d287db92fffe88dca41299162ea

jobs:
validate-common:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Script, console} from "forge-std/Script.sol";
import {IAnchorStateRegistry} from "interfaces/L1/proofs/IAnchorStateRegistry.sol";
import {IDelayedWETH} from "interfaces/L1/proofs/IDelayedWETH.sol";
import {IDisputeGameFactory} from "interfaces/L1/proofs/IDisputeGameFactory.sol";
import {IProtocolVersions} from "interfaces/L1/IProtocolVersions.sol";
import {IVerifier} from "interfaces/L1/proofs/IVerifier.sol";

import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier.sol";
Expand Down Expand Up @@ -35,6 +36,10 @@ contract DeployAggregateVerifier is Script {
uint256 internal immutable currentL2ChainId;
uint256 internal immutable currentBlockInterval;
uint256 internal immutable currentIntermediateBlockInterval;
IProtocolVersions internal immutable currentProtocolVersions;
uint256 internal immutable currentL2GenesisBlockNumber;
uint64 internal immutable currentL2GenesisTimestamp;
uint64 internal immutable currentL2BlockTime;

// Deployment output written to addresses.json.
address public aggregateVerifier;
Expand All @@ -58,6 +63,10 @@ contract DeployAggregateVerifier is Script {
currentL2ChainId = currentAggregate.L2_CHAIN_ID();
currentBlockInterval = currentAggregate.BLOCK_INTERVAL();
currentIntermediateBlockInterval = currentAggregate.INTERMEDIATE_BLOCK_INTERVAL();
currentProtocolVersions = currentAggregate.PROTOCOL_VERSIONS();
currentL2GenesisBlockNumber = currentAggregate.L2_GENESIS_BLOCK_NUMBER();
currentL2GenesisTimestamp = currentAggregate.L2_GENESIS_TIMESTAMP();
currentL2BlockTime = currentAggregate.L2_BLOCK_TIME();
}

function setUp() public view {
Expand Down Expand Up @@ -92,7 +101,13 @@ contract DeployAggregateVerifier is Script {
configHash: currentConfigHash,
l2ChainId: currentL2ChainId,
blockInterval: currentBlockInterval,
intermediateBlockInterval: currentIntermediateBlockInterval
intermediateBlockInterval: currentIntermediateBlockInterval,
scheduleConfig: AggregateVerifier.ScheduleConfig({
protocolVersions: currentProtocolVersions,
genesisBlockNumber: currentL2GenesisBlockNumber,
genesisTimestamp: currentL2GenesisTimestamp,
blockTime: currentL2BlockTime
})
})
);

Expand Down Expand Up @@ -126,6 +141,10 @@ contract DeployAggregateVerifier is Script {
av.INTERMEDIATE_BLOCK_INTERVAL() == currentIntermediateBlockInterval,
"aggregate intermediate interval mismatch"
);
require(address(av.PROTOCOL_VERSIONS()) == address(currentProtocolVersions), "aggregate registry mismatch");
require(av.L2_GENESIS_BLOCK_NUMBER() == currentL2GenesisBlockNumber, "aggregate genesis block mismatch");
require(av.L2_GENESIS_TIMESTAMP() == currentL2GenesisTimestamp, "aggregate genesis timestamp mismatch");
require(av.L2_BLOCK_TIME() == currentL2BlockTime, "aggregate l2 block time mismatch");
}

function _writeAddresses() internal {
Expand All @@ -136,5 +155,30 @@ contract DeployAggregateVerifier is Script {
vm.serializeAddress({objectKey: root, valueKey: "aggregateVerifier", value: aggregateVerifier});
string memory path = vm.envString("ADDRESSES_JSON");
vm.writeJson({json: json, path: path});
vm.writeJson(
vm.toString(
abi.encode(
currentGameType,
currentAnchorStateRegistry,
currentDelayedWeth,
IVerifier(currentTeeVerifier),
IVerifier(currentZkVerifier),
teeImageHashEnv,
AggregateVerifier.ZkHashes({rangeHash: zkRangeHashEnv, aggregateHash: zkAggregateHashEnv}),
currentConfigHash,
currentL2ChainId,
currentBlockInterval,
currentIntermediateBlockInterval,
AggregateVerifier.ScheduleConfig({
protocolVersions: currentProtocolVersions,
genesisBlockNumber: currentL2GenesisBlockNumber,
genesisTimestamp: currentL2GenesisTimestamp,
blockTime: currentL2BlockTime
})
)
),
path,
".aggregateVerifierConstructorArgs"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier
import {GameType} from "@base-contracts/src/libraries/bridge/Types.sol";

interface IDisputeGameFactoryAdmin {
function gameArgs(GameType gameType) external view returns (bytes memory);
function gameCount() external view returns (uint256);
function owner() external view returns (address);
function gameImpls(GameType gameType) external view returns (address);
function setImplementation(GameType gameType, address impl, bytes calldata args) external;
function setImplementation(GameType gameType, address impl) external;
}

/// @notice Updates the live multiproof implementation in the DisputeGameFactory to
Expand All @@ -32,6 +34,7 @@ contract UpdateVerifierHashes is MultisigScript {
// Deployment output produced by the EOA script and read from addresses.json.
address internal immutable nextAggregateVerifier;
GameType internal immutable nextGameType;
uint256 internal immutable gameCountBefore;

constructor() {
ownerSafeEnv = vm.envAddress("PROXY_ADMIN_OWNER");
Expand All @@ -48,6 +51,7 @@ contract UpdateVerifierHashes is MultisigScript {
nextAggregateVerifier = vm.parseJsonAddress({json: json, key: ".aggregateVerifier"});

nextGameType = AggregateVerifier(nextAggregateVerifier).gameType();
gameCountBefore = IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv).gameCount();
}

function setUp() public view {
Expand All @@ -58,6 +62,14 @@ contract UpdateVerifierHashes is MultisigScript {
require(teeImageHashEnv != bytes32(0), "tee image hash not set");
require(zkRangeHashEnv != bytes32(0), "zk range hash not set");
require(zkAggregateHashEnv != bytes32(0), "zk aggregate hash not set");
require(
keccak256(bytes(AggregateVerifier(nextAggregateVerifier).version())) == keccak256(bytes("0.2.0")),
"next aggregate verifier version mismatch"
);
require(
IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv).gameArgs(gameTypeEnv).length == 0,
"aggregate game args not empty"
);

AggregateVerifier currentAggregate = AggregateVerifier(currentAggregateVerifier);
AggregateVerifier nextAggregate = AggregateVerifier(nextAggregateVerifier);
Expand All @@ -68,6 +80,11 @@ contract UpdateVerifierHashes is MultisigScript {
require(GameType.unwrap(nextGameType) == GameType.unwrap(gameTypeEnv), "next game type mismatch");

_assertUpdatedHashes(nextAggregate);
require(
teeImageHashEnv != currentAggregate.TEE_IMAGE_HASH() || zkRangeHashEnv != currentAggregate.ZK_RANGE_HASH()
|| zkAggregateHashEnv != currentAggregate.ZK_AGGREGATE_HASH(),
"all hashes are identical to the current aggregate verifier"
);
_assertImmutableContinuity(currentAggregate, nextAggregate);
}

Expand All @@ -77,7 +94,7 @@ contract UpdateVerifierHashes is MultisigScript {
calls[0] = Call({
operation: Enum.Operation.Call,
target: disputeGameFactoryProxyEnv,
data: abi.encodeCall(IDisputeGameFactoryAdmin.setImplementation, (nextGameType, nextAggregateVerifier, "")),
data: abi.encodeCall(IDisputeGameFactoryAdmin.setImplementation, (nextGameType, nextAggregateVerifier)),
value: 0
});

Expand All @@ -90,6 +107,8 @@ contract UpdateVerifierHashes is MultisigScript {
AggregateVerifier nextAggregate = AggregateVerifier(nextAggregateVerifier);

require(dgf.gameImpls(nextGameType) == nextAggregateVerifier, "dgf aggregate verifier mismatch");
require(dgf.gameCount() == gameCountBefore, "game count changed");
require(dgf.gameArgs(nextGameType).length == 0, "aggregate game args changed");

_assertUpdatedHashes(nextAggregate);
_assertImmutableContinuity(currentAggregate, nextAggregate);
Expand Down Expand Up @@ -135,6 +154,21 @@ contract UpdateVerifierHashes is MultisigScript {
nextAggregate.INTERMEDIATE_BLOCK_INTERVAL() == currentAggregate.INTERMEDIATE_BLOCK_INTERVAL(),
"next aggregate intermediate interval mismatch"
);
require(
address(nextAggregate.PROTOCOL_VERSIONS()) == address(currentAggregate.PROTOCOL_VERSIONS()),
"next aggregate registry mismatch"
);
require(
nextAggregate.L2_GENESIS_BLOCK_NUMBER() == currentAggregate.L2_GENESIS_BLOCK_NUMBER(),
"next aggregate genesis block mismatch"
);
require(
nextAggregate.L2_GENESIS_TIMESTAMP() == currentAggregate.L2_GENESIS_TIMESTAMP(),
"next aggregate genesis timestamp mismatch"
);
require(
nextAggregate.L2_BLOCK_TIME() == currentAggregate.L2_BLOCK_TIME(), "next aggregate l2 block time mismatch"
);
}

function _ownerSafe() internal view override returns (address) {
Expand Down
106 changes: 106 additions & 0 deletions archive/evm/2026-09-16-update-cobalt-verifier-hashes/FACILITATOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Update Cobalt Verifier Hashes — Facilitator Guide

This task redeploys Zeronet's game type 621 `AggregateVerifier` with updated proof program hashes
and registers it in the `DisputeGameFactory`. The three hashes are constructor immutables, so they
cannot be changed on the existing verifier.

Replace `<network>` with the rollout network, for example `zeronet`. Every command requires
`TASK_NETWORK` explicitly. Run everything from this directory
(`active/evm/tasks/2026-09-16-update-cobalt-verifier-hashes/`).

## 1. Install dependencies

```bash
make TASK_NETWORK=<network> deps
```

This installs the `base/contracts` commit and dependencies used by the Cobalt deployment. The new
`AggregateVerifier` is compiled with the same 999999 optimizer runs and bytecode settings as the
verifier deployed by the original Cobalt task.

## 2. Review the network config

Open `config/<network>/.env` and confirm:

- `GAME_TYPE` is 621 and resolves to the expected current `AggregateVerifier`.
- `TEE_IMAGE_HASH` is the PCR0 of the intended Nitro enclave image.
- `ZK_RANGE_HASH` is the intended SP1 range verification key.
- `ZK_AGGREGATE_HASH` is the intended SP1 aggregation verification key.

The configured Zeronet changes are:

| Hash | Current verifier | New verifier |
| --- | --- | --- |
| TEE image | `0xe8dc2300cf325b2527cbfa5e664a70acc40a9f2fc0617bd35cdfa61652a5fc30` | `0xb3d3746cf4b830046e9196dc196244d69fd96d3d436bd45ab84e35a9236f351f` |
| ZK range | `0x7713943c2c2412314b135c5606cdb3923a54f7555d91d9f31c2f64e34e16f2f4` | `0x776848834e7efcb029c48cf7215175b77098e8db28f27d2b074a64106f1c2b16` |
| ZK aggregation | `0x002663a1072dc1e1938e13d3b269fff88e843eb4ef76d6c6953dde4f5152973b` | `0x002663a1072dc1e1938e13d3b269fff88e843eb4ef76d6c6953dde4f5152973b` |

The aggregation key intentionally stays unchanged. The deploy script refuses to run if any new
hash is zero or all three hashes match the current verifier. Confirm and record the exact
`base/base` release or commit used to produce the TEE measurement and ZK keys before deployment.

## 3. Deploy and verify

```bash
make TASK_NETWORK=<network> deploy
VERIFIER_API_KEY=<key> make TASK_NETWORK=<network> verify
```

The shared deploy script reads every unchanged constructor value from the currently registered verifier:

- game type, anchor state registry, delayed WETH, TEE verifier, and ZK verifier;
- config hash, L2 chain ID, block intervals;
- `ProtocolVersions` registry; and
- L2 genesis block, genesis timestamp, and block time.

Only the three configured proof program hashes are supplied independently. The script checks every
copied immutable after deployment and writes the new verifier address and encoded constructor
arguments to `config/<network>/addresses.json`.

Commit `config/<network>/addresses.json` and the task-scoped `records/` broadcast artifacts.

## 4. Generate validation files

```bash
make TASK_NETWORK=<network> gen-validation-cb
make TASK_NETWORK=<network> gen-validation-sc
```

These write `config/<network>/validations/base-signer.json` and `security-council-signer.json`.
For Zeronet, remove the generated `taskOriginConfig` and add
`"skipTaskOriginValidation": true` at the JSON root before committing the files.

## 5. Collect signatures and execute

The `DisputeGameFactory` owner is the same 2-of-2 Safe used by the Cobalt upgrade, so the Coinbase
multisig and Security Council each approve their nested Safe before the outer transaction runs.

```bash
SIGNATURES=<concatenated base signatures> make TASK_NETWORK=<network> approve-cb
SIGNATURES=<concatenated security council signatures> make TASK_NETWORK=<network> approve-sc
make TASK_NETWORK=<network> execute
```

`execute` re-runs the pre- and postconditions and reverts instead of registering an unexpected
verifier.

## What the transaction does

One call from the `DisputeGameFactory` owner Safe:

```text
DisputeGameFactory.setImplementation(621, newAggregateVerifier)
```

This changes the implementation used when creating new game type 621 games. Existing games remain
bound to the implementation with which they were created. The task also asserts that the game
count and game constructor arguments do not change.

## Worth re-checking before signing

- **The version string remains `0.2.0`.** Confirm the new verifier by its address and the three hash
getters rather than by `version()` alone.
- **Only the proof program hashes should change.** All other constructor immutables are copied from
the live verifier and checked after deployment and registration.
- **The old verifier remains deployed.** The factory mapping changes for new games; this task does
not alter or migrate existing games.
Loading
Loading