diff --git a/.github/workflows/validate-common-scripts.yml b/.github/workflows/validate-common-scripts.yml index 817b67b2..3a34dc2c 100644 --- a/.github/workflows/validate-common-scripts.yml +++ b/.github/workflows/validate-common-scripts.yml @@ -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: diff --git a/Makefile b/Makefile index 5361c82c..3f753041 100644 --- a/Makefile +++ b/Makefile @@ -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) ## diff --git a/active/evm/script/common/README.md b/active/evm/script/common/README.md index d7db9255..17ff0f89 100644 --- a/active/evm/script/common/README.md +++ b/active/evm/script/common/README.md @@ -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//config//.env`, `ADDRESSES_JSON=tasks//config//addresses.json` | | `verifier-update/` | `UpdateVerifierHashes.s.sol` | Multisig script that updates `DisputeGameFactory.gameImpls(gameType)` to a deployed `AggregateVerifier`. | `ADDRESSES_JSON=tasks//config//addresses.json` containing `aggregateVerifier` | | `funding/` | `Fund.s.sol` | Sends native token from a Safe to recipients listed in `funding.json`. | `funding.json` | diff --git a/active/evm/script/common/multiproof-game-type/DeployMultiproofGameType.s.sol b/active/evm/script/common/multiproof-game-type/DeployMultiproofGameType.s.sol new file mode 100644 index 00000000..f39af991 --- /dev/null +++ b/active/evm/script/common/multiproof-game-type/DeployMultiproofGameType.s.sol @@ -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")); + } +} diff --git a/active/evm/script/common/multiproof-game-type/MultiproofGameTypeChecks.sol b/active/evm/script/common/multiproof-game-type/MultiproofGameTypeChecks.sol new file mode 100644 index 00000000..ce493737 --- /dev/null +++ b/active/evm/script/common/multiproof-game-type/MultiproofGameTypeChecks.sol @@ -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" + ); + } +} diff --git a/active/evm/script/common/multiproof-game-type/RegisterMultiproofGameType.s.sol b/active/evm/script/common/multiproof-game-type/RegisterMultiproofGameType.s.sol new file mode 100644 index 00000000..07f68f47 --- /dev/null +++ b/active/evm/script/common/multiproof-game-type/RegisterMultiproofGameType.s.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {IProtocolVersions} from "interfaces/L1/IProtocolVersions.sol"; + +import {MultisigScript, Enum} from "@base-contracts/scripts/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/scripts/universal/Simulation.sol"; +import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier.sol"; +import {GameType} from "@base-contracts/src/libraries/bridge/Types.sol"; + +import {MultiproofGameTypeChecks} from "./MultiproofGameTypeChecks.sol"; + +interface IDisputeGameFactoryAdmin { + function owner() external view returns (address); + function gameImpls(GameType gameType) external view returns (address); + function initBonds(GameType gameType) external view returns (uint256); + function setImplementation(GameType gameType, address impl, bytes calldata args) external; + function setInitBond(GameType gameType, uint256 initBond) external; +} + +/// @notice Registers a deployed AggregateVerifier and its initialization bond under a new game type. +contract RegisterMultiproofGameType is MultisigScript { + address internal immutable ownerSafeEnv; + address internal immutable disputeGameFactoryProxyEnv; + GameType internal immutable newGameTypeEnv; + uint256 internal immutable initBondEnv; + bytes32 internal immutable teeImageHashEnv; + bytes32 internal immutable zkRangeHashEnv; + bytes32 internal immutable zkAggregateHashEnv; + IProtocolVersions internal immutable protocolVersionsEnv; + uint256 internal immutable l2GenesisBlockNumberEnv; + uint64 internal immutable l2GenesisTimestampEnv; + uint64 internal immutable denimActivationTimestampEnv; + + address internal immutable currentAggregateVerifier; + address internal immutable aggregateVerifier; + address internal immutable teeVerifier; + address internal immutable zkVerifier; + + constructor() { + ownerSafeEnv = vm.envAddress("PROXY_ADMIN_OWNER"); + 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"); + newGameTypeEnv = GameType.wrap(uint32(newGameType)); + initBondEnv = vm.envUint("INIT_BOND"); + teeImageHashEnv = vm.envBytes32("TEE_IMAGE_HASH"); + zkRangeHashEnv = vm.envBytes32("ZK_RANGE_HASH"); + zkAggregateHashEnv = vm.envBytes32("ZK_AGGREGATE_HASH"); + protocolVersionsEnv = IProtocolVersions(vm.envAddress("PROTOCOL_VERSIONS")); + l2GenesisBlockNumberEnv = vm.envUint("L2_GENESIS_BLOCK_NUMBER"); + uint256 l2GenesisTimestamp = vm.envUint("L2_GENESIS_TIMESTAMP"); + uint256 denimActivationTimestamp = vm.envUint("DENIM_ACTIVATION_TIMESTAMP"); + require( + l2GenesisTimestamp <= type(uint64).max && denimActivationTimestamp <= type(uint64).max, "l2 time overflow" + ); + require(denimActivationTimestamp != 0, "denim activation not set"); + l2GenesisTimestampEnv = uint64(l2GenesisTimestamp); + denimActivationTimestampEnv = uint64(denimActivationTimestamp); + + currentAggregateVerifier = + IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv).gameImpls(GameType.wrap(uint32(currentGameType))); + + string memory json = vm.readFile(vm.envString("ADDRESSES_JSON")); + aggregateVerifier = vm.parseJsonAddress(json, ".aggregateVerifier"); + teeVerifier = vm.parseJsonAddress(json, ".teeVerifier"); + zkVerifier = vm.parseJsonAddress(json, ".zkVerifier"); + } + + function setUp() public view { + IDisputeGameFactoryAdmin factory = IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv); + require(factory.owner() == ownerSafeEnv, "factory owner mismatch"); + require(factory.gameImpls(newGameTypeEnv) == address(0), "new game type already registered"); + require(currentAggregateVerifier != address(0), "current aggregate verifier not found"); + require(aggregateVerifier != address(0), "aggregate verifier not set"); + require(teeVerifier != address(0), "tee verifier not set"); + require(zkVerifier != address(0), "zk verifier not set"); + 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"); + + 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 _buildCalls() internal view override returns (Call[] memory) { + Call[] memory calls = new Call[](2); + calls[0] = Call({ + operation: Enum.Operation.Call, + target: disputeGameFactoryProxyEnv, + data: abi.encodeCall( + IDisputeGameFactoryAdmin.setImplementation, (newGameTypeEnv, aggregateVerifier, bytes("")) + ), + value: 0 + }); + calls[1] = Call({ + operation: Enum.Operation.Call, + target: disputeGameFactoryProxyEnv, + data: abi.encodeCall(IDisputeGameFactoryAdmin.setInitBond, (newGameTypeEnv, initBondEnv)), + value: 0 + }); + return calls; + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + IDisputeGameFactoryAdmin factory = IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv); + require(factory.gameImpls(newGameTypeEnv) == aggregateVerifier, "factory implementation mismatch"); + require(factory.initBonds(newGameTypeEnv) == initBondEnv, "factory init bond mismatch"); + } + + function _ownerSafe() internal view override returns (address) { + return ownerSafeEnv; + } +} diff --git a/active/evm/script/common/multiproof-game-type/SetRespectedGameType.s.sol b/active/evm/script/common/multiproof-game-type/SetRespectedGameType.s.sol new file mode 100644 index 00000000..52700e95 --- /dev/null +++ b/active/evm/script/common/multiproof-game-type/SetRespectedGameType.s.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {IProtocolVersions} from "interfaces/L1/IProtocolVersions.sol"; +import {IAnchorStateRegistry} from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; + +import {MultisigScript, Enum} from "@base-contracts/scripts/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/scripts/universal/Simulation.sol"; +import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier.sol"; +import {TEEVerifier} from "@base-contracts/src/L1/proofs/tee/TEEVerifier.sol"; +import {GameType} from "@base-contracts/src/libraries/bridge/Types.sol"; + +/// @notice Cuts the AnchorStateRegistry over to a preregistered multiproof game type. +contract SetRespectedGameType is MultisigScript { + uint256 internal constant DENIM_UPGRADE_INDEX = 13; + + IAnchorStateRegistry internal immutable anchorStateRegistry; + address internal immutable guardian; + GameType internal immutable currentGameTypeEnv; + GameType internal immutable newGameTypeEnv; + IProtocolVersions internal immutable protocolVersionsEnv; + uint64 internal immutable denimActivationTimestampEnv; + uint64 internal immutable retirementTimestamp; + + address internal immutable aggregateVerifier; + + constructor() { + anchorStateRegistry = IAnchorStateRegistry(vm.envAddress("ANCHOR_STATE_REGISTRY_PROXY")); + guardian = anchorStateRegistry.systemConfig().guardian(); + retirementTimestamp = anchorStateRegistry.retirementTimestamp(); + 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)); + protocolVersionsEnv = IProtocolVersions(vm.envAddress("PROTOCOL_VERSIONS")); + uint256 denimActivationTimestamp = vm.envUint("DENIM_ACTIVATION_TIMESTAMP"); + require(denimActivationTimestamp <= type(uint64).max, "denim activation overflow"); + require(denimActivationTimestamp != 0, "denim activation not set"); + denimActivationTimestampEnv = uint64(denimActivationTimestamp); + + string memory json = vm.readFile(vm.envString("ADDRESSES_JSON")); + aggregateVerifier = vm.parseJsonAddress(json, ".aggregateVerifier"); + } + + function setUp() public view { + require( + GameType.unwrap(anchorStateRegistry.respectedGameType()) == GameType.unwrap(currentGameTypeEnv), + "current game type not respected" + ); + require(GameType.unwrap(currentGameTypeEnv) != GameType.unwrap(newGameTypeEnv), "game type already respected"); + address implementation = address(anchorStateRegistry.disputeGameFactory().gameImpls(newGameTypeEnv)); + require(implementation == aggregateVerifier, "registered implementation mismatch"); + AggregateVerifier aggregate = AggregateVerifier(aggregateVerifier); + require(GameType.unwrap(aggregate.gameType()) == GameType.unwrap(newGameTypeEnv), "game type mismatch"); + require(address(aggregate.anchorStateRegistry()) == address(anchorStateRegistry), "asr mismatch"); + require( + address(aggregate.DISPUTE_GAME_FACTORY()) == address(anchorStateRegistry.disputeGameFactory()), + "factory mismatch" + ); + require(aggregate.L2_BLOCK_TIME() == 2, "l2 block time mismatch"); + require(aggregate.BLOCK_INTERVAL() == 6000, "block interval mismatch"); + require(aggregate.INTERMEDIATE_BLOCK_INTERVAL() == 300, "intermediate block interval mismatch"); + require(address(aggregate.PROTOCOL_VERSIONS()) == address(protocolVersionsEnv), "protocol versions mismatch"); + + uint64[] memory schedule = protocolVersionsEnv.getSchedule(); + require( + schedule.length > DENIM_UPGRADE_INDEX && schedule[DENIM_UPGRADE_INDEX] == denimActivationTimestampEnv, + "denim activation mismatch" + ); + + TEEVerifier tee = TEEVerifier(address(aggregate.TEE_VERIFIER())); + require(!tee.nullified(), "tee verifier nullified"); + require( + GameType.unwrap(tee.TEE_PROVER_REGISTRY().gameType()) == GameType.unwrap(newGameTypeEnv), + "tee registry not cut over" + ); + } + + function _buildCalls() internal view override returns (Call[] memory) { + Call[] memory calls = new Call[](1); + calls[0] = Call({ + operation: Enum.Operation.Call, + target: address(anchorStateRegistry), + data: abi.encodeCall(IAnchorStateRegistry.setRespectedGameType, (newGameTypeEnv)), + value: 0 + }); + return calls; + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + require( + GameType.unwrap(anchorStateRegistry.respectedGameType()) == GameType.unwrap(newGameTypeEnv), + "respected game type mismatch" + ); + require(anchorStateRegistry.retirementTimestamp() == retirementTimestamp, "retirement timestamp changed"); + } + + function _ownerSafe() internal view override returns (address) { + return guardian; + } +} diff --git a/active/evm/script/common/multiproof-game-type/SetTEEProverRegistryGameType.s.sol b/active/evm/script/common/multiproof-game-type/SetTEEProverRegistryGameType.s.sol new file mode 100644 index 00000000..6a37062d --- /dev/null +++ b/active/evm/script/common/multiproof-game-type/SetTEEProverRegistryGameType.s.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {MultisigScript, Enum} from "@base-contracts/scripts/universal/MultisigScript.sol"; +import {Simulation} from "@base-contracts/scripts/universal/Simulation.sol"; +import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier.sol"; +import {TEEProverRegistry} from "@base-contracts/src/L1/proofs/tee/TEEProverRegistry.sol"; +import {GameType} from "@base-contracts/src/libraries/bridge/Types.sol"; + +/// @notice Cuts the existing TEEProverRegistry over to a preregistered multiproof game type. +contract SetTEEProverRegistryGameType is MultisigScript { + TEEProverRegistry internal immutable teeProverRegistry; + address internal immutable ownerSafe; + GameType internal immutable newGameTypeEnv; + bytes32 internal immutable teeImageHashEnv; + address internal immutable aggregateVerifier; + + constructor() { + teeProverRegistry = TEEProverRegistry(vm.envAddress("TEE_PROVER_REGISTRY_PROXY")); + ownerSafe = teeProverRegistry.owner(); + uint256 newGameType = vm.envUint("NEW_GAME_TYPE"); + require(newGameType <= type(uint32).max, "game type overflow"); + newGameTypeEnv = GameType.wrap(uint32(newGameType)); + teeImageHashEnv = vm.envBytes32("TEE_IMAGE_HASH"); + + string memory json = vm.readFile(vm.envString("ADDRESSES_JSON")); + aggregateVerifier = vm.parseJsonAddress(json, ".aggregateVerifier"); + } + + function setUp() public view { + require( + GameType.unwrap(teeProverRegistry.gameType()) != GameType.unwrap(newGameTypeEnv), "game type already set" + ); + require(teeImageHashEnv != bytes32(0), "tee image hash not set"); + address implementation = address(teeProverRegistry.DISPUTE_GAME_FACTORY().gameImpls(newGameTypeEnv)); + require(implementation == aggregateVerifier, "registered implementation mismatch"); + AggregateVerifier aggregate = AggregateVerifier(aggregateVerifier); + require(aggregate.TEE_IMAGE_HASH() == teeImageHashEnv, "tee image hash mismatch"); + require(!aggregate.TEE_VERIFIER().nullified(), "tee verifier nullified"); + } + + function _buildCalls() internal view override returns (Call[] memory) { + Call[] memory calls = new Call[](1); + calls[0] = Call({ + operation: Enum.Operation.Call, + target: address(teeProverRegistry), + data: abi.encodeCall(TEEProverRegistry.setGameType, (newGameTypeEnv)), + value: 0 + }); + return calls; + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + require(GameType.unwrap(teeProverRegistry.gameType()) == GameType.unwrap(newGameTypeEnv), "game type mismatch"); + require(teeProverRegistry.getExpectedImageHash() == teeImageHashEnv, "tee image hash mismatch"); + } + + function _ownerSafe() internal view override returns (address) { + return ownerSafe; + } +} diff --git a/active/evm/script/common/verifier-update/DeployAggregateVerifier.s.sol b/active/evm/script/common/verifier-update/DeployAggregateVerifier.s.sol index d8958252..26fa38a8 100644 --- a/active/evm/script/common/verifier-update/DeployAggregateVerifier.s.sol +++ b/active/evm/script/common/verifier-update/DeployAggregateVerifier.s.sol @@ -3,6 +3,7 @@ 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"; @@ -33,8 +34,12 @@ contract DeployAggregateVerifier is Script { address internal immutable currentZkVerifier; bytes32 internal immutable currentConfigHash; uint256 internal immutable currentL2ChainId; + uint256 internal immutable currentL2GenesisBlockNumber; + uint64 internal immutable currentL2GenesisTimestamp; + uint64 internal immutable currentL2BlockTime; uint256 internal immutable currentBlockInterval; uint256 internal immutable currentIntermediateBlockInterval; + IProtocolVersions internal immutable currentProtocolVersions; // Deployment output written to addresses.json. address public aggregateVerifier; @@ -56,8 +61,12 @@ contract DeployAggregateVerifier is Script { currentZkVerifier = address(currentAggregate.ZK_VERIFIER()); currentConfigHash = currentAggregate.CONFIG_HASH(); currentL2ChainId = currentAggregate.L2_CHAIN_ID(); + currentL2GenesisBlockNumber = currentAggregate.L2_GENESIS_BLOCK_NUMBER(); + currentL2GenesisTimestamp = currentAggregate.L2_GENESIS_TIMESTAMP(); + currentL2BlockTime = currentAggregate.L2_BLOCK_TIME(); currentBlockInterval = currentAggregate.BLOCK_INTERVAL(); currentIntermediateBlockInterval = currentAggregate.INTERMEDIATE_BLOCK_INTERVAL(); + currentProtocolVersions = currentAggregate.PROTOCOL_VERSIONS(); } function setUp() public view { @@ -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 + }) }) ); @@ -121,11 +136,17 @@ contract DeployAggregateVerifier is Script { require(address(av.ZK_VERIFIER()) == currentZkVerifier, "aggregate zk verifier mismatch"); require(av.CONFIG_HASH() == currentConfigHash, "aggregate config hash mismatch"); require(av.L2_CHAIN_ID() == currentL2ChainId, "aggregate l2 chain id 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"); require(av.BLOCK_INTERVAL() == currentBlockInterval, "aggregate block interval mismatch"); require( av.INTERMEDIATE_BLOCK_INTERVAL() == currentIntermediateBlockInterval, "aggregate intermediate interval mismatch" ); + require( + address(av.PROTOCOL_VERSIONS()) == address(currentProtocolVersions), "aggregate protocol versions mismatch" + ); } function _writeAddresses() internal { diff --git a/active/evm/script/common/verifier-update/UpdateVerifierHashes.s.sol b/active/evm/script/common/verifier-update/UpdateVerifierHashes.s.sol index be68f9e4..a50402c5 100644 --- a/active/evm/script/common/verifier-update/UpdateVerifierHashes.s.sol +++ b/active/evm/script/common/verifier-update/UpdateVerifierHashes.s.sol @@ -127,6 +127,17 @@ contract UpdateVerifierHashes is MultisigScript { ); require(nextAggregate.CONFIG_HASH() == currentAggregate.CONFIG_HASH(), "next aggregate config hash mismatch"); require(nextAggregate.L2_CHAIN_ID() == currentAggregate.L2_CHAIN_ID(), "next aggregate l2 chain id 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" + ); require( nextAggregate.BLOCK_INTERVAL() == currentAggregate.BLOCK_INTERVAL(), "next aggregate block interval mismatch" @@ -135,6 +146,10 @@ 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 protocol versions mismatch" + ); } function _ownerSafe() internal view override returns (address) {