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

env:
# base-contracts v8.2.1 — 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
# The version the active/evm shared Foundry project and script/common are written against.
# Bump this together with the task .env pins when moving to a newer base-contracts.
BASE_CONTRACTS_COMMIT: a27edbf364180714c0fdfdcfdd7d3e4c2829e574

jobs:
validate-common:
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,11 @@ clean-lib:
forge-deps:
[ -n "$(BASE_CONTRACTS_COMMIT)" ] || (echo "BASE_CONTRACTS_COMMIT must be set in .env" && exit 1)
cd $(PROJECT_DIR) && $(MISE_EXEC) forge install --no-git github.com/foundry-rs/forge-std@0844d7e1fc5e60d77b68e469bff60265f236c398 \
github.com/OpenZeppelin/openzeppelin-contracts@ecd2ca2cd7cac116f7a37d0e474bbb3d7d5e1c4d \
github.com/OpenZeppelin/openzeppelin-contracts-upgradeable@0a2cb9a445c365870ed7a8ab461b12acf3e27d63 \
github.com/Vectorized/solady@502cc1ea718e6fa73b380635ee0868b0740595f0 \
github.com/ethereum-optimism/lib-keccak@$(LIB_KECCAK_COMMIT) \
github.com/base/nitro-validator@0ea0d12366b4fa44f9e07e4755f2ad36561cb674 \
github.com/base/contracts@$(BASE_CONTRACTS_COMMIT)

##
Expand Down
4 changes: 4 additions & 0 deletions active/evm/script/common/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ This keeps validation JSON portable and avoids ambiguity when different files de

| Folder | Script | Purpose | Required task files |
| --- | --- | --- | --- |
| `multiproof-game-type/` | `DeployMultiproofGameType.s.sol` | Deploys fresh TEE, ZK, and aggregate verifiers for a new game type while copying chain-level immutables from the current implementation. | `.env` deployment inputs and `ADDRESSES_JSON` |
| `multiproof-game-type/` | `RegisterMultiproofGameType.s.sol` | Registers the deployed aggregate verifier and initialization bond in the dispute game factory. | `.env` deployment inputs and deployment `ADDRESSES_JSON` |
| `multiproof-game-type/` | `SetTEEProverRegistryGameType.s.sol` | Generates the TEE registry owner's explicit game-type cutover. | `.env` cutover inputs and deployment `ADDRESSES_JSON` |
| `multiproof-game-type/` | `SetRespectedGameType.s.sol` | Generates the AnchorStateRegistry guardian's explicit respected-game-type cutover. | `.env` cutover inputs and deployment `ADDRESSES_JSON` |
| `verifier-update/` | `DeployAggregateVerifier.s.sol` | Deploys a replacement `AggregateVerifier` by copying immutable constructor inputs from the live implementation and replacing verifier hashes. | `tasks/<task-id>/config/<network>/.env`, `ADDRESSES_JSON=tasks/<task-id>/config/<network>/addresses.json` |
| `verifier-update/` | `UpdateVerifierHashes.s.sol` | Multisig script that updates `DisputeGameFactory.gameImpls(gameType)` to a deployed `AggregateVerifier`. | `ADDRESSES_JSON=tasks/<task-id>/config/<network>/addresses.json` containing `aggregateVerifier` |
| `funding/` | `Fund.s.sol` | Sends native token from a Safe to recipients listed in `funding.json`. | `funding.json` |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {Script, console} from "forge-std/Script.sol";

import {IProtocolVersions} from "interfaces/L1/IProtocolVersions.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 {IVerifier} from "interfaces/L1/proofs/IVerifier.sol";
import {ISP1Verifier} from "interfaces/L1/proofs/zk/ISP1Verifier.sol";

import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier.sol";
import {TEEProverRegistry} from "@base-contracts/src/L1/proofs/tee/TEEProverRegistry.sol";
import {TEEVerifier} from "@base-contracts/src/L1/proofs/tee/TEEVerifier.sol";
import {ZKVerifier} from "@base-contracts/src/L1/proofs/zk/ZKVerifier.sol";
import {GameType} from "@base-contracts/src/libraries/bridge/Types.sol";

import {MultiproofGameTypeChecks} from "./MultiproofGameTypeChecks.sol";

interface IDisputeGameFactoryAdmin {
function setImplementation(GameType gameType, address impl, bytes calldata args) external;
function setInitBond(GameType gameType, uint256 initBond) external;
}

/// @notice Deploys fresh TEE, ZK, and aggregate verifiers for a new multiproof game type.
/// Existing chain-level immutable values are copied from the current AggregateVerifier.
contract DeployMultiproofGameType is Script {
address internal immutable disputeGameFactoryProxyEnv;
GameType internal immutable currentGameTypeEnv;
GameType internal immutable newGameTypeEnv;
bytes32 internal immutable teeImageHashEnv;
bytes32 internal immutable zkRangeHashEnv;
bytes32 internal immutable zkAggregateHashEnv;
uint256 internal immutable blockIntervalEnv;
uint256 internal immutable intermediateBlockIntervalEnv;
uint256 internal immutable initBondEnv;
IProtocolVersions internal immutable protocolVersionsEnv;
uint256 internal immutable l2GenesisBlockNumberEnv;
uint64 internal immutable l2GenesisTimestampEnv;
uint64 internal immutable l2BlockTimeEnv;
uint64 internal immutable denimActivationTimestampEnv;

address internal immutable currentAggregateVerifier;
IAnchorStateRegistry internal immutable currentAnchorStateRegistry;
IDelayedWETH internal immutable currentDelayedWeth;
TEEProverRegistry internal immutable currentTeeProverRegistry;
ISP1Verifier internal immutable currentSp1Verifier;
bytes32 internal immutable currentConfigHash;
uint256 internal immutable currentL2ChainId;

address public teeVerifier;
address public zkVerifier;
address public aggregateVerifier;

constructor() {
disputeGameFactoryProxyEnv = vm.envAddress("DISPUTE_GAME_FACTORY_PROXY");
uint256 currentGameType = vm.envUint("CURRENT_GAME_TYPE");
uint256 newGameType = vm.envUint("NEW_GAME_TYPE");
require(currentGameType <= type(uint32).max && newGameType <= type(uint32).max, "game type overflow");
currentGameTypeEnv = GameType.wrap(uint32(currentGameType));
newGameTypeEnv = GameType.wrap(uint32(newGameType));
teeImageHashEnv = vm.envBytes32("TEE_IMAGE_HASH");
zkRangeHashEnv = vm.envBytes32("ZK_RANGE_HASH");
zkAggregateHashEnv = vm.envBytes32("ZK_AGGREGATE_HASH");
blockIntervalEnv = vm.envUint("BLOCK_INTERVAL");
intermediateBlockIntervalEnv = vm.envUint("INTERMEDIATE_BLOCK_INTERVAL");
initBondEnv = vm.envUint("INIT_BOND");
protocolVersionsEnv = IProtocolVersions(vm.envAddress("PROTOCOL_VERSIONS"));
l2GenesisBlockNumberEnv = vm.envUint("L2_GENESIS_BLOCK_NUMBER");
uint256 l2GenesisTimestamp = vm.envUint("L2_GENESIS_TIMESTAMP");
uint256 l2BlockTime = vm.envUint("L2_BLOCK_TIME");
uint256 denimActivationTimestamp = vm.envUint("DENIM_ACTIVATION_TIMESTAMP");
require(
l2GenesisTimestamp <= type(uint64).max && l2BlockTime <= type(uint64).max
&& denimActivationTimestamp <= type(uint64).max,
"l2 time overflow"
);
require(denimActivationTimestamp != 0, "denim activation not set");
l2GenesisTimestampEnv = uint64(l2GenesisTimestamp);
l2BlockTimeEnv = uint64(l2BlockTime);
denimActivationTimestampEnv = uint64(denimActivationTimestamp);

IDisputeGameFactory factory = IDisputeGameFactory(disputeGameFactoryProxyEnv);
currentAggregateVerifier = address(factory.gameImpls(currentGameTypeEnv));
AggregateVerifier current = AggregateVerifier(currentAggregateVerifier);
currentAnchorStateRegistry = current.anchorStateRegistry();
currentDelayedWeth = current.DELAYED_WETH();
currentTeeProverRegistry = TEEVerifier(address(current.TEE_VERIFIER())).TEE_PROVER_REGISTRY();
currentSp1Verifier = ZKVerifier(address(current.ZK_VERIFIER())).SP1_VERIFIER();
currentConfigHash = current.CONFIG_HASH();
currentL2ChainId = current.L2_CHAIN_ID();
}

function setUp() public view {
IDisputeGameFactory factory = IDisputeGameFactory(disputeGameFactoryProxyEnv);
require(currentAggregateVerifier != address(0), "current aggregate verifier not found");
require(
GameType.unwrap(AggregateVerifier(currentAggregateVerifier).gameType())
== GameType.unwrap(currentGameTypeEnv),
"current game type mismatch"
);
require(GameType.unwrap(newGameTypeEnv) != GameType.unwrap(currentGameTypeEnv), "new game type is current");
require(address(factory.gameImpls(newGameTypeEnv)) == address(0), "new game type already registered");
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(address(protocolVersionsEnv) != address(0), "protocol versions not set");
require(l2BlockTimeEnv == 2, "l2 block time must be two seconds");
require(blockIntervalEnv == 6000, "block interval must be 6000");
require(intermediateBlockIntervalEnv == 300, "intermediate block interval must be 300");

uint64[] memory schedule = protocolVersionsEnv.getSchedule();
require(
schedule.length > MultiproofGameTypeChecks.DENIM_UPGRADE_INDEX
&& schedule[MultiproofGameTypeChecks.DENIM_UPGRADE_INDEX] == denimActivationTimestampEnv,
"denim activation mismatch"
);
}

function run() external {
vm.startBroadcast();

teeVerifier = address(new TEEVerifier(currentTeeProverRegistry, currentAnchorStateRegistry));
zkVerifier = address(new ZKVerifier(currentSp1Verifier, currentAnchorStateRegistry));
aggregateVerifier = address(
new AggregateVerifier({
gameType_: newGameTypeEnv,
anchorStateRegistry_: currentAnchorStateRegistry,
delayedWETH: currentDelayedWeth,
teeVerifier: IVerifier(teeVerifier),
zkVerifier: IVerifier(zkVerifier),
teeImageHash: teeImageHashEnv,
zkHashes: AggregateVerifier.ZkHashes({rangeHash: zkRangeHashEnv, aggregateHash: zkAggregateHashEnv}),
configHash: currentConfigHash,
l2ChainId: currentL2ChainId,
blockInterval: blockIntervalEnv,
intermediateBlockInterval: intermediateBlockIntervalEnv,
scheduleConfig: AggregateVerifier.ScheduleConfig({
protocolVersions: protocolVersionsEnv,
genesisBlockNumber: l2GenesisBlockNumberEnv,
genesisTimestamp: l2GenesisTimestampEnv,
blockTime: l2BlockTimeEnv
})
})
);

vm.stopBroadcast();

_postCheck();
_writeAddresses();
}

function _postCheck() internal view {
MultiproofGameTypeChecks.assertDeployment(
AggregateVerifier(aggregateVerifier),
AggregateVerifier(currentAggregateVerifier),
MultiproofGameTypeChecks.Expected({
gameType: newGameTypeEnv,
disputeGameFactory: disputeGameFactoryProxyEnv,
teeVerifier: teeVerifier,
zkVerifier: zkVerifier,
teeImageHash: teeImageHashEnv,
zkRangeHash: zkRangeHashEnv,
zkAggregateHash: zkAggregateHashEnv,
protocolVersions: protocolVersionsEnv,
l2GenesisBlockNumber: l2GenesisBlockNumberEnv,
l2GenesisTimestamp: l2GenesisTimestampEnv,
denimActivationTimestamp: denimActivationTimestampEnv
})
);
}

function _writeAddresses() internal {
console.log("TEEVerifier:", teeVerifier);
console.log("ZKVerifier:", zkVerifier);
console.log("AggregateVerifier:", aggregateVerifier);

string memory root = "root";
vm.serializeAddress(root, "teeVerifier", teeVerifier);
vm.serializeAddress(root, "zkVerifier", zkVerifier);
vm.serializeAddress(root, "aggregateVerifier", aggregateVerifier);
vm.serializeBytes(
root,
"setImplementationCalldata",
abi.encodeCall(IDisputeGameFactoryAdmin.setImplementation, (newGameTypeEnv, aggregateVerifier, bytes("")))
);
vm.serializeBytes(
root,
"setInitBondCalldata",
abi.encodeCall(IDisputeGameFactoryAdmin.setInitBond, (newGameTypeEnv, initBondEnv))
);
vm.serializeBytes(
root, "setTEEGameTypeCalldata", abi.encodeCall(TEEProverRegistry.setGameType, (newGameTypeEnv))
);
string memory json = vm.serializeBytes(
root,
"setRespectedGameTypeCalldata",
abi.encodeCall(IAnchorStateRegistry.setRespectedGameType, (newGameTypeEnv))
);
vm.writeJson(json, vm.envString("ADDRESSES_JSON"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {IProtocolVersions} from "interfaces/L1/IProtocolVersions.sol";

import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier.sol";
import {TEEVerifier} from "@base-contracts/src/L1/proofs/tee/TEEVerifier.sol";
import {ZKVerifier} from "@base-contracts/src/L1/proofs/zk/ZKVerifier.sol";
import {GameType} from "@base-contracts/src/libraries/bridge/Types.sol";

library MultiproofGameTypeChecks {
uint256 internal constant DENIM_UPGRADE_INDEX = 13;
uint256 internal constant BLOCK_INTERVAL = 6000;
uint256 internal constant INTERMEDIATE_BLOCK_INTERVAL = 300;
uint64 internal constant L2_BLOCK_TIME = 2;

struct Expected {
GameType gameType;
address disputeGameFactory;
address teeVerifier;
address zkVerifier;
bytes32 teeImageHash;
bytes32 zkRangeHash;
bytes32 zkAggregateHash;
IProtocolVersions protocolVersions;
uint256 l2GenesisBlockNumber;
uint64 l2GenesisTimestamp;
uint64 denimActivationTimestamp;
}

function assertDeployment(AggregateVerifier aggregate, AggregateVerifier current, Expected memory expected)
internal
view
{
require(GameType.unwrap(aggregate.gameType()) == GameType.unwrap(expected.gameType), "game type mismatch");
require(address(current.DISPUTE_GAME_FACTORY()) == expected.disputeGameFactory, "current factory mismatch");
require(address(aggregate.DISPUTE_GAME_FACTORY()) == expected.disputeGameFactory, "factory mismatch");
require(address(aggregate.anchorStateRegistry()) == address(current.anchorStateRegistry()), "asr mismatch");
require(
GameType.unwrap(current.anchorStateRegistry().respectedGameType()) == GameType.unwrap(current.gameType()),
"current game type not respected"
);
require(address(aggregate.DELAYED_WETH()) == address(current.DELAYED_WETH()), "delayed weth mismatch");
require(address(aggregate.TEE_VERIFIER()) == expected.teeVerifier, "tee verifier mismatch");
require(address(aggregate.ZK_VERIFIER()) == expected.zkVerifier, "zk verifier mismatch");
require(expected.teeVerifier != address(current.TEE_VERIFIER()), "tee verifier not fresh");
require(expected.zkVerifier != address(current.ZK_VERIFIER()), "zk verifier not fresh");
require(aggregate.TEE_IMAGE_HASH() == expected.teeImageHash, "tee image hash mismatch");
require(aggregate.ZK_RANGE_HASH() == expected.zkRangeHash, "zk range hash mismatch");
require(aggregate.ZK_AGGREGATE_HASH() == expected.zkAggregateHash, "zk aggregate hash mismatch");
require(aggregate.CONFIG_HASH() == current.CONFIG_HASH(), "config hash mismatch");
require(aggregate.L2_CHAIN_ID() == current.L2_CHAIN_ID(), "l2 chain id mismatch");
require(aggregate.L2_GENESIS_BLOCK_NUMBER() == expected.l2GenesisBlockNumber, "genesis block mismatch");
require(aggregate.L2_GENESIS_TIMESTAMP() == expected.l2GenesisTimestamp, "genesis timestamp mismatch");
require(aggregate.L2_BLOCK_TIME() == L2_BLOCK_TIME, "l2 block time mismatch");
require(aggregate.BLOCK_INTERVAL() == BLOCK_INTERVAL, "block interval mismatch");
require(
aggregate.INTERMEDIATE_BLOCK_INTERVAL() == INTERMEDIATE_BLOCK_INTERVAL,
"intermediate block interval mismatch"
);
require(
address(aggregate.PROTOCOL_VERSIONS()) == address(expected.protocolVersions), "protocol versions mismatch"
);
require(
aggregate.intermediateOutputRootsCount() == BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL,
"intermediate root count mismatch"
);

TEEVerifier tee = TEEVerifier(expected.teeVerifier);
TEEVerifier currentTee = TEEVerifier(address(current.TEE_VERIFIER()));
require(!tee.nullified(), "tee verifier nullified");
require(
address(tee.TEE_PROVER_REGISTRY()) == address(currentTee.TEE_PROVER_REGISTRY()), "tee registry mismatch"
);
require(address(tee.ANCHOR_STATE_REGISTRY()) == address(current.anchorStateRegistry()), "tee asr mismatch");

ZKVerifier zk = ZKVerifier(expected.zkVerifier);
ZKVerifier currentZk = ZKVerifier(address(current.ZK_VERIFIER()));
require(!zk.nullified(), "zk verifier nullified");
require(address(zk.SP1_VERIFIER()) == address(currentZk.SP1_VERIFIER()), "zk sp1 verifier mismatch");
require(address(zk.ANCHOR_STATE_REGISTRY()) == address(current.anchorStateRegistry()), "zk asr mismatch");

uint64[] memory schedule = expected.protocolVersions.getSchedule();
require(
schedule.length > DENIM_UPGRADE_INDEX && schedule[DENIM_UPGRADE_INDEX] == expected.denimActivationTimestamp,
"denim activation mismatch"
);
}
}
Loading
Loading