diff --git a/.github/workflows/validate-templates.yml b/.github/workflows/validate-templates.yml index 4b74f85a..9cd1b25d 100644 --- a/.github/workflows/validate-templates.yml +++ b/.github/workflows/validate-templates.yml @@ -40,6 +40,10 @@ jobs: setup_target: "setup-upgrade-fault-proofs" setup_args: "network=sepolia" task_dir_suffix: upgrade-fault-proofs + - template: template-upgrade-zk-and-tee-hash + setup_target: "setup-upgrade-zk-and-tee-hash" + setup_args: "network=sepolia" + task_dir_suffix: upgrade-zk-and-tee-hash - template: template-pause-bridge-base setup_target: "setup-bridge-pause" setup_args: "network=sepolia" diff --git a/Makefile b/Makefile index 40437f2c..0538450d 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ PROJECT_DIR = $(network)/$(shell date +'%Y-%m-%d')-$(task) GAS_INCREASE_DIR = $(network)/$(shell date +'%Y-%m-%d')-increase-gas-limit GAS_AND_ELASTICITY_INCREASE_DIR = $(network)/$(shell date +'%Y-%m-%d')-increase-gas-and-elasticity-limit FAULT_PROOF_UPGRADE_DIR = $(network)/$(shell date +'%Y-%m-%d')-upgrade-fault-proofs +ZK_AND_TEE_HASH_UPGRADE_DIR = $(network)/$(shell date +'%Y-%m-%d')-upgrade-zk-and-tee-hash SAFE_MANAGEMENT_DIR = $(network)/$(shell date +'%Y-%m-%d')-safe-management FUNDING_DIR = $(network)/$(shell date +'%Y-%m-%d')-funding SET_BASE_BRIDGE_PARTNER_THRESHOLD_DIR = $(network)/$(shell date +'%Y-%m-%d')-pause-bridge-base @@ -19,6 +20,7 @@ TEMPLATE_GENERIC = setup-templates/template-generic TEMPLATE_GAS_INCREASE = setup-templates/template-gas-increase TEMPLATE_GAS_AND_ELASTICITY_INCREASE = setup-templates/template-gas-and-elasticity-increase TEMPLATE_UPGRADE_FAULT_PROOFS = setup-templates/template-upgrade-fault-proofs +TEMPLATE_UPGRADE_ZK_AND_TEE_HASH = setup-templates/template-upgrade-zk-and-tee-hash TEMPLATE_SAFE_MANAGEMENT = setup-templates/template-safe-management TEMPLATE_FUNDING = setup-templates/template-funding TEMPLATE_SET_BASE_BRIDGE_PARTNER_THRESHOLD = setup-templates/template-set-bridge-partner-threshold @@ -99,6 +101,12 @@ setup-upgrade-fault-proofs: cp -r $(TEMPLATE_UPGRADE_FAULT_PROOFS) $(FAULT_PROOF_UPGRADE_DIR) mkdir -p $(network)/signatures/$(notdir $(FAULT_PROOF_UPGRADE_DIR)) +# Run `make setup-upgrade-zk-and-tee-hash network=` +setup-upgrade-zk-and-tee-hash: + rm -rf $(TEMPLATE_UPGRADE_ZK_AND_TEE_HASH)/cache $(TEMPLATE_UPGRADE_ZK_AND_TEE_HASH)/lib $(TEMPLATE_UPGRADE_ZK_AND_TEE_HASH)/out + cp -r $(TEMPLATE_UPGRADE_ZK_AND_TEE_HASH) $(ZK_AND_TEE_HASH_UPGRADE_DIR) + mkdir -p $(network)/signatures/$(notdir $(ZK_AND_TEE_HASH_UPGRADE_DIR)) + # Run `make setup-safe-management network=` setup-safe-management: rm -rf $(TEMPLATE_SAFE_MANAGEMENT)/cache $(TEMPLATE_SAFE_MANAGEMENT)/lib $(TEMPLATE_SAFE_MANAGEMENT)/out diff --git a/README.md b/README.md index 96d5de0c..5f3fbc2a 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ To execute a new task, run one of the following commands (depending on the type - For gas increase tasks: `make setup-gas-increase network=` - For combined gas, elasticity, and DA footprint gas scalar tasks: `make setup-gas-and-elasticity-increase network=` - For fault proof upgrade: `make setup-upgrade-fault-proofs network=` +- For upgrading multiproof ZK and TEE hashes: `make setup-upgrade-zk-and-tee-hash network=` - For safe management tasks: `make setup-safe-management network=` - For funding tasks: `make setup-funding network=` - For updating the partner threshold in Base Bridge: `make setup-bridge-partner-threshold network=` @@ -117,7 +118,7 @@ Each task will have a directory structure similar to the following: ## CI — Template Validation -A GitHub Actions workflow automatically validates all 10 setup-templates on every pull request and on pushes to `main`. +A GitHub Actions workflow automatically validates all 11 setup-templates on every pull request and on pushes to `main`. **What CI checks for each template:** @@ -269,6 +270,20 @@ This template is used to upgrade the fault proof contracts. This is commonly don 1. Check in the task when it's ready to sign and collect signatures from signers 1. Once executed, check in the records files and mark the task `EXECUTED` in the README. +## Using the ZK and TEE hash upgrade template + +This template is used to redeploy a multiproof `AggregateVerifier` with new `TEE_IMAGE_HASH`, `ZK_RANGE_HASH`, and `ZK_AGGREGATE_HASH` values, then update `DisputeGameFactory.gameImpls(gameType)` to point at the new verifier. + +1. Ensure you have followed the instructions above in `setup`. +1. Run `make setup-upgrade-zk-and-tee-hash network=` and go to the folder that was created by this command. +1. Specify the commit of [Base contracts code](https://github.com/base/contracts) you intend to use in the `.env` file. +1. Set `GAME_TYPE`, `TEE_IMAGE_HASH`, `ZK_RANGE_HASH`, `ZK_AGGREGATE_HASH`, `PROXY_ADMIN_OWNER`, and `DISPUTE_GAME_FACTORY_PROXY` in the task's `.env` file. +1. Run `make deps` and `forge build`. +1. Deploy the replacement verifier with `make deploy-aggregate-verifier VERIFIER_API_KEY=...`; this writes `addresses.json`. +1. Generate signer validations with `make gen-validation-update-verifier-hashes-cb` and `make gen-validation-update-verifier-hashes-sc`. +1. Check in the task when it's ready to sign and request facilitators to collect signatures from signers. +1. Once executed, check in the records files and mark the task `EXECUTED` in the README. + ## Using the safe management template This template is used to perform ownership management on a Gnosis Safe, like the incident multisig, specifically it can be used to change the owners of the multisig. diff --git a/setup-templates/template-upgrade-zk-and-tee-hash/.env b/setup-templates/template-upgrade-zk-and-tee-hash/.env new file mode 100644 index 00000000..fd6e68e0 --- /dev/null +++ b/setup-templates/template-upgrade-zk-and-tee-hash/.env @@ -0,0 +1,22 @@ +# Required: Git commit hash for https://github.com/base/contracts +BASE_CONTRACTS_COMMIT=e225648a7ed538e7e28c041d44f3b7a606ba7743 + +# Network-specific addresses are automatically loaded from {network}/.env via include ../.env. + +# Multiproof configuration. +# The scripts use DISPUTE_GAME_FACTORY_PROXY + GAME_TYPE as the source of truth +# to recover the live AggregateVerifier and its current immutables. +GAME_TYPE=621 + +# Verifier deployment inputs. +TEE_IMAGE_HASH= +ZK_RANGE_HASH= +ZK_AGGREGATE_HASH= + +# Re-exported for task simulation UI. The signer-tool invokes forge directly and +# does not pick up variables coming from the Makefile's include ../.env. +PROXY_ADMIN_OWNER= +DISPUTE_GAME_FACTORY_PROXY= + +# Validation generation +RECORD_STATE_DIFF=true diff --git a/setup-templates/template-upgrade-zk-and-tee-hash/FACILITATOR.md b/setup-templates/template-upgrade-zk-and-tee-hash/FACILITATOR.md new file mode 100644 index 00000000..08758997 --- /dev/null +++ b/setup-templates/template-upgrade-zk-and-tee-hash/FACILITATOR.md @@ -0,0 +1,116 @@ +# Facilitator Guide + +Guide for facilitators managing this task. + +## Deployment prerequisites + +Before collecting signatures, complete the EOA-authorized phase: + +```bash +cd contract-deployments +git pull +cd /-upgrade-zk-and-tee-hash +make deps +make deploy-aggregate-verifier VERIFIER_API_KEY=... +``` + +`make deploy-aggregate-verifier` runs `DeployAggregateVerifier`: + +- redeploys `AggregateVerifier` with the same immutables as the existing one, overriding `TEE_IMAGE_HASH`, `ZK_RANGE_HASH`, and `ZK_AGGREGATE_HASH` +- reuses the existing `ZkVerifier` from the current onchain AggregateVerifier +- writes `aggregateVerifier` to `addresses.json` + +Expected `addresses.json` keys: + +- `aggregateVerifier` + +## Task origin signing + +After setting up the task, generate cryptographic attestations to prove who created and facilitated the task. These signatures are stored in `/signatures//`. + +### Task creator + +```bash +make sign-as-task-creator +``` + +### Base facilitator + +```bash +make sign-as-base-facilitator +``` + +### Security Council facilitator + +```bash +make sign-as-sc-facilitator +``` + +## Generate validation files + +```bash +cd contract-deployments +git pull +cd /-upgrade-zk-and-tee-hash +make deps +make gen-validation-update-verifier-hashes-cb +make gen-validation-update-verifier-hashes-sc +``` + +This produces: + +- `validations/base-signer.json` +- `validations/security-council-signer.json` + +## Execute the transaction + +### 1. Update repo + +```bash +cd contract-deployments +git pull +cd /-upgrade-zk-and-tee-hash +make deps +``` + +### 2. Collect signatures for `CB_MULTISIG` + +Concatenate all signatures and export as the `SIGNATURES` environment variable: + +```bash +export SIGNATURES="[SIGNATURE1][SIGNATURE2]..." +``` + +Then run: + +```bash +SIGNATURES=$SIGNATURES make approve-update-verifier-hashes-cb +``` + +### 3. Collect signatures for `BASE_SECURITY_COUNCIL` + +Concatenate all signatures and export as the `SIGNATURES` environment variable: + +```bash +export SIGNATURES="[SIGNATURE1][SIGNATURE2]..." +``` + +Then run: + +```bash +SIGNATURES=$SIGNATURES make approve-update-verifier-hashes-sc +``` + +### 4. Execute upgrade batch + +```bash +make execute-update-verifier-hashes +``` + +Post-checks enforced by script: + +- `DisputeGameFactory.gameImpls(gameType)` equals the newly deployed `aggregateVerifier` +- `aggregateVerifier.TEE_IMAGE_HASH()` equals the configured `TEE_IMAGE_HASH` +- `aggregateVerifier.ZK_RANGE_HASH()` equals the configured `ZK_RANGE_HASH` +- `aggregateVerifier.ZK_AGGREGATE_HASH()` equals the configured `ZK_AGGREGATE_HASH` +- all other AggregateVerifier immutables (`ZK_VERIFIER`, `TEE_VERIFIER`, `DELAYED_WETH`, `CONFIG_HASH`, etc.) match the previous AggregateVerifier diff --git a/setup-templates/template-upgrade-zk-and-tee-hash/Makefile b/setup-templates/template-upgrade-zk-and-tee-hash/Makefile new file mode 100644 index 00000000..8f3c6952 --- /dev/null +++ b/setup-templates/template-upgrade-zk-and-tee-hash/Makefile @@ -0,0 +1,79 @@ +include ../../Makefile +include ../../Multisig.mk +include ../.env +include .env + +SIGNER_TOOL_PATH = ../../signer-tool +RPC_URL = $(L1_RPC_URL) +DEPLOYER = $(shell $(MISE_EXEC) cast wallet address --ledger --mnemonic-derivation-path $(LEDGER_HD_PATH)) +UPDATE_VERIFIER_HASHES_SCRIPT_NAME = script/UpdateVerifierHashes.s.sol:UpdateVerifierHashes +UPDATE_VERIFIER_HASHES_CB_SENDER = $(shell $(MISE_EXEC) cast call $(CB_MULTISIG) "getOwners()(address[])" --rpc-url $(L1_RPC_URL) | tr -d '[]' | cut -d',' -f1) +UPDATE_VERIFIER_HASHES_SC_SENDER = $(shell $(MISE_EXEC) cast call $(BASE_SECURITY_COUNCIL) "getOwners()(address[])" --rpc-url $(L1_RPC_URL) | tr -d '[]' | cut -d',' -f1) + +.PHONY: deps +deps: install-eip712sign clean-lib forge-deps clone-oz-upgradeable checkout-base-contracts-commit task-extra-deps + +.PHONY: task-extra-deps +task-extra-deps: + $(MISE_EXEC) forge install --no-git github.com/base/op-enclave@a2d5398f04c3a8e4df929d58ee638ba4a037bfec + $(MISE_EXEC) forge install --no-git github.com/risc0/risc0-ethereum@a78ac4a52fe9cfa14120c3b496430f0d42e1d8d3 + $(MISE_EXEC) forge install --no-git github.com/succinctlabs/sp1-contracts@22c4a47cd0a388cb4e25b4f2513954e4275c74ca + git clone --no-checkout https://github.com/OpenZeppelin/openzeppelin-contracts.git lib/openzeppelin-contracts-v5 && \ + cd lib/openzeppelin-contracts-v5 && git checkout dbb6104ce834628e473d2173bbc9d47f81a9eec3 && rm -rf .git + git clone --no-checkout https://github.com/Vectorized/solady.git lib/solady-v0.0.245 && \ + cd lib/solady-v0.0.245 && git checkout e0ef35adb0ccd1032794731a995cb599bba7b537 && rm -rf .git + +.PHONY: validate-config +validate-config: + @test -n "$(BASE_CONTRACTS_COMMIT)" || (echo "BASE_CONTRACTS_COMMIT required" && exit 1) + @test -n "$(GAME_TYPE)" || (echo "GAME_TYPE required" && exit 1) + @test -n "$(TEE_IMAGE_HASH)" || (echo "TEE_IMAGE_HASH required" && exit 1) + @test -n "$(ZK_RANGE_HASH)" || (echo "ZK_RANGE_HASH required" && exit 1) + @test -n "$(ZK_AGGREGATE_HASH)" || (echo "ZK_AGGREGATE_HASH required" && exit 1) + @test -n "$(PROXY_ADMIN_OWNER)" || (echo "PROXY_ADMIN_OWNER required" && exit 1) + @test -n "$(DISPUTE_GAME_FACTORY_PROXY)" || (echo "DISPUTE_GAME_FACTORY_PROXY required" && exit 1) + @echo "Configuration validated successfully" + +## +# Deployment (EOA) +## +.PHONY: deploy-aggregate-verifier +deploy-aggregate-verifier: validate-config +ifndef VERIFIER_API_KEY + $(error VERIFIER_API_KEY is not set) +endif + $(MISE_EXEC) forge script --rpc-url $(L1_RPC_URL) script/DeployAggregateVerifier.s.sol:DeployAggregateVerifier \ + --ledger --hd-paths $(LEDGER_HD_PATH) --broadcast --verify \ + --verifier custom \ + --verifier-url "https://api.etherscan.io/v2/api?chainid=$(L1_CHAIN_ID)" \ + --verifier-api-key $(VERIFIER_API_KEY) \ + -vvvv --sender $(DEPLOYER) + +## +# Update verifier hashes (multisig) +## +.PHONY: gen-validation-update-verifier-hashes-cb +gen-validation-update-verifier-hashes-cb: validate-config deps-signer-tool + $(call GEN_VALIDATION,$(UPDATE_VERIFIER_HASHES_SCRIPT_NAME),$(CB_MULTISIG),$(UPDATE_VERIFIER_HASHES_CB_SENDER),base-signer.json,) + +.PHONY: gen-validation-update-verifier-hashes-sc +gen-validation-update-verifier-hashes-sc: validate-config deps-signer-tool + $(call GEN_VALIDATION,$(UPDATE_VERIFIER_HASHES_SCRIPT_NAME),$(BASE_SECURITY_COUNCIL),$(UPDATE_VERIFIER_HASHES_SC_SENDER),security-council-signer.json,) + +.PHONY: approve-update-verifier-hashes-cb +approve-update-verifier-hashes-cb: validate-config +approve-update-verifier-hashes-cb: SCRIPT_NAME := $(UPDATE_VERIFIER_HASHES_SCRIPT_NAME) +approve-update-verifier-hashes-cb: + $(call MULTISIG_APPROVE,$(CB_MULTISIG),$(SIGNATURES)) + +.PHONY: approve-update-verifier-hashes-sc +approve-update-verifier-hashes-sc: validate-config +approve-update-verifier-hashes-sc: SCRIPT_NAME := $(UPDATE_VERIFIER_HASHES_SCRIPT_NAME) +approve-update-verifier-hashes-sc: + $(call MULTISIG_APPROVE,$(BASE_SECURITY_COUNCIL),$(SIGNATURES)) + +.PHONY: execute-update-verifier-hashes +execute-update-verifier-hashes: validate-config +execute-update-verifier-hashes: SCRIPT_NAME := $(UPDATE_VERIFIER_HASHES_SCRIPT_NAME) +execute-update-verifier-hashes: + $(call MULTISIG_EXECUTE,0x) diff --git a/setup-templates/template-upgrade-zk-and-tee-hash/README.md b/setup-templates/template-upgrade-zk-and-tee-hash/README.md new file mode 100644 index 00000000..dd0ceb38 --- /dev/null +++ b/setup-templates/template-upgrade-zk-and-tee-hash/README.md @@ -0,0 +1,65 @@ +# Upgrade ZK and TEE Hash + +Status: READY TO SIGN + +## Description + +This template updates the TEE and ZK verifier hashes of a multiproof implementation by: + +- redeploying `AggregateVerifier` with identical immutables, overriding `TEE_IMAGE_HASH`, `ZK_RANGE_HASH`, and `ZK_AGGREGATE_HASH` +- pointing `DisputeGameFactory.gameImpls(gameType)` at the new `AggregateVerifier` + +## Setup + +From the repository root: + +```bash +make setup-upgrade-zk-and-tee-hash network= +cd /-upgrade-zk-and-tee-hash +``` + +Fill in all required values in `.env`: + +- `BASE_CONTRACTS_COMMIT` +- `GAME_TYPE` +- `TEE_IMAGE_HASH` +- `ZK_RANGE_HASH` +- `ZK_AGGREGATE_HASH` +- `PROXY_ADMIN_OWNER` +- `DISPUTE_GAME_FACTORY_PROXY` + +Then install dependencies and build: + +```bash +make deps +forge build +``` + +## Procedure + +### Sign task + +#### 1. Update repo + +```bash +cd contract-deployments +git pull +``` + +#### 2. Run signing tool + +```bash +cd contract-deployments +make sign-task +``` + +#### 3. Open the UI at [http://localhost:3000](http://localhost:3000) + +- Select the correct signer role from the list of available users to sign. +- After completion, close the signer tool with `Ctrl + C`. + +#### 4. Send signature to facilitator + +Copy the signature output and send it to the designated facilitator via the agreed communication channel. + +For facilitator instructions, see `FACILITATOR.md`. diff --git a/setup-templates/template-upgrade-zk-and-tee-hash/foundry.toml b/setup-templates/template-upgrade-zk-and-tee-hash/foundry.toml new file mode 100644 index 00000000..e700572b --- /dev/null +++ b/setup-templates/template-upgrade-zk-and-tee-hash/foundry.toml @@ -0,0 +1,36 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] +broadcast = 'records' +fs_permissions = [{ access = "read-write", path = "./" }] +optimizer = true +optimizer_runs = 200 +auto_detect_solc = true +evm_version = "prague" +via-ir = false +remappings = [ + '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts', + '@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts', + '@base-contracts/=lib/contracts/', + '@solady/=lib/solady/src/', + '@solady-v0.0.245/=lib/solady-v0.0.245/src/', + 'forge-std/=lib/forge-std/src/', + 'interfaces/dispute/=lib/contracts/interfaces/L1/proofs/', + 'interfaces/L1/=lib/contracts/interfaces/L1/', + 'interfaces/legacy/=lib/contracts/interfaces/legacy/', + 'interfaces/multiproof/=lib/contracts/interfaces/L1/proofs/', + 'interfaces/universal/=lib/contracts/interfaces/universal/', + 'src/L1/=lib/contracts/src/L1/', + 'src/libraries/=lib/contracts/src/libraries/', + 'src/dispute/=lib/contracts/src/L1/proofs/', + 'src/universal/=lib/contracts/src/universal/', + 'lib/op-enclave/=lib/op-enclave/', + 'openzeppelin/=lib/risc0-ethereum/lib/openzeppelin-contracts/', + '@lib-keccak/=lib/lib-keccak/contracts/lib/', + 'interfaces/cannon/=lib/contracts/interfaces/cannon/', + 'src/cannon/=lib/contracts/src/cannon/', +] + +[lint] +lint_on_build = false diff --git a/setup-templates/template-upgrade-zk-and-tee-hash/script/DeployAggregateVerifier.s.sol b/setup-templates/template-upgrade-zk-and-tee-hash/script/DeployAggregateVerifier.s.sol new file mode 100644 index 00000000..43ed248c --- /dev/null +++ b/setup-templates/template-upgrade-zk-and-tee-hash/script/DeployAggregateVerifier.s.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +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 {IVerifier} from "interfaces/L1/proofs/IVerifier.sol"; + +import {GameType} from "@base-contracts/src/libraries/bridge/Types.sol"; +import {AggregateVerifier} from "@base-contracts/src/L1/proofs/AggregateVerifier.sol"; + +/// @notice Redeploys AggregateVerifier with updated TEE_IMAGE_HASH, ZK_RANGE_HASH, +/// and ZK_AGGREGATE_HASH. All other immutables (including the existing ZkVerifier) +/// are read onchain from the current AggregateVerifier to guarantee continuity. +contract DeployAggregateVerifier is Script { + // Task config from .env. + address internal immutable disputeGameFactoryProxyEnv; + GameType internal immutable gameTypeEnv; + bytes32 internal immutable teeImageHashEnv; + bytes32 internal immutable zkRangeHashEnv; + bytes32 internal immutable zkAggregateHashEnv; + + // Live multiproof implementation currently registered in the DGF. + address internal immutable currentAggregateVerifier; + + // Immutable constructor args copied from the live AggregateVerifier. + GameType internal immutable currentGameType; + IAnchorStateRegistry internal immutable currentAnchorStateRegistry; + IDelayedWETH internal immutable currentDelayedWeth; + address internal immutable currentTeeVerifier; + address internal immutable currentZkVerifier; + bytes32 internal immutable currentConfigHash; + uint256 internal immutable currentL2ChainId; + uint256 internal immutable currentBlockInterval; + uint256 internal immutable currentIntermediateBlockInterval; + + // Deployment output written to addresses.json. + address public aggregateVerifier; + + constructor() { + disputeGameFactoryProxyEnv = vm.envAddress("DISPUTE_GAME_FACTORY_PROXY"); + gameTypeEnv = GameType.wrap(uint32(vm.envUint("GAME_TYPE"))); + teeImageHashEnv = vm.envBytes32("TEE_IMAGE_HASH"); + zkRangeHashEnv = vm.envBytes32("ZK_RANGE_HASH"); + zkAggregateHashEnv = vm.envBytes32("ZK_AGGREGATE_HASH"); + + currentAggregateVerifier = address(IDisputeGameFactory(disputeGameFactoryProxyEnv).gameImpls(gameTypeEnv)); + + AggregateVerifier currentAggregate = AggregateVerifier(currentAggregateVerifier); + currentGameType = currentAggregate.gameType(); + currentAnchorStateRegistry = currentAggregate.anchorStateRegistry(); + currentDelayedWeth = currentAggregate.DELAYED_WETH(); + currentTeeVerifier = address(currentAggregate.TEE_VERIFIER()); + currentZkVerifier = address(currentAggregate.ZK_VERIFIER()); + currentConfigHash = currentAggregate.CONFIG_HASH(); + currentL2ChainId = currentAggregate.L2_CHAIN_ID(); + currentBlockInterval = currentAggregate.BLOCK_INTERVAL(); + currentIntermediateBlockInterval = currentAggregate.INTERMEDIATE_BLOCK_INTERVAL(); + } + + function setUp() public view { + require(currentAggregateVerifier != address(0), "current aggregate verifier not found"); + require(GameType.unwrap(currentGameType) == GameType.unwrap(gameTypeEnv), "current game type mismatch"); + require(currentTeeVerifier != address(0), "current tee verifier not found"); + require(currentZkVerifier != address(0), "current zk verifier not found"); + 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"); + + // Verify new hashes differ from current. + AggregateVerifier currentAggregate = AggregateVerifier(currentAggregateVerifier); + 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" + ); + } + + function run() external { + vm.startBroadcast(); + + aggregateVerifier = address( + new AggregateVerifier({ + gameType_: currentGameType, + anchorStateRegistry_: currentAnchorStateRegistry, + delayedWETH: currentDelayedWeth, + teeVerifier: IVerifier(currentTeeVerifier), + zkVerifier: IVerifier(currentZkVerifier), + teeImageHash: teeImageHashEnv, + zkHashes: AggregateVerifier.ZkHashes({rangeHash: zkRangeHashEnv, aggregateHash: zkAggregateHashEnv}), + configHash: currentConfigHash, + l2ChainId: currentL2ChainId, + blockInterval: currentBlockInterval, + intermediateBlockInterval: currentIntermediateBlockInterval + }) + ); + + vm.stopBroadcast(); + + _postCheck(); + _writeAddresses(); + } + + function _postCheck() internal view { + AggregateVerifier currentAggregate = AggregateVerifier(currentAggregateVerifier); + AggregateVerifier av = AggregateVerifier(aggregateVerifier); + + // Verify updated hashes. + require(av.TEE_IMAGE_HASH() == teeImageHashEnv, "aggregate tee image hash mismatch"); + require(av.ZK_RANGE_HASH() == zkRangeHashEnv, "aggregate zk range hash mismatch"); + require(av.ZK_AGGREGATE_HASH() == zkAggregateHashEnv, "aggregate zk aggregate hash mismatch"); + + // Verify all other immutables are preserved from the current AggregateVerifier. + require(GameType.unwrap(av.gameType()) == GameType.unwrap(currentGameType), "aggregate game type mismatch"); + require(address(av.anchorStateRegistry()) == address(currentAnchorStateRegistry), "aggregate asr mismatch"); + require( + address(av.DISPUTE_GAME_FACTORY()) == address(currentAggregate.DISPUTE_GAME_FACTORY()), + "aggregate dgf mismatch" + ); + require(address(av.DELAYED_WETH()) == address(currentDelayedWeth), "aggregate delayed weth mismatch"); + require(address(av.TEE_VERIFIER()) == currentTeeVerifier, "aggregate tee verifier mismatch"); + 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.BLOCK_INTERVAL() == currentBlockInterval, "aggregate block interval mismatch"); + require( + av.INTERMEDIATE_BLOCK_INTERVAL() == currentIntermediateBlockInterval, + "aggregate intermediate interval mismatch" + ); + } + + function _writeAddresses() internal { + console.log("AggregateVerifier:", aggregateVerifier); + + string memory root = "root"; + string memory json = + vm.serializeAddress({objectKey: root, valueKey: "aggregateVerifier", value: aggregateVerifier}); + vm.writeJson({json: json, path: "addresses.json"}); + } +} diff --git a/setup-templates/template-upgrade-zk-and-tee-hash/script/UpdateVerifierHashes.s.sol b/setup-templates/template-upgrade-zk-and-tee-hash/script/UpdateVerifierHashes.s.sol new file mode 100644 index 00000000..10808d1d --- /dev/null +++ b/setup-templates/template-upgrade-zk-and-tee-hash/script/UpdateVerifierHashes.s.sol @@ -0,0 +1,168 @@ +// 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 {GameType} from "@base-contracts/src/libraries/bridge/Types.sol"; + +interface IDisputeGameFactoryAdmin { + function owner() external view returns (address); + function gameImpls(GameType gameType) external view returns (address); + function setImplementation(GameType gameType, address impl, bytes calldata args) external; +} + +/// @notice Updates the live multiproof implementation in the DisputeGameFactory to +/// the newly deployed AggregateVerifier carrying updated TEE_IMAGE_HASH, +/// ZK_RANGE_HASH, and ZK_AGGREGATE_HASH. +contract UpdateVerifierHashes is MultisigScript { + // Task config from .env. + address internal ownerSafeEnv; + address internal disputeGameFactoryProxyEnv; + GameType internal gameTypeEnv; + bytes32 internal teeImageHashEnv; + bytes32 internal zkRangeHashEnv; + bytes32 internal zkAggregateHashEnv; + + // Live onchain state. + address internal currentAggregateVerifier; + + // Deployment output produced by the EOA script and read from addresses.json. + address internal nextAggregateVerifier; + GameType internal nextGameType; + + function setUp() public { + ownerSafeEnv = vm.envAddress("PROXY_ADMIN_OWNER"); + disputeGameFactoryProxyEnv = vm.envAddress("DISPUTE_GAME_FACTORY_PROXY"); + gameTypeEnv = GameType.wrap(uint32(vm.envUint("GAME_TYPE"))); + teeImageHashEnv = vm.envBytes32("TEE_IMAGE_HASH"); + zkRangeHashEnv = vm.envBytes32("ZK_RANGE_HASH"); + zkAggregateHashEnv = vm.envBytes32("ZK_AGGREGATE_HASH"); + + currentAggregateVerifier = IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv).gameImpls(gameTypeEnv); + + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/addresses.json"); + string memory json = vm.readFile(path); + nextAggregateVerifier = vm.parseJsonAddress({json: json, key: ".aggregateVerifier"}); + require(nextAggregateVerifier != address(0), "next aggregate verifier not set"); + + nextGameType = AggregateVerifier(nextAggregateVerifier).gameType(); + + require(IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv).owner() == ownerSafeEnv, "dgf owner mismatch"); + + require(currentAggregateVerifier != address(0), "current aggregate verifier not found"); + + AggregateVerifier currentAggregate = AggregateVerifier(currentAggregateVerifier); + require( + GameType.unwrap(currentAggregate.gameType()) == GameType.unwrap(gameTypeEnv), "current game type mismatch" + ); + + require(nextAggregateVerifier != currentAggregateVerifier, "next aggregate verifier equals current"); + + // Validate the new AggregateVerifier carries the expected hashes. + AggregateVerifier nextAggregate = AggregateVerifier(nextAggregateVerifier); + + require(GameType.unwrap(nextGameType) == GameType.unwrap(gameTypeEnv), "next game type mismatch"); + require(nextAggregate.TEE_IMAGE_HASH() == teeImageHashEnv, "next aggregate tee image hash mismatch"); + require(nextAggregate.ZK_RANGE_HASH() == zkRangeHashEnv, "next aggregate zk range hash mismatch"); + require(nextAggregate.ZK_AGGREGATE_HASH() == zkAggregateHashEnv, "next aggregate zk aggregate hash mismatch"); + + // Validate all other immutables are preserved. + require( + address(nextAggregate.anchorStateRegistry()) == address(currentAggregate.anchorStateRegistry()), + "next aggregate asr mismatch" + ); + require( + address(nextAggregate.DISPUTE_GAME_FACTORY()) == address(currentAggregate.DISPUTE_GAME_FACTORY()), + "next aggregate dgf mismatch" + ); + require( + address(nextAggregate.DELAYED_WETH()) == address(currentAggregate.DELAYED_WETH()), + "next aggregate delayed weth mismatch" + ); + require( + address(nextAggregate.TEE_VERIFIER()) == address(currentAggregate.TEE_VERIFIER()), + "next aggregate tee verifier mismatch" + ); + require( + address(nextAggregate.ZK_VERIFIER()) == address(currentAggregate.ZK_VERIFIER()), + "next aggregate zk verifier mismatch" + ); + 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.BLOCK_INTERVAL() == currentAggregate.BLOCK_INTERVAL(), + "next aggregate block interval mismatch" + ); + require( + nextAggregate.INTERMEDIATE_BLOCK_INTERVAL() == currentAggregate.INTERMEDIATE_BLOCK_INTERVAL(), + "next aggregate intermediate interval mismatch" + ); + } + + function _buildCalls() internal view override returns (Call[] memory) { + Call[] memory calls = new Call[](1); + + calls[0] = Call({ + operation: Enum.Operation.Call, + target: disputeGameFactoryProxyEnv, + data: abi.encodeCall(IDisputeGameFactoryAdmin.setImplementation, (nextGameType, nextAggregateVerifier, "")), + value: 0 + }); + + return calls; + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal view override { + IDisputeGameFactoryAdmin dgf = IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv); + AggregateVerifier currentAggregate = AggregateVerifier(currentAggregateVerifier); + AggregateVerifier nextAggregate = AggregateVerifier(nextAggregateVerifier); + + // Verify DGF now points to the new AggregateVerifier. + require(dgf.gameImpls(nextGameType) == nextAggregateVerifier, "dgf aggregate verifier mismatch"); + + // Verify updated hashes. + require(nextAggregate.TEE_IMAGE_HASH() == teeImageHashEnv, "next aggregate tee image hash mismatch"); + require(nextAggregate.ZK_RANGE_HASH() == zkRangeHashEnv, "next aggregate zk range hash mismatch"); + require(nextAggregate.ZK_AGGREGATE_HASH() == zkAggregateHashEnv, "next aggregate zk aggregate hash mismatch"); + + // Verify all other immutables are preserved. + require( + address(nextAggregate.anchorStateRegistry()) == address(currentAggregate.anchorStateRegistry()), + "next aggregate asr mismatch" + ); + require( + address(nextAggregate.DISPUTE_GAME_FACTORY()) == address(currentAggregate.DISPUTE_GAME_FACTORY()), + "next aggregate dgf mismatch" + ); + require( + address(nextAggregate.DELAYED_WETH()) == address(currentAggregate.DELAYED_WETH()), + "next aggregate delayed weth mismatch" + ); + require( + address(nextAggregate.TEE_VERIFIER()) == address(currentAggregate.TEE_VERIFIER()), + "next aggregate tee verifier mismatch" + ); + require( + address(nextAggregate.ZK_VERIFIER()) == address(currentAggregate.ZK_VERIFIER()), + "next aggregate zk verifier mismatch" + ); + 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.BLOCK_INTERVAL() == currentAggregate.BLOCK_INTERVAL(), + "next aggregate block interval mismatch" + ); + require( + nextAggregate.INTERMEDIATE_BLOCK_INTERVAL() == currentAggregate.INTERMEDIATE_BLOCK_INTERVAL(), + "next aggregate intermediate interval mismatch" + ); + } + + function _ownerSafe() internal view override returns (address) { + return ownerSafeEnv; + } +}