diff --git a/mainnet/.env b/mainnet/.env index 8035e7a0..48199649 100644 --- a/mainnet/.env +++ b/mainnet/.env @@ -10,9 +10,9 @@ export BASE_SECURITY_COUNCIL=0x20AcF55A3DCfe07fC4cecaCFa1628F788EC8A4Dd export BATCH_INBOX=0xff00000000000000000000000000000000008453 export BATCH_SENDER=0x5050f69a9786f081509234f1a7f4684b5e5b76c9 export CB_MULTISIG=0x9855054731540A48b28990B63DcF4f33d8AE46A1 -export CHALLENGER=0x8Ca1E12404d16373Aef756179B185F27b2994F3a +export CHALLENGER=0x819501cdA743a606A93dbEF254FE0D263Ce7d102 export INCIDENT_MULTISIG=0x14536667Cd30e52C0b458BaACcB9faDA7046E056 -export PROPOSER=0x642229f238fb9de03374be34b0ed8d9de80752c5 +export PROPOSER=0xc1366Fabe614d42D367A1ecE61821238A1d31cF5 export PROXY_ADMIN_OWNER=0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c # L1 Addresses diff --git a/mainnet/2026-04-28-activate-multiproof/.env b/mainnet/2026-04-28-activate-multiproof/.env new file mode 100644 index 00000000..7e000a4c --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/.env @@ -0,0 +1,50 @@ +BASE_CONTRACTS_COMMIT=01dad230390cd69bcf130b5fc7a7a580b31650a7 + +# Multiproof identity and deployment constants. +GAME_TYPE=621 +SP1_VERIFIER=0x397A5f7f3dBd538f23DE225B51f532c34448dA9B +TEE_IMAGE_HASH=TODO +ZK_RANGE_HASH=TODO +ZK_AGGREGATE_HASH=TODO +CONFIG_HASH=TODO + +# Deployment timings and economic parameters. +BLOCK_INTERVAL=600 +INTERMEDIATE_BLOCK_INTERVAL=30 +PROOF_THRESHOLD=1 +INIT_BOND=50000000000000000 +PROOF_MATURITY_DELAY_SECONDS=86400 +DISPUTE_GAME_FINALITY_DELAY_SECONDS=0 +DELAYED_WETH_DELAY_SECONDS=86400 + +# TEE roles. +TEE_PROVER_REGISTRY_OWNER=0x9855054731540A48b28990B63DcF4f33d8AE46A1 +TEE_PROVER_REGISTRY_MANAGER=0xd87488Dbb5b6F47cc6c15Dd95Bb60c83D3031b04 + +# NitroEnclaveVerifier deployment. +NITRO_INITIAL_MAX_TIME_DIFF_SECONDS=3600 +NITRO_INITIAL_ROOT_CERT=0x641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68f79bb5b +RISC0_VERIFIER_ROUTER=0x8EaB2D97Dfce405A1692a21b3ff3A172d593D319 +RISC0_SET_VERIFIER=0x5005aBa3DFf7C940fcc1e48DccCAD611a80eEB85 +RISC0_SET_BUILDER_IMAGE_ID=0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9 +NITRO_ZK_VERIFIER_ID=0x15051db631d6ed382d957c795a558a0abdd00d0d22a1670455721bc2712d3d6e +NITRO_VERIFIER_PROOF_ID=0x00640947da751a4ae37280ab2f6572319795ae5c88973d042d657a7bbba60ca2 +NITRO_REVOKER=0xd87488Dbb5b6F47cc6c15Dd95Bb60c83D3031b04 + +# AnchorStateRegistry bootstrap values for the reinitializer. +STARTING_ANCHOR_ROOT=TODO +STARTING_ANCHOR_L2_BLOCK_NUMBER=TODO + +# Re-exported for task simulation UI. +PROXY_ADMIN_OWNER=0x7bB41C3008B3f03FE483B28b8DB90e19Cf07595c +L1_PROXY_ADMIN=0x0475cBCAebd9CE8AfA5025828d5b98DFb67E059E +SYSTEM_CONFIG=0x73a79Fab69143498Ed3712e519A88a918e1f4072 +OPTIMISM_PORTAL=0x49048044D57e1C92A77f79988d21Fa8fAF74E97e +DISPUTE_GAME_FACTORY_PROXY=0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e +ANCHOR_STATE_REGISTRY_PROXY=0x909f6cf47ed12f010A796527f562bFc26C7F4E72 +PROPOSER=0xc1366Fabe614d42D367A1ecE61821238A1d31cF5 +CHALLENGER=0x819501cdA743a606A93dbEF254FE0D263Ce7d102 +L2_CHAIN_ID=8453 + +# Validation generation. +RECORD_STATE_DIFF=true diff --git a/mainnet/2026-04-28-activate-multiproof/FACILITATOR.md b/mainnet/2026-04-28-activate-multiproof/FACILITATOR.md new file mode 100644 index 00000000..8ac0656b --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/FACILITATOR.md @@ -0,0 +1,164 @@ +# Facilitator Guide + +Guide for facilitators managing this task. + +## Task Origin Signing + +After setting up the task, generate cryptographic attestations to prove who created and facilitated the task. These signatures are stored in `mainnet/signatures/2026-04-28-activate-multiproof/`. + +### Task creator + +```bash +cd contract-deployments/mainnet/2026-04-28-activate-multiproof +make sign-as-task-creator +``` + +### Base facilitator + +```bash +cd contract-deployments/mainnet/2026-04-28-activate-multiproof +make sign-as-base-facilitator +``` + +### Security Council facilitator + +```bash +cd contract-deployments/mainnet/2026-04-28-activate-multiproof +make sign-as-sc-facilitator +``` + +## Finalize task inputs + +Before collecting signatures, confirm that the task parameters in `.env` match the intended mainnet cutover values. + +Revalidate `STARTING_ANCHOR_*` against mainnet before collecting signatures. + +## Deployment prerequisites + +Before collecting signatures, complete all deploy steps: + +```bash +cd contract-deployments +git pull +cd mainnet/2026-04-28-activate-multiproof +make deps +make deploy-nitro-verifier +make deploy-multiproof +make setup-nitro +``` + +`make deps` applies the local `AnchorStateRegistry` patch so the ASR reinitializer clears the old `anchorGame` pointer and the new `STARTING_ANCHOR_*` values take effect after cutover. + +`make deploy-nitro-verifier` deploys `NitroEnclaveVerifier` and wires the RiscZero verify route using the configured `RISC0_SET_VERIFIER`. Initializes `addresses.json`. + +`make deploy-multiproof` deploys the remaining contracts (`TEEProverRegistry`, `DelayedWETH`, `TEEVerifier`, `ZkVerifier`, `AggregateVerifier`, etc.). The `TEEProverRegistry` and `DelayedWETH` proxies are initialized at deploy time and their admin is transferred to the L1 `ProxyAdmin`. Appends addresses to `addresses.json`. + +`make setup-nitro` configures the `NitroEnclaveVerifier`: sets the `proofSubmitter` to the `TEEProverRegistry` proxy and transfers ownership to `TEE_PROVER_REGISTRY_OWNER`. This step runs directly via the deployer Ledger. + +The activation batch upgrades the L1 proxies, registers the new multiproof game type, and disables new `CANNON` game creation. + +Expected `addresses.json` keys: + +- `nitroEnclaveVerifier` +- `teeProverRegistryImpl` +- `teeProverRegistryProxy` +- `teeVerifier` +- `zkVerifier` +- `delayedWETHImpl` +- `delayedWETHProxy` +- `aggregateVerifier` +- `optimismPortal2Impl` +- `disputeGameFactoryImpl` +- `anchorStateRegistryImpl` + +## Generate validation files + +```bash +cd contract-deployments +git pull +cd mainnet/2026-04-28-activate-multiproof +make deps +make gen-validation-multiproof-cb +make gen-validation-multiproof-sc +``` + +This produces: + +- `validations/multiproof-cb-signer.json` +- `validations/multiproof-sc-signer.json` + +Mainnet validation files must not contain `skipTaskOriginValidation`. + +## Pre-sign check: `STARTING_ANCHOR_*` correctness + +`STARTING_ANCHOR_ROOT` and `STARTING_ANCHOR_L2_BLOCK_NUMBER` are chain-critical. +Before collecting signatures, verify both values against the target RPC endpoints. + +### 1. Validate that the anchor block number is expected + +Confirm `STARTING_ANCHOR_L2_BLOCK_NUMBER` matches the planned cutover value. + +```bash +BLOCK=$STARTING_ANCHOR_L2_BLOCK_NUMBER +cast block $BLOCK --rpc-url $L2_RPC_URL +``` + +### 2. Derive output root from that exact block + +Use `optimism_outputAtBlock` with the same block and compare to `STARTING_ANCHOR_ROOT` from `.env`. + +```bash +BLOCK=$STARTING_ANCHOR_L2_BLOCK_NUMBER +OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast 2h $BLOCK) --rpc-url $OP_NODE_RPC_URL | jq -r '.outputRoot') +echo $OUTPUT_ROOT +echo $STARTING_ANCHOR_ROOT +``` + +Expected result: + +- `OUTPUT_ROOT == STARTING_ANCHOR_ROOT` + +## Execute the transaction + +### 1. Update repo + +```bash +cd contract-deployments +git pull +cd mainnet/2026-04-28-activate-multiproof +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-multiproof-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-multiproof-sc +``` + +### 4. Execute the multiproof activation batch + +```bash +make execute-activate-multiproof +``` diff --git a/mainnet/2026-04-28-activate-multiproof/Makefile b/mainnet/2026-04-28-activate-multiproof/Makefile new file mode 100644 index 00000000..7a8a9a51 --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/Makefile @@ -0,0 +1,86 @@ +include ../../Makefile +include ../../Multisig.mk +include ../.env +include .env + +SIGNER_TOOL_PATH = ../../signer-tool +RPC_URL = $(L1_RPC_URL) +DEPLOYER = $(shell cast wallet address --ledger --mnemonic-derivation-path $(LEDGER_HD_PATH)) +ACTIVATE_MULTIPROOF_SCRIPT_NAME = script/ActivateMultiproofStack.s.sol:ActivateMultiproofStack +MULTIPROOF_CB_SENDER = $(shell cast call $(CB_MULTISIG) "getOwners()(address[])" --rpc-url $(L1_RPC_URL) | tr -d '[]' | cut -d',' -f1) +MULTIPROOF_SC_SENDER = $(shell cast call $(BASE_SECURITY_COUNCIL) "getOwners()(address[])" --rpc-url $(L1_RPC_URL) | tr -d '[]' | cut -d',' -f1) + +.PHONY: apply-patches +apply-patches: + cd lib/contracts && patch -p1 < ../../patch/asr-reset-anchor-game.patch + +.PHONY: deps +deps: task-extra-deps apply-patches + +.PHONY: task-extra-deps +task-extra-deps: + forge install --no-git github.com/base/op-enclave@a2d5398f04c3a8e4df929d58ee638ba4a037bfec + forge install --no-git github.com/risc0/risc0-ethereum@a78ac4a52fe9cfa14120c3b496430f0d42e1d8d3 + 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 + git clone --no-checkout https://github.com/Vectorized/solady.git lib/solady-v0.0.245 && \ + cd lib/solady-v0.0.245 && git checkout e0ef35adb0ccd1032794731a995cb599bba7b537 + +## +# Deployment +## +.PHONY: deploy-nitro-verifier +deploy-nitro-verifier: +ifndef VERIFIER_API_KEY + $(error VERIFIER_API_KEY is not set) +endif + forge script --rpc-url $(L1_RPC_URL) script/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + --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) + +.PHONY: deploy-multiproof +deploy-multiproof: +ifndef VERIFIER_API_KEY + $(error VERIFIER_API_KEY is not set) +endif + forge script --rpc-url $(L1_RPC_URL) script/DeployMultiproofStack.s.sol:DeployMultiproofStack \ + --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) + +.PHONY: setup-nitro +setup-nitro: + forge script --rpc-url $(L1_RPC_URL) script/SetupNitroEnclaveVerifier.s.sol:SetupNitroEnclaveVerifier \ + --ledger --hd-paths $(LEDGER_HD_PATH) --broadcast -vvvv --sender $(DEPLOYER) + +## +# Activate Multiproof +## +.PHONY: gen-validation-multiproof-cb +gen-validation-multiproof-cb: deps-signer-tool + $(call GEN_VALIDATION,$(ACTIVATE_MULTIPROOF_SCRIPT_NAME),$(CB_MULTISIG),$(MULTIPROOF_CB_SENDER),multiproof-cb-signer.json,) + +.PHONY: gen-validation-multiproof-sc +gen-validation-multiproof-sc: deps-signer-tool + $(call GEN_VALIDATION,$(ACTIVATE_MULTIPROOF_SCRIPT_NAME),$(BASE_SECURITY_COUNCIL),$(MULTIPROOF_SC_SENDER),multiproof-sc-signer.json,) + +.PHONY: approve-multiproof-cb +approve-multiproof-cb: SCRIPT_NAME := $(ACTIVATE_MULTIPROOF_SCRIPT_NAME) +approve-multiproof-cb: + $(call MULTISIG_APPROVE,$(CB_MULTISIG),$(SIGNATURES)) + +.PHONY: approve-multiproof-sc +approve-multiproof-sc: SCRIPT_NAME := $(ACTIVATE_MULTIPROOF_SCRIPT_NAME) +approve-multiproof-sc: + $(call MULTISIG_APPROVE,$(BASE_SECURITY_COUNCIL),$(SIGNATURES)) + +.PHONY: execute-activate-multiproof +execute-activate-multiproof: SCRIPT_NAME := $(ACTIVATE_MULTIPROOF_SCRIPT_NAME) +execute-activate-multiproof: + $(call MULTISIG_EXECUTE,0x) diff --git a/mainnet/2026-04-28-activate-multiproof/README.md b/mainnet/2026-04-28-activate-multiproof/README.md new file mode 100644 index 00000000..9792b30f --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/README.md @@ -0,0 +1,41 @@ +# Activate Multiproof + +Status: PENDING + +## Description + +This task deploys and activates multiproof on `mainnet`. + +- deploys `NitroEnclaveVerifier` +- deploys the multiproof stack with `TEEVerifier`, `ZkVerifier`, and `AggregateVerifier` +- activates the new multiproof game type through the mainnet `ProxyAdminOwner` +- disables new `CANNON` game creation + +## Procedure + +## Sign Task + +### 1. Update repo + +```bash +cd contract-deployments +git pull +``` + +### 2. Run the 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/mainnet/2026-04-28-activate-multiproof/foundry.toml b/mainnet/2026-04-28-activate-multiproof/foundry.toml new file mode 100644 index 00000000..0519fed0 --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/foundry.toml @@ -0,0 +1,38 @@ +[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 = [ + '@eth-optimism-bedrock/=lib/optimism/packages/contracts-bedrock/', + '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts', + '@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts', + '@rari-capital/solmate/=lib/solmate/', + '@base-contracts/=lib/contracts/', + 'solady/=lib/solady/src/', + '@solady/=lib/solady/src/', + '@solady-v0.0.245/=lib/solady-v0.0.245/src/', + '@lib-keccak/=lib/lib-keccak/contracts/lib', + 'forge-std/=lib/forge-std/src/', + 'interfaces/dispute/=lib/contracts/interfaces/dispute/', + 'interfaces/cannon/=lib/contracts/interfaces/cannon/', + 'interfaces/multiproof/=lib/contracts/interfaces/multiproof/', + 'interfaces/universal/=lib/contracts/interfaces/universal/', + 'interfaces/L1/=lib/contracts/interfaces/L1/', + 'interfaces/legacy/=lib/contracts/interfaces/legacy/', + 'src/dispute/=lib/contracts/src/dispute/', + 'src/multiproof/=lib/contracts/src/multiproof/', + 'src/universal/=lib/contracts/src/universal/', + 'src/L1/=lib/contracts/src/L1/', + 'src/cannon/=lib/contracts/src/cannon/', + 'src/vendor/=lib/contracts/src/vendor/', + 'src/libraries/=lib/contracts/src/libraries/', + 'lib/op-enclave/=lib/op-enclave/', + 'openzeppelin/=lib/risc0-ethereum/lib/openzeppelin-contracts/', +] diff --git a/mainnet/2026-04-28-activate-multiproof/patch/asr-reset-anchor-game.patch b/mainnet/2026-04-28-activate-multiproof/patch/asr-reset-anchor-game.patch new file mode 100644 index 00000000..d9c9bbd3 --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/patch/asr-reset-anchor-game.patch @@ -0,0 +1,12 @@ +diff --git a/src/dispute/AnchorStateRegistry.sol b/src/dispute/AnchorStateRegistry.sol +--- a/src/dispute/AnchorStateRegistry.sol ++++ b/src/dispute/AnchorStateRegistry.sol +@@ -101,6 +101,7 @@ + // Now perform initialization logic. + systemConfig = _systemConfig; + disputeGameFactory = _disputeGameFactory; + startingAnchorRoot = _startingAnchorRoot; ++ anchorGame = IFaultDisputeGame(address(0)); + respectedGameType = _startingRespectedGameType; + + // Set the retirement timestamp to the current timestamp the first time the contract is diff --git a/mainnet/2026-04-28-activate-multiproof/script/ActivateMultiproofStack.s.sol b/mainnet/2026-04-28-activate-multiproof/script/ActivateMultiproofStack.s.sol new file mode 100644 index 00000000..f2362ff7 --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/script/ActivateMultiproofStack.s.sol @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Vm} from "forge-std/Vm.sol"; + +import {ISystemConfig} from "interfaces/L1/ISystemConfig.sol"; +import {IDisputeGameFactory} from "interfaces/dispute/IDisputeGameFactory.sol"; + +import {Simulation} from "@base-contracts/script/universal/Simulation.sol"; +import {MultisigScript} from "@base-contracts/script/universal/MultisigScript.sol"; +import {Enum} from "@base-contracts/script/universal/IGnosisSafe.sol"; +import {GameType, Hash, Proposal} from "@base-contracts/src/dispute/lib/Types.sol"; +import {AnchorStateRegistry} from "@base-contracts/src/dispute/AnchorStateRegistry.sol"; +import {DelayedWETH} from "@base-contracts/src/dispute/DelayedWETH.sol"; +import {OptimismPortal2} from "@base-contracts/src/L1/OptimismPortal2.sol"; +import {TEEProverRegistry} from "@base-contracts/src/multiproof/tee/TEEProverRegistry.sol"; +import {AggregateVerifier} from "@base-contracts/src/multiproof/AggregateVerifier.sol"; +import {ZkVerifier} from "@base-contracts/src/multiproof/zk/ZKVerifier.sol"; + +interface IProxyAdmin { + function upgrade(address proxy, address implementation) external; + function upgradeAndCall(address proxy, address implementation, bytes calldata data) external payable; +} + +interface IProxy { + function implementation() external view returns (address); +} + +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; +} + +contract ActivateMultiproofStack is MultisigScript { + uint32 internal constant CANNON_GAME_TYPE = 0; + + address internal ownerSafeEnv; + address internal proxyAdminEnv; + address internal systemConfigEnv; + address internal optimismPortalEnv; + address internal disputeGameFactoryProxyEnv; + address internal anchorStateRegistryProxyEnv; + address internal sp1VerifierEnv; + + uint32 internal gameTypeEnv; + uint256 internal initBondEnv; + bytes32 internal teeImageHashEnv; + bytes32 internal zkRangeHashEnv; + bytes32 internal zkAggregateHashEnv; + bytes32 internal configHashEnv; + uint256 internal l2ChainIdEnv; + uint256 internal blockIntervalEnv; + uint256 internal intermediateBlockIntervalEnv; + uint256 internal proofThresholdEnv; + uint256 internal proofMaturityDelaySecondsEnv; + bytes32 internal startingAnchorRootEnv; + uint256 internal startingAnchorL2BlockNumberEnv; + + address internal teeProverRegistryOwnerEnv; + address internal teeProverRegistryManagerEnv; + address internal proposerEnv; + address internal challengerEnv; + + address internal newAggregateVerifier; + address internal newTeeVerifier; + address internal newZkVerifier; + address internal newOptimismPortalImpl; + address internal newDgfImpl; + address internal newAsrImpl; + address internal newTeeProverRegistryImpl; + address internal newTeeProverRegistryProxy; + address internal newDelayedWethImpl; + address internal newDelayedWethProxy; + + function setUp() public { + ownerSafeEnv = vm.envAddress("PROXY_ADMIN_OWNER"); + proxyAdminEnv = vm.envAddress("L1_PROXY_ADMIN"); + systemConfigEnv = vm.envAddress("SYSTEM_CONFIG"); + optimismPortalEnv = vm.envAddress("OPTIMISM_PORTAL"); + disputeGameFactoryProxyEnv = vm.envAddress("DISPUTE_GAME_FACTORY_PROXY"); + anchorStateRegistryProxyEnv = vm.envAddress("ANCHOR_STATE_REGISTRY_PROXY"); + sp1VerifierEnv = vm.envAddress("SP1_VERIFIER"); + + gameTypeEnv = uint32(vm.envUint("GAME_TYPE")); + initBondEnv = vm.envUint("INIT_BOND"); + teeImageHashEnv = vm.envBytes32("TEE_IMAGE_HASH"); + zkRangeHashEnv = vm.envBytes32("ZK_RANGE_HASH"); + zkAggregateHashEnv = vm.envBytes32("ZK_AGGREGATE_HASH"); + configHashEnv = vm.envBytes32("CONFIG_HASH"); + l2ChainIdEnv = vm.envUint("L2_CHAIN_ID"); + blockIntervalEnv = vm.envUint("BLOCK_INTERVAL"); + intermediateBlockIntervalEnv = vm.envUint("INTERMEDIATE_BLOCK_INTERVAL"); + proofThresholdEnv = vm.envUint("PROOF_THRESHOLD"); + proofMaturityDelaySecondsEnv = vm.envUint("PROOF_MATURITY_DELAY_SECONDS"); + startingAnchorRootEnv = vm.envBytes32("STARTING_ANCHOR_ROOT"); + startingAnchorL2BlockNumberEnv = vm.envUint("STARTING_ANCHOR_L2_BLOCK_NUMBER"); + + teeProverRegistryOwnerEnv = vm.envAddress("TEE_PROVER_REGISTRY_OWNER"); + teeProverRegistryManagerEnv = vm.envAddress("TEE_PROVER_REGISTRY_MANAGER"); + proposerEnv = vm.envAddress("PROPOSER"); + challengerEnv = vm.envAddress("CHALLENGER"); + + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/addresses.json"); + string memory json = vm.readFile(path); + + newAggregateVerifier = vm.parseJsonAddress({json: json, key: ".aggregateVerifier"}); + newTeeVerifier = vm.parseJsonAddress({json: json, key: ".teeVerifier"}); + newZkVerifier = vm.parseJsonAddress({json: json, key: ".zkVerifier"}); + newOptimismPortalImpl = vm.parseJsonAddress({json: json, key: ".optimismPortal2Impl"}); + newDgfImpl = vm.parseJsonAddress({json: json, key: ".disputeGameFactoryImpl"}); + newAsrImpl = vm.parseJsonAddress({json: json, key: ".anchorStateRegistryImpl"}); + newTeeProverRegistryImpl = vm.parseJsonAddress({json: json, key: ".teeProverRegistryImpl"}); + newTeeProverRegistryProxy = vm.parseJsonAddress({json: json, key: ".teeProverRegistryProxy"}); + newDelayedWethImpl = vm.parseJsonAddress({json: json, key: ".delayedWETHImpl"}); + newDelayedWethProxy = vm.parseJsonAddress({json: json, key: ".delayedWETHProxy"}); + + require( + IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv).owner() == ownerSafeEnv, + "DGF owner != PROXY_ADMIN_OWNER" + ); + require(ISystemConfig(systemConfigEnv).guardian() == ownerSafeEnv, "Guardian != PROXY_ADMIN_OWNER"); + + _checkAggregateVerifier(); + _checkZkVerifier(); + } + + function _buildCalls() internal view override returns (Call[] memory) { + Call[] memory calls = new Call[](7); + + calls[0] = Call({ + operation: Enum.Operation.Call, + target: proxyAdminEnv, + data: abi.encodeCall(IProxyAdmin.upgrade, (optimismPortalEnv, newOptimismPortalImpl)), + value: 0 + }); + + calls[1] = Call({ + operation: Enum.Operation.Call, + target: proxyAdminEnv, + data: abi.encodeCall(IProxyAdmin.upgrade, (disputeGameFactoryProxyEnv, newDgfImpl)), + value: 0 + }); + + calls[2] = Call({ + operation: Enum.Operation.Call, + target: proxyAdminEnv, + data: abi.encodeCall( + IProxyAdmin.upgradeAndCall, + ( + anchorStateRegistryProxyEnv, + newAsrImpl, + abi.encodeCall( + AnchorStateRegistry.initialize, + ( + ISystemConfig(systemConfigEnv), + IDisputeGameFactory(disputeGameFactoryProxyEnv), + Proposal({ + root: Hash.wrap(startingAnchorRootEnv), l2SequenceNumber: startingAnchorL2BlockNumberEnv + }), + GameType.wrap(gameTypeEnv) + ) + ) + ) + ), + value: 0 + }); + + calls[3] = Call({ + operation: Enum.Operation.Call, + target: disputeGameFactoryProxyEnv, + data: abi.encodeCall( + IDisputeGameFactoryAdmin.setImplementation, (GameType.wrap(gameTypeEnv), newAggregateVerifier, "") + ), + value: 0 + }); + + calls[4] = Call({ + operation: Enum.Operation.Call, + target: disputeGameFactoryProxyEnv, + data: abi.encodeCall(IDisputeGameFactoryAdmin.setInitBond, (GameType.wrap(gameTypeEnv), initBondEnv)), + value: 0 + }); + + calls[5] = Call({ + operation: Enum.Operation.Call, + target: anchorStateRegistryProxyEnv, + data: abi.encodeCall(AnchorStateRegistry.updateRetirementTimestamp, ()), + value: 0 + }); + + calls[6] = Call({ + operation: Enum.Operation.Call, + target: disputeGameFactoryProxyEnv, + data: abi.encodeCall( + IDisputeGameFactoryAdmin.setImplementation, (GameType.wrap(CANNON_GAME_TYPE), address(0), "") + ), + value: 0 + }); + + return calls; + } + + function _postCheck(Vm.AccountAccess[] memory, Simulation.Payload memory) internal override { + _checkProxyUpgrades(); + _checkAnchorStateRegistry(); + _checkTeeProverRegistryProxy(); + _checkDelayedWethProxy(); + _checkDisputeGameFactory(); + _checkAggregateVerifier(); + _checkZkVerifier(); + } + + function _checkProxyUpgrades() internal { + vm.prank(proxyAdminEnv); + require(IProxy(optimismPortalEnv).implementation() == newOptimismPortalImpl, "portal impl mismatch"); + vm.prank(proxyAdminEnv); + require(IProxy(disputeGameFactoryProxyEnv).implementation() == newDgfImpl, "dgf impl mismatch"); + vm.prank(proxyAdminEnv); + require(IProxy(anchorStateRegistryProxyEnv).implementation() == newAsrImpl, "asr impl mismatch"); + } + + function _checkAnchorStateRegistry() internal view { + AnchorStateRegistry asr = AnchorStateRegistry(anchorStateRegistryProxyEnv); + + require(address(asr.systemConfig()) == systemConfigEnv, "asr system config mismatch"); + require(address(asr.disputeGameFactory()) == disputeGameFactoryProxyEnv, "asr dgf mismatch"); + + Proposal memory startingAnchor = asr.getStartingAnchorRoot(); + require(Hash.unwrap(startingAnchor.root) == startingAnchorRootEnv, "anchor root mismatch"); + require(startingAnchor.l2SequenceNumber == startingAnchorL2BlockNumberEnv, "anchor block mismatch"); + require(GameType.unwrap(asr.respectedGameType()) == gameTypeEnv, "respected game type mismatch"); + require(asr.retirementTimestamp() == uint64(block.timestamp), "retirement timestamp mismatch"); + require( + address(OptimismPortal2(payable(optimismPortalEnv)).anchorStateRegistry()) == anchorStateRegistryProxyEnv, + "portal asr mismatch" + ); + require( + OptimismPortal2(payable(optimismPortalEnv)).proofMaturityDelaySeconds() == proofMaturityDelaySecondsEnv, + "portal proof maturity delay mismatch" + ); + } + + function _checkTeeProverRegistryProxy() internal { + vm.prank(proxyAdminEnv); + require( + IProxy(newTeeProverRegistryProxy).implementation() == newTeeProverRegistryImpl, "tee registry impl mismatch" + ); + + TEEProverRegistry registry = TEEProverRegistry(newTeeProverRegistryProxy); + require(registry.owner() == teeProverRegistryOwnerEnv, "tee registry owner mismatch"); + require(registry.manager() == teeProverRegistryManagerEnv, "tee registry manager mismatch"); + require(GameType.unwrap(registry.gameType()) == gameTypeEnv, "tee registry game type mismatch"); + require(registry.isValidProposer(proposerEnv), "tee registry proposer mismatch"); + require(registry.isValidProposer(challengerEnv), "tee registry challenger mismatch"); + } + + function _checkDelayedWethProxy() internal { + vm.prank(proxyAdminEnv); + require(IProxy(newDelayedWethProxy).implementation() == newDelayedWethImpl, "delayed weth impl mismatch"); + require( + address(DelayedWETH(payable(newDelayedWethProxy)).systemConfig()) == systemConfigEnv, + "delayed weth systemConfig mismatch" + ); + } + + function _checkDisputeGameFactory() internal view { + IDisputeGameFactoryAdmin dgf = IDisputeGameFactoryAdmin(disputeGameFactoryProxyEnv); + require(dgf.gameImpls(GameType.wrap(gameTypeEnv)) == newAggregateVerifier, "game impl mismatch"); + require(dgf.gameImpls(GameType.wrap(CANNON_GAME_TYPE)) == address(0), "cannon impl mismatch"); + require(dgf.initBonds(GameType.wrap(gameTypeEnv)) == initBondEnv, "init bond mismatch"); + } + + function _checkAggregateVerifier() internal view { + AggregateVerifier av = AggregateVerifier(newAggregateVerifier); + require(GameType.unwrap(av.gameType()) == gameTypeEnv, "aggregate game type mismatch"); + require(address(av.anchorStateRegistry()) == anchorStateRegistryProxyEnv, "aggregate asr mismatch"); + require(address(av.DISPUTE_GAME_FACTORY()) == disputeGameFactoryProxyEnv, "aggregate dgf mismatch"); + require(address(av.DELAYED_WETH()) == newDelayedWethProxy, "aggregate delayed weth mismatch"); + require(address(av.TEE_VERIFIER()) == newTeeVerifier, "aggregate tee verifier mismatch"); + require(address(av.ZK_VERIFIER()) == newZkVerifier, "aggregate zk verifier mismatch"); + 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"); + require(av.CONFIG_HASH() == configHashEnv, "aggregate config hash mismatch"); + require(av.L2_CHAIN_ID() == l2ChainIdEnv, "aggregate l2 chain id mismatch"); + require(av.BLOCK_INTERVAL() == blockIntervalEnv, "aggregate block interval mismatch"); + require( + av.INTERMEDIATE_BLOCK_INTERVAL() == intermediateBlockIntervalEnv, + "aggregate intermediate interval mismatch" + ); + require(av.PROOF_THRESHOLD() == proofThresholdEnv, "aggregate proof threshold mismatch"); + } + + function _checkZkVerifier() internal view { + require(address(ZkVerifier(newZkVerifier).SP1_VERIFIER()) == sp1VerifierEnv, "zk verifier sp1 mismatch"); + require( + address(ZkVerifier(newZkVerifier).ANCHOR_STATE_REGISTRY()) == anchorStateRegistryProxyEnv, + "zk verifier asr mismatch" + ); + } + + function _ownerSafe() internal view override returns (address) { + return ownerSafeEnv; + } +} diff --git a/mainnet/2026-04-28-activate-multiproof/script/DeployMultiproofStack.s.sol b/mainnet/2026-04-28-activate-multiproof/script/DeployMultiproofStack.s.sol new file mode 100644 index 00000000..3fced622 --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/script/DeployMultiproofStack.s.sol @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import {Script, console} from "forge-std/Script.sol"; + +import {IAnchorStateRegistry} from "interfaces/dispute/IAnchorStateRegistry.sol"; +import {IDelayedWETH} from "interfaces/dispute/IDelayedWETH.sol"; +import {IDisputeGameFactory} from "interfaces/dispute/IDisputeGameFactory.sol"; +import {ISystemConfig} from "interfaces/L1/ISystemConfig.sol"; +import {INitroEnclaveVerifier} from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import {IVerifier} from "interfaces/multiproof/IVerifier.sol"; + +import {GameType} from "@base-contracts/src/dispute/lib/Types.sol"; +import {DelayedWETH} from "@base-contracts/src/dispute/DelayedWETH.sol"; +import {DisputeGameFactory} from "@base-contracts/src/dispute/DisputeGameFactory.sol"; +import {AnchorStateRegistry} from "@base-contracts/src/dispute/AnchorStateRegistry.sol"; +import {OptimismPortal2} from "@base-contracts/src/L1/OptimismPortal2.sol"; +import {TEEProverRegistry} from "@base-contracts/src/multiproof/tee/TEEProverRegistry.sol"; +import {TEEVerifier} from "@base-contracts/src/multiproof/tee/TEEVerifier.sol"; +import {AggregateVerifier} from "@base-contracts/src/multiproof/AggregateVerifier.sol"; +import {ZkVerifier} from "@base-contracts/src/multiproof/zk/ZKVerifier.sol"; +import {Proxy} from "@base-contracts/src/universal/Proxy.sol"; +import {ISP1Verifier} from "src/dispute/zk/ISP1Verifier.sol"; + +interface IProxy { + function implementation() external view returns (address); +} + +contract DeployMultiproofStack is Script { + // Existing L1 dependencies consumed by the deploy. + address internal l1ProxyAdminEnv; + address internal anchorStateRegistryProxyEnv; + address internal disputeGameFactoryProxyEnv; + + // Multiproof identity and chain-level parameters. + uint32 internal gameTypeEnv; + address internal sp1VerifierEnv; + bytes32 internal teeImageHashEnv; + bytes32 internal zkRangeHashEnv; + bytes32 internal zkAggregateHashEnv; + bytes32 internal configHashEnv; + uint256 internal l2ChainIdEnv; + uint256 internal blockIntervalEnv; + uint256 internal intermediateBlockIntervalEnv; + uint256 internal proofThresholdEnv; + + // Delay / timing parameters for the newly deployed implementations. + uint256 internal proofMaturityDelaySecondsEnv; + uint256 internal disputeGameFinalityDelaySecondsEnv; + uint256 internal delayedWethDelaySecondsEnv; + + // Proxy initialization parameters (TEEProverRegistry + DelayedWETH). + address internal systemConfigEnv; + address internal teeProverRegistryOwnerEnv; + address internal teeProverRegistryManagerEnv; + address internal proposerEnv; + address internal challengerEnv; + + // Predeployed RISC Zero / Nitro contracts consumed by this task. + address public nitroEnclaveVerifier; + + // Freshly deployed contracts / implementations produced by this task. + address public teeProverRegistryImpl; + address public teeProverRegistryProxy; + address public teeVerifier; + address public zkVerifier; + address public delayedWethImpl; + address public delayedWethProxy; + address public aggregateVerifier; + address public optimismPortal2Impl; + address public disputeGameFactoryImpl; + address public anchorStateRegistryImpl; + + function setUp() public { + l1ProxyAdminEnv = vm.envAddress("L1_PROXY_ADMIN"); + anchorStateRegistryProxyEnv = vm.envAddress("ANCHOR_STATE_REGISTRY_PROXY"); + disputeGameFactoryProxyEnv = vm.envAddress("DISPUTE_GAME_FACTORY_PROXY"); + + gameTypeEnv = uint32(vm.envUint("GAME_TYPE")); + sp1VerifierEnv = vm.envAddress("SP1_VERIFIER"); + teeImageHashEnv = vm.envBytes32("TEE_IMAGE_HASH"); + zkRangeHashEnv = vm.envBytes32("ZK_RANGE_HASH"); + zkAggregateHashEnv = vm.envBytes32("ZK_AGGREGATE_HASH"); + configHashEnv = vm.envBytes32("CONFIG_HASH"); + l2ChainIdEnv = vm.envUint("L2_CHAIN_ID"); + blockIntervalEnv = vm.envUint("BLOCK_INTERVAL"); + intermediateBlockIntervalEnv = vm.envUint("INTERMEDIATE_BLOCK_INTERVAL"); + proofThresholdEnv = vm.envUint("PROOF_THRESHOLD"); + + proofMaturityDelaySecondsEnv = vm.envUint("PROOF_MATURITY_DELAY_SECONDS"); + disputeGameFinalityDelaySecondsEnv = vm.envUint("DISPUTE_GAME_FINALITY_DELAY_SECONDS"); + delayedWethDelaySecondsEnv = vm.envUint("DELAYED_WETH_DELAY_SECONDS"); + + systemConfigEnv = vm.envAddress("SYSTEM_CONFIG"); + teeProverRegistryOwnerEnv = vm.envAddress("TEE_PROVER_REGISTRY_OWNER"); + teeProverRegistryManagerEnv = vm.envAddress("TEE_PROVER_REGISTRY_MANAGER"); + proposerEnv = vm.envAddress("PROPOSER"); + challengerEnv = vm.envAddress("CHALLENGER"); + + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/addresses.json"); + string memory json = vm.readFile(path); + nitroEnclaveVerifier = vm.parseJsonAddress({json: json, key: ".nitroEnclaveVerifier"}); + } + + function run() external { + vm.startBroadcast(); + + teeProverRegistryImpl = address( + new TEEProverRegistry({ + nitroVerifier: INitroEnclaveVerifier(nitroEnclaveVerifier), + factory: IDisputeGameFactory(disputeGameFactoryProxyEnv) + }) + ); + + delayedWethImpl = address(new DelayedWETH({_delay: delayedWethDelaySecondsEnv})); + + { + address[] memory initialProposers = new address[](2); + initialProposers[0] = proposerEnv; + initialProposers[1] = challengerEnv; + + Proxy teeProxy = new Proxy(msg.sender); + teeProxy.upgradeToAndCall( + teeProverRegistryImpl, + abi.encodeCall( + TEEProverRegistry.initialize, + ( + teeProverRegistryOwnerEnv, + teeProverRegistryManagerEnv, + initialProposers, + GameType.wrap(gameTypeEnv) + ) + ) + ); + teeProxy.changeAdmin(l1ProxyAdminEnv); + teeProverRegistryProxy = address(teeProxy); + + Proxy wethProxy = new Proxy(msg.sender); + wethProxy.upgradeToAndCall( + delayedWethImpl, abi.encodeCall(DelayedWETH.initialize, (ISystemConfig(systemConfigEnv))) + ); + wethProxy.changeAdmin(l1ProxyAdminEnv); + delayedWethProxy = address(wethProxy); + } + + teeVerifier = address( + new TEEVerifier({ + teeProverRegistry: TEEProverRegistry(teeProverRegistryProxy), + anchorStateRegistry: IAnchorStateRegistry(anchorStateRegistryProxyEnv) + }) + ); + + zkVerifier = address( + new ZkVerifier({ + sp1Verifier: ISP1Verifier(sp1VerifierEnv), + anchorStateRegistry: IAnchorStateRegistry(anchorStateRegistryProxyEnv) + }) + ); + + aggregateVerifier = address( + new AggregateVerifier({ + gameType_: GameType.wrap(gameTypeEnv), + anchorStateRegistry_: IAnchorStateRegistry(anchorStateRegistryProxyEnv), + delayedWETH: IDelayedWETH(payable(delayedWethProxy)), + teeVerifier: TEEVerifier(teeVerifier), + zkVerifier: IVerifier(zkVerifier), + teeImageHash: teeImageHashEnv, + zkHashes: AggregateVerifier.ZkHashes({rangeHash: zkRangeHashEnv, aggregateHash: zkAggregateHashEnv}), + configHash: configHashEnv, + l2ChainId: l2ChainIdEnv, + blockInterval: blockIntervalEnv, + intermediateBlockInterval: intermediateBlockIntervalEnv, + proofThreshold: proofThresholdEnv + }) + ); + + optimismPortal2Impl = address(new OptimismPortal2({_proofMaturityDelaySeconds: proofMaturityDelaySecondsEnv})); + disputeGameFactoryImpl = address(new DisputeGameFactory()); + anchorStateRegistryImpl = + address(new AnchorStateRegistry({_disputeGameFinalityDelaySeconds: disputeGameFinalityDelaySecondsEnv})); + + vm.stopBroadcast(); + + _postCheck(); + _writeAddresses(); + } + + function _postCheck() internal { + _checkTeeProverRegistryImpl(); + _checkTeeProverRegistryProxy(); + _checkTeeVerifier(); + _checkZkVerifier(); + _checkDelayedWethImpl(); + _checkDelayedWethProxy(); + _checkAggregateVerifier(); + _checkUpgradeTargetImpls(); + } + + function _checkTeeProverRegistryImpl() internal view { + TEEProverRegistry impl = TEEProverRegistry(teeProverRegistryImpl); + + require(address(impl.NITRO_VERIFIER()) == nitroEnclaveVerifier, "registry nitro verifier mismatch"); + require(address(impl.DISPUTE_GAME_FACTORY()) == disputeGameFactoryProxyEnv, "registry dgf mismatch"); + } + + function _checkTeeProverRegistryProxy() internal { + vm.prank(l1ProxyAdminEnv); + require(IProxy(teeProverRegistryProxy).implementation() == teeProverRegistryImpl, "tee registry impl mismatch"); + + TEEProverRegistry registry = TEEProverRegistry(teeProverRegistryProxy); + require(registry.owner() == teeProverRegistryOwnerEnv, "tee registry owner mismatch"); + require(registry.manager() == teeProverRegistryManagerEnv, "tee registry manager mismatch"); + require(GameType.unwrap(registry.gameType()) == gameTypeEnv, "tee registry game type mismatch"); + require(registry.isValidProposer(proposerEnv), "tee registry proposer mismatch"); + require(registry.isValidProposer(challengerEnv), "tee registry challenger mismatch"); + } + + function _checkTeeVerifier() internal view { + require( + address(TEEVerifier(teeVerifier).TEE_PROVER_REGISTRY()) == teeProverRegistryProxy, + "tee verifier registry mismatch" + ); + require( + address(TEEVerifier(teeVerifier).ANCHOR_STATE_REGISTRY()) == anchorStateRegistryProxyEnv, + "tee verifier asr mismatch" + ); + } + + function _checkZkVerifier() internal view { + require(address(ZkVerifier(zkVerifier).SP1_VERIFIER()) == sp1VerifierEnv, "zk verifier sp1 mismatch"); + require( + address(ZkVerifier(zkVerifier).ANCHOR_STATE_REGISTRY()) == anchorStateRegistryProxyEnv, + "zk verifier asr mismatch" + ); + } + + function _checkDelayedWethImpl() internal view { + require( + DelayedWETH(payable(delayedWethImpl)).delay() == delayedWethDelaySecondsEnv, "delayed weth delay mismatch" + ); + } + + function _checkDelayedWethProxy() internal { + vm.prank(l1ProxyAdminEnv); + require(IProxy(delayedWethProxy).implementation() == delayedWethImpl, "delayed weth impl mismatch"); + require( + address(DelayedWETH(payable(delayedWethProxy)).systemConfig()) == systemConfigEnv, + "delayed weth systemConfig mismatch" + ); + } + + function _checkAggregateVerifier() internal view { + AggregateVerifier av = AggregateVerifier(aggregateVerifier); + + require(GameType.unwrap(av.gameType()) == gameTypeEnv, "aggregate game type mismatch"); + require(address(av.anchorStateRegistry()) == anchorStateRegistryProxyEnv, "aggregate asr mismatch"); + require(address(av.DISPUTE_GAME_FACTORY()) == disputeGameFactoryProxyEnv, "aggregate dgf mismatch"); + require(address(av.DELAYED_WETH()) == delayedWethProxy, "aggregate delayed weth mismatch"); + require(address(av.TEE_VERIFIER()) == teeVerifier, "aggregate tee verifier mismatch"); + require(address(av.ZK_VERIFIER()) == zkVerifier, "aggregate zk verifier mismatch"); + 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"); + require(av.CONFIG_HASH() == configHashEnv, "aggregate config hash mismatch"); + require(av.L2_CHAIN_ID() == l2ChainIdEnv, "aggregate l2 chain mismatch"); + require(av.BLOCK_INTERVAL() == blockIntervalEnv, "aggregate block interval mismatch"); + require(av.INTERMEDIATE_BLOCK_INTERVAL() == intermediateBlockIntervalEnv, "aggregate intermediate mismatch"); + require(av.PROOF_THRESHOLD() == proofThresholdEnv, "aggregate proof threshold mismatch"); + } + + function _checkUpgradeTargetImpls() internal view { + require( + OptimismPortal2(payable(optimismPortal2Impl)).proofMaturityDelaySeconds() == proofMaturityDelaySecondsEnv, + "portal delay mismatch" + ); + require( + AnchorStateRegistry(anchorStateRegistryImpl).disputeGameFinalityDelaySeconds() + == disputeGameFinalityDelaySecondsEnv, + "asr finality delay mismatch" + ); + } + + function _writeAddresses() internal { + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + console.log("TEEProverRegistry impl:", teeProverRegistryImpl); + console.log("TEEProverRegistry proxy:", teeProverRegistryProxy); + console.log("TEEVerifier:", teeVerifier); + console.log("ZKVerifier:", zkVerifier); + console.log("DelayedWETH impl:", delayedWethImpl); + console.log("DelayedWETH proxy:", delayedWethProxy); + console.log("AggregateVerifier:", aggregateVerifier); + console.log("OptimismPortal2 impl:", optimismPortal2Impl); + console.log("DisputeGameFactory impl:", disputeGameFactoryImpl); + console.log("AnchorStateRegistry impl:", anchorStateRegistryImpl); + + _writeAddress({key: "nitroEnclaveVerifier", value: nitroEnclaveVerifier}); + _writeAddress({key: "teeProverRegistryImpl", value: teeProverRegistryImpl}); + _writeAddress({key: "teeProverRegistryProxy", value: teeProverRegistryProxy}); + _writeAddress({key: "teeVerifier", value: teeVerifier}); + _writeAddress({key: "zkVerifier", value: zkVerifier}); + _writeAddress({key: "delayedWETHImpl", value: delayedWethImpl}); + _writeAddress({key: "delayedWETHProxy", value: delayedWethProxy}); + _writeAddress({key: "aggregateVerifier", value: aggregateVerifier}); + _writeAddress({key: "optimismPortal2Impl", value: optimismPortal2Impl}); + _writeAddress({key: "disputeGameFactoryImpl", value: disputeGameFactoryImpl}); + _writeAddress({key: "anchorStateRegistryImpl", value: anchorStateRegistryImpl}); + } + + function _writeAddress(string memory key, address value) internal { + vm.writeJson({json: vm.toString(value), path: "addresses.json", valueKey: string.concat(".", key)}); + } +} diff --git a/mainnet/2026-04-28-activate-multiproof/script/DeployNitroVerifier.s.sol b/mainnet/2026-04-28-activate-multiproof/script/DeployNitroVerifier.s.sol new file mode 100644 index 00000000..a0f9578e --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/script/DeployNitroVerifier.s.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Script, console} from "forge-std/Script.sol"; + +import { + INitroEnclaveVerifier, + ZkCoProcessorConfig, + ZkCoProcessorType +} from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; + +import {RiscZeroSetVerifier, RiscZeroSetVerifierLib} from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; +import {NitroEnclaveVerifier} from "@base-contracts/src/multiproof/tee/NitroEnclaveVerifier.sol"; + +contract DeployNitroVerifier is Script { + address internal teeProverRegistryOwnerEnv; + address internal nitroRevokerEnv; + uint64 internal nitroInitialMaxTimeDiffSecondsEnv; + bytes32 internal nitroInitialRootCertEnv; + address internal riscZeroVerifierRouterEnv; + address internal riscZeroSetVerifierEnv; + bytes32 internal riscZeroSetBuilderImageIdEnv; + bytes32 internal nitroZkVerifierIdEnv; + bytes32 internal nitroVerifierProofIdEnv; + + address public riscZeroSetVerifier; + address public nitroEnclaveVerifier; + + function setUp() public { + teeProverRegistryOwnerEnv = vm.envAddress("TEE_PROVER_REGISTRY_OWNER"); + nitroRevokerEnv = vm.envAddress("NITRO_REVOKER"); + nitroInitialMaxTimeDiffSecondsEnv = uint64(vm.envUint("NITRO_INITIAL_MAX_TIME_DIFF_SECONDS")); + nitroInitialRootCertEnv = vm.envBytes32("NITRO_INITIAL_ROOT_CERT"); + riscZeroVerifierRouterEnv = vm.envAddress("RISC0_VERIFIER_ROUTER"); + riscZeroSetVerifierEnv = vm.envAddress("RISC0_SET_VERIFIER"); + riscZeroSetBuilderImageIdEnv = vm.envBytes32("RISC0_SET_BUILDER_IMAGE_ID"); + nitroZkVerifierIdEnv = vm.envBytes32("NITRO_ZK_VERIFIER_ID"); + nitroVerifierProofIdEnv = vm.envBytes32("NITRO_VERIFIER_PROOF_ID"); + } + + function run() external { + bytes32[] memory trustedCerts = new bytes32[](0); + uint64[] memory trustedCertExpiries = new uint64[](0); + riscZeroSetVerifier = riscZeroSetVerifierEnv; + + vm.startBroadcast(); + + nitroEnclaveVerifier = address( + new NitroEnclaveVerifier({ + owner: msg.sender, + initialMaxTimeDiff: nitroInitialMaxTimeDiffSecondsEnv, + initializeTrustedCerts: trustedCerts, + initializeTrustedCertExpiries: trustedCertExpiries, + initialRootCert: nitroInitialRootCertEnv, + initialProofSubmitter: teeProverRegistryOwnerEnv, + initialRevoker: nitroRevokerEnv, + zkCoProcessor: ZkCoProcessorType.RiscZero, + config: ZkCoProcessorConfig({ + verifierId: nitroZkVerifierIdEnv, aggregatorId: bytes32(0), zkVerifier: riscZeroVerifierRouterEnv + }), + verifierProofId: nitroVerifierProofIdEnv + }) + ); + + NitroEnclaveVerifier(nitroEnclaveVerifier) + .addVerifyRoute( + ZkCoProcessorType.RiscZero, + RiscZeroSetVerifierLib.selector(riscZeroSetBuilderImageIdEnv), + riscZeroSetVerifier + ); + + vm.stopBroadcast(); + + _postCheck(); + _writeAddresses(); + } + + function _postCheck() internal view { + _checkRiscZeroSetVerifier(); + _checkNitroEnclaveVerifier(); + } + + function _checkRiscZeroSetVerifier() internal view { + RiscZeroSetVerifier setVerifier = RiscZeroSetVerifier(riscZeroSetVerifier); + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(riscZeroSetBuilderImageIdEnv); + + require(address(setVerifier.VERIFIER()) == riscZeroVerifierRouterEnv, "set verifier router mismatch"); + require(setVerifier.SELECTOR() == setVerifierSelector, "set verifier selector mismatch"); + } + + function _checkNitroEnclaveVerifier() internal view { + NitroEnclaveVerifier nev = NitroEnclaveVerifier(nitroEnclaveVerifier); + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(riscZeroSetBuilderImageIdEnv); + + require(nev.maxTimeDiff() == nitroInitialMaxTimeDiffSecondsEnv, "nitro max time diff mismatch"); + require(nev.rootCert() == nitroInitialRootCertEnv, "nitro root cert mismatch"); + require(nev.proofSubmitter() == teeProverRegistryOwnerEnv, "nitro placeholder submitter mismatch"); + require(nev.revoker() == nitroRevokerEnv, "nitro revoker mismatch"); + + ZkCoProcessorConfig memory cfg = nev.getZkConfig(ZkCoProcessorType.RiscZero); + require(cfg.verifierId == nitroZkVerifierIdEnv, "nitro verifier id mismatch"); + require(cfg.aggregatorId == bytes32(0), "nitro aggregator id mismatch"); + require(cfg.zkVerifier == riscZeroVerifierRouterEnv, "nitro router mismatch"); + require( + nev.getVerifierProofId(ZkCoProcessorType.RiscZero) == nitroVerifierProofIdEnv, + "nitro verifier proof id mismatch" + ); + require( + INitroEnclaveVerifier(nitroEnclaveVerifier).getZkVerifier(ZkCoProcessorType.RiscZero, setVerifierSelector) + == riscZeroSetVerifier, + "nitro set-verifier route mismatch" + ); + } + + function _writeAddresses() internal { + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + + string memory root = "root"; + string memory json = + vm.serializeAddress({objectKey: root, valueKey: "nitroEnclaveVerifier", value: nitroEnclaveVerifier}); + vm.writeJson({json: json, path: "addresses.json"}); + } +} diff --git a/mainnet/2026-04-28-activate-multiproof/script/SetupNitroEnclaveVerifier.s.sol b/mainnet/2026-04-28-activate-multiproof/script/SetupNitroEnclaveVerifier.s.sol new file mode 100644 index 00000000..dc192800 --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/script/SetupNitroEnclaveVerifier.s.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Script} from "forge-std/Script.sol"; + +import {INitroEnclaveVerifier, ZkCoProcessorType} from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; + +import {RiscZeroSetVerifierLib} from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; +import {NitroEnclaveVerifier} from "@base-contracts/src/multiproof/tee/NitroEnclaveVerifier.sol"; + +contract SetupNitroEnclaveVerifier is Script { + address internal teeProverRegistryOwnerEnv; + bytes32 internal riscZeroSetBuilderImageIdEnv; + address internal riscZeroSetVerifierEnv; + + address internal nitroEnclaveVerifier; + address internal teeProverRegistryProxy; + + function setUp() public { + teeProverRegistryOwnerEnv = vm.envAddress("TEE_PROVER_REGISTRY_OWNER"); + riscZeroSetBuilderImageIdEnv = vm.envBytes32("RISC0_SET_BUILDER_IMAGE_ID"); + riscZeroSetVerifierEnv = vm.envAddress("RISC0_SET_VERIFIER"); + + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/addresses.json"); + string memory json = vm.readFile(path); + + nitroEnclaveVerifier = vm.parseJsonAddress({json: json, key: ".nitroEnclaveVerifier"}); + teeProverRegistryProxy = vm.parseJsonAddress({json: json, key: ".teeProverRegistryProxy"}); + } + + function run() external { + vm.startBroadcast(); + + NitroEnclaveVerifier(nitroEnclaveVerifier).setProofSubmitter(teeProverRegistryProxy); + NitroEnclaveVerifier(nitroEnclaveVerifier).transferOwnership(teeProverRegistryOwnerEnv); + + vm.stopBroadcast(); + + _postCheck(); + } + + function _postCheck() internal view { + NitroEnclaveVerifier nev = NitroEnclaveVerifier(nitroEnclaveVerifier); + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(riscZeroSetBuilderImageIdEnv); + + require(nev.owner() == teeProverRegistryOwnerEnv, "nitro owner mismatch"); + require(nev.proofSubmitter() == teeProverRegistryProxy, "nitro proof submitter mismatch"); + require( + INitroEnclaveVerifier(nitroEnclaveVerifier).getZkVerifier(ZkCoProcessorType.RiscZero, setVerifierSelector) + == riscZeroSetVerifierEnv, + "nitro set-verifier route mismatch" + ); + } +} diff --git a/mainnet/2026-04-28-activate-multiproof/validations/.gitkeep b/mainnet/2026-04-28-activate-multiproof/validations/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/mainnet/2026-04-28-activate-multiproof/validations/.gitkeep @@ -0,0 +1 @@ + diff --git a/mainnet/signatures/2026-04-28-activate-multiproof/.gitkeep b/mainnet/signatures/2026-04-28-activate-multiproof/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/mainnet/signatures/2026-04-28-activate-multiproof/.gitkeep @@ -0,0 +1 @@ +