From 70b7dc7c7777a9b5b8b7e8b6362015c394a5456b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 28 Apr 2026 21:14:01 -0400 Subject: [PATCH 001/146] feat: add TDX verifier and TDX-aware TEE prover registry Adds production-shape Intel TDX DCAP signer-registration path for multiproof. The heavy attestation work happens off-chain in a ZK guest (RISC Zero or SP1); Solidity verifies the proof and enforces on-chain policy (trusted Intel root, TCB status, collateral expiry, quote freshness, signer derivation, REPORTDATA public-key binding) before registering the signer via TDXTEEProverRegistry. --- interfaces/multiproof/tee/ITDXVerifier.sol | 89 ++ scripts/multiproof/README.md | 61 +- snapshots/abi/TDXTEEProverRegistry.json | 556 ++++++++++++ snapshots/abi/TDXVerifier.json | 808 ++++++++++++++++++ snapshots/semver-lock.json | 10 +- .../storageLayout/TDXTEEProverRegistry.json | 79 ++ snapshots/storageLayout/TDXVerifier.json | 37 + src/multiproof/tee/TDXTEEProverRegistry.sol | 66 ++ src/multiproof/tee/TDXVerifier.sol | 263 ++++++ test/multiproof/TDXTEEProverRegistry.t.sol | 114 +++ test/multiproof/TDXVerifier.t.sol | 277 ++++++ 11 files changed, 2349 insertions(+), 11 deletions(-) create mode 100644 interfaces/multiproof/tee/ITDXVerifier.sol create mode 100644 snapshots/abi/TDXTEEProverRegistry.json create mode 100644 snapshots/abi/TDXVerifier.json create mode 100644 snapshots/storageLayout/TDXTEEProverRegistry.json create mode 100644 snapshots/storageLayout/TDXVerifier.json create mode 100644 src/multiproof/tee/TDXTEEProverRegistry.sol create mode 100644 src/multiproof/tee/TDXVerifier.sol create mode 100644 test/multiproof/TDXTEEProverRegistry.t.sol create mode 100644 test/multiproof/TDXVerifier.t.sol diff --git a/interfaces/multiproof/tee/ITDXVerifier.sol b/interfaces/multiproof/tee/ITDXVerifier.sol new file mode 100644 index 000000000..d0eb7f3ad --- /dev/null +++ b/interfaces/multiproof/tee/ITDXVerifier.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ZkCoProcessorType, ZkCoProcessorConfig } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; + +/// @notice Statuses that a TDX quote/collateral verifier may emit. +/// @dev Unknown is index 0 so uninitialized values fail closed. +enum TDXVerificationResult { + Unknown, + Success, + InvalidQuote, + QuoteSignatureInvalid, + RootCaNotTrusted, + PckCertChainInvalid, + TcbInfoInvalid, + QeIdentityInvalid, + TcbStatusNotAllowed, + CollateralExpired, + InvalidTimestamp, + ReportDataMismatch +} + +/// @notice Intel TDX TCB status reduced to the statuses this contract's policy needs. +/// @dev Unknown is index 0 so uninitialized values fail closed. +enum TDXTcbStatus { + Unknown, + UpToDate, + SwHardeningNeeded, + ConfigurationNeeded, + ConfigurationAndSwHardeningNeeded, + OutOfDate, + OutOfDateConfigurationNeeded, + Revoked +} + +/// @notice Public journal emitted by the off-chain/ZK TDX DCAP verifier. +/// @param result Overall verification result after quote and collateral validation in the guest. +/// @param tcbStatus Intel TDX TCB status for the platform. +/// @param timestamp Quote timestamp in milliseconds since Unix epoch. +/// @param collateralExpiration Earliest expiration timestamp in seconds across accepted collateral. +/// @param rootCaHash Hash of the Intel root CA used to validate the PCK/collateral signing chains. +/// @param pckCertHash Hash of the PCK leaf certificate that signed the quote attestation key chain. +/// @param tcbInfoHash Hash of the TCB info collateral consumed by the guest. +/// @param qeIdentityHash Hash of the QE identity collateral consumed by the guest. +/// @param publicKey Uncompressed secp256k1 public key: 0x04 || x || y. +/// @param signer Ethereum address derived from publicKey. +/// @param imageHash Multiproof-compatible image hash derived from MRTD and RTMR0-3. +/// @param mrTdHash keccak256 hash of the 48-byte MRTD measurement. +/// @param reportDataPrefix First 32 bytes of TDREPORT.REPORTDATA. +/// @param reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA, available for app-specific binding. +struct TDXVerifierJournal { + TDXVerificationResult result; + TDXTcbStatus tcbStatus; + uint64 timestamp; + uint64 collateralExpiration; + bytes32 rootCaHash; + bytes32 pckCertHash; + bytes32 tcbInfoHash; + bytes32 qeIdentityHash; + bytes publicKey; + address signer; + bytes32 imageHash; + bytes32 mrTdHash; + bytes32 reportDataPrefix; + bytes32 reportDataSuffix; +} + +/// @title ITDXVerifier +/// @notice Interface for Intel TDX quote verification used by TDX-aware TEE prover registries. +interface ITDXVerifier { + /// @notice Verifies a ZK proof of Intel TDX DCAP quote verification and returns attested signer metadata. + /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. + /// @param zkCoprocessor ZK proving system used to generate the proof. + /// @param proofBytes ZK proof bytes. + /// @return journal Verified TDX attestation metadata. + function verify( + bytes calldata output, + ZkCoProcessorType zkCoprocessor, + bytes calldata proofBytes + ) + external + returns (TDXVerifierJournal memory journal); + + /// @notice Retrieves the configuration for a specific coprocessor. + function getZkConfig(ZkCoProcessorType zkCoprocessor) external view returns (ZkCoProcessorConfig memory); + + /// @notice Returns whether a TCB status is accepted by verifier policy. + function allowedTcbStatuses(TDXTcbStatus status) external view returns (bool); +} diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 01155f444..de93d01ed 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -40,13 +40,13 @@ Ensure `finalSystemOwner` is set to the address you will deploy from (i.e. the a Other relevant fields: -| Field | Description | -|---|---| -| `teeProposer` | Address to be registered as the TEE proposer | -| `teeImageHash` | PCR0 hash used when registering the dev signer (use `bytes32(0x01...01)` for dev) | -| `multiproofGameType` | Game type ID for the dispute game | -| `multiproofGenesisOutputRoot` | Initial anchor output root | -| `multiproofGenesisBlockNumber` | Initial anchor L2 block number | +| Field | Description | +| ------------------------------ | --------------------------------------------------------------------------------- | +| `teeProposer` | Address to be registered as the TEE proposer | +| `teeImageHash` | PCR0 hash used when registering the dev signer (use `bytes32(0x01...01)` for dev) | +| `multiproofGameType` | Game type ID for the dispute game | +| `multiproofGenesisOutputRoot` | Initial anchor output root | +| `multiproofGenesisBlockNumber` | Initial anchor L2 block number | ### Step 2: Deploy contracts @@ -124,6 +124,47 @@ The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` --- +## Path 3: TDX (Production-Path PoC) + +The TDX path follows the same split as Nitro: expensive attestation verification happens off-chain in a ZK guest, +and Solidity verifies the proof plus the on-chain acceptance policy before registering the signer. + +| Contract | Purpose | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TDXVerifier` | Verifies a RISC Zero or SP1 proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | +| `TDXTEEProverRegistry` | Extends `TEEProverRegistry` with `registerTDXSigner(bytes output, ZkCoProcessorType zkCoprocessor, bytes proofBytes)`, storing the signer address and TDX image hash in the same registry fields used by `TEEVerifier`. | + +The ZK verifier guest is expected to perform the full Intel DCAP verification path: + +```text +TD Quote signature +PCK certificate chain +TCB info signing chain and TCB status +QE identity signing chain +CRLs/revocation state +TDREPORT field extraction +``` + +The Solidity verifier then enforces local policy over the proven journal. The PoC maps TDX measurements into the existing multiproof `TEE_IMAGE_HASH` field as: + +```text +keccak256(MRTD || RTMR0 || RTMR1 || RTMR2 || RTMR3) +``` + +The attested public key must be supplied as an uncompressed 65-byte secp256k1 public key: + +```text +0x04 || x || y +``` + +The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 bytes. The last 32 bytes are returned by the verifier as app-specific binding data and emitted by the registry. + +`TEEVerifier` is still the proposal-proof verifier. TDX only changes signer registration: once a TDX signer is registered, proposal proofs use the existing `proposer + signature` proof bytes and the same `signerImageHash` check as Nitro. + +> **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining off-chain piece is the actual RISC Zero/SP1 TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. + +--- + ## Pre-Seeding Games (Post-Deployment) After deploying via either path, you can pre-seed the `DisputeGameFactory` with a chain of `AggregateVerifier` games. This is useful for testing forward traversal at proposer restart — the proposer can walk the linked list of games to find where to resume. @@ -184,9 +225,9 @@ forge script scripts/multiproof/SeedGames.s.sol \ Optional env vars: -| Variable | Default | Description | -|---|---|---| -| `GAME_COUNT` | 500 | Number of games to create | +| Variable | Default | Description | +| ------------ | ------------ | ----------------------------- | +| `GAME_COUNT` | 500 | Number of games to create | | `ROOTS_FILE` | `roots.json` | Path to the output roots JSON | ### Step 5: Verify on-chain diff --git a/snapshots/abi/TDXTEEProverRegistry.json b/snapshots/abi/TDXTEEProverRegistry.json new file mode 100644 index 000000000..51e55f31a --- /dev/null +++ b/snapshots/abi/TDXTEEProverRegistry.json @@ -0,0 +1,556 @@ +[ + { + "inputs": [ + { + "internalType": "contract INitroEnclaveVerifier", + "name": "nitroVerifier", + "type": "address" + }, + { + "internalType": "contract ITDXVerifier", + "name": "tdxVerifier", + "type": "address" + }, + { + "internalType": "contract IDisputeGameFactory", + "name": "factory", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "DISPUTE_GAME_FACTORY", + "outputs": [ + { + "internalType": "contract IDisputeGameFactory", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_AGE", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "NITRO_VERIFIER", + "outputs": [ + { + "internalType": "contract INitroEnclaveVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TDX_VERIFIER", + "outputs": [ + { + "internalType": "contract ITDXVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "deregisterSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "gameType", + "outputs": [ + { + "internalType": "GameType", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExpectedImageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getRegisteredSigners", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "initialOwner", + "type": "address" + }, + { + "internalType": "address", + "name": "initialManager", + "type": "address" + }, + { + "internalType": "address[]", + "name": "initialProposers", + "type": "address[]" + }, + { + "internalType": "GameType", + "name": "gameType_", + "type": "uint32" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isRegisteredSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "isValidProposer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "isValidSigner", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "manager", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "output", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "proofBytes", + "type": "bytes" + } + ], + "name": "registerSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "output", + "type": "bytes" + }, + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "proofBytes", + "type": "bytes" + } + ], + "name": "registerTDXSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceManagement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "GameType", + "name": "gameType_", + "type": "uint32" + } + ], + "name": "setGameType", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "internalType": "bool", + "name": "isValid", + "type": "bool" + } + ], + "name": "setProposer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "signerImageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newManager", + "type": "address" + } + ], + "name": "transferManagement", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "GameType", + "name": "gameType", + "type": "uint32" + } + ], + "name": "GameTypeUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousManager", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newManager", + "type": "address" + } + ], + "name": "ManagementTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proposer", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isValid", + "type": "bool" + } + ], + "name": "ProposerSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "SignerDeregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "SignerRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "reportDataSuffix", + "type": "bytes32" + } + ], + "name": "TDXSignerRegistered", + "type": "event" + }, + { + "inputs": [], + "name": "AttestationTooOld", + "type": "error" + }, + { + "inputs": [], + "name": "AttestationVerificationFailed", + "type": "error" + }, + { + "inputs": [], + "name": "DisputeGameFactoryNotSet", + "type": "error" + }, + { + "inputs": [], + "name": "ImageHashReadFailed", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidGameType", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPublicKey", + "type": "error" + }, + { + "inputs": [], + "name": "PCR0NotFound", + "type": "error" + }, + { + "inputs": [], + "name": "TDXVerifierNotSet", + "type": "error" + } +] \ No newline at end of file diff --git a/snapshots/abi/TDXVerifier.json b/snapshots/abi/TDXVerifier.json new file mode 100644 index 000000000..bda2cd07b --- /dev/null +++ b/snapshots/abi/TDXVerifier.json @@ -0,0 +1,808 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint64", + "name": "initialMaxTimeDiff", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "initialRootCaHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "initialProofSubmitter", + "type": "address" + }, + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "internalType": "struct ZkCoProcessorConfig", + "name": "config", + "type": "tuple" + }, + { + "internalType": "enum TDXTcbStatus[]", + "name": "initialAllowedTcbStatuses", + "type": "uint8[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "enum TDXTcbStatus", + "name": "", + "type": "uint8" + } + ], + "name": "allowedTcbStatuses", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cancelOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "completeOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + } + ], + "name": "getZkConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "internalType": "struct ZkCoProcessorConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxTimeDiff", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "result", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "ownershipHandoverExpiresAt", + "outputs": [ + { + "internalType": "uint256", + "name": "result", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofSubmitter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "requestOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "rootCaHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "newMaxTimeDiff", + "type": "uint64" + } + ], + "name": "setMaxTimeDiff", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newProofSubmitter", + "type": "address" + } + ], + "name": "setProofSubmitter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "newRootCaHash", + "type": "bytes32" + } + ], + "name": "setRootCaHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum TDXTcbStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setTcbStatusAllowed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "internalType": "struct ZkCoProcessorConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "setZkConfiguration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "output", + "type": "bytes" + }, + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "proofBytes", + "type": "bytes" + } + ], + "name": "verify", + "outputs": [ + { + "components": [ + { + "internalType": "enum TDXVerificationResult", + "name": "result", + "type": "uint8" + }, + { + "internalType": "enum TDXTcbStatus", + "name": "tcbStatus", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "collateralExpiration", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "rootCaHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "pckCertHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "tcbInfoHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "qeIdentityHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "mrTdHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "reportDataPrefix", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "reportDataSuffix", + "type": "bytes32" + } + ], + "internalType": "struct TDXVerifierJournal", + "name": "journal", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "", + "type": "uint8" + } + ], + "name": "zkConfig", + "outputs": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "enum TDXTcbStatus", + "name": "tcbStatus", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pckCertHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "tcbInfoHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "qeIdentityHash", + "type": "bytes32" + } + ], + "name": "AttestationSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "maxTimeDiff", + "type": "uint64" + } + ], + "name": "MaxTimeDiffUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "OwnershipHandoverCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "OwnershipHandoverRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proofSubmitter", + "type": "address" + } + ], + "name": "ProofSubmitterChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "rootCaHash", + "type": "bytes32" + } + ], + "name": "RootCaHashUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum TDXTcbStatus", + "name": "status", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "TcbStatusPolicyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct ZkCoProcessorConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "ZKConfigurationUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "CallerNotProofSubmitter", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "collateralExpiration", + "type": "uint64" + } + ], + "name": "CollateralExpired", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPublicKey", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "timestampSeconds", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "currentTimestamp", + "type": "uint256" + } + ], + "name": "InvalidTimestamp", + "type": "error" + }, + { + "inputs": [], + "name": "NewOwnerIsZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "NoHandoverRequest", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actual", + "type": "bytes32" + } + ], + "name": "ReportDataMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actual", + "type": "bytes32" + } + ], + "name": "RootCaHashMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "expected", + "type": "address" + }, + { + "internalType": "address", + "name": "actual", + "type": "address" + } + ], + "name": "SignerMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum TDXVerificationResult", + "name": "result", + "type": "uint8" + } + ], + "name": "TDXVerificationFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum TDXTcbStatus", + "name": "status", + "type": "uint8" + } + ], + "name": "TcbStatusNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "UnknownZkCoprocessor", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroMaxTimeDiff", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroProofSubmitter", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroRootCaHash", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + } + ], + "name": "ZkVerifierNotConfigured", + "type": "error" + } +] \ No newline at end of file diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index c7ce7ce69..caec4886c 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -239,6 +239,14 @@ "initCodeHash": "0x100364f9b0c63a61538386ba91e73dcffba22d3f6dfe8efdbbf5ff347b6fce47", "sourceCodeHash": "0xed5d92ee1f32f2fde5e89ba278f3142c8113c774f6ce7bd831781a8abe7bddf3" }, + "src/multiproof/tee/TDXTEEProverRegistry.sol:TDXTEEProverRegistry": { + "initCodeHash": "0x266f220204f2c62f341b700bdb1e9bbda8c6489ced1e14d17b4ffe7dd33b1d8b", + "sourceCodeHash": "0x90bf694159e3f47e8f42aeaaa856947bb493acd5f64bf14fe277718dc62b4463" + }, + "src/multiproof/tee/TDXVerifier.sol:TDXVerifier": { + "initCodeHash": "0x0f511b2f67bd82ff976935ca0a178c3fa9d9cf9b89c6e67b3fd7f1006f5e5147", + "sourceCodeHash": "0x7cdf2abefaefa3126960103aa810699973c1c47147114f8a92e63c427b9b1656" + }, "src/multiproof/tee/TEEProverRegistry.sol:TEEProverRegistry": { "initCodeHash": "0xfd1942e1c2f59b0aa72b33d698a948a53b6e4cf1040106f173fb5d89f63f57b0", "sourceCodeHash": "0xf1ec1f02f540da659a204b26acf986fdce7d7d63bba67a87923f52453fb92ccb" @@ -299,4 +307,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} +} \ No newline at end of file diff --git a/snapshots/storageLayout/TDXTEEProverRegistry.json b/snapshots/storageLayout/TDXTEEProverRegistry.json new file mode 100644 index 000000000..93dcbfff3 --- /dev/null +++ b/snapshots/storageLayout/TDXTEEProverRegistry.json @@ -0,0 +1,79 @@ +[ + { + "bytes": "1", + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "uint8" + }, + { + "bytes": "1", + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "bool" + }, + { + "bytes": "1600", + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "uint256[50]" + }, + { + "bytes": "20", + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "address" + }, + { + "bytes": "20", + "label": "_manager", + "offset": 0, + "slot": "52", + "type": "address" + }, + { + "bytes": "1536", + "label": "__gap", + "offset": 0, + "slot": "53", + "type": "uint256[48]" + }, + { + "bytes": "4", + "label": "gameType", + "offset": 0, + "slot": "101", + "type": "GameType" + }, + { + "bytes": "32", + "label": "isRegisteredSigner", + "offset": 0, + "slot": "102", + "type": "mapping(address => bool)" + }, + { + "bytes": "32", + "label": "signerImageHash", + "offset": 0, + "slot": "103", + "type": "mapping(address => bytes32)" + }, + { + "bytes": "32", + "label": "isValidProposer", + "offset": 0, + "slot": "104", + "type": "mapping(address => bool)" + }, + { + "bytes": "32", + "label": "_registeredSigners", + "offset": 0, + "slot": "105", + "type": "struct EnumerableSetLib.AddressSet" + } +] \ No newline at end of file diff --git a/snapshots/storageLayout/TDXVerifier.json b/snapshots/storageLayout/TDXVerifier.json new file mode 100644 index 000000000..1a05d86c7 --- /dev/null +++ b/snapshots/storageLayout/TDXVerifier.json @@ -0,0 +1,37 @@ +[ + { + "bytes": "8", + "label": "maxTimeDiff", + "offset": 0, + "slot": "0", + "type": "uint64" + }, + { + "bytes": "32", + "label": "rootCaHash", + "offset": 0, + "slot": "1", + "type": "bytes32" + }, + { + "bytes": "20", + "label": "proofSubmitter", + "offset": 0, + "slot": "2", + "type": "address" + }, + { + "bytes": "32", + "label": "zkConfig", + "offset": 0, + "slot": "3", + "type": "mapping(enum ZkCoProcessorType => struct ZkCoProcessorConfig)" + }, + { + "bytes": "32", + "label": "allowedTcbStatuses", + "offset": 0, + "slot": "4", + "type": "mapping(enum TDXTcbStatus => bool)" + } +] \ No newline at end of file diff --git a/src/multiproof/tee/TDXTEEProverRegistry.sol b/src/multiproof/tee/TDXTEEProverRegistry.sol new file mode 100644 index 000000000..a4b05b63c --- /dev/null +++ b/src/multiproof/tee/TDXTEEProverRegistry.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { EnumerableSetLib } from "@solady-v0.0.245/utils/EnumerableSetLib.sol"; + +import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; +import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier, TDXVerifierJournal, ZkCoProcessorType } from "interfaces/multiproof/tee/ITDXVerifier.sol"; + +import { TEEProverRegistry } from "./TEEProverRegistry.sol"; + +/// @title TDXTEEProverRegistry +/// @notice TEE prover registry extension that can register signers from Intel TDX quotes. +/// @dev Nitro registration remains available through TEEProverRegistry.registerSigner(). This contract adds a +/// separate TDX path so existing Nitro deployments and verifier integrations do not need to change. +contract TDXTEEProverRegistry is TEEProverRegistry { + using EnumerableSetLib for EnumerableSetLib.AddressSet; + + /// @notice The TDX verifier used to validate ZK-proven TD quote verification journals. + ITDXVerifier public immutable TDX_VERIFIER; + + /// @notice Emitted when a TDX signer is registered. + event TDXSignerRegistered(address indexed signer, bytes32 indexed imageHash, bytes32 reportDataSuffix); + + /// @notice Thrown when the TDX verifier is not configured. + error TDXVerifierNotSet(); + + constructor( + INitroEnclaveVerifier nitroVerifier, + ITDXVerifier tdxVerifier, + IDisputeGameFactory factory + ) + TEEProverRegistry(nitroVerifier, factory) + { + if (address(tdxVerifier) == address(0)) revert TDXVerifierNotSet(); + TDX_VERIFIER = tdxVerifier; + } + + /// @notice Registers a signer using a ZK proof of Intel TDX DCAP quote verification. + /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. + /// @param zkCoprocessor ZK proving system used to generate the proof. + /// @param proofBytes ZK proof bytes. + function registerTDXSigner( + bytes calldata output, + ZkCoProcessorType zkCoprocessor, + bytes calldata proofBytes + ) + external + onlyOwnerOrManager + { + TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, zkCoprocessor, proofBytes); + + isRegisteredSigner[journal.signer] = true; + signerImageHash[journal.signer] = journal.imageHash; + _registeredSigners.add(journal.signer); + + emit SignerRegistered(journal.signer); + emit TDXSignerRegistered(journal.signer, journal.imageHash, journal.reportDataSuffix); + } + + /// @notice Semantic version. + /// @custom:semver 0.1.0 + function version() public pure override returns (string memory) { + return "0.1.0"; + } +} diff --git a/src/multiproof/tee/TDXVerifier.sol b/src/multiproof/tee/TDXVerifier.sol new file mode 100644 index 000000000..14ed8ea9c --- /dev/null +++ b/src/multiproof/tee/TDXVerifier.sol @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Ownable } from "@solady/auth/Ownable.sol"; +import { ISP1Verifier } from "lib/sp1-contracts/contracts/src/ISP1Verifier.sol"; +import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; + +import { ISemver } from "interfaces/universal/ISemver.sol"; +import { + ITDXVerifier, + TDXTcbStatus, + TDXVerificationResult, + TDXVerifierJournal, + ZkCoProcessorType, + ZkCoProcessorConfig +} from "interfaces/multiproof/tee/ITDXVerifier.sol"; + +/// @title TDXVerifier +/// @notice Production-shape Intel TDX DCAP verifier for multiproof signer registration. +/// @dev The heavy TDX work is expected to happen in a ZK guest: quote signature verification, PCK chain +/// validation, TCB info validation, QE identity validation, CRL checks, and extraction of TDREPORT fields. +/// This contract verifies the ZK proof and enforces on-chain policy over the verified journal. +contract TDXVerifier is Ownable, ITDXVerifier, ISemver { + /// @notice Conversion factor from milliseconds to seconds. + uint256 private constant MS_PER_SECOND = 1000; + + /// @notice Maximum accepted age of a TDX quote, in seconds. + uint64 public maxTimeDiff; + + /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. + bytes32 public rootCaHash; + + /// @notice Address authorized to submit TDX proofs, expected to be the TDX-aware registry. + address public proofSubmitter; + + /// @notice Configuration mapping for each supported ZK coprocessor type. + mapping(ZkCoProcessorType => ZkCoProcessorConfig) public zkConfig; + + /// @inheritdoc ITDXVerifier + mapping(TDXTcbStatus => bool) public allowedTcbStatuses; + + /// @notice Emitted when the trusted Intel root CA hash changes. + event RootCaHashUpdated(bytes32 indexed rootCaHash); + + /// @notice Emitted when the proof submitter changes. + event ProofSubmitterChanged(address indexed proofSubmitter); + + /// @notice Emitted when the quote timestamp tolerance changes. + event MaxTimeDiffUpdated(uint64 maxTimeDiff); + + /// @notice Emitted when a TCB status policy changes. + event TcbStatusPolicyUpdated(TDXTcbStatus indexed status, bool allowed); + + /// @notice Emitted when ZK configuration changes. + event ZKConfigurationUpdated(ZkCoProcessorType indexed zkCoprocessor, ZkCoProcessorConfig config); + + /// @notice Emitted after a TDX attestation journal is accepted. + event AttestationSubmitted( + address indexed signer, + bytes32 indexed imageHash, + TDXTcbStatus indexed tcbStatus, + bytes32 pckCertHash, + bytes32 tcbInfoHash, + bytes32 qeIdentityHash + ); + + /// @notice Thrown when a zero maxTimeDiff is provided. + error ZeroMaxTimeDiff(); + + /// @notice Thrown when a zero address is provided for the proof submitter. + error ZeroProofSubmitter(); + + /// @notice Thrown when a zero root CA hash is provided. + error ZeroRootCaHash(); + + /// @notice Thrown when the caller is not the configured proof submitter. + error CallerNotProofSubmitter(); + + /// @notice Thrown when the ZK coprocessor type is unknown. + error UnknownZkCoprocessor(); + + /// @notice Thrown when the configured ZK verifier address is zero. + error ZkVerifierNotConfigured(ZkCoProcessorType zkCoprocessor); + + /// @notice Thrown when the TDX verifier guest did not report success. + error TDXVerificationFailed(TDXVerificationResult result); + + /// @notice Thrown when the journal root does not match the trusted Intel root. + error RootCaHashMismatch(bytes32 expected, bytes32 actual); + + /// @notice Thrown when the journal's TCB status is not allowed. + error TcbStatusNotAllowed(TDXTcbStatus status); + + /// @notice Thrown when collateral consumed by the ZK guest is expired. + error CollateralExpired(uint64 collateralExpiration); + + /// @notice Thrown when the quote timestamp is outside policy. + error InvalidTimestamp(uint64 timestampSeconds, uint256 currentTimestamp); + + /// @notice Thrown when the public key is not an uncompressed secp256k1 public key. + error InvalidPublicKey(); + + /// @notice Thrown when the journal signer does not match the supplied public key. + error SignerMismatch(address expected, address actual); + + /// @notice Thrown when TDREPORT.REPORTDATA does not bind the supplied public key. + error ReportDataMismatch(bytes32 expected, bytes32 actual); + + constructor( + address owner, + uint64 initialMaxTimeDiff, + bytes32 initialRootCaHash, + address initialProofSubmitter, + ZkCoProcessorType zkCoprocessor, + ZkCoProcessorConfig memory config, + TDXTcbStatus[] memory initialAllowedTcbStatuses + ) { + _initializeOwner(owner); + _setMaxTimeDiff(initialMaxTimeDiff); + _setRootCaHash(initialRootCaHash); + _setProofSubmitter(initialProofSubmitter); + _setZkConfiguration(zkCoprocessor, config); + + for (uint256 i = 0; i < initialAllowedTcbStatuses.length; i++) { + _setTcbStatusAllowed(initialAllowedTcbStatuses[i], true); + } + } + + /// @inheritdoc ITDXVerifier + function getZkConfig(ZkCoProcessorType zkCoprocessor) external view returns (ZkCoProcessorConfig memory) { + return zkConfig[zkCoprocessor]; + } + + /// @notice Sets the trusted Intel root CA hash. + function setRootCaHash(bytes32 newRootCaHash) external onlyOwner { + _setRootCaHash(newRootCaHash); + } + + /// @notice Sets the proof submitter, expected to be the TDX-aware registry. + function setProofSubmitter(address newProofSubmitter) external onlyOwner { + _setProofSubmitter(newProofSubmitter); + } + + /// @notice Sets maximum allowed quote age, in seconds. + function setMaxTimeDiff(uint64 newMaxTimeDiff) external onlyOwner { + _setMaxTimeDiff(newMaxTimeDiff); + } + + /// @notice Sets whether a TDX TCB status is accepted. + function setTcbStatusAllowed(TDXTcbStatus status, bool allowed) external onlyOwner { + _setTcbStatusAllowed(status, allowed); + } + + /// @notice Configures a ZK verifier/program for a coprocessor. + function setZkConfiguration(ZkCoProcessorType zkCoprocessor, ZkCoProcessorConfig memory config) external onlyOwner { + _setZkConfiguration(zkCoprocessor, config); + } + + /// @inheritdoc ITDXVerifier + function verify( + bytes calldata output, + ZkCoProcessorType zkCoprocessor, + bytes calldata proofBytes + ) + external + returns (TDXVerifierJournal memory journal) + { + if (msg.sender != proofSubmitter) revert CallerNotProofSubmitter(); + + _verifyZk(zkCoprocessor, output, proofBytes); + journal = abi.decode(output, (TDXVerifierJournal)); + _verifyJournal(journal); + + emit AttestationSubmitted( + journal.signer, + journal.imageHash, + journal.tcbStatus, + journal.pckCertHash, + journal.tcbInfoHash, + journal.qeIdentityHash + ); + } + + /// @notice Semantic version. + /// @custom:semver 0.2.0 + function version() public pure virtual returns (string memory) { + return "0.2.0"; + } + + function _verifyJournal(TDXVerifierJournal memory journal) internal view { + if (journal.result != TDXVerificationResult.Success) revert TDXVerificationFailed(journal.result); + if (journal.rootCaHash != rootCaHash) revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); + if (!allowedTcbStatuses[journal.tcbStatus]) revert TcbStatusNotAllowed(journal.tcbStatus); + if (journal.collateralExpiration <= block.timestamp) revert CollateralExpired(journal.collateralExpiration); + + uint64 timestamp = journal.timestamp / uint64(MS_PER_SECOND); + if (timestamp + maxTimeDiff <= block.timestamp || timestamp >= block.timestamp) { + revert InvalidTimestamp(timestamp, block.timestamp); + } + + bytes32 publicKeyHash = _derivePublicKeyHash(journal.publicKey); + address signer = address(uint160(uint256(publicKeyHash))); + if (journal.signer != signer) revert SignerMismatch(signer, journal.signer); + if (journal.reportDataPrefix != publicKeyHash) { + revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); + } + } + + function _verifyZk( + ZkCoProcessorType zkCoprocessor, + bytes calldata output, + bytes calldata proofBytes + ) + internal + view + { + ZkCoProcessorConfig memory config = zkConfig[zkCoprocessor]; + if (config.zkVerifier == address(0)) revert ZkVerifierNotConfigured(zkCoprocessor); + + if (zkCoprocessor == ZkCoProcessorType.RiscZero) { + IRiscZeroVerifier(config.zkVerifier).verify(proofBytes, config.verifierId, sha256(output)); + } else if (zkCoprocessor == ZkCoProcessorType.Succinct) { + ISP1Verifier(config.zkVerifier).verifyProof(config.verifierId, output, proofBytes); + } else { + revert UnknownZkCoprocessor(); + } + } + + function _derivePublicKeyHash(bytes memory publicKey) internal pure returns (bytes32 publicKeyHash) { + if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); + assembly { + publicKeyHash := keccak256(add(publicKey, 0x21), 64) + } + } + + function _setRootCaHash(bytes32 newRootCaHash) internal { + if (newRootCaHash == bytes32(0)) revert ZeroRootCaHash(); + rootCaHash = newRootCaHash; + emit RootCaHashUpdated(newRootCaHash); + } + + function _setProofSubmitter(address newProofSubmitter) internal { + if (newProofSubmitter == address(0)) revert ZeroProofSubmitter(); + proofSubmitter = newProofSubmitter; + emit ProofSubmitterChanged(newProofSubmitter); + } + + function _setMaxTimeDiff(uint64 newMaxTimeDiff) internal { + if (newMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); + maxTimeDiff = newMaxTimeDiff; + emit MaxTimeDiffUpdated(newMaxTimeDiff); + } + + function _setTcbStatusAllowed(TDXTcbStatus status, bool allowed) internal { + allowedTcbStatuses[status] = allowed; + emit TcbStatusPolicyUpdated(status, allowed); + } + + function _setZkConfiguration(ZkCoProcessorType zkCoprocessor, ZkCoProcessorConfig memory config) internal { + zkConfig[zkCoprocessor] = config; + emit ZKConfigurationUpdated(zkCoprocessor, config); + } +} diff --git a/test/multiproof/TDXTEEProverRegistry.t.sol b/test/multiproof/TDXTEEProverRegistry.t.sol new file mode 100644 index 000000000..6e40c87a9 --- /dev/null +++ b/test/multiproof/TDXTEEProverRegistry.t.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { Test } from "forge-std/Test.sol"; + +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; +import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { + ITDXVerifier, + TDXTcbStatus, + TDXVerificationResult, + TDXVerifierJournal +} from "interfaces/multiproof/tee/ITDXVerifier.sol"; +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { GameType } from "src/dispute/lib/Types.sol"; + +import { TDXTEEProverRegistry } from "src/multiproof/tee/TDXTEEProverRegistry.sol"; + +/// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. +contract MockAggregateVerifierForTDXRegistry { + bytes32 public TEE_IMAGE_HASH; + + constructor(bytes32 imageHash) { + TEE_IMAGE_HASH = imageHash; + } +} + +/// @notice Mock DisputeGameFactory that returns a fixed game implementation. +contract MockDisputeGameFactoryForTDXRegistry { + mapping(uint32 => address) internal _impls; + + function setImpl(uint32 gameType, address impl) external { + _impls[gameType] = impl; + } + + function gameImpls(GameType gameType) external view returns (IDisputeGame) { + return IDisputeGame(_impls[GameType.unwrap(gameType)]); + } +} + +contract MockTDXVerifierForRegistry is ITDXVerifier { + TDXVerifierJournal internal _journal; + + function setJournal(TDXVerifierJournal memory journal) external { + _journal = journal; + } + + function verify( + bytes calldata, + ZkCoProcessorType, + bytes calldata + ) + external + view + returns (TDXVerifierJournal memory) + { + return _journal; + } + + function getZkConfig(ZkCoProcessorType) external pure returns (ZkCoProcessorConfig memory) { + return ZkCoProcessorConfig({ verifierId: bytes32(0), aggregatorId: bytes32(0), zkVerifier: address(0) }); + } + + function allowedTcbStatuses(TDXTcbStatus) external pure returns (bool) { + return true; + } +} + +contract TDXTEEProverRegistryTest is Test { + bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); + bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); + + function testRegisterTDXSignerStoresImageHash() public { + TDXVerifierJournal memory journal = _successJournal(); + MockTDXVerifierForRegistry verifier = new MockTDXVerifierForRegistry(); + verifier.setJournal(journal); + + MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); + factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); + + TDXTEEProverRegistry registry = new TDXTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(verifier)), IDisputeGameFactory(address(factory)) + ); + + vm.prank(address(0xdEaD)); + registry.registerTDXSigner("", ZkCoProcessorType.Succinct, ""); + + assertTrue(registry.isRegisteredSigner(journal.signer)); + assertEq(registry.signerImageHash(journal.signer), IMAGE_HASH); + assertTrue(registry.isValidSigner(journal.signer)); + } + + function _successJournal() internal pure returns (TDXVerifierJournal memory journal) { + address signer = address(0x1234); + + journal = TDXVerifierJournal({ + result: TDXVerificationResult.Success, + tcbStatus: TDXTcbStatus.UpToDate, + timestamp: 0, + collateralExpiration: 0, + rootCaHash: bytes32(0), + pckCertHash: bytes32(0), + tcbInfoHash: bytes32(0), + qeIdentityHash: bytes32(0), + publicKey: "", + signer: signer, + imageHash: IMAGE_HASH, + mrTdHash: bytes32(0), + reportDataPrefix: bytes32(0), + reportDataSuffix: REPORT_DATA_SUFFIX + }); + } +} diff --git a/test/multiproof/TDXVerifier.t.sol b/test/multiproof/TDXVerifier.t.sol new file mode 100644 index 000000000..291b34614 --- /dev/null +++ b/test/multiproof/TDXVerifier.t.sol @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; + +import { TDXTcbStatus, TDXVerificationResult, TDXVerifierJournal } from "interfaces/multiproof/tee/ITDXVerifier.sol"; +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; + +import { TDXVerifier } from "src/multiproof/tee/TDXVerifier.sol"; + +contract TDXVerifierTest is Test { + TDXVerifier internal verifier; + + address internal owner; + address internal proofSubmitter; + address internal mockRiscZeroVerifier; + address internal mockSP1Verifier; + + bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); + bytes32 internal constant WRONG_ROOT_CA_HASH = keccak256("wrong-root-ca"); + bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); + bytes32 internal constant AGGREGATOR_ID = keccak256("tdx-aggregator-id"); + bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); + bytes32 internal constant MRTD_HASH = keccak256("mrtd"); + bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); + + uint64 internal constant MAX_TIME_DIFF = 3600; + uint256 internal constant NOW = 1_700_000_000; + + function setUp() public { + vm.warp(NOW); + + owner = address(this); + proofSubmitter = address(this); + mockRiscZeroVerifier = makeAddr("mock-risc-zero"); + mockSP1Verifier = makeAddr("mock-sp1"); + + TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); + allowedStatuses[0] = TDXTcbStatus.UpToDate; + allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; + + verifier = new TDXVerifier( + owner, + MAX_TIME_DIFF, + ROOT_CA_HASH, + proofSubmitter, + ZkCoProcessorType.Succinct, + ZkCoProcessorConfig({ verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockSP1Verifier }), + allowedStatuses + ); + } + + function testVerifySucceedsWithSP1ProofAndAllowedJournal() public { + TDXVerifierJournal memory journal = _successJournal(); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + + assertEq(result.signer, journal.signer); + assertEq(result.imageHash, IMAGE_HASH); + assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.UpToDate)); + } + + function testVerifySucceedsWithRiscZeroProof() public { + ZkCoProcessorConfig memory config = ZkCoProcessorConfig({ + verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockRiscZeroVerifier + }); + verifier.setZkConfiguration(ZkCoProcessorType.RiscZero, config); + + TDXVerifierJournal memory journal = _successJournal(); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"5678"; + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + + TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); + + assertEq(result.signer, journal.signer); + assertEq(result.imageHash, IMAGE_HASH); + } + + function testVerifyRevertsIfNotProofSubmitter() public { + bytes memory output = abi.encode(_successJournal()); + + vm.prank(makeAddr("not-submitter")); + vm.expectRevert(TDXVerifier.CallerNotProofSubmitter.selector); + verifier.verify(output, ZkCoProcessorType.Succinct, ""); + } + + function testVerifyRevertsIfZkVerifierNotConfigured() public { + bytes memory output = abi.encode(_successJournal()); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.ZkVerifierNotConfigured.selector, ZkCoProcessorType.RiscZero) + ); + verifier.verify(output, ZkCoProcessorType.RiscZero, ""); + } + + function testVerifyRevertsForUnknownCoprocessor() public { + ZkCoProcessorConfig memory config = + ZkCoProcessorConfig({ verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockSP1Verifier }); + verifier.setZkConfiguration(ZkCoProcessorType.Unknown, config); + + bytes memory output = abi.encode(_successJournal()); + + vm.expectRevert(TDXVerifier.UnknownZkCoprocessor.selector); + verifier.verify(output, ZkCoProcessorType.Unknown, ""); + } + + function testVerifyRevertsWhenGuestReportsFailure() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.result = TDXVerificationResult.PckCertChainInvalid; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TDXVerificationFailed.selector, journal.result)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenRootCaHashMismatches() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.rootCaHash = WRONG_ROOT_CA_HASH; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, WRONG_ROOT_CA_HASH) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenTcbStatusIsNotAllowed() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TcbStatusNotAllowed.selector, journal.tcbStatus)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testOwnerCanAllowAdditionalTcbStatus() public { + verifier.setTcbStatusAllowed(TDXTcbStatus.ConfigurationNeeded, true); + + TDXVerifierJournal memory journal = _successJournal(); + journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + + assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.ConfigurationNeeded)); + } + + function testVerifyRevertsWhenCollateralExpired() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.collateralExpiration = uint64(block.timestamp); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.CollateralExpired.selector, journal.collateralExpiration)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenTimestampTooOld() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.timestamp = uint64(block.timestamp - MAX_TIME_DIFF) * 1000; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector( + TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp - MAX_TIME_DIFF), block.timestamp + ) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenTimestampIsFromFuture() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.timestamp = uint64(block.timestamp) * 1000; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp), block.timestamp) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenReportDataDoesNotBindPublicKey() public { + TDXVerifierJournal memory journal = _successJournal(); + bytes32 expected = journal.reportDataPrefix; + journal.reportDataPrefix = keccak256("wrong-report-data"); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, expected, journal.reportDataPrefix) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenSignerDoesNotMatchPublicKey() public { + TDXVerifierJournal memory journal = _successJournal(); + address expected = journal.signer; + journal.signer = makeAddr("wrong-signer"); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.SignerMismatch.selector, expected, journal.signer)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function _successJournal() internal view returns (TDXVerifierJournal memory journal) { + bytes memory publicKey = _publicKey(); + bytes32 publicKeyHash; + assembly { + publicKeyHash := keccak256(add(publicKey, 0x21), 64) + } + + journal = TDXVerifierJournal({ + result: TDXVerificationResult.Success, + tcbStatus: TDXTcbStatus.UpToDate, + timestamp: uint64(block.timestamp - 1) * 1000, + collateralExpiration: uint64(block.timestamp + 1 days), + rootCaHash: ROOT_CA_HASH, + pckCertHash: keccak256("pck-cert"), + tcbInfoHash: keccak256("tcb-info"), + qeIdentityHash: keccak256("qe-identity"), + publicKey: publicKey, + signer: address(uint160(uint256(publicKeyHash))), + imageHash: IMAGE_HASH, + mrTdHash: MRTD_HASH, + reportDataPrefix: publicKeyHash, + reportDataSuffix: REPORT_DATA_SUFFIX + }); + } + + function _publicKey() internal pure returns (bytes memory publicKey) { + publicKey = new bytes(65); + publicKey[0] = 0x04; + for (uint256 i = 1; i < publicKey.length; i++) { + publicKey[i] = bytes1(uint8(i)); + } + } + + function _mockRiscZeroVerify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { + vm.mockCall( + mockRiscZeroVerifier, + abi.encodeWithSelector( + bytes4(keccak256("verify(bytes,bytes32,bytes32)")), proofBytes, programId, sha256(output) + ), + "" + ); + } + + function _mockSP1Verify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { + vm.mockCall( + mockSP1Verifier, + abi.encodeWithSelector( + bytes4(keccak256("verifyProof(bytes32,bytes,bytes)")), programId, output, proofBytes + ), + "" + ); + } +} From 65bc3628c3c189e393be93b40a012ff42e269365 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 08:14:43 -0400 Subject: [PATCH 002/146] remove zkCoprocessor input --- src/multiproof/tee/TDXTEEProverRegistry.sol | 12 ++---------- test/multiproof/TDXTEEProverRegistry.t.sol | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/multiproof/tee/TDXTEEProverRegistry.sol b/src/multiproof/tee/TDXTEEProverRegistry.sol index a4b05b63c..5efff43b5 100644 --- a/src/multiproof/tee/TDXTEEProverRegistry.sol +++ b/src/multiproof/tee/TDXTEEProverRegistry.sol @@ -38,17 +38,9 @@ contract TDXTEEProverRegistry is TEEProverRegistry { /// @notice Registers a signer using a ZK proof of Intel TDX DCAP quote verification. /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. - /// @param zkCoprocessor ZK proving system used to generate the proof. /// @param proofBytes ZK proof bytes. - function registerTDXSigner( - bytes calldata output, - ZkCoProcessorType zkCoprocessor, - bytes calldata proofBytes - ) - external - onlyOwnerOrManager - { - TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, zkCoprocessor, proofBytes); + function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { + TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, ZkCoProcessorType.RiscZero, proofBytes); isRegisteredSigner[journal.signer] = true; signerImageHash[journal.signer] = journal.imageHash; diff --git a/test/multiproof/TDXTEEProverRegistry.t.sol b/test/multiproof/TDXTEEProverRegistry.t.sol index 6e40c87a9..1158c04e2 100644 --- a/test/multiproof/TDXTEEProverRegistry.t.sol +++ b/test/multiproof/TDXTEEProverRegistry.t.sol @@ -84,7 +84,7 @@ contract TDXTEEProverRegistryTest is Test { ); vm.prank(address(0xdEaD)); - registry.registerTDXSigner("", ZkCoProcessorType.Succinct, ""); + registry.registerTDXSigner("", ""); assertTrue(registry.isRegisteredSigner(journal.signer)); assertEq(registry.signerImageHash(journal.signer), IMAGE_HASH); From 1220336db8ec0e75331923b67615a54c9f311eef Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 13:31:50 -0400 Subject: [PATCH 003/146] test deployment and fix tests --- deploy-config/hardhat.json | 1 + deploy-config/sepolia.json | 7 +- scripts/deploy/Deploy.s.sol | 1 + scripts/deploy/DeployConfig.s.sol | 2 + scripts/deploy/DeployImplementations.s.sol | 7 +- scripts/multiproof/DeployDevNoNitro.s.sol | 13 +- scripts/multiproof/DeployDevWithNitro.s.sol | 11 +- scripts/multiproof/DeployDevWithTDX.s.sol | 217 +++++++ scripts/multiproof/DeployTDXVerifier.s.sol | 94 +++ scripts/multiproof/README.md | 78 ++- scripts/multiproof/justfile | 84 +++ snapshots/abi/DevTEEProverRegistry.json | 66 +++ snapshots/abi/TDXTEEProverRegistry.json | 556 ------------------ snapshots/abi/TEEProverRegistry.json | 66 +++ snapshots/semver-lock.json | 8 +- .../storageLayout/TDXTEEProverRegistry.json | 79 --- .../mocks/MockDevTEEProverRegistry.sol | 12 +- src/multiproof/tee/TDXTEEProverRegistry.sol | 58 -- src/multiproof/tee/TEEProverRegistry.sol | 49 +- test/multiproof/TEEProverRegistry.t.sol | 13 +- ...istry.t.sol => TEEProverRegistryTDX.t.sol} | 16 +- test/multiproof/TEEVerifier.t.sol | 6 +- test/opcm/DeployImplementations.t.sol | 7 + test/opcm/DeployOPChain.t.sol | 1 + 24 files changed, 718 insertions(+), 734 deletions(-) create mode 100644 scripts/multiproof/DeployDevWithTDX.s.sol create mode 100644 scripts/multiproof/DeployTDXVerifier.s.sol create mode 100644 scripts/multiproof/justfile delete mode 100644 snapshots/abi/TDXTEEProverRegistry.json delete mode 100644 snapshots/storageLayout/TDXTEEProverRegistry.json delete mode 100644 src/multiproof/tee/TDXTEEProverRegistry.sol rename test/multiproof/{TDXTEEProverRegistry.t.sol => TEEProverRegistryTDX.t.sol} (85%) diff --git a/deploy-config/hardhat.json b/deploy-config/hardhat.json index dd0bd375e..1c6ac22b4 100644 --- a/deploy-config/hardhat.json +++ b/deploy-config/hardhat.json @@ -79,6 +79,7 @@ "zkRangeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "zkAggregationHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nitroEnclaveVerifier": "0x0000000000000000000000000000000000000000", + "tdxVerifier": "0x0000000000000000000000000000000000000001", "multiproofGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", "multiproofGenesisBlockNumber": 0, "multiproofBlockInterval": 100, diff --git a/deploy-config/sepolia.json b/deploy-config/sepolia.json index 2e898f91f..c3f0767ee 100644 --- a/deploy-config/sepolia.json +++ b/deploy-config/sepolia.json @@ -1,5 +1,5 @@ { - "finalSystemOwner": "0x6e427c3212C0b63BE0C382F97715D49b011bFF33", + "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", "superchainConfigGuardian": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", "l1StartingBlockTag": "0x48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b", "l1ChainID": 11155111, @@ -63,7 +63,7 @@ "gasPayingTokenSymbol": "", "nativeAssetLiquidityAmount": null, "liquidityControllerOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "teeImageHash": "0xd04c8147410930fade36986c9357fd9a7591231e38e6e87e51183bd5909f509c", + "teeImageHash": "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6", "multiproofConfigHash": "0x12e9c45f19f9817c6d4385fad29e7a70c355502cf0883e76a9a7e478a85d1360", "multiproofGameType": 621, "teeProposer": "0xdb84125f2f4229c81c579f41bc129c71b174eb58", @@ -71,10 +71,11 @@ "zkRangeHash": "0x51708a6b4a3b800a14607e902c1aad47719905c12698a3e01ca8b3321761bc52", "zkAggregationHash": "0x005aa369dd9445e172018a9eaa4a0f9767b2c2079ece90ca120422b3c4c65f11", "nitroEnclaveVerifier": "0x77461a6434fFE3435206B19658F33274f3104e07", + "tdxVerifier": "0x5431a6Fd5365e9629BD2B0969e54c4f09d6781bA", "multiproofGenesisOutputRoot": "0xbc273d5876d1858ecd5aaf4ce4eaf16c73f0187ca4271b774ed5da7d2254ba79", "multiproofGenesisBlockNumber": 37223829, "multiproofBlockInterval": 600, "multiproofIntermediateBlockInterval": 30, - "risc0VerifierRouter": "0xb121b667dd2cf27f95f9f5107137696f56f188f6", + "risc0VerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", "risc0SetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9" } diff --git a/scripts/deploy/Deploy.s.sol b/scripts/deploy/Deploy.s.sol index 3dabdee77..4ab3bf10e 100644 --- a/scripts/deploy/Deploy.s.sol +++ b/scripts/deploy/Deploy.s.sol @@ -243,6 +243,7 @@ contract Deploy is Deployer { multiproofConfigHash: cfg.multiproofConfigHash(), multiproofGameType: cfg.multiproofGameType(), nitroEnclaveVerifier: cfg.nitroEnclaveVerifier(), + tdxVerifier: cfg.tdxVerifier(), l2ChainID: cfg.l2ChainID(), multiproofBlockInterval: cfg.multiproofBlockInterval(), multiproofIntermediateBlockInterval: cfg.multiproofIntermediateBlockInterval(), diff --git a/scripts/deploy/DeployConfig.s.sol b/scripts/deploy/DeployConfig.s.sol index 2b5822ddd..b7524192d 100644 --- a/scripts/deploy/DeployConfig.s.sol +++ b/scripts/deploy/DeployConfig.s.sol @@ -99,6 +99,7 @@ contract DeployConfig is Script { bytes32 public zkRangeHash; bytes32 public zkAggregationHash; address public nitroEnclaveVerifier; + address public tdxVerifier; bytes32 public multiproofGenesisOutputRoot; uint256 public multiproofGenesisBlockNumber; uint256 public multiproofBlockInterval; @@ -216,6 +217,7 @@ contract DeployConfig is Script { zkRangeHash = stdJson.readBytes32(_json, "$.zkRangeHash"); zkAggregationHash = stdJson.readBytes32(_json, "$.zkAggregationHash"); nitroEnclaveVerifier = _readOr(_json, "$.nitroEnclaveVerifier", address(0)); + tdxVerifier = _readOr(_json, "$.tdxVerifier", address(0)); multiproofGenesisOutputRoot = bytes32(_readOr(_json, "$.multiproofGenesisOutputRoot", uint256(1))); multiproofGenesisBlockNumber = _readOr(_json, "$.multiproofGenesisBlockNumber", 0); multiproofBlockInterval = _readOr(_json, "$.multiproofBlockInterval", 100); diff --git a/scripts/deploy/DeployImplementations.s.sol b/scripts/deploy/DeployImplementations.s.sol index 35c224091..2a304bed7 100644 --- a/scripts/deploy/DeployImplementations.s.sol +++ b/scripts/deploy/DeployImplementations.s.sol @@ -46,6 +46,7 @@ import { MockVerifier } from "src/multiproof/mocks/MockVerifier.sol"; import { TEEVerifier } from "src/multiproof/tee/TEEVerifier.sol"; import { AggregateVerifier } from "src/multiproof/AggregateVerifier.sol"; import { GameType } from "src/dispute/lib/Types.sol"; +import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; contract DeployImplementations is Script { struct Input { @@ -66,6 +67,7 @@ contract DeployImplementations is Script { bytes32 multiproofConfigHash; uint256 multiproofGameType; address nitroEnclaveVerifier; + address tdxVerifier; uint256 l2ChainID; uint256 multiproofBlockInterval; uint256 multiproofIntermediateBlockInterval; @@ -632,7 +634,9 @@ contract DeployImplementations is Script { address teeVerifierImpl; { TEEProverRegistry scgImpl = new TEEProverRegistry( - INitroEnclaveVerifier(_input.nitroEnclaveVerifier), IDisputeGameFactory(address(1)) + INitroEnclaveVerifier(_input.nitroEnclaveVerifier), + ITDXVerifier(_input.tdxVerifier), + IDisputeGameFactory(address(1)) ); vm.label(address(scgImpl), "TEEProverRegistryImpl"); _output.teeProverRegistryImpl = scgImpl; @@ -697,6 +701,7 @@ contract DeployImplementations is Script { "DeployImplementations: disputeGameFinalityDelaySeconds not set" ); require(_input.mipsVersion != 0, "DeployImplementations: mipsVersion not set"); + require(_input.tdxVerifier != address(0), "DeployImplementations: tdxVerifier not set"); require( address(_input.superchainConfigProxy) != address(0), "DeployImplementations: superchainConfigProxy not set" ); diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 13cfb48a0..92385240f 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -72,6 +72,7 @@ import { IVerifier } from "interfaces/multiproof/IVerifier.sol"; import { MockVerifier } from "src/multiproof/mocks/MockVerifier.sol"; import { DevTEEProverRegistry } from "src/multiproof/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/multiproof/tee/TEEProverRegistry.sol"; +import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; import { TEEVerifier } from "src/multiproof/tee/TEEVerifier.sol"; import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; @@ -89,6 +90,7 @@ contract DeployDevNoNitro is Script { DeployConfig public constant cfg = DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); + address public tdxVerifierAddr; address public teeProverRegistryProxy; address public teeVerifier; address public disputeGameFactory; @@ -99,6 +101,7 @@ contract DeployDevNoNitro is Script { function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); cfg.read(Config.deployConfigPath()); + tdxVerifierAddr = cfg.tdxVerifier(); } function run() public { @@ -110,8 +113,10 @@ contract DeployDevNoNitro is Script { console.log("TEE Proposer:", cfg.teeProposer()); console.log("TEE Challenger:", cfg.teeChallenger()); console.log("Game Type:", cfg.multiproofGameType()); + console.log("TDXVerifier:", tdxVerifierAddr); console.log(""); console.log("NOTE: Using DevTEEProverRegistry - NO attestation required."); + require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); vm.startBroadcast(); @@ -128,7 +133,11 @@ contract DeployDevNoNitro is Script { function _deployTEEContracts(GameType gameType) internal { address owner = cfg.finalSystemOwner(); address teeRegistryImpl = address( - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(disputeGameFactory)) + new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), + ITDXVerifier(tdxVerifierAddr), + IDisputeGameFactory(disputeGameFactory) + ) ); address[] memory initialProposers = new address[](2); initialProposers[0] = cfg.teeProposer(); @@ -197,6 +206,7 @@ contract DeployDevNoNitro is Script { console.log("========================================"); console.log("\nTEE Contracts:"); console.log(" DevTEEProverRegistry:", teeProverRegistryProxy); + console.log(" TDXVerifier:", tdxVerifierAddr); console.log(" TEEVerifier:", teeVerifier); console.log("\nInfrastructure:"); console.log(" DisputeGameFactory:", disputeGameFactory); @@ -219,6 +229,7 @@ contract DeployDevNoNitro is Script { function _writeOutput() internal { string memory key = "deployment"; vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); vm.serializeAddress(key, "TEEVerifier", teeVerifier); vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index ed34a3f5e..92ad2b17a 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -37,6 +37,7 @@ pragma solidity 0.8.15; */ import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; import { Proxy } from "src/universal/Proxy.sol"; import { Script } from "forge-std/Script.sol"; import { console2 as console } from "forge-std/console2.sol"; @@ -74,6 +75,7 @@ contract DeployDevWithNitro is Script { DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); address public nitroEnclaveVerifierAddr; + address public tdxVerifierAddr; address public teeProverRegistryProxy; address public teeVerifier; address public disputeGameFactory; @@ -91,10 +93,12 @@ contract DeployDevWithNitro is Script { // NitroEnclaveVerifier must be pre-deployed (via DeployRiscZeroStack.s.sol) nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); + tdxVerifierAddr = cfg.tdxVerifier(); require( nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be set in config (deploy via DeployRiscZeroStack.s.sol first)" ); + require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); console.log("=== Deploying Dev Infrastructure (WITH NITRO) ==="); console.log("Chain ID:", block.chainid); @@ -103,6 +107,7 @@ contract DeployDevWithNitro is Script { console.log("TEE Challenger:", cfg.teeChallenger()); console.log("Game Type:", cfg.multiproofGameType()); console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); + console.log("TDXVerifier:", tdxVerifierAddr); console.log(""); console.log("NOTE: Using REAL TEEProverRegistry - ZK attestation proof REQUIRED."); @@ -122,7 +127,9 @@ contract DeployDevWithNitro is Script { address owner = cfg.finalSystemOwner(); address teeRegistryImpl = address( new TEEProverRegistry( - INitroEnclaveVerifier(nitroEnclaveVerifierAddr), IDisputeGameFactory(disputeGameFactory) + INitroEnclaveVerifier(nitroEnclaveVerifierAddr), + ITDXVerifier(tdxVerifierAddr), + IDisputeGameFactory(disputeGameFactory) ) ); address[] memory initialProposers = new address[](2); @@ -192,6 +199,7 @@ contract DeployDevWithNitro is Script { console.log("========================================"); console.log("\nTEE Contracts:"); console.log(" NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); + console.log(" TDXVerifier:", tdxVerifierAddr); console.log(" TEEProverRegistry:", teeProverRegistryProxy); console.log(" TEEVerifier:", teeVerifier); console.log("\nInfrastructure:"); @@ -216,6 +224,7 @@ contract DeployDevWithNitro is Script { vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); vm.serializeAddress(key, "TEEVerifier", teeVerifier); vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol new file mode 100644 index 000000000..d7ecf6f0c --- /dev/null +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +/** + * @title DeployDevWithTDX + * @notice Development deployment using the TDX signer-registration path. + * + * This deploys the same local multiproof testing infrastructure as the existing + * dev scripts, but configures TEEProverRegistry for TDX signer registration. Deploy + * TDXVerifier first with DeployTDXVerifier.s.sol and pass its address to run(). + * The default run(address) entrypoint configures DEFAULT_TDX_REGISTRATION_MANAGER + * as the registry manager so it can submit TDX signer registrations. + */ + +import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; +import { Proxy } from "src/universal/Proxy.sol"; +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; +import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; +import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; +import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; +import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; +import { GameType, Hash } from "src/dispute/lib/Types.sol"; + +import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; +import { Config } from "scripts/libraries/Config.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +import { AggregateVerifier } from "src/multiproof/AggregateVerifier.sol"; +import { IVerifier } from "interfaces/multiproof/IVerifier.sol"; +import { MockVerifier } from "src/multiproof/mocks/MockVerifier.sol"; +import { TEEProverRegistry } from "src/multiproof/tee/TEEProverRegistry.sol"; +import { TEEVerifier } from "src/multiproof/tee/TEEVerifier.sol"; + +import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; +import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; +import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; + +interface IProofSubmitterSetter { + function setProofSubmitter(address newProofSubmitter) external; +} + +contract DeployDevWithTDX is Script { + uint256 public constant INIT_BOND = 0.00001 ether; + address public constant DEFAULT_TDX_REGISTRATION_MANAGER = 0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7; + + DeployConfig public constant cfg = + DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); + + address public tdxVerifierAddr; + address public tdxRegistrationManager; + address public teeProverRegistryProxy; + address public teeVerifier; + address public disputeGameFactory; + IAnchorStateRegistry public mockAnchorRegistry; + address public mockDelayedWETH; + address public aggregateVerifier; + + function setUp() public { + DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); + cfg.read(Config.deployConfigPath()); + } + + function run(address tdxVerifier) public { + run(tdxVerifier, DEFAULT_TDX_REGISTRATION_MANAGER); + } + + function run(address tdxVerifier, address registrationManager) public { + require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); + require(registrationManager != address(0), "registrationManager must be non-zero"); + tdxVerifierAddr = tdxVerifier; + tdxRegistrationManager = registrationManager; + + GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); + + console.log("=== Deploying Dev Infrastructure (WITH TDX) ==="); + console.log("Chain ID:", block.chainid); + console.log("Owner:", cfg.finalSystemOwner()); + console.log("TEE Proposer:", cfg.teeProposer()); + console.log("TEE Challenger:", cfg.teeChallenger()); + console.log("Game Type:", cfg.multiproofGameType()); + console.log("TDXVerifier:", tdxVerifierAddr); + console.log("TDX Registration Manager:", tdxRegistrationManager); + console.log(""); + console.log("NOTE: TDXVerifier owner must be the broadcaster/finalSystemOwner."); + + vm.startBroadcast(); + + _deployInfrastructure(gameType); + _deployTDXContracts(gameType); + _deployAggregateVerifier(gameType); + + vm.stopBroadcast(); + + _printSummary(); + _writeOutput(); + } + + function _deployTDXContracts(GameType gameType) internal { + address owner = cfg.finalSystemOwner(); + address registryImpl = address( + new TEEProverRegistry( + INitroEnclaveVerifier(address(0)), + ITDXVerifier(tdxVerifierAddr), + IDisputeGameFactory(disputeGameFactory) + ) + ); + + address[] memory initialProposers = new address[](2); + initialProposers[0] = cfg.teeProposer(); + initialProposers[1] = cfg.teeChallenger(); + + Proxy registryProxy = new Proxy(msg.sender); + registryProxy.upgradeToAndCall( + registryImpl, + abi.encodeCall(TEEProverRegistry.initialize, (owner, tdxRegistrationManager, initialProposers, gameType)) + ); + registryProxy.changeAdmin(address(0xdead)); + teeProverRegistryProxy = address(registryProxy); + + IProofSubmitterSetter(tdxVerifierAddr).setProofSubmitter(teeProverRegistryProxy); + + teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); + } + + function _deployInfrastructure(GameType gameType) internal { + address factoryImpl = address(new DisputeGameFactory()); + MinimalProxyAdmin proxyAdmin = new MinimalProxyAdmin(cfg.finalSystemOwner()); + + Proxy proxy = new Proxy(msg.sender); + proxy.upgradeTo(factoryImpl); + proxy.changeAdmin(address(proxyAdmin)); + DisputeGameFactory(address(proxy)).initialize(cfg.finalSystemOwner()); + disputeGameFactory = address(proxy); + + MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); + mockAnchorRegistry = IAnchorStateRegistry(address(asr)); + asr.initialize( + disputeGameFactory, + Hash.wrap(cfg.multiproofGenesisOutputRoot()), + cfg.multiproofGenesisBlockNumber(), + gameType + ); + } + + function _deployAggregateVerifier(GameType gameType) internal { + address zkVerifier = address(new MockVerifier(mockAnchorRegistry)); + mockDelayedWETH = address(new MockDelayedWETH()); + + AggregateVerifier.ZkHashes memory zkHashes = + AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }); + + aggregateVerifier = address( + new AggregateVerifier( + gameType, + mockAnchorRegistry, + IDelayedWETH(payable(mockDelayedWETH)), + IVerifier(teeVerifier), + IVerifier(zkVerifier), + cfg.teeImageHash(), + zkHashes, + cfg.multiproofConfigHash(), + cfg.l2ChainID(), + cfg.multiproofBlockInterval(), + cfg.multiproofIntermediateBlockInterval() + ) + ); + + DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); + factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); + factory.setInitBond(gameType, INIT_BOND); + } + + function _printSummary() internal view { + console.log("\n========================================"); + console.log(" DEV DEPLOYMENT COMPLETE (TDX)"); + console.log("========================================"); + console.log("\nTDX Contracts:"); + console.log(" TDXVerifier:", tdxVerifierAddr); + console.log(" TEEProverRegistry:", teeProverRegistryProxy); + console.log(" TDX Registration Manager:", tdxRegistrationManager); + console.log(" TEEVerifier:", teeVerifier); + console.log("\nInfrastructure:"); + console.log(" DisputeGameFactory:", disputeGameFactory); + console.log(" AnchorStateRegistry (mock):", address(mockAnchorRegistry)); + console.log(" DelayedWETH (mock):", mockDelayedWETH); + console.log("\nGame:"); + console.log(" AggregateVerifier:", aggregateVerifier); + console.log(" Game Type:", cfg.multiproofGameType()); + console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); + console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); + console.log("========================================"); + console.log("\n>>> NEXT STEP: Register TDX signer with a ZK-proven TDX journal <<<"); + console.log("\n cast send", teeProverRegistryProxy); + console.log(' "registerTDXSigner(bytes,bytes)" '); + console.log(" --private-key --rpc-url "); + console.log("\n========================================\n"); + } + + function _writeOutput() internal { + string memory key = "deployment"; + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); + vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); + vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); + vm.serializeAddress(key, "TEEVerifier", teeVerifier); + vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); + vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); + vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); + string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-dev-with-tdx.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol new file mode 100644 index 000000000..0b1ff6fcd --- /dev/null +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title DeployTDXVerifier + * @notice Deploys the Solidity TDX policy verifier used by TEEProverRegistry. + * + * This script is separated from DeployDevWithTDX.s.sol because TDXVerifier imports + * verifier interfaces that require Solidity ^0.8.20, while the multiproof stack is + * pinned to Solidity 0.8.15. + * + * Usage: + * + * forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ + * --sig "run(address,address,bytes32,bytes32)" \ + * \ + * --rpc-url --broadcast --private-key + * + * After running DeployDevWithTDX.s.sol, the TDX verifier's proofSubmitter is + * updated to the deployed TEEProverRegistry. + */ + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; + +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { TDXTcbStatus } from "interfaces/multiproof/tee/ITDXVerifier.sol"; +import { TDXVerifier } from "src/multiproof/tee/TDXVerifier.sol"; + +contract DeployTDXVerifier is Script { + /// @notice Maximum TDX quote age accepted by TDXVerifier. + uint64 public constant TDX_MAX_TIME_DIFF = 3600; + + address public tdxVerifier; + + /// @param owner Owner for TDXVerifier. + /// @param risc0VerifierRouter Existing RISC Zero verifier router. + /// @param tdxVerifierId RISC Zero image ID for the TDX DCAP verifier guest. + /// @param intelRootCaHash Hash of the trusted Intel root CA consumed by the guest. + function run(address owner, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) public { + require(owner != address(0), "owner must be non-zero"); + require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); + require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); + require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); + + TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); + allowedStatuses[0] = TDXTcbStatus.UpToDate; + allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: tdxVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + console.log("=== Deploying TDXVerifier ==="); + console.log("Owner:", owner); + console.log("RISC Zero Verifier Router:", risc0VerifierRouter); + console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); + console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); + console.log("Max Time Diff:", TDX_MAX_TIME_DIFF); + console.log(""); + console.log("NOTE: proofSubmitter is set to owner as a temporary placeholder."); + console.log(" DeployDevWithTDX.s.sol updates it to TEEProverRegistry."); + console.log(""); + + vm.startBroadcast(); + + tdxVerifier = address( + new TDXVerifier( + owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, ZkCoProcessorType.RiscZero, zkConfig, allowedStatuses + ) + ); + + vm.stopBroadcast(); + + console.log("TDXVerifier:", tdxVerifier); + console.log(""); + console.log(">>> Use this address as the DeployDevWithTDX.s.sol argument <<<"); + + _writeOutput(risc0VerifierRouter, tdxVerifierId, intelRootCaHash); + } + + function _writeOutput(address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) internal { + string memory key = "deployment"; + vm.serializeAddress(key, "TDXVerifier", tdxVerifier); + vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); + vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); + vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); + string memory json = vm.serializeUint(key, "MaxTimeDiff", TDX_MAX_TIME_DIFF); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-tdx-verifier.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index de93d01ed..9e1fe9565 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -132,7 +132,7 @@ and Solidity verifies the proof plus the on-chain acceptance policy before regis | Contract | Purpose | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TDXVerifier` | Verifies a RISC Zero or SP1 proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | -| `TDXTEEProverRegistry` | Extends `TEEProverRegistry` with `registerTDXSigner(bytes output, ZkCoProcessorType zkCoprocessor, bytes proofBytes)`, storing the signer address and TDX image hash in the same registry fields used by `TEEVerifier`. | +| `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, storing both in the same registry fields used by `TEEVerifier`. | The ZK verifier guest is expected to perform the full Intel DCAP verification path: @@ -163,6 +163,82 @@ The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 b > **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining off-chain piece is the actual RISC Zero/SP1 TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. +### Step 1: Deploy `TDXVerifier` + +`TDXVerifier` is deployed separately because it depends on verifier interfaces that require Solidity `^0.8.20`, while the rest of the multiproof deployment stack is pinned to Solidity `0.8.15`. + +For Sepolia TDX testing, `scripts/multiproof/justfile` defaults to: + +```bash +RISC0_VERIFIER_ROUTER=0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 +TDX_VERIFIER_ID=0x1f1bc81fae82605af46a4c9a20f641922b7542befd9219644c7e6c59ccdeccab +INTEL_ROOT_CA_HASH=0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009 +TDX_IMAGE_HASH=0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6 +TDX_SIGNER=0x6A1f38f20044e8e69EAC755144F14f973e7b8d6E +TDX_REGISTRATION_MANAGER=0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7 +``` + +`deploy-config/sepolia.json` uses the same `RISC0_VERIFIER_ROUTER` and `TDX_IMAGE_HASH`. The `TEEProverRegistry` +constructor also requires a non-zero `tdxVerifier`; for config-driven deployments, set `tdxVerifier` in the deploy +config to the deployed `TDXVerifier` address. + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-verifier +``` + +To override any verifier input manually, pass all three verifier args: + +```bash +forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ + --sig "run(address,address,bytes32,bytes32)" \ + $OWNER \ + $RISC0_VERIFIER_ROUTER \ + $TDX_VERIFIER_ID \ + $INTEL_ROOT_CA_HASH \ + --rpc-url $L1_RPC_URL \ + --broadcast \ + --private-key $PRIVATE_KEY +``` + +The script saves output to `deployments/-tdx-verifier.json`. + +### Step 2: Deploy the TDX multiproof test stack + +Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `TDXVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. + +The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-stack $TDX_VERIFIER +``` + +To override the manager manually, use: + +```bash +forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ + --sig "run(address,address)" \ + $TDX_VERIFIER \ + $TDX_REGISTRATION_MANAGER \ + --rpc-url $L1_RPC_URL \ + --broadcast \ + --private-key $PRIVATE_KEY +``` + +The script saves output to `deployments/-dev-with-tdx.json`. + +### Step 3: Register a TDX signer + +Once you have the ABI-encoded `TDXVerifierJournal` output and matching RISC Zero proof bytes from the TDX DCAP guest, register the signer through the TDX-aware registry: + +```bash +cast send $TEE_PROVER_REGISTRY \ + "registerTDXSigner(bytes,bytes)" \ + $TDX_OUTPUT \ + $PROOF_BYTES \ + --rpc-url $L1_RPC_URL \ + --private-key $PRIVATE_KEY +``` + --- ## Pre-Seeding Games (Post-Deployment) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile new file mode 100644 index 000000000..86559bb92 --- /dev/null +++ b/scripts/multiproof/justfile @@ -0,0 +1,84 @@ +set dotenv-load + +repo_root := justfile_directory() + "/../.." +justfile_path := justfile_directory() + "/justfile" +default_deploy_config := "deploy-config/sepolia.json" +forge_account := "testnet-admin" +l1_rpc_url := "https://ethereum-full-sepolia-k8s-dev.cbhq.net" + +sepolia_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" +sepolia_tdx_verifier_id := "0x1f1bc81fae82605af46a4c9a20f641922b7542befd9219644c7e6c59ccdeccab" +sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009" +sepolia_tdx_image_hash := "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6" +sepolia_tdx_signer := "0x6A1f38f20044e8e69EAC755144F14f973e7b8d6E" +sepolia_tdx_registration_manager := "0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7" + +# List available multiproof deployment recipes. +default: + @just --justfile "{{justfile_path}}" --list + +# Deploy the TDXVerifier policy contract. +# +# Required env: +# L1_RPC_URL +# +# Args: +# risc0_router RISC Zero verifier router +# tdx_verifier_id RISC Zero image ID for the TDX DCAP verifier guest +# intel_root_ca_hash Trusted Intel root CA hash consumed by the guest +deploy-tdx-verifier risc0_router=sepolia_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as owner/deployer: $owner" + forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ + --sig "run(address,address,bytes32,bytes32)" \ + "$owner" \ + "{{risc0_router}}" \ + "{{tdx_verifier_id}}" \ + "{{intel_root_ca_hash}}" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Print the Sepolia TDX constants used by these recipes. +tdx-sepolia-config: + @echo "RISC0_VERIFIER_ROUTER={{sepolia_risc0_verifier_router}}" + @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" + @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" + @echo "TDX_IMAGE_HASH={{sepolia_tdx_image_hash}}" + @echo "TDX_SIGNER={{sepolia_tdx_signer}}" + @echo "TDX_REGISTRATION_MANAGER={{sepolia_tdx_registration_manager}}" + +# Deploy the TDX multiproof dev/test stack using deploy-config/sepolia.json. +# +# Required env: +# L1_RPC_URL +# +# Args: +# tdx_verifier TDXVerifier address from deploy-tdx-verifier +# tdx_manager Manager that can call registerTDXSigner() +deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=default_deploy_config: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as deployer: $owner" + DEPLOY_CONFIG_PATH="{{deploy_config}}" \ + forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ + --sig "run(address,address)" \ + "{{tdx_verifier}}" \ + "{{tdx_manager}}" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Print the two-step Sepolia command template. +tdx-sepolia-help: + @echo 'export L1_RPC_URL=' + @echo 'just deploy-tdx-verifier' + @echo 'just deploy-tdx-stack [tdx-manager]' + @echo 'just tdx-sepolia-config' diff --git a/snapshots/abi/DevTEEProverRegistry.json b/snapshots/abi/DevTEEProverRegistry.json index 6d66717e0..ff1329861 100644 --- a/snapshots/abi/DevTEEProverRegistry.json +++ b/snapshots/abi/DevTEEProverRegistry.json @@ -6,6 +6,11 @@ "name": "nitroVerifier", "type": "address" }, + { + "internalType": "contract ITDXVerifier", + "name": "tdxVerifier", + "type": "address" + }, { "internalType": "contract IDisputeGameFactory", "name": "factory", @@ -54,6 +59,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "TDX_VERIFIER", + "outputs": [ + { + "internalType": "contract ITDXVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -253,6 +271,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "output", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "proofBytes", + "type": "bytes" + } + ], + "name": "registerTDXSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "renounceManagement", @@ -465,6 +501,31 @@ "name": "SignerRegistered", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "reportDataSuffix", + "type": "bytes32" + } + ], + "name": "TDXSignerRegistered", + "type": "event" + }, { "inputs": [], "name": "AttestationTooOld", @@ -499,5 +560,10 @@ "inputs": [], "name": "PCR0NotFound", "type": "error" + }, + { + "inputs": [], + "name": "TDXVerifierNotSet", + "type": "error" } ] \ No newline at end of file diff --git a/snapshots/abi/TDXTEEProverRegistry.json b/snapshots/abi/TDXTEEProverRegistry.json deleted file mode 100644 index 51e55f31a..000000000 --- a/snapshots/abi/TDXTEEProverRegistry.json +++ /dev/null @@ -1,556 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract INitroEnclaveVerifier", - "name": "nitroVerifier", - "type": "address" - }, - { - "internalType": "contract ITDXVerifier", - "name": "tdxVerifier", - "type": "address" - }, - { - "internalType": "contract IDisputeGameFactory", - "name": "factory", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [], - "name": "DISPUTE_GAME_FACTORY", - "outputs": [ - { - "internalType": "contract IDisputeGameFactory", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MAX_AGE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "NITRO_VERIFIER", - "outputs": [ - { - "internalType": "contract INitroEnclaveVerifier", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "TDX_VERIFIER", - "outputs": [ - { - "internalType": "contract ITDXVerifier", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "signer", - "type": "address" - } - ], - "name": "deregisterSigner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "gameType", - "outputs": [ - { - "internalType": "GameType", - "name": "", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getExpectedImageHash", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getRegisteredSigners", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "initialOwner", - "type": "address" - }, - { - "internalType": "address", - "name": "initialManager", - "type": "address" - }, - { - "internalType": "address[]", - "name": "initialProposers", - "type": "address[]" - }, - { - "internalType": "GameType", - "name": "gameType_", - "type": "uint32" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "isRegisteredSigner", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "isValidProposer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "signer", - "type": "address" - } - ], - "name": "isValidSigner", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "manager", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "output", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "proofBytes", - "type": "bytes" - } - ], - "name": "registerSigner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "output", - "type": "bytes" - }, - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoprocessor", - "type": "uint8" - }, - { - "internalType": "bytes", - "name": "proofBytes", - "type": "bytes" - } - ], - "name": "registerTDXSigner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceManagement", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "GameType", - "name": "gameType_", - "type": "uint32" - } - ], - "name": "setGameType", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "proposer", - "type": "address" - }, - { - "internalType": "bool", - "name": "isValid", - "type": "bool" - } - ], - "name": "setProposer", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "signerImageHash", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newManager", - "type": "address" - } - ], - "name": "transferManagement", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "version", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "GameType", - "name": "gameType", - "type": "uint32" - } - ], - "name": "GameTypeUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousManager", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newManager", - "type": "address" - } - ], - "name": "ManagementTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "proposer", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isValid", - "type": "bool" - } - ], - "name": "ProposerSet", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "signer", - "type": "address" - } - ], - "name": "SignerDeregistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "signer", - "type": "address" - } - ], - "name": "SignerRegistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "signer", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "imageHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "reportDataSuffix", - "type": "bytes32" - } - ], - "name": "TDXSignerRegistered", - "type": "event" - }, - { - "inputs": [], - "name": "AttestationTooOld", - "type": "error" - }, - { - "inputs": [], - "name": "AttestationVerificationFailed", - "type": "error" - }, - { - "inputs": [], - "name": "DisputeGameFactoryNotSet", - "type": "error" - }, - { - "inputs": [], - "name": "ImageHashReadFailed", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidGameType", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidPublicKey", - "type": "error" - }, - { - "inputs": [], - "name": "PCR0NotFound", - "type": "error" - }, - { - "inputs": [], - "name": "TDXVerifierNotSet", - "type": "error" - } -] \ No newline at end of file diff --git a/snapshots/abi/TEEProverRegistry.json b/snapshots/abi/TEEProverRegistry.json index 510962c5d..9bec65dec 100644 --- a/snapshots/abi/TEEProverRegistry.json +++ b/snapshots/abi/TEEProverRegistry.json @@ -6,6 +6,11 @@ "name": "nitroVerifier", "type": "address" }, + { + "internalType": "contract ITDXVerifier", + "name": "tdxVerifier", + "type": "address" + }, { "internalType": "contract IDisputeGameFactory", "name": "factory", @@ -54,6 +59,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "TDX_VERIFIER", + "outputs": [ + { + "internalType": "contract ITDXVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -235,6 +253,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "output", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "proofBytes", + "type": "bytes" + } + ], + "name": "registerTDXSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "renounceManagement", @@ -447,6 +483,31 @@ "name": "SignerRegistered", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "reportDataSuffix", + "type": "bytes32" + } + ], + "name": "TDXSignerRegistered", + "type": "event" + }, { "inputs": [], "name": "AttestationTooOld", @@ -481,5 +542,10 @@ "inputs": [], "name": "PCR0NotFound", "type": "error" + }, + { + "inputs": [], + "name": "TDXVerifierNotSet", + "type": "error" } ] \ No newline at end of file diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 77715d027..c29210d10 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -151,17 +151,13 @@ "initCodeHash": "0x100364f9b0c63a61538386ba91e73dcffba22d3f6dfe8efdbbf5ff347b6fce47", "sourceCodeHash": "0xbedb66738a0ab18600b2ed21de51d552a210c4cc71b2c971440e325791dc3ce7" }, - "src/multiproof/tee/TDXTEEProverRegistry.sol:TDXTEEProverRegistry": { - "initCodeHash": "0x266f220204f2c62f341b700bdb1e9bbda8c6489ced1e14d17b4ffe7dd33b1d8b", - "sourceCodeHash": "0x90bf694159e3f47e8f42aeaaa856947bb493acd5f64bf14fe277718dc62b4463" - }, "src/multiproof/tee/TDXVerifier.sol:TDXVerifier": { "initCodeHash": "0x0f511b2f67bd82ff976935ca0a178c3fa9d9cf9b89c6e67b3fd7f1006f5e5147", "sourceCodeHash": "0x7cdf2abefaefa3126960103aa810699973c1c47147114f8a92e63c427b9b1656" }, "src/multiproof/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0xfd1942e1c2f59b0aa72b33d698a948a53b6e4cf1040106f173fb5d89f63f57b0", - "sourceCodeHash": "0xf1ec1f02f540da659a204b26acf986fdce7d7d63bba67a87923f52453fb92ccb" + "initCodeHash": "0xf050a9503675e4351de148eb311d3446f04d59bd2083710617e045dd8b64bb26", + "sourceCodeHash": "0x64e7860f2b0d3b07aff5e1da82928a88ea4da94bf2fcb93a342f0f02844ece73" }, "src/multiproof/tee/TEEVerifier.sol:TEEVerifier": { "initCodeHash": "0x655576cc21cc5c603d55fb4dd2a2f0ef57b11deeaabd3e539b0a70a5f7e2c9af", diff --git a/snapshots/storageLayout/TDXTEEProverRegistry.json b/snapshots/storageLayout/TDXTEEProverRegistry.json deleted file mode 100644 index 93dcbfff3..000000000 --- a/snapshots/storageLayout/TDXTEEProverRegistry.json +++ /dev/null @@ -1,79 +0,0 @@ -[ - { - "bytes": "1", - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "uint8" - }, - { - "bytes": "1", - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "bool" - }, - { - "bytes": "1600", - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "uint256[50]" - }, - { - "bytes": "20", - "label": "_owner", - "offset": 0, - "slot": "51", - "type": "address" - }, - { - "bytes": "20", - "label": "_manager", - "offset": 0, - "slot": "52", - "type": "address" - }, - { - "bytes": "1536", - "label": "__gap", - "offset": 0, - "slot": "53", - "type": "uint256[48]" - }, - { - "bytes": "4", - "label": "gameType", - "offset": 0, - "slot": "101", - "type": "GameType" - }, - { - "bytes": "32", - "label": "isRegisteredSigner", - "offset": 0, - "slot": "102", - "type": "mapping(address => bool)" - }, - { - "bytes": "32", - "label": "signerImageHash", - "offset": 0, - "slot": "103", - "type": "mapping(address => bytes32)" - }, - { - "bytes": "32", - "label": "isValidProposer", - "offset": 0, - "slot": "104", - "type": "mapping(address => bool)" - }, - { - "bytes": "32", - "label": "_registeredSigners", - "offset": 0, - "slot": "105", - "type": "struct EnumerableSetLib.AddressSet" - } -] \ No newline at end of file diff --git a/src/multiproof/mocks/MockDevTEEProverRegistry.sol b/src/multiproof/mocks/MockDevTEEProverRegistry.sol index c01b124f1..02cf538a5 100644 --- a/src/multiproof/mocks/MockDevTEEProverRegistry.sol +++ b/src/multiproof/mocks/MockDevTEEProverRegistry.sol @@ -2,8 +2,8 @@ pragma solidity 0.8.15; import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { EnumerableSetLib } from "@solady-v0.0.245/utils/EnumerableSetLib.sol"; import { TEEProverRegistry } from "src/multiproof/tee/TEEProverRegistry.sol"; @@ -12,13 +12,12 @@ import { TEEProverRegistry } from "src/multiproof/tee/TEEProverRegistry.sol"; /// @dev This contract adds addDevSigner() which bypasses AWS Nitro attestation verification. /// DO NOT deploy this contract to production networks. contract DevTEEProverRegistry is TEEProverRegistry { - using EnumerableSetLib for EnumerableSetLib.AddressSet; - constructor( INitroEnclaveVerifier nitroVerifier, + ITDXVerifier tdxVerifier, IDisputeGameFactory factory ) - TEEProverRegistry(nitroVerifier, factory) + TEEProverRegistry(nitroVerifier, tdxVerifier, factory) { } /// @notice Registers a signer for testing (bypasses attestation verification). @@ -28,9 +27,6 @@ contract DevTEEProverRegistry is TEEProverRegistry { /// @param signer The address of the signer to register. /// @param imageHash The TEE image hash to associate with this signer. function addDevSigner(address signer, bytes32 imageHash) external onlyOwner { - isRegisteredSigner[signer] = true; - signerImageHash[signer] = imageHash; - _registeredSigners.add(signer); - emit SignerRegistered(signer); + _registerSigner(signer, imageHash); } } diff --git a/src/multiproof/tee/TDXTEEProverRegistry.sol b/src/multiproof/tee/TDXTEEProverRegistry.sol deleted file mode 100644 index 5efff43b5..000000000 --- a/src/multiproof/tee/TDXTEEProverRegistry.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { EnumerableSetLib } from "@solady-v0.0.245/utils/EnumerableSetLib.sol"; - -import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier, TDXVerifierJournal, ZkCoProcessorType } from "interfaces/multiproof/tee/ITDXVerifier.sol"; - -import { TEEProverRegistry } from "./TEEProverRegistry.sol"; - -/// @title TDXTEEProverRegistry -/// @notice TEE prover registry extension that can register signers from Intel TDX quotes. -/// @dev Nitro registration remains available through TEEProverRegistry.registerSigner(). This contract adds a -/// separate TDX path so existing Nitro deployments and verifier integrations do not need to change. -contract TDXTEEProverRegistry is TEEProverRegistry { - using EnumerableSetLib for EnumerableSetLib.AddressSet; - - /// @notice The TDX verifier used to validate ZK-proven TD quote verification journals. - ITDXVerifier public immutable TDX_VERIFIER; - - /// @notice Emitted when a TDX signer is registered. - event TDXSignerRegistered(address indexed signer, bytes32 indexed imageHash, bytes32 reportDataSuffix); - - /// @notice Thrown when the TDX verifier is not configured. - error TDXVerifierNotSet(); - - constructor( - INitroEnclaveVerifier nitroVerifier, - ITDXVerifier tdxVerifier, - IDisputeGameFactory factory - ) - TEEProverRegistry(nitroVerifier, factory) - { - if (address(tdxVerifier) == address(0)) revert TDXVerifierNotSet(); - TDX_VERIFIER = tdxVerifier; - } - - /// @notice Registers a signer using a ZK proof of Intel TDX DCAP quote verification. - /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. - /// @param proofBytes ZK proof bytes. - function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { - TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - - isRegisteredSigner[journal.signer] = true; - signerImageHash[journal.signer] = journal.imageHash; - _registeredSigners.add(journal.signer); - - emit SignerRegistered(journal.signer); - emit TDXSignerRegistered(journal.signer, journal.imageHash, journal.reportDataSuffix); - } - - /// @notice Semantic version. - /// @custom:semver 0.1.0 - function version() public pure override returns (string memory) { - return "0.1.0"; - } -} diff --git a/src/multiproof/tee/TEEProverRegistry.sol b/src/multiproof/tee/TEEProverRegistry.sol index ec6652b44..fbce8f4bd 100644 --- a/src/multiproof/tee/TEEProverRegistry.sol +++ b/src/multiproof/tee/TEEProverRegistry.sol @@ -9,6 +9,7 @@ import { Pcr, Bytes48 } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier, TDXVerifierJournal } from "interfaces/multiproof/tee/ITDXVerifier.sol"; import { OwnableManagedUpgradeable } from "lib/op-enclave/contracts/src/OwnableManagedUpgradeable.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { EnumerableSetLib } from "@solady-v0.0.245/utils/EnumerableSetLib.sol"; @@ -16,9 +17,10 @@ import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol" import { GameType } from "src/dispute/lib/Types.sol"; /// @title TEEProverRegistry -/// @notice Manages TEE signer registration via ZK-verified AWS Nitro attestation. -/// @dev Signers are registered by providing a ZK proof of a valid AWS Nitro attestation document, -/// verified through an external NitroEnclaveVerifier contract (Risc0). +/// @notice Manages TEE signer registration via ZK-verified Nitro or TDX attestation. +/// @dev Nitro signers are registered by providing a ZK proof of a valid AWS Nitro attestation document, +/// verified through an external NitroEnclaveVerifier contract (Risc0). TDX signers are registered +/// through the TDXVerifier configured on the implementation. /// Registration is PCR0-agnostic: any enclave with a valid attestation can register, /// enabling pre-registration before hardforks. PCR0 enforcement happens at proof-submission /// time in TEEVerifier, which checks signerImageHash against the AggregateVerifier's @@ -36,6 +38,9 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice The external NitroEnclaveVerifier contract used for ZK attestation verification. INitroEnclaveVerifier public immutable NITRO_VERIFIER; + /// @notice The external TDXVerifier contract used for ZK TDX quote verification. + ITDXVerifier public immutable TDX_VERIFIER; + /// @notice The DisputeGameFactory used to look up the current AggregateVerifier and its TEE_IMAGE_HASH. IDisputeGameFactory public immutable DISPUTE_GAME_FACTORY; @@ -65,6 +70,9 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Emitted when a signer is registered. event SignerRegistered(address indexed signer); + /// @notice Emitted when a TDX signer is registered. + event TDXSignerRegistered(address indexed signer, bytes32 indexed imageHash, bytes32 reportDataSuffix); + /// @notice Emitted when a signer is deregistered. event SignerDeregistered(address indexed signer); @@ -95,9 +103,14 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Thrown when setting a game type whose AggregateVerifier has no TEE_IMAGE_HASH. error InvalidGameType(); - constructor(INitroEnclaveVerifier nitroVerifier, IDisputeGameFactory factory) { + /// @notice Thrown when the TDX verifier is not configured. + error TDXVerifierNotSet(); + + constructor(INitroEnclaveVerifier nitroVerifier, ITDXVerifier tdxVerifier, IDisputeGameFactory factory) { if (address(factory) == address(0)) revert DisputeGameFactoryNotSet(); + if (address(tdxVerifier) == address(0)) revert TDXVerifierNotSet(); NITRO_VERIFIER = nitroVerifier; + TDX_VERIFIER = tdxVerifier; DISPUTE_GAME_FACTORY = factory; initialize({ initialOwner: address(0xdEaD), @@ -167,10 +180,18 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } address enclaveAddress = address(uint160(uint256(publicKeyHash))); - isRegisteredSigner[enclaveAddress] = true; - signerImageHash[enclaveAddress] = pcr0Hash; - _registeredSigners.add(enclaveAddress); - emit SignerRegistered(enclaveAddress); + _registerSigner(enclaveAddress, pcr0Hash); + } + + /// @notice Registers a signer using a ZK proof of Intel TDX DCAP quote verification. + /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. + /// @param proofBytes ZK proof bytes. + function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { + TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, ZkCoProcessorType.RiscZero, proofBytes); + + _registerSigner(journal.signer, journal.imageHash); + + emit TDXSignerRegistered(journal.signer, journal.imageHash, journal.reportDataSuffix); } /// @notice Deregisters a signer. @@ -233,9 +254,17 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } /// @notice Semantic version. - /// @custom:semver 0.5.0 + /// @custom:semver 0.6.0 function version() public pure virtual returns (string memory) { - return "0.5.0"; + return "0.6.0"; + } + + /// @dev Registers a signer and stores the image hash enforced by TEEVerifier at proof-submission time. + function _registerSigner(address signer, bytes32 imageHash) internal { + isRegisteredSigner[signer] = true; + signerImageHash[signer] = imageHash; + _registeredSigners.add(signer); + emit SignerRegistered(signer); } /// @dev Reads TEE_IMAGE_HASH from the AggregateVerifier registered in the factory. diff --git a/test/multiproof/TEEProverRegistry.t.sol b/test/multiproof/TEEProverRegistry.t.sol index b4a64a178..ecaa2c06b 100644 --- a/test/multiproof/TEEProverRegistry.t.sol +++ b/test/multiproof/TEEProverRegistry.t.sol @@ -7,6 +7,7 @@ import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/trans import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { GameType } from "src/dispute/lib/Types.sol"; @@ -75,8 +76,9 @@ contract TEEProverRegistryTest is Test { mockFactory.setImpl(TEST_GAME_TYPE, address(mockVerifier)); // Deploy implementation (using DevTEEProverRegistry for test flexibility) - DevTEEProverRegistry impl = - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(address(mockFactory))); + DevTEEProverRegistry impl = new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(mockFactory)) + ); // Deploy proxy admin proxyAdmin = new ProxyAdmin(address(this)); @@ -98,15 +100,16 @@ contract TEEProverRegistryTest is Test { function testInitialization() public view { assertEq(teeProverRegistry.owner(), owner); assertEq(teeProverRegistry.manager(), manager); - assertEq(teeProverRegistry.version(), "0.5.0"); + assertEq(teeProverRegistry.version(), "0.6.0"); } function testInitializationWithProposers() public { address proposer1 = makeAddr("proposer1"); address proposer2 = makeAddr("proposer2"); address proposer3 = makeAddr("proposer3"); - DevTEEProverRegistry impl2 = - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(address(1))); + DevTEEProverRegistry impl2 = new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(1)) + ); ProxyAdmin proxyAdmin2 = new ProxyAdmin(address(this)); address[] memory proposers = new address[](3); proposers[0] = proposer1; diff --git a/test/multiproof/TDXTEEProverRegistry.t.sol b/test/multiproof/TEEProverRegistryTDX.t.sol similarity index 85% rename from test/multiproof/TDXTEEProverRegistry.t.sol rename to test/multiproof/TEEProverRegistryTDX.t.sol index 1158c04e2..43bd99f7f 100644 --- a/test/multiproof/TDXTEEProverRegistry.t.sol +++ b/test/multiproof/TEEProverRegistryTDX.t.sol @@ -15,7 +15,7 @@ import { import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; import { GameType } from "src/dispute/lib/Types.sol"; -import { TDXTEEProverRegistry } from "src/multiproof/tee/TDXTEEProverRegistry.sol"; +import { TEEProverRegistry } from "src/multiproof/tee/TEEProverRegistry.sol"; /// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. contract MockAggregateVerifierForTDXRegistry { @@ -67,7 +67,7 @@ contract MockTDXVerifierForRegistry is ITDXVerifier { } } -contract TDXTEEProverRegistryTest is Test { +contract TEEProverRegistryTDXTest is Test { bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); @@ -79,7 +79,7 @@ contract TDXTEEProverRegistryTest is Test { MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); - TDXTEEProverRegistry registry = new TDXTEEProverRegistry( + TEEProverRegistry registry = new TEEProverRegistry( INitroEnclaveVerifier(address(0)), ITDXVerifier(address(verifier)), IDisputeGameFactory(address(factory)) ); @@ -91,6 +91,16 @@ contract TDXTEEProverRegistryTest is Test { assertTrue(registry.isValidSigner(journal.signer)); } + function testConstructorRevertsIfTDXVerifierNotSet() public { + MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); + factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); + + vm.expectRevert(TEEProverRegistry.TDXVerifierNotSet.selector); + new TEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(0)), IDisputeGameFactory(address(factory)) + ); + } + function _successJournal() internal pure returns (TDXVerifierJournal memory journal) { address signer = address(0x1234); diff --git a/test/multiproof/TEEVerifier.t.sol b/test/multiproof/TEEVerifier.t.sol index 89302645a..19faa27f1 100644 --- a/test/multiproof/TEEVerifier.t.sol +++ b/test/multiproof/TEEVerifier.t.sol @@ -7,6 +7,7 @@ import { TransparentUpgradeableProxy } from "@openzeppelin/contracts/proxy/trans import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; import { GameType } from "src/dispute/lib/Types.sol"; @@ -67,8 +68,9 @@ contract TEEVerifierTest is Test { mockFactory.setImpl(TEST_GAME_TYPE, address(mockVerifier)); // Deploy implementation (NitroEnclaveVerifier not needed for dev signer tests) - DevTEEProverRegistry impl = - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(address(mockFactory))); + DevTEEProverRegistry impl = new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(mockFactory)) + ); // Deploy proxy admin proxyAdmin = new ProxyAdmin(address(this)); diff --git a/test/opcm/DeployImplementations.t.sol b/test/opcm/DeployImplementations.t.sol index cb2f103d2..18b6326f9 100644 --- a/test/opcm/DeployImplementations.t.sol +++ b/test/opcm/DeployImplementations.t.sol @@ -177,6 +177,7 @@ contract DeployImplementations_Test is Test, FeatureFlags { bytes32(0), // multiproofConfigHash 621, // multiproofGameType address(0), // nitroEnclaveVerifier + address(1), // tdxVerifier 8453, // l2ChainID 100, // multiproofBlockInterval 10, // multiproofIntermediateBlockInterval @@ -313,6 +314,11 @@ contract DeployImplementations_Test is Test, FeatureFlags { vm.expectRevert("DeployImplementations: mipsVersion not set"); deployImplementations.run(input); + input = defaultInput(); + input.tdxVerifier = address(0); + vm.expectRevert("DeployImplementations: tdxVerifier not set"); + deployImplementations.run(input); + input = defaultInput(); input.superchainConfigProxy = ISuperchainConfig(address(0)); vm.expectRevert("DeployImplementations: superchainConfigProxy not set"); @@ -400,6 +406,7 @@ contract DeployImplementations_Test is Test, FeatureFlags { bytes32(0), // multiproofConfigHash 621, // multiproofGameType address(0), // nitroEnclaveVerifier + address(1), // tdxVerifier 8453, // l2ChainID 100, // multiproofBlockInterval 10, // multiproofIntermediateBlockInterval diff --git a/test/opcm/DeployOPChain.t.sol b/test/opcm/DeployOPChain.t.sol index 2da623316..9c1c7166d 100644 --- a/test/opcm/DeployOPChain.t.sol +++ b/test/opcm/DeployOPChain.t.sol @@ -90,6 +90,7 @@ contract DeployOPChain_TestBase is Test, FeatureFlags { multiproofConfigHash: bytes32(0), multiproofGameType: 621, nitroEnclaveVerifier: address(0), + tdxVerifier: address(1), l2ChainID: 8453, multiproofBlockInterval: 100, multiproofIntermediateBlockInterval: 10, From 55718a96fc540816a0e92d05e52343b941fd3107 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 13:55:33 -0400 Subject: [PATCH 004/146] update AggregateVerifier --- scripts/multiproof/DeployDevNoNitro.s.sol | 20 ++- scripts/multiproof/DeployDevWithNitro.s.sol | 7 +- scripts/multiproof/DeployDevWithTDX.s.sol | 13 +- scripts/multiproof/README.md | 38 ++++-- snapshots/abi/DevTEEProverRegistry.json | 63 +++++++++ snapshots/abi/TEEProverRegistry.json | 45 +++++++ snapshots/abi/TEEVerifier.json | 21 +++ snapshots/semver-lock.json | 20 ++- .../storageLayout/DevTEEProverRegistry.json | 7 + .../storageLayout/TEEProverRegistry.json | 7 + src/multiproof/AggregateVerifier.sol | 11 +- .../mocks/MockDevTEEProverRegistry.sol | 12 +- src/multiproof/tee/TEEProverRegistry.sol | 35 ++++- src/multiproof/tee/TEEVerifier.sol | 78 +++++++++--- test/multiproof/AggregateVerifier.t.sol | 8 +- test/multiproof/BaseTest.t.sol | 29 ++++- test/multiproof/Challenge.t.sol | 6 +- test/multiproof/Nullify.t.sol | 6 +- test/multiproof/TEEProverRegistry.t.sol | 31 ++++- test/multiproof/TEEProverRegistryTDX.t.sol | 1 + test/multiproof/TEEVerifier.t.sol | 120 ++++++++++++------ 21 files changed, 471 insertions(+), 107 deletions(-) diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 92385240f..50b383af1 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -26,11 +26,15 @@ pragma solidity 0.8.15; * SIGNER REGISTRATION (SIMPLIFIED) * ───────────────────────────────────────────────────────────────────────────────── * - * After deployment, register a signer with a single call: + * After deployment, register one dev Nitro signer and one dev TDX signer: * - * cast send $TEE_PROVER_REGISTRY \ - * "addDevSigner(address,bytes32)" $SIGNER_ADDRESS $TEE_IMAGE_HASH \ - * --private-key $OWNER_KEY --rpc-url $RPC_URL + * cast send $TEE_PROVER_REGISTRY \ + * "addDevSigner(address,bytes32)" $NITRO_SIGNER_ADDRESS $TEE_IMAGE_HASH \ + * --private-key $OWNER_KEY --rpc-url $RPC_URL + * + * cast send $TEE_PROVER_REGISTRY \ + * "addDevTDXSigner(address,bytes32)" $TDX_SIGNER_ADDRESS $TEE_IMAGE_HASH \ + * --private-key $OWNER_KEY --rpc-url $RPC_URL * * No attestation, PCR0 registration, or certificate validation required. * @@ -218,9 +222,13 @@ contract DeployDevNoNitro is Script { console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); - console.log("\n>>> NEXT STEP - Register dev signer (NO ATTESTATION NEEDED) <<<"); + console.log("\n>>> NEXT STEP - Register dev Nitro and TDX signers (NO ATTESTATION NEEDED) <<<"); console.log("\ncast send", teeProverRegistryProxy); - console.log(' "addDevSigner(address,bytes32)" '); + console.log(' "addDevSigner(address,bytes32)" '); + console.log(" ", vm.toString(cfg.teeImageHash())); + console.log(" --private-key --rpc-url "); + console.log("\ncast send", teeProverRegistryProxy); + console.log(' "addDevTDXSigner(address,bytes32)" '); console.log(" ", vm.toString(cfg.teeImageHash())); console.log(" --private-key --rpc-url "); console.log("\n========================================\n"); diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index 92ad2b17a..5a3cae27f 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -212,9 +212,12 @@ contract DeployDevWithNitro is Script { console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); - console.log("\n>>> NEXT STEP: Register signer with ZK attestation proof <<<"); + console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); console.log("\n cast send", teeProverRegistryProxy); - console.log(' "registerSigner(bytes,bytes)" '); + console.log(' "registerSigner(bytes,bytes)" '); + console.log(" --private-key --rpc-url "); + console.log("\n cast send", teeProverRegistryProxy); + console.log(' "registerTDXSigner(bytes,bytes)" '); console.log(" --private-key --rpc-url "); console.log("\n========================================\n"); } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index d7ecf6f0c..592bee867 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -49,6 +49,7 @@ contract DeployDevWithTDX is Script { DeployConfig public constant cfg = DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); + address public nitroEnclaveVerifierAddr; address public tdxVerifierAddr; address public tdxRegistrationManager; address public teeProverRegistryProxy; @@ -61,6 +62,7 @@ contract DeployDevWithTDX is Script { function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); cfg.read(Config.deployConfigPath()); + nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); } function run(address tdxVerifier) public { @@ -69,6 +71,7 @@ contract DeployDevWithTDX is Script { function run(address tdxVerifier, address registrationManager) public { require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); + require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be set in config"); require(registrationManager != address(0), "registrationManager must be non-zero"); tdxVerifierAddr = tdxVerifier; tdxRegistrationManager = registrationManager; @@ -81,6 +84,7 @@ contract DeployDevWithTDX is Script { console.log("TEE Proposer:", cfg.teeProposer()); console.log("TEE Challenger:", cfg.teeChallenger()); console.log("Game Type:", cfg.multiproofGameType()); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); console.log("TDXVerifier:", tdxVerifierAddr); console.log("TDX Registration Manager:", tdxRegistrationManager); console.log(""); @@ -102,7 +106,7 @@ contract DeployDevWithTDX is Script { address owner = cfg.finalSystemOwner(); address registryImpl = address( new TEEProverRegistry( - INitroEnclaveVerifier(address(0)), + INitroEnclaveVerifier(nitroEnclaveVerifierAddr), ITDXVerifier(tdxVerifierAddr), IDisputeGameFactory(disputeGameFactory) ) @@ -178,6 +182,7 @@ contract DeployDevWithTDX is Script { console.log(" DEV DEPLOYMENT COMPLETE (TDX)"); console.log("========================================"); console.log("\nTDX Contracts:"); + console.log(" NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); console.log(" TDXVerifier:", tdxVerifierAddr); console.log(" TEEProverRegistry:", teeProverRegistryProxy); console.log(" TDX Registration Manager:", tdxRegistrationManager); @@ -192,7 +197,10 @@ contract DeployDevWithTDX is Script { console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); - console.log("\n>>> NEXT STEP: Register TDX signer with a ZK-proven TDX journal <<<"); + console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); + console.log("\n cast send", teeProverRegistryProxy); + console.log(' "registerSigner(bytes,bytes)" '); + console.log(" --private-key --rpc-url "); console.log("\n cast send", teeProverRegistryProxy); console.log(' "registerTDXSigner(bytes,bytes)" '); console.log(" --private-key --rpc-url "); @@ -201,6 +209,7 @@ contract DeployDevWithTDX is Script { function _writeOutput() internal { string memory key = "deployment"; + vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 9e1fe9565..826664341 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -96,9 +96,9 @@ This returns a raw byte array representing an uncompressed secp256k1 public key 0x0cbe4A965B41DA6B2D5AF4d53c0C16a37d6f9F7D ``` -### Step 5: Register the dev signer +### Step 5: Register the dev signers -Call `addDevSigner` on the deployed `DevTEEProverRegistry` with the **signer address** derived in Step 4. +Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signer on the deployed `DevTEEProverRegistry`. > **Note:** PCR0 enforcement is handled by `AggregateVerifier` (which bakes `teeImageHash` into the > journal the enclave signs). The registry only tracks which signer addresses are valid. @@ -106,10 +106,19 @@ Call `addDevSigner` on the deployed `DevTEEProverRegistry` with the **signer add ```bash # Replace: # 0x587d... with the TEEProverRegistry address from your deployment output -# 0x080f... with the signer address derived in Step 4 +# 0x080f... with the Nitro signer address derived in Step 4 cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ - "addDevSigner(address)" \ + "addDevSigner(address,bytes32)" \ 0x080f42420846c613158D7b4334257C78bE5A9B90 \ + $TEE_IMAGE_HASH \ + --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ + --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" + +# Register a TDX dev signer for the same image hash. +cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ + "addDevTDXSigner(address,bytes32)" \ + $TDX_SIGNER_ADDRESS \ + $TEE_IMAGE_HASH \ --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" ``` @@ -132,7 +141,7 @@ and Solidity verifies the proof plus the on-chain acceptance policy before regis | Contract | Purpose | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TDXVerifier` | Verifies a RISC Zero or SP1 proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | -| `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, storing both in the same registry fields used by `TEEVerifier`. | +| `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, tracking which TEE type each signer came from for `TEEVerifier`. | The ZK verifier guest is expected to perform the full Intel DCAP verification path: @@ -159,7 +168,7 @@ The attested public key must be supplied as an uncompressed 65-byte secp256k1 pu The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 bytes. The last 32 bytes are returned by the verifier as app-specific binding data and emitted by the registry. -`TEEVerifier` is still the proposal-proof verifier. TDX only changes signer registration: once a TDX signer is registered, proposal proofs use the existing `proposer + signature` proof bytes and the same `signerImageHash` check as Nitro. +`TEEVerifier` is still the proposal-proof verifier, but a TEE proposal proof now requires two signatures over the same journal: one from a Nitro-registered signer and one from a TDX-registered signer. The proof bytes are `proposer || signatureA || signatureB`; either signature order is accepted as long as both registered TEE types are present and both signers match the expected `TEE_IMAGE_HASH`. > **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining off-chain piece is the actual RISC Zero/SP1 TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. @@ -204,9 +213,9 @@ The script saves output to `deployments/-tdx-verifier.json`. ### Step 2: Deploy the TDX multiproof test stack -Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `TDXVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. +Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `TDXVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. -The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. +The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. ```bash just --justfile scripts/multiproof/justfile deploy-tdx-stack $TDX_VERIFIER @@ -226,7 +235,18 @@ forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ The script saves output to `deployments/-dev-with-tdx.json`. -### Step 3: Register a TDX signer +### Step 3: Register Nitro and TDX signers + +Register a Nitro signer with a ZK-proven Nitro attestation: + +```bash +cast send $TEE_PROVER_REGISTRY \ + "registerSigner(bytes,bytes)" \ + $NITRO_OUTPUT \ + $NITRO_PROOF_BYTES \ + --rpc-url $L1_RPC_URL \ + --private-key $PRIVATE_KEY +``` Once you have the ABI-encoded `TDXVerifierJournal` output and matching RISC Zero proof bytes from the TDX DCAP guest, register the signer through the TDX-aware registry: diff --git a/snapshots/abi/DevTEEProverRegistry.json b/snapshots/abi/DevTEEProverRegistry.json index ff1329861..0448f242a 100644 --- a/snapshots/abi/DevTEEProverRegistry.json +++ b/snapshots/abi/DevTEEProverRegistry.json @@ -90,6 +90,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + } + ], + "name": "addDevTDXSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -353,6 +371,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "signerTEEType", + "outputs": [ + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -556,11 +593,37 @@ "name": "InvalidPublicKey", "type": "error" }, + { + "inputs": [], + "name": "InvalidTEEType", + "type": "error" + }, { "inputs": [], "name": "PCR0NotFound", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "existingTEEType", + "type": "uint8" + }, + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "newTEEType", + "type": "uint8" + } + ], + "name": "SignerTEETypeMismatch", + "type": "error" + }, { "inputs": [], "name": "TDXVerifierNotSet", diff --git a/snapshots/abi/TEEProverRegistry.json b/snapshots/abi/TEEProverRegistry.json index 9bec65dec..ceff44a08 100644 --- a/snapshots/abi/TEEProverRegistry.json +++ b/snapshots/abi/TEEProverRegistry.json @@ -335,6 +335,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "signerTEEType", + "outputs": [ + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -538,11 +557,37 @@ "name": "InvalidPublicKey", "type": "error" }, + { + "inputs": [], + "name": "InvalidTEEType", + "type": "error" + }, { "inputs": [], "name": "PCR0NotFound", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "existingTEEType", + "type": "uint8" + }, + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "newTEEType", + "type": "uint8" + } + ], + "name": "SignerTEETypeMismatch", + "type": "error" + }, { "inputs": [], "name": "TDXVerifierNotSet", diff --git a/snapshots/abi/TEEVerifier.json b/snapshots/abi/TEEVerifier.json index b491ef7a5..fff275ed7 100644 --- a/snapshots/abi/TEEVerifier.json +++ b/snapshots/abi/TEEVerifier.json @@ -116,6 +116,17 @@ "name": "VerifierNullified", "type": "event" }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + } + ], + "name": "DuplicateSigner", + "type": "error" + }, { "inputs": [ { @@ -164,6 +175,16 @@ "name": "InvalidSigner", "type": "error" }, + { + "inputs": [], + "name": "MissingNitroSignature", + "type": "error" + }, + { + "inputs": [], + "name": "MissingTDXSignature", + "type": "error" + }, { "inputs": [], "name": "NotProperGame", diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index c29210d10..4e75dca07 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -144,8 +144,12 @@ "sourceCodeHash": "0x955bd0c9b47e43219865e4e92abf28d916c96de20cbdf2f94c8ab14d02083759" }, "src/multiproof/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0x10bb6a60f21de103d1da9ff310a38f571c53a8113d04a029087648debf3f0341", - "sourceCodeHash": "0xcd4c508223b9b461ec230bde03c1630fe68cd00b82ee84c39b93d9f3c0d71d37" + "initCodeHash": "0xea8f199813975e1307fa964b70e30832cf5ea1e848ffa78368cc565cc9e919d1", + "sourceCodeHash": "0x9a48d272dd19ccd6df51999dcb6117308aea712f6d3731bdcc8b8d02b867c348" + }, + "src/multiproof/AggregateVerifier.sol:AggregateVerifier:dispute": { + "initCodeHash": "0xa92e0ce6d4dddea92719bad6f1e964c3abf838ec892a12fc16c4a5b9fbfb9af1", + "sourceCodeHash": "0x9a48d272dd19ccd6df51999dcb6117308aea712f6d3731bdcc8b8d02b867c348" }, "src/multiproof/tee/NitroEnclaveVerifier.sol:NitroEnclaveVerifier": { "initCodeHash": "0x100364f9b0c63a61538386ba91e73dcffba22d3f6dfe8efdbbf5ff347b6fce47", @@ -156,12 +160,16 @@ "sourceCodeHash": "0x7cdf2abefaefa3126960103aa810699973c1c47147114f8a92e63c427b9b1656" }, "src/multiproof/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0xf050a9503675e4351de148eb311d3446f04d59bd2083710617e045dd8b64bb26", - "sourceCodeHash": "0x64e7860f2b0d3b07aff5e1da82928a88ea4da94bf2fcb93a342f0f02844ece73" + "initCodeHash": "0xf96d42f3dc474f4f8d29af0e7fbb59b13035a61bc53903ddd14814ae0b3f2f73", + "sourceCodeHash": "0x12bf06d225633d1983746ea06e72469388a3db63fd7d8a10144b4267f13f0b5d" }, "src/multiproof/tee/TEEVerifier.sol:TEEVerifier": { - "initCodeHash": "0x655576cc21cc5c603d55fb4dd2a2f0ef57b11deeaabd3e539b0a70a5f7e2c9af", - "sourceCodeHash": "0xd5e01b4f4e69313e56e51f2f46b7bbe699ef8dc24b8a6385b8ebac4162e05353" + "initCodeHash": "0x37883165bb1924631d830772a811f172059f529fd44ff34694d2511ff52c29c9", + "sourceCodeHash": "0x549df133a04af10305dcc5e70818ac49ac47546c79c957151184f9a21223d6c1" + }, + "src/multiproof/tee/TEEVerifier.sol:TEEVerifier:dispute": { + "initCodeHash": "0x37883165bb1924631d830772a811f172059f529fd44ff34694d2511ff52c29c9", + "sourceCodeHash": "0x549df133a04af10305dcc5e70818ac49ac47546c79c957151184f9a21223d6c1" }, "src/multiproof/zk/ZKVerifier.sol:ZkVerifier": { "initCodeHash": "0xcdc5bd5fa8ffad76358b8f34a53b8c0cb36540176c31f82d52546373d0bd5245", diff --git a/snapshots/storageLayout/DevTEEProverRegistry.json b/snapshots/storageLayout/DevTEEProverRegistry.json index 93dcbfff3..30836afaa 100644 --- a/snapshots/storageLayout/DevTEEProverRegistry.json +++ b/snapshots/storageLayout/DevTEEProverRegistry.json @@ -75,5 +75,12 @@ "offset": 0, "slot": "105", "type": "struct EnumerableSetLib.AddressSet" + }, + { + "bytes": "32", + "label": "signerTEEType", + "offset": 0, + "slot": "106", + "type": "mapping(address => enum TEEProverRegistry.TEEType)" } ] \ No newline at end of file diff --git a/snapshots/storageLayout/TEEProverRegistry.json b/snapshots/storageLayout/TEEProverRegistry.json index 93dcbfff3..30836afaa 100644 --- a/snapshots/storageLayout/TEEProverRegistry.json +++ b/snapshots/storageLayout/TEEProverRegistry.json @@ -75,5 +75,12 @@ "offset": 0, "slot": "105", "type": "struct EnumerableSetLib.AddressSet" + }, + { + "bytes": "32", + "label": "signerTEEType", + "offset": 0, + "slot": "106", + "type": "mapping(address => enum TEEProverRegistry.TEEType)" } ] \ No newline at end of file diff --git a/src/multiproof/AggregateVerifier.sol b/src/multiproof/AggregateVerifier.sol index 4c99f1d84..0c63db975 100644 --- a/src/multiproof/AggregateVerifier.sol +++ b/src/multiproof/AggregateVerifier.sol @@ -65,6 +65,9 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The minimum number of proofs required to resolve the game. uint256 public constant PROOF_THRESHOLD = 1; + + /// @notice TEE proof payload size: nitro signature(65) + tdx signature(65). + uint256 internal constant TEE_PROOF_BYTES_LENGTH = 130; //////////////////////////////////////////////////////////////// // Immutables // //////////////////////////////////////////////////////////////// @@ -877,7 +880,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } /// @notice Verifies a TEE proof for the current game. - /// @param proofBytes The proof: signature(65). + /// @param proofBytes The proof: nitro signature(65) + tdx signature(65). function _verifyTeeProof( bytes calldata proofBytes, address proposer, @@ -891,6 +894,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { internal view { + if (proofBytes.length != TEE_PROOF_BYTES_LENGTH) revert InvalidProof(); + bytes32 journal = keccak256( abi.encodePacked( proposer, @@ -1036,8 +1041,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } /// @notice Semantic version. - /// @custom:semver 0.1.0 + /// @custom:semver 0.2.0 function version() public pure virtual returns (string memory) { - return "0.1.0"; + return "0.2.0"; } } diff --git a/src/multiproof/mocks/MockDevTEEProverRegistry.sol b/src/multiproof/mocks/MockDevTEEProverRegistry.sol index 02cf538a5..e21c04b7c 100644 --- a/src/multiproof/mocks/MockDevTEEProverRegistry.sol +++ b/src/multiproof/mocks/MockDevTEEProverRegistry.sol @@ -20,13 +20,21 @@ contract DevTEEProverRegistry is TEEProverRegistry { TEEProverRegistry(nitroVerifier, tdxVerifier, factory) { } - /// @notice Registers a signer for testing (bypasses attestation verification). + /// @notice Registers a Nitro signer for testing (bypasses attestation verification). /// @dev Only callable by owner. For development/testing use only. /// The imageHash parameter is stored so isValidSigner() can validate against /// the current AggregateVerifier's TEE_IMAGE_HASH. /// @param signer The address of the signer to register. /// @param imageHash The TEE image hash to associate with this signer. function addDevSigner(address signer, bytes32 imageHash) external onlyOwner { - _registerSigner(signer, imageHash); + _registerSigner(signer, imageHash, TEEType.NITRO); + } + + /// @notice Registers a TDX signer for testing (bypasses attestation verification). + /// @dev Only callable by owner. For development/testing use only. + /// @param signer The address of the signer to register. + /// @param imageHash The TEE image hash to associate with this signer. + function addDevTDXSigner(address signer, bytes32 imageHash) external onlyOwner { + _registerSigner(signer, imageHash, TEEType.TDX); } } diff --git a/src/multiproof/tee/TEEProverRegistry.sol b/src/multiproof/tee/TEEProverRegistry.sol index fbce8f4bd..1079ba7df 100644 --- a/src/multiproof/tee/TEEProverRegistry.sol +++ b/src/multiproof/tee/TEEProverRegistry.sol @@ -24,9 +24,15 @@ import { GameType } from "src/dispute/lib/Types.sol"; /// Registration is PCR0-agnostic: any enclave with a valid attestation can register, /// enabling pre-registration before hardforks. PCR0 enforcement happens at proof-submission /// time in TEEVerifier, which checks signerImageHash against the AggregateVerifier's -/// TEE_IMAGE_HASH. +/// TEE_IMAGE_HASH and requires one Nitro signer plus one TDX signer. contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { using EnumerableSetLib for EnumerableSetLib.AddressSet; + enum TEEType { + NONE, + NITRO, + TDX + } + /// @notice Maximum age of an attestation document (60 minutes), in seconds. uint256 public constant MAX_AGE = 60 minutes; @@ -67,6 +73,9 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// Enables O(1) on-chain enumeration via `getRegisteredSigners()`. EnumerableSetLib.AddressSet internal _registeredSigners; + /// @notice Mapping of signer address to the attestation type used to register it. + mapping(address => TEEType) public signerTEEType; + /// @notice Emitted when a signer is registered. event SignerRegistered(address indexed signer); @@ -106,6 +115,12 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Thrown when the TDX verifier is not configured. error TDXVerifierNotSet(); + /// @notice Thrown when attempting to register a signer with no TEE type. + error InvalidTEEType(); + + /// @notice Thrown when a signer is already registered under another TEE type. + error SignerTEETypeMismatch(address signer, TEEType existingTEEType, TEEType newTEEType); + constructor(INitroEnclaveVerifier nitroVerifier, ITDXVerifier tdxVerifier, IDisputeGameFactory factory) { if (address(factory) == address(0)) revert DisputeGameFactoryNotSet(); if (address(tdxVerifier) == address(0)) revert TDXVerifierNotSet(); @@ -180,7 +195,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } address enclaveAddress = address(uint160(uint256(publicKeyHash))); - _registerSigner(enclaveAddress, pcr0Hash); + _registerSigner(enclaveAddress, pcr0Hash, TEEType.NITRO); } /// @notice Registers a signer using a ZK proof of Intel TDX DCAP quote verification. @@ -189,7 +204,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - _registerSigner(journal.signer, journal.imageHash); + _registerSigner(journal.signer, journal.imageHash, TEEType.TDX); emit TDXSignerRegistered(journal.signer, journal.imageHash, journal.reportDataSuffix); } @@ -199,6 +214,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { function deregisterSigner(address signer) external onlyOwnerOrManager { delete isRegisteredSigner[signer]; delete signerImageHash[signer]; + delete signerTEEType[signer]; _registeredSigners.remove(signer); emit SignerDeregistered(signer); } @@ -254,15 +270,22 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } /// @notice Semantic version. - /// @custom:semver 0.6.0 + /// @custom:semver 0.7.0 function version() public pure virtual returns (string memory) { - return "0.6.0"; + return "0.7.0"; } /// @dev Registers a signer and stores the image hash enforced by TEEVerifier at proof-submission time. - function _registerSigner(address signer, bytes32 imageHash) internal { + function _registerSigner(address signer, bytes32 imageHash, TEEType teeType) internal { + if (teeType == TEEType.NONE) revert InvalidTEEType(); + TEEType existingTEEType = signerTEEType[signer]; + if (existingTEEType != TEEType.NONE && existingTEEType != teeType) { + revert SignerTEETypeMismatch(signer, existingTEEType, teeType); + } + isRegisteredSigner[signer] = true; signerImageHash[signer] = imageHash; + signerTEEType[signer] = teeType; _registeredSigners.add(signer); emit SignerRegistered(signer); } diff --git a/src/multiproof/tee/TEEVerifier.sol b/src/multiproof/tee/TEEVerifier.sol index 73878af80..3a13b9758 100644 --- a/src/multiproof/tee/TEEVerifier.sol +++ b/src/multiproof/tee/TEEVerifier.sol @@ -10,19 +10,25 @@ import { TEEProverRegistry } from "./TEEProverRegistry.sol"; import { Verifier } from "../Verifier.sol"; /// @title TEEVerifier -/// @notice Stateless TEE proof verifier that validates signatures against registered signers. +/// @notice Stateless TEE proof verifier that validates Nitro and TDX signatures against registered signers. /// @dev This contract is designed to be used as the TEE_VERIFIER in the AggregateVerifier. -/// It verifies that proofs are signed by enclave addresses registered in TEEProverRegistry -/// via AWS Nitro attestation. PCR0 (enclave image hash) enforcement is handled by +/// It verifies that proofs are signed by a Nitro signer and a TDX signer registered in +/// TEEProverRegistry. PCR0 / TDX image hash enforcement is handled by /// AggregateVerifier, which bakes TEE_IMAGE_HASH into the journal that the enclave signs. /// The contract is intentionally stateless - all state related to output proposals and /// L1 origin verification is managed by the calling contract (e.g., AggregateVerifier). contract TEEVerifier is Verifier, ISemver { /// @notice The TEEProverRegistry contract that manages valid TEE signers. - /// @dev Signers are registered via AWS Nitro attestation in TEEProverRegistry. + /// @dev Signers are registered via Nitro or TDX attestation in TEEProverRegistry. TEEProverRegistry public immutable TEE_PROVER_REGISTRY; - /// @notice Thrown when the recovered signer is not a valid registered signer. + /// @notice Size of an ECDSA signature in bytes. + uint256 internal constant SIGNATURE_SIZE = 65; + + /// @notice Size of a TEE proof: proposer(20) + nitro signature(65) + tdx signature(65). + uint256 internal constant TEE_PROOF_SIZE = 20 + SIGNATURE_SIZE * 2; + + /// @notice Thrown when a recovered signer is not a valid registered signer. error InvalidSigner(address signer); /// @notice Thrown when the signer's registered image hash does not match the claimed imageId. @@ -37,6 +43,15 @@ contract TEEVerifier is Verifier, ISemver { /// @notice Thrown when the proposer is not a valid registered proposer. error InvalidProposer(address proposer); + /// @notice Thrown when both signatures recover to the same signer. + error DuplicateSigner(address signer); + + /// @notice Thrown when neither signature came from a Nitro-registered signer. + error MissingNitroSignature(); + + /// @notice Thrown when neither signature came from a TDX-registered signer. + error MissingTDXSignature(); + /// @notice Constructs the TEEVerifier contract. /// @param teeProverRegistry The TEEProverRegistry contract address. constructor( @@ -49,9 +64,10 @@ contract TEEVerifier is Verifier, ISemver { } /// @notice Verifies a TEE proof for a state transition. - /// @param proofBytes The proof: proposer(20) + signature(65) = 85 bytes. + /// @param proofBytes The proof: proposer(20) + two signatures(65 each) = 150 bytes. + /// One signature must recover to a Nitro signer and the other to a TDX signer. /// @param imageId The claimed TEE image hash (from the calling AggregateVerifier's TEE_IMAGE_HASH). - /// Validated against the signer's registered image hash to prevent cross-game-type attacks. + /// Validated against each signer's registered image hash to prevent cross-game-type attacks. /// @param journal The keccak256 hash of the proof's public inputs. /// @return valid Whether the proof is valid. function verify( @@ -65,23 +81,49 @@ contract TEEVerifier is Verifier, ISemver { notNullified returns (bool) { - if (proofBytes.length < 85) revert InvalidProofFormat(); + if (proofBytes.length != TEE_PROOF_SIZE) revert InvalidProofFormat(); address proposer = address(bytes20(proofBytes[0:20])); - bytes calldata signature = proofBytes[20:85]; + bytes calldata firstSignature = proofBytes[20:85]; + bytes calldata secondSignature = proofBytes[85:150]; - // Recover the signer from the signature - // The signature should be over the journal hash directly (not eth-signed-message prefixed) - (address signer, ECDSA.RecoverError err) = ECDSA.tryRecover(journal, signature); + address firstSigner = _recoverSigner(journal, firstSignature); + address secondSigner = _recoverSigner(journal, secondSignature); - if (err != ECDSA.RecoverError.NoError) { - revert InvalidSignature(); - } + if (firstSigner == secondSigner) revert DuplicateSigner(firstSigner); if (!TEE_PROVER_REGISTRY.isValidProposer(proposer)) { revert InvalidProposer(proposer); } + TEEProverRegistry.TEEType firstTEEType = _validateSigner(firstSigner, imageId); + TEEProverRegistry.TEEType secondTEEType = _validateSigner(secondSigner, imageId); + + if (firstTEEType != TEEProverRegistry.TEEType.NITRO && secondTEEType != TEEProverRegistry.TEEType.NITRO) { + revert MissingNitroSignature(); + } + if (firstTEEType != TEEProverRegistry.TEEType.TDX && secondTEEType != TEEProverRegistry.TEEType.TDX) { + revert MissingTDXSignature(); + } + + return true; + } + + function _recoverSigner(bytes32 journal, bytes calldata signature) internal pure returns (address signer) { + // The signature should be over the journal hash directly (not eth-signed-message prefixed). + ECDSA.RecoverError err; + (signer, err) = ECDSA.tryRecover(journal, signature); + if (err != ECDSA.RecoverError.NoError) revert InvalidSignature(); + } + + function _validateSigner( + address signer, + bytes32 imageId + ) + internal + view + returns (TEEProverRegistry.TEEType teeType) + { // Check that the signer is registered if (!TEE_PROVER_REGISTRY.isRegisteredSigner(signer)) { revert InvalidSigner(signer); @@ -95,12 +137,12 @@ contract TEEVerifier is Verifier, ISemver { revert ImageIdMismatch(registeredImageHash, imageId); } - return true; + teeType = TEE_PROVER_REGISTRY.signerTEEType(signer); } /// @notice Semantic version. - /// @custom:semver 0.2.0 + /// @custom:semver 0.3.0 function version() public pure virtual returns (string memory) { - return "0.2.0"; + return "0.3.0"; } } diff --git a/test/multiproof/AggregateVerifier.t.sol b/test/multiproof/AggregateVerifier.t.sol index c8bf67fd1..92f5b9028 100644 --- a/test/multiproof/AggregateVerifier.t.sol +++ b/test/multiproof/AggregateVerifier.t.sol @@ -268,7 +268,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) @@ -290,7 +290,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); _createAggregateVerifierGame( @@ -326,7 +326,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes @@ -352,7 +352,7 @@ contract AggregateVerifierTest is BaseTest { ); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), expectedHash, l1OriginNumber, rootClaim.raw()); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), expectedHash, l1OriginNumber, new bytes(130)); _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes diff --git a/test/multiproof/BaseTest.t.sol b/test/multiproof/BaseTest.t.sol index 55c4d311d..7fd9e0b0d 100644 --- a/test/multiproof/BaseTest.t.sol +++ b/test/multiproof/BaseTest.t.sol @@ -218,10 +218,33 @@ contract BaseTest is Test { bytes32 l1OriginHash = blockhash(block.number - 1); // Use the previous block number as l1OriginNumber uint256 l1OriginNumber = block.number - 1; - // Add some padding/signature data (65 bytes minimum for a signature) - bytes memory signature = abi.encodePacked(salt, bytes32(0), bytes32(0), uint8(27)); + return abi.encodePacked(uint8(proofType), l1OriginHash, l1OriginNumber, _generateProofBody(salt, proofType)); + } + + function _generateProposalProof( + bytes memory salt, + AggregateVerifier.ProofType proofType + ) + internal + pure + returns (bytes memory) + { + return abi.encodePacked(uint8(proofType), _generateProofBody(salt, proofType)); + } - return abi.encodePacked(uint8(proofType), l1OriginHash, l1OriginNumber, signature); + function _generateProofBody( + bytes memory salt, + AggregateVerifier.ProofType proofType + ) + internal + pure + returns (bytes memory) + { + if (proofType == AggregateVerifier.ProofType.TEE) { + bytes32 saltHash = keccak256(salt); + return abi.encodePacked(saltHash, bytes32(0), uint8(27), saltHash, bytes32(uint256(1)), uint8(28)); + } + return abi.encodePacked(salt, bytes32(0), bytes32(0), uint8(27)); } function _generateIntermediateRootsExceptLast(uint256 l2BlockNumber) internal pure returns (bytes memory) { diff --git a/test/multiproof/Challenge.t.sol b/test/multiproof/Challenge.t.sol index b36cf03e4..29399e507 100644 --- a/test/multiproof/Challenge.t.sol +++ b/test/multiproof/Challenge.t.sol @@ -80,7 +80,7 @@ contract ChallengeTest is BaseTest { ); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); vm.expectRevert(AggregateVerifier.InvalidProofType.selector); game1.challenge(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); @@ -170,7 +170,7 @@ contract ChallengeTest is BaseTest { ); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); @@ -284,7 +284,7 @@ contract ChallengeTest is BaseTest { _createAggregateVerifierGame(TEE_PROVER, rootClaimB, currentL2BlockNumber, address(gameA), teeProofB); Claim rootNullifyB = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee-nullify-b"))); - bytes memory teeNullifyB = _generateProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); + bytes memory teeNullifyB = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); uint256 lastIdx = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; gameB.nullify(teeNullifyB, lastIdx, rootNullifyB.raw()); assertTrue(teeVerifier.nullified()); diff --git a/test/multiproof/Nullify.t.sol b/test/multiproof/Nullify.t.sol index 5bc8c4aa9..3a28acc8c 100644 --- a/test/multiproof/Nullify.t.sol +++ b/test/multiproof/Nullify.t.sol @@ -20,7 +20,7 @@ contract NullifyTest is BaseTest { ); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); @@ -87,7 +87,7 @@ contract NullifyTest is BaseTest { assertEq(game.expectedResolution().raw(), block.timestamp + 1 days); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); @@ -195,7 +195,7 @@ contract NullifyTest is BaseTest { assertEq(gameA.proofCount(), 1); Claim rootClaimNullify = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "nullify-b"))); - bytes memory teeProofNullify = _generateProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); + bytes memory teeProofNullify = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); uint256 lastIntermediateIdx = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; gameB.nullify(teeProofNullify, lastIntermediateIdx, rootClaimNullify.raw()); diff --git a/test/multiproof/TEEProverRegistry.t.sol b/test/multiproof/TEEProverRegistry.t.sol index ecaa2c06b..ebab8ebac 100644 --- a/test/multiproof/TEEProverRegistry.t.sol +++ b/test/multiproof/TEEProverRegistry.t.sol @@ -100,7 +100,7 @@ contract TEEProverRegistryTest is Test { function testInitialization() public view { assertEq(teeProverRegistry.owner(), owner); assertEq(teeProverRegistry.manager(), manager); - assertEq(teeProverRegistry.version(), "0.6.0"); + assertEq(teeProverRegistry.version(), "0.7.0"); } function testInitializationWithProposers() public { @@ -216,6 +216,35 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); assertTrue(teeProverRegistry.isValidSigner(signer)); + assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.NITRO)); + } + + function testAddDevTDXSigner() public { + address signer = makeAddr("dev-tdx-signer"); + + vm.prank(owner); + teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); + + assertTrue(teeProverRegistry.isValidSigner(signer)); + assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); + } + + function testRegisteringSameSignerWithDifferentTEETypeFails() public { + address signer = makeAddr("dev-signer"); + + vm.prank(owner); + teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); + + vm.prank(owner); + vm.expectRevert( + abi.encodeWithSelector( + TEEProverRegistry.SignerTEETypeMismatch.selector, + signer, + TEEProverRegistry.TEEType.NITRO, + TEEProverRegistry.TEEType.TDX + ) + ); + teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); } // ============ MAX_AGE Tests ============ diff --git a/test/multiproof/TEEProverRegistryTDX.t.sol b/test/multiproof/TEEProverRegistryTDX.t.sol index 43bd99f7f..725daf990 100644 --- a/test/multiproof/TEEProverRegistryTDX.t.sol +++ b/test/multiproof/TEEProverRegistryTDX.t.sol @@ -88,6 +88,7 @@ contract TEEProverRegistryTDXTest is Test { assertTrue(registry.isRegisteredSigner(journal.signer)); assertEq(registry.signerImageHash(journal.signer), IMAGE_HASH); + assertEq(uint8(registry.signerTEEType(journal.signer)), uint8(TEEProverRegistry.TEEType.TDX)); assertTrue(registry.isValidSigner(journal.signer)); } diff --git a/test/multiproof/TEEVerifier.t.sol b/test/multiproof/TEEVerifier.t.sol index 19faa27f1..83b397f09 100644 --- a/test/multiproof/TEEVerifier.t.sol +++ b/test/multiproof/TEEVerifier.t.sol @@ -46,9 +46,12 @@ contract TEEVerifierTest is Test { ProxyAdmin public proxyAdmin; MockAnchorStateRegistry public anchorStateRegistry; - // Test signer - we'll derive address from private key - uint256 internal constant SIGNER_PRIVATE_KEY = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - address internal signerAddress; + uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = + 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + uint256 internal constant TDX_SIGNER_PRIVATE_KEY = + 0x2234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + address internal nitroSignerAddress; + address internal tdxSignerAddress; bytes32 internal constant IMAGE_ID = keccak256("test-image-id"); uint32 internal constant TEST_GAME_TYPE = 621; @@ -59,8 +62,8 @@ contract TEEVerifierTest is Test { function setUp() public { owner = address(this); - // Derive signer address from private key - signerAddress = vm.addr(SIGNER_PRIVATE_KEY); + nitroSignerAddress = vm.addr(NITRO_SIGNER_PRIVATE_KEY); + tdxSignerAddress = vm.addr(TDX_SIGNER_PRIVATE_KEY); // Deploy mock factory and verifier MockAggregateVerifierForVerifier mockVerifier = new MockAggregateVerifierForVerifier(IMAGE_ID); @@ -86,8 +89,9 @@ contract TEEVerifierTest is Test { teeProverRegistry = DevTEEProverRegistry(address(proxy)); - // Register the signer with the correct image hash - teeProverRegistry.addDevSigner(signerAddress, IMAGE_ID); + // Register one Nitro signer and one TDX signer with the correct image hash. + teeProverRegistry.addDevSigner(nitroSignerAddress, IMAGE_ID); + teeProverRegistry.addDevTDXSigner(tdxSignerAddress, IMAGE_ID); // Set the proposer as valid teeProverRegistry.setProposer(PROPOSER, true); @@ -99,45 +103,29 @@ contract TEEVerifierTest is Test { ); } - function testVerifyValidSignature() public view { - // Create a journal hash + function testVerifyValidSignatures() public view { bytes32 journal = keccak256("test-journal"); - - // Sign the journal with the signer's private key - (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_PRIVATE_KEY, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - // Construct proof: proposer(20) + signature(65) = 85 bytes - bytes memory proofBytes = abi.encodePacked(PROPOSER, signature); - - // Verify should return true regardless of imageId (enforced via journal hash, not registry) + bytes memory proofBytes = _proofBytes(journal); bool result = verifier.verify(proofBytes, IMAGE_ID, journal); assertTrue(result); } function testVerifyFailsWithInvalidSignature() public { bytes32 journal = keccak256("test-journal"); - - // Create an invalid signature (all zeros except v) bytes memory invalidSignature = new bytes(65); invalidSignature[64] = bytes1(uint8(27)); // Set v to 27 - bytes memory proofBytes = abi.encodePacked(PROPOSER, invalidSignature); + bytes memory proofBytes = _buildProof(PROPOSER, invalidSignature, _signature(TDX_SIGNER_PRIVATE_KEY, journal)); vm.expectRevert(TEEVerifier.InvalidSignature.selector); verifier.verify(proofBytes, IMAGE_ID, journal); } function testVerifyFailsWithInvalidProposer() public { - // Create a journal hash bytes32 journal = keccak256("test-journal"); - - // Sign the journal with the signer's private key - (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_PRIVATE_KEY, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - // Construct proof: proposer(20) + signature(65) = 85 bytes - bytes memory proofBytes = abi.encodePacked(address(0), signature); + bytes memory proofBytes = _buildProof( + address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, journal), _signature(TDX_SIGNER_PRIVATE_KEY, journal) + ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); verifier.verify(proofBytes, IMAGE_ID, journal); @@ -150,10 +138,8 @@ contract TEEVerifierTest is Test { bytes32 journal = keccak256("test-journal"); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(unregisteredKey, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - bytes memory proofBytes = abi.encodePacked(PROPOSER, signature); + bytes memory proofBytes = + _buildProof(PROPOSER, _signature(unregisteredKey, journal), _signature(TDX_SIGNER_PRIVATE_KEY, journal)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); verifier.verify(proofBytes, IMAGE_ID, journal); @@ -161,11 +147,7 @@ contract TEEVerifierTest is Test { function testVerifyFailsWithImageIdMismatch() public { bytes32 journal = keccak256("test-journal"); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_PRIVATE_KEY, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - bytes memory proofBytes = abi.encodePacked(PROPOSER, signature); + bytes memory proofBytes = _proofBytes(journal); // Different imageId should fail — signer was registered with IMAGE_ID bytes32 wrongImageId = keccak256("different-image"); @@ -173,10 +155,47 @@ contract TEEVerifierTest is Test { verifier.verify(proofBytes, wrongImageId, journal); } + function testVerifyFailsWithDuplicateSigner() public { + bytes32 journal = keccak256("test-journal"); + bytes memory nitroSignature = _signature(NITRO_SIGNER_PRIVATE_KEY, journal); + bytes memory proofBytes = _buildProof(PROPOSER, nitroSignature, nitroSignature); + + vm.expectRevert(abi.encodeWithSelector(TEEVerifier.DuplicateSigner.selector, nitroSignerAddress)); + verifier.verify(proofBytes, IMAGE_ID, journal); + } + + function testVerifyFailsWithoutNitroSignature() public { + bytes32 journal = keccak256("test-journal"); + uint256 secondTDXKey = 0x3234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + + vm.prank(owner); + teeProverRegistry.addDevTDXSigner(vm.addr(secondTDXKey), IMAGE_ID); + + bytes memory proofBytes = + _buildProof(PROPOSER, _signature(TDX_SIGNER_PRIVATE_KEY, journal), _signature(secondTDXKey, journal)); + + vm.expectRevert(TEEVerifier.MissingNitroSignature.selector); + verifier.verify(proofBytes, IMAGE_ID, journal); + } + + function testVerifyFailsWithoutTDXSignature() public { + bytes32 journal = keccak256("test-journal"); + uint256 secondNitroKey = 0x4234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + + vm.prank(owner); + teeProverRegistry.addDevSigner(vm.addr(secondNitroKey), IMAGE_ID); + + bytes memory proofBytes = + _buildProof(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal), _signature(secondNitroKey, journal)); + + vm.expectRevert(TEEVerifier.MissingTDXSignature.selector); + verifier.verify(proofBytes, IMAGE_ID, journal); + } + function testVerifyFailsWithInvalidProofFormat() public { bytes32 journal = keccak256("test-journal"); - // Proof too short (less than 85 bytes) + // Proof too short (less than proposer + two signatures). bytes memory shortProof = new bytes(50); vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); @@ -186,4 +205,27 @@ contract TEEVerifierTest is Test { function testConstants() public view { assertEq(address(verifier.TEE_PROVER_REGISTRY()), address(teeProverRegistry)); } + + function _proofBytes(bytes32 journal) internal view returns (bytes memory) { + return _buildProof( + PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal), _signature(TDX_SIGNER_PRIVATE_KEY, journal) + ); + } + + function _signature(uint256 privateKey, bytes32 journal) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, journal); + return abi.encodePacked(r, s, v); + } + + function _buildProof( + address proposer, + bytes memory firstSignature, + bytes memory secondSignature + ) + internal + pure + returns (bytes memory) + { + return abi.encodePacked(proposer, firstSignature, secondSignature); + } } From 0cd4fc87c63c1ef4f0a9c95825231c6d81f1f09b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 14:08:47 -0400 Subject: [PATCH 005/146] fmt --- scripts/multiproof/DeployDevNoNitro.s.sol | 16 ++++++++-------- snapshots/semver-lock.json | 12 ++---------- src/multiproof/tee/TEEVerifier.sol | 5 +---- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 50b383af1..d142b952c 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -26,15 +26,15 @@ pragma solidity 0.8.15; * SIGNER REGISTRATION (SIMPLIFIED) * ───────────────────────────────────────────────────────────────────────────────── * - * After deployment, register one dev Nitro signer and one dev TDX signer: + * After deployment, register one dev Nitro signer and one dev TDX signer: * - * cast send $TEE_PROVER_REGISTRY \ - * "addDevSigner(address,bytes32)" $NITRO_SIGNER_ADDRESS $TEE_IMAGE_HASH \ - * --private-key $OWNER_KEY --rpc-url $RPC_URL - * - * cast send $TEE_PROVER_REGISTRY \ - * "addDevTDXSigner(address,bytes32)" $TDX_SIGNER_ADDRESS $TEE_IMAGE_HASH \ - * --private-key $OWNER_KEY --rpc-url $RPC_URL + * cast send $TEE_PROVER_REGISTRY \ + * "addDevSigner(address,bytes32)" $NITRO_SIGNER_ADDRESS $TEE_IMAGE_HASH \ + * --private-key $OWNER_KEY --rpc-url $RPC_URL + * + * cast send $TEE_PROVER_REGISTRY \ + * "addDevTDXSigner(address,bytes32)" $TDX_SIGNER_ADDRESS $TEE_IMAGE_HASH \ + * --private-key $OWNER_KEY --rpc-url $RPC_URL * * No attestation, PCR0 registration, or certificate validation required. * diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 4e75dca07..26635aedc 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -144,11 +144,7 @@ "sourceCodeHash": "0x955bd0c9b47e43219865e4e92abf28d916c96de20cbdf2f94c8ab14d02083759" }, "src/multiproof/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0xea8f199813975e1307fa964b70e30832cf5ea1e848ffa78368cc565cc9e919d1", - "sourceCodeHash": "0x9a48d272dd19ccd6df51999dcb6117308aea712f6d3731bdcc8b8d02b867c348" - }, - "src/multiproof/AggregateVerifier.sol:AggregateVerifier:dispute": { - "initCodeHash": "0xa92e0ce6d4dddea92719bad6f1e964c3abf838ec892a12fc16c4a5b9fbfb9af1", + "initCodeHash": "0x9a77922f9f05c738e1a0884a1803a19302cd046e122af9ff38b8c3dc43e89409", "sourceCodeHash": "0x9a48d272dd19ccd6df51999dcb6117308aea712f6d3731bdcc8b8d02b867c348" }, "src/multiproof/tee/NitroEnclaveVerifier.sol:NitroEnclaveVerifier": { @@ -165,11 +161,7 @@ }, "src/multiproof/tee/TEEVerifier.sol:TEEVerifier": { "initCodeHash": "0x37883165bb1924631d830772a811f172059f529fd44ff34694d2511ff52c29c9", - "sourceCodeHash": "0x549df133a04af10305dcc5e70818ac49ac47546c79c957151184f9a21223d6c1" - }, - "src/multiproof/tee/TEEVerifier.sol:TEEVerifier:dispute": { - "initCodeHash": "0x37883165bb1924631d830772a811f172059f529fd44ff34694d2511ff52c29c9", - "sourceCodeHash": "0x549df133a04af10305dcc5e70818ac49ac47546c79c957151184f9a21223d6c1" + "sourceCodeHash": "0x506aba49b3b5b29e565a151c828b8f24662ed5794418bdaa2be149b085b05a7e" }, "src/multiproof/zk/ZKVerifier.sol:ZkVerifier": { "initCodeHash": "0xcdc5bd5fa8ffad76358b8f34a53b8c0cb36540176c31f82d52546373d0bd5245", diff --git a/src/multiproof/tee/TEEVerifier.sol b/src/multiproof/tee/TEEVerifier.sol index 3a13b9758..0fb0e50b5 100644 --- a/src/multiproof/tee/TEEVerifier.sol +++ b/src/multiproof/tee/TEEVerifier.sol @@ -116,10 +116,7 @@ contract TEEVerifier is Verifier, ISemver { if (err != ECDSA.RecoverError.NoError) revert InvalidSignature(); } - function _validateSigner( - address signer, - bytes32 imageId - ) + function _validateSigner(address signer, bytes32 imageId) internal view returns (TEEProverRegistry.TEEType teeType) From c1a2a0aed34ac65e592ecc6c7d14ce790c4af06c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 14:30:23 -0400 Subject: [PATCH 006/146] clean DeployDevWithTDX script --- interfaces/multiproof/tee/ITDXVerifier.sol | 3 +++ scripts/multiproof/DeployDevWithTDX.s.sol | 8 ++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/interfaces/multiproof/tee/ITDXVerifier.sol b/interfaces/multiproof/tee/ITDXVerifier.sol index d0eb7f3ad..24fc2a6ed 100644 --- a/interfaces/multiproof/tee/ITDXVerifier.sol +++ b/interfaces/multiproof/tee/ITDXVerifier.sol @@ -86,4 +86,7 @@ interface ITDXVerifier { /// @notice Returns whether a TCB status is accepted by verifier policy. function allowedTcbStatuses(TDXTcbStatus status) external view returns (bool); + + /// @notice Updates the address authorized to submit verified proofs. + function setProofSubmitter(address newProofSubmitter) external; } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 592bee867..bc9776b49 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -38,10 +38,6 @@ import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; -interface IProofSubmitterSetter { - function setProofSubmitter(address newProofSubmitter) external; -} - contract DeployDevWithTDX is Script { uint256 public constant INIT_BOND = 0.00001 ether; address public constant DEFAULT_TDX_REGISTRATION_MANAGER = 0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7; @@ -62,7 +58,6 @@ contract DeployDevWithTDX is Script { function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); cfg.read(Config.deployConfigPath()); - nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); } function run(address tdxVerifier) public { @@ -70,6 +65,7 @@ contract DeployDevWithTDX is Script { } function run(address tdxVerifier, address registrationManager) public { + nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be set in config"); require(registrationManager != address(0), "registrationManager must be non-zero"); @@ -124,7 +120,7 @@ contract DeployDevWithTDX is Script { registryProxy.changeAdmin(address(0xdead)); teeProverRegistryProxy = address(registryProxy); - IProofSubmitterSetter(tdxVerifierAddr).setProofSubmitter(teeProverRegistryProxy); + ITDXVerifier(tdxVerifierAddr).setProofSubmitter(teeProverRegistryProxy); teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); } From cd1af349bb54e53035bbe613a59fb27d9e3dc1c2 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 14:34:44 -0400 Subject: [PATCH 007/146] clean DeployTDXVerifier script --- scripts/multiproof/DeployTDXVerifier.s.sol | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index 0b1ff6fcd..7deadf4a0 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -29,9 +29,7 @@ import { TDXVerifier } from "src/multiproof/tee/TDXVerifier.sol"; contract DeployTDXVerifier is Script { /// @notice Maximum TDX quote age accepted by TDXVerifier. - uint64 public constant TDX_MAX_TIME_DIFF = 3600; - - address public tdxVerifier; + uint64 internal constant TDX_MAX_TIME_DIFF = 3600; /// @param owner Owner for TDXVerifier. /// @param risc0VerifierRouter Existing RISC Zero verifier router. @@ -58,13 +56,10 @@ contract DeployTDXVerifier is Script { console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); console.log("Max Time Diff:", TDX_MAX_TIME_DIFF); console.log(""); - console.log("NOTE: proofSubmitter is set to owner as a temporary placeholder."); - console.log(" DeployDevWithTDX.s.sol updates it to TEEProverRegistry."); - console.log(""); vm.startBroadcast(); - tdxVerifier = address( + address tdxVerifier = address( new TDXVerifier( owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, ZkCoProcessorType.RiscZero, zkConfig, allowedStatuses ) @@ -76,10 +71,10 @@ contract DeployTDXVerifier is Script { console.log(""); console.log(">>> Use this address as the DeployDevWithTDX.s.sol argument <<<"); - _writeOutput(risc0VerifierRouter, tdxVerifierId, intelRootCaHash); + _writeOutput(tdxVerifier, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); } - function _writeOutput(address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) internal { + function _writeOutput(address tdxVerifier, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) internal { string memory key = "deployment"; vm.serializeAddress(key, "TDXVerifier", tdxVerifier); vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); From 74d593aa133a5739270ef97007637b0759583822 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 14:41:40 -0400 Subject: [PATCH 008/146] clean TDXVerifier --- src/multiproof/tee/TDXVerifier.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/multiproof/tee/TDXVerifier.sol b/src/multiproof/tee/TDXVerifier.sol index 14ed8ea9c..bd3ec4cd8 100644 --- a/src/multiproof/tee/TDXVerifier.sol +++ b/src/multiproof/tee/TDXVerifier.sol @@ -27,12 +27,12 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { /// @notice Maximum accepted age of a TDX quote, in seconds. uint64 public maxTimeDiff; - /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. - bytes32 public rootCaHash; - /// @notice Address authorized to submit TDX proofs, expected to be the TDX-aware registry. address public proofSubmitter; + /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. + bytes32 public rootCaHash; + /// @notice Configuration mapping for each supported ZK coprocessor type. mapping(ZkCoProcessorType => ZkCoProcessorConfig) public zkConfig; @@ -189,7 +189,8 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { function _verifyJournal(TDXVerifierJournal memory journal) internal view { if (journal.result != TDXVerificationResult.Success) revert TDXVerificationFailed(journal.result); - if (journal.rootCaHash != rootCaHash) revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); + bytes32 expectedRootCaHash = rootCaHash; + if (journal.rootCaHash != expectedRootCaHash) revert RootCaHashMismatch(expectedRootCaHash, journal.rootCaHash); if (!allowedTcbStatuses[journal.tcbStatus]) revert TcbStatusNotAllowed(journal.tcbStatus); if (journal.collateralExpiration <= block.timestamp) revert CollateralExpired(journal.collateralExpiration); @@ -228,6 +229,7 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { function _derivePublicKeyHash(bytes memory publicKey) internal pure returns (bytes32 publicKeyHash) { if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); + // Skip the 32-byte length word and the 0x04 uncompressed prefix; hash the 64-byte X||Y. assembly { publicKeyHash := keccak256(add(publicKey, 0x21), 64) } From 4766367d3fe07a61b6eb43046e9f49e5eaf10b29 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 14:48:02 -0400 Subject: [PATCH 009/146] clean TEEProverRegistry --- src/multiproof/tee/TEEProverRegistry.sol | 27 ++++++++-------------- test/multiproof/TEEProverRegistryTDX.t.sol | 2 ++ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/multiproof/tee/TEEProverRegistry.sol b/src/multiproof/tee/TEEProverRegistry.sol index 1079ba7df..81b255c8d 100644 --- a/src/multiproof/tee/TEEProverRegistry.sol +++ b/src/multiproof/tee/TEEProverRegistry.sol @@ -147,14 +147,8 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @dev Validates that the new game type has an AggregateVerifier with a non-zero TEE_IMAGE_HASH. /// @param gameType_ The new game type ID. function setGameType(GameType gameType_) external onlyOwner { - // Validate the new game type points to a valid AggregateVerifier with a TEE_IMAGE_HASH - GameType oldGameType = gameType; + if (_getExpectedImageHash(gameType_) == bytes32(0)) revert InvalidGameType(); gameType = gameType_; - bytes32 imageHash = _getExpectedImageHash(); - if (imageHash == bytes32(0)) { - gameType = oldGameType; - revert InvalidGameType(); - } emit GameTypeUpdated(gameType_); } @@ -179,18 +173,14 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { // the attestation is generated and when it is submitted to this contract. if (journal.timestamp / MS_PER_SECOND + MAX_AGE <= block.timestamp) revert AttestationTooOld(); - // Extract the attestation's PCR0 and store it for TEEVerifier to check at - // proof-submission time. No comparison against the current TEE_IMAGE_HASH - // here — the registry accepts any valid attestation. bytes32 pcr0Hash = _extractPCR0Hash(journal.pcrs); - // The publicKey is encoded in ANSI X9.62 format: 0x04 || x || y (65 bytes). - // We skip the first byte (0x04 prefix) when hashing to derive the address. + // publicKey is ANSI X9.62 uncompressed: 0x04 || x || y (65 bytes). Skip the prefix and hash + // only the 64-byte x||y to derive the signer address. bytes memory pubKey = journal.publicKey; if (pubKey.length != 65) revert InvalidPublicKey(); bytes32 publicKeyHash; assembly { - // Length is hardcoded to 64 to skip the 0x04 prefix and hash only the x and y coordinates publicKeyHash := keccak256(add(pubKey, 0x21), 64) } address enclaveAddress = address(uint160(uint256(publicKeyHash))); @@ -226,7 +216,8 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @param signer The address to check. /// @return True if the signer is registered with the current image hash, false otherwise. function isValidSigner(address signer) external view returns (bool) { - return isRegisteredSigner[signer] && signerImageHash[signer] == _getExpectedImageHash(); + if (!isRegisteredSigner[signer]) return false; + return signerImageHash[signer] == _getExpectedImageHash(gameType); } /// @notice Returns all currently registered signer addresses. @@ -240,7 +231,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Returns the expected TEE image hash from the current AggregateVerifier. /// @return The TEE_IMAGE_HASH from the AggregateVerifier registered in the factory. function getExpectedImageHash() external view returns (bytes32) { - return _getExpectedImageHash(); + return _getExpectedImageHash(gameType); } /// @notice Initializes the contract with owner, manager, proposers, and game type. @@ -290,9 +281,9 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { emit SignerRegistered(signer); } - /// @dev Reads TEE_IMAGE_HASH from the AggregateVerifier registered in the factory. - function _getExpectedImageHash() internal view returns (bytes32) { - address impl = address(DISPUTE_GAME_FACTORY.gameImpls(gameType)); + /// @dev Reads TEE_IMAGE_HASH from the AggregateVerifier registered in the factory for `gameType_`. + function _getExpectedImageHash(GameType gameType_) internal view returns (bytes32) { + address impl = address(DISPUTE_GAME_FACTORY.gameImpls(gameType_)); // AggregateVerifier.TEE_IMAGE_HASH() selector (bool success, bytes memory data) = impl.staticcall(abi.encodeWithSignature("TEE_IMAGE_HASH()")); if (!success || data.length != 32) revert ImageHashReadFailed(); diff --git a/test/multiproof/TEEProverRegistryTDX.t.sol b/test/multiproof/TEEProverRegistryTDX.t.sol index 725daf990..0df45c5b6 100644 --- a/test/multiproof/TEEProverRegistryTDX.t.sol +++ b/test/multiproof/TEEProverRegistryTDX.t.sol @@ -65,6 +65,8 @@ contract MockTDXVerifierForRegistry is ITDXVerifier { function allowedTcbStatuses(TDXTcbStatus) external pure returns (bool) { return true; } + + function setProofSubmitter(address) external { } } contract TEEProverRegistryTDXTest is Test { From f120667eb1cdc981f74005d31fd0b76cd7292c4a Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 14:52:21 -0400 Subject: [PATCH 010/146] clean TEEVerifier --- src/multiproof/tee/TEEVerifier.sol | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/multiproof/tee/TEEVerifier.sol b/src/multiproof/tee/TEEVerifier.sol index 0fb0e50b5..6fa772ee6 100644 --- a/src/multiproof/tee/TEEVerifier.sol +++ b/src/multiproof/tee/TEEVerifier.sol @@ -84,21 +84,21 @@ contract TEEVerifier is Verifier, ISemver { if (proofBytes.length != TEE_PROOF_SIZE) revert InvalidProofFormat(); address proposer = address(bytes20(proofBytes[0:20])); - bytes calldata firstSignature = proofBytes[20:85]; - bytes calldata secondSignature = proofBytes[85:150]; - - address firstSigner = _recoverSigner(journal, firstSignature); - address secondSigner = _recoverSigner(journal, secondSignature); - - if (firstSigner == secondSigner) revert DuplicateSigner(firstSigner); - if (!TEE_PROVER_REGISTRY.isValidProposer(proposer)) { revert InvalidProposer(proposer); } + bytes calldata firstSignature = proofBytes[20:20 + SIGNATURE_SIZE]; + bytes calldata secondSignature = proofBytes[20 + SIGNATURE_SIZE:TEE_PROOF_SIZE]; + + address firstSigner = _recoverSigner(journal, firstSignature); + address secondSigner = _recoverSigner(journal, secondSignature); + TEEProverRegistry.TEEType firstTEEType = _validateSigner(firstSigner, imageId); TEEProverRegistry.TEEType secondTEEType = _validateSigner(secondSigner, imageId); + if (firstSigner == secondSigner) revert DuplicateSigner(firstSigner); + if (firstTEEType != TEEProverRegistry.TEEType.NITRO && secondTEEType != TEEProverRegistry.TEEType.NITRO) { revert MissingNitroSignature(); } @@ -121,20 +121,17 @@ contract TEEVerifier is Verifier, ISemver { view returns (TEEProverRegistry.TEEType teeType) { - // Check that the signer is registered - if (!TEE_PROVER_REGISTRY.isRegisteredSigner(signer)) { - revert InvalidSigner(signer); - } + // A registered signer always has a non-NONE TEE type, so this single read also + // serves as the registration check (saves an SLOAD versus calling isRegisteredSigner). + teeType = TEE_PROVER_REGISTRY.signerTEEType(signer); + if (teeType == TEEProverRegistry.TEEType.NONE) revert InvalidSigner(signer); - // Check that the signer's registered image hash matches the calling AggregateVerifier's imageId. - // This prevents a signer registered under one enclave image from being used in a game + // Prevents a signer registered under one enclave image from being used in a game // that expects a different image (e.g., after an upgrade or across game types). bytes32 registeredImageHash = TEE_PROVER_REGISTRY.signerImageHash(signer); if (registeredImageHash != imageId) { revert ImageIdMismatch(registeredImageHash, imageId); } - - teeType = TEE_PROVER_REGISTRY.signerTEEType(signer); } /// @notice Semantic version. From 042548548d2254b30fe9cc657b7186ecb0e14256 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 3 May 2026 15:12:43 -0400 Subject: [PATCH 011/146] clean TDXVerifier --- scripts/multiproof/DeployTDXVerifier.s.sol | 9 ++++++++- snapshots/semver-lock.json | 12 ++++++------ snapshots/storageLayout/TDXVerifier.json | 18 +++++++++--------- src/multiproof/tee/TDXVerifier.sol | 4 +++- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index 7deadf4a0..e5f955771 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -74,7 +74,14 @@ contract DeployTDXVerifier is Script { _writeOutput(tdxVerifier, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); } - function _writeOutput(address tdxVerifier, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) internal { + function _writeOutput( + address tdxVerifier, + address risc0VerifierRouter, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + { string memory key = "deployment"; vm.serializeAddress(key, "TDXVerifier", tdxVerifier); vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 26635aedc..c1a707102 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -152,16 +152,16 @@ "sourceCodeHash": "0xbedb66738a0ab18600b2ed21de51d552a210c4cc71b2c971440e325791dc3ce7" }, "src/multiproof/tee/TDXVerifier.sol:TDXVerifier": { - "initCodeHash": "0x0f511b2f67bd82ff976935ca0a178c3fa9d9cf9b89c6e67b3fd7f1006f5e5147", - "sourceCodeHash": "0x7cdf2abefaefa3126960103aa810699973c1c47147114f8a92e63c427b9b1656" + "initCodeHash": "0xdfd5a00dc139565ec7102fd5ac05d91f923bfe1ef724e16a4cdfbcb60cd19c3c", + "sourceCodeHash": "0xb28e2632fc3591ecdb2d96cbc6b99a08854048a513f444d8282511293a0739e2" }, "src/multiproof/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0xf96d42f3dc474f4f8d29af0e7fbb59b13035a61bc53903ddd14814ae0b3f2f73", - "sourceCodeHash": "0x12bf06d225633d1983746ea06e72469388a3db63fd7d8a10144b4267f13f0b5d" + "initCodeHash": "0x5304faf5062088ee90dbd08eedbdacea39bdf48ffca689228e63d8f71f952bfb", + "sourceCodeHash": "0x37ed4874679b18cdf447a4d1890ecfeff648b0d762d8302827cd5e823b0af3ce" }, "src/multiproof/tee/TEEVerifier.sol:TEEVerifier": { - "initCodeHash": "0x37883165bb1924631d830772a811f172059f529fd44ff34694d2511ff52c29c9", - "sourceCodeHash": "0x506aba49b3b5b29e565a151c828b8f24662ed5794418bdaa2be149b085b05a7e" + "initCodeHash": "0x6af4a7e162b2589076869a920a1f5b8b458d7f0c555068fdd2b82d44a51f3b0f", + "sourceCodeHash": "0xb0453b1fee0d1f2d1f53e22cbac4c43dd9e33ab33ac373ec54f81d433aeabbd5" }, "src/multiproof/zk/ZKVerifier.sol:ZkVerifier": { "initCodeHash": "0xcdc5bd5fa8ffad76358b8f34a53b8c0cb36540176c31f82d52546373d0bd5245", diff --git a/snapshots/storageLayout/TDXVerifier.json b/snapshots/storageLayout/TDXVerifier.json index 1a05d86c7..2f13d341b 100644 --- a/snapshots/storageLayout/TDXVerifier.json +++ b/snapshots/storageLayout/TDXVerifier.json @@ -6,6 +6,13 @@ "slot": "0", "type": "uint64" }, + { + "bytes": "20", + "label": "proofSubmitter", + "offset": 8, + "slot": "0", + "type": "address" + }, { "bytes": "32", "label": "rootCaHash", @@ -13,25 +20,18 @@ "slot": "1", "type": "bytes32" }, - { - "bytes": "20", - "label": "proofSubmitter", - "offset": 0, - "slot": "2", - "type": "address" - }, { "bytes": "32", "label": "zkConfig", "offset": 0, - "slot": "3", + "slot": "2", "type": "mapping(enum ZkCoProcessorType => struct ZkCoProcessorConfig)" }, { "bytes": "32", "label": "allowedTcbStatuses", "offset": 0, - "slot": "4", + "slot": "3", "type": "mapping(enum TDXTcbStatus => bool)" } ] \ No newline at end of file diff --git a/src/multiproof/tee/TDXVerifier.sol b/src/multiproof/tee/TDXVerifier.sol index bd3ec4cd8..d8175830a 100644 --- a/src/multiproof/tee/TDXVerifier.sol +++ b/src/multiproof/tee/TDXVerifier.sol @@ -190,7 +190,9 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { function _verifyJournal(TDXVerifierJournal memory journal) internal view { if (journal.result != TDXVerificationResult.Success) revert TDXVerificationFailed(journal.result); bytes32 expectedRootCaHash = rootCaHash; - if (journal.rootCaHash != expectedRootCaHash) revert RootCaHashMismatch(expectedRootCaHash, journal.rootCaHash); + if (journal.rootCaHash != expectedRootCaHash) { + revert RootCaHashMismatch(expectedRootCaHash, journal.rootCaHash); + } if (!allowedTcbStatuses[journal.tcbStatus]) revert TcbStatusNotAllowed(journal.tcbStatus); if (journal.collateralExpiration <= block.timestamp) revert CollateralExpired(journal.collateralExpiration); From 85bd21a8741a92897b0363560b3144284cdf3be7 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 5 May 2026 18:55:32 -0400 Subject: [PATCH 012/146] fix imports --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 2 +- scripts/multiproof/DeployDevWithTDX.s.sol | 26 +++++++++++----------- scripts/multiproof/DeployTDXVerifier.s.sol | 6 ++--- src/L1/proofs/tee/TDXVerifier.sol | 2 +- test/L1/proofs/TDXVerifier.t.sol | 6 ++--- test/L1/proofs/TEEProverRegistryTDX.t.sol | 14 ++++++------ 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index 24fc2a6ed..de7ae0f05 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { ZkCoProcessorType, ZkCoProcessorConfig } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { ZkCoProcessorType, ZkCoProcessorConfig } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; /// @notice Statuses that a TDX quote/collateral verifier may emit. /// @dev Unknown is index 0 so uninitialized values fail closed. diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index bc9776b49..f38846a83 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -12,27 +12,27 @@ pragma solidity 0.8.15; * as the registry manager so it can submit TDX signer registrations. */ -import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier } from "interfaces/multiproof/tee/ITDXVerifier.sol"; +import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { Proxy } from "src/universal/Proxy.sol"; import { Script } from "forge-std/Script.sol"; import { console2 as console } from "forge-std/console2.sol"; -import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; -import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; -import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; -import { GameType, Hash } from "src/dispute/lib/Types.sol"; +import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; +import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; +import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; +import { DisputeGameFactory } from "src/L1/proofs/DisputeGameFactory.sol"; +import { GameType, Hash } from "src/libraries/bridge/Types.sol"; import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; import { Config } from "scripts/libraries/Config.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; -import { AggregateVerifier } from "src/multiproof/AggregateVerifier.sol"; -import { IVerifier } from "interfaces/multiproof/IVerifier.sol"; -import { MockVerifier } from "src/multiproof/mocks/MockVerifier.sol"; -import { TEEProverRegistry } from "src/multiproof/tee/TEEProverRegistry.sol"; -import { TEEVerifier } from "src/multiproof/tee/TEEVerifier.sol"; +import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; +import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; +import { MockVerifier } from "test/mocks/MockVerifier.sol"; +import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; +import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index e5f955771..f083a527f 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -23,9 +23,9 @@ pragma solidity ^0.8.20; import { Script } from "forge-std/Script.sol"; import { console2 as console } from "forge-std/console2.sol"; -import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; -import { TDXTcbStatus } from "interfaces/multiproof/tee/ITDXVerifier.sol"; -import { TDXVerifier } from "src/multiproof/tee/TDXVerifier.sol"; +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { TDXTcbStatus } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract DeployTDXVerifier is Script { /// @notice Maximum TDX quote age accepted by TDXVerifier. diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index d8175830a..d47eb5c7e 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -13,7 +13,7 @@ import { TDXVerifierJournal, ZkCoProcessorType, ZkCoProcessorConfig -} from "interfaces/multiproof/tee/ITDXVerifier.sol"; +} from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; /// @title TDXVerifier /// @notice Production-shape Intel TDX DCAP verifier for multiproof signer registration. diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index 291b34614..83c74afbd 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.0; import { Test } from "forge-std/Test.sol"; -import { TDXTcbStatus, TDXVerificationResult, TDXVerifierJournal } from "interfaces/multiproof/tee/ITDXVerifier.sol"; -import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { TDXTcbStatus, TDXVerificationResult, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { TDXVerifier } from "src/multiproof/tee/TDXVerifier.sol"; +import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract TDXVerifierTest is Test { TDXVerifier internal verifier; diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 0df45c5b6..742960312 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -3,19 +3,19 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; -import { IDisputeGame } from "interfaces/dispute/IDisputeGame.sol"; -import { IDisputeGameFactory } from "interfaces/dispute/IDisputeGameFactory.sol"; -import { INitroEnclaveVerifier } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; +import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; +import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier, TDXTcbStatus, TDXVerificationResult, TDXVerifierJournal -} from "interfaces/multiproof/tee/ITDXVerifier.sol"; -import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/multiproof/tee/INitroEnclaveVerifier.sol"; -import { GameType } from "src/dispute/lib/Types.sol"; +} from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { GameType } from "src/libraries/bridge/Types.sol"; -import { TEEProverRegistry } from "src/multiproof/tee/TEEProverRegistry.sol"; +import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; /// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. contract MockAggregateVerifierForTDXRegistry { From 92aeb9cf01db4bb9db459a5c4e35c3606c338989 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Wed, 6 May 2026 09:15:14 -0400 Subject: [PATCH 013/146] deploy dev --- deployments/11155111-dev-with-tdx.json | 11 +++++++++++ deployments/11155111-tdx-verifier.json | 7 +++++++ scripts/multiproof/justfile | 8 +++----- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 deployments/11155111-dev-with-tdx.json create mode 100644 deployments/11155111-tdx-verifier.json diff --git a/deployments/11155111-dev-with-tdx.json b/deployments/11155111-dev-with-tdx.json new file mode 100644 index 000000000..b8a23d465 --- /dev/null +++ b/deployments/11155111-dev-with-tdx.json @@ -0,0 +1,11 @@ +{ + "AggregateVerifier": "0x5392E1C4C94721091A887096CAfD04850B812D59", + "AnchorStateRegistry": "0x9fB436292fEAD6bAadD64688C8911d6FEb813C65", + "DelayedWETH": "0x02B34E49D429B9499E59034e922b483054785D73", + "DisputeGameFactory": "0xe87372f9e11E5e3050969B81A5C5dc448ae2132e", + "NitroEnclaveVerifier": "0x77461a6434fFE3435206B19658F33274f3104e07", + "TDXRegistrationManager": "0x44E999A5859c2D12378a349882fAe5805DCE71b9", + "TDXVerifier": "0x9c38b756B31529Cad02D54A4ba810F3ff1e00054", + "TEEProverRegistry": "0x28Ce5A280A7775ee97587C513C4b4533597fB00b", + "TEEVerifier": "0x268b5AcA6a85f08fD61D3564f012b73D64926F49" +} diff --git a/deployments/11155111-tdx-verifier.json b/deployments/11155111-tdx-verifier.json new file mode 100644 index 000000000..e35a8cffc --- /dev/null +++ b/deployments/11155111-tdx-verifier.json @@ -0,0 +1,7 @@ +{ + "IntelRootCaHash": "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009", + "MaxTimeDiff": 3600, + "RiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "TDXVerifier": "0x9c38b756B31529Cad02D54A4ba810F3ff1e00054", + "TDXVerifierId": "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" +} diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 86559bb92..2ce1b00d6 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -7,11 +7,10 @@ forge_account := "testnet-admin" l1_rpc_url := "https://ethereum-full-sepolia-k8s-dev.cbhq.net" sepolia_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" -sepolia_tdx_verifier_id := "0x1f1bc81fae82605af46a4c9a20f641922b7542befd9219644c7e6c59ccdeccab" +sepolia_tdx_verifier_id := "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009" -sepolia_tdx_image_hash := "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6" -sepolia_tdx_signer := "0x6A1f38f20044e8e69EAC755144F14f973e7b8d6E" -sepolia_tdx_registration_manager := "0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7" +sepolia_tdx_image_hash := "0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66" +sepolia_tdx_registration_manager := "0x44E999A5859c2D12378a349882fAe5805DCE71b9" # List available multiproof deployment recipes. default: @@ -49,7 +48,6 @@ tdx-sepolia-config: @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" @echo "TDX_IMAGE_HASH={{sepolia_tdx_image_hash}}" - @echo "TDX_SIGNER={{sepolia_tdx_signer}}" @echo "TDX_REGISTRATION_MANAGER={{sepolia_tdx_registration_manager}}" # Deploy the TDX multiproof dev/test stack using deploy-config/sepolia.json. From 604854e956d3b2ab352fc1dacde3ffa0c9194a28 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Wed, 6 May 2026 10:27:35 -0400 Subject: [PATCH 014/146] add asr config to tdx script --- deployments/11155111-dev-with-tdx.json | 16 ++++---- scripts/multiproof/DeployDevWithTDX.s.sol | 29 ++++++++++--- scripts/multiproof/README.md | 20 ++++++--- scripts/multiproof/justfile | 50 +++++++++++++++++++++-- src/L1/proofs/tee/TDXVerifier.sol | 2 +- 5 files changed, 94 insertions(+), 23 deletions(-) diff --git a/deployments/11155111-dev-with-tdx.json b/deployments/11155111-dev-with-tdx.json index b8a23d465..1c90ea2b9 100644 --- a/deployments/11155111-dev-with-tdx.json +++ b/deployments/11155111-dev-with-tdx.json @@ -1,11 +1,13 @@ { - "AggregateVerifier": "0x5392E1C4C94721091A887096CAfD04850B812D59", - "AnchorStateRegistry": "0x9fB436292fEAD6bAadD64688C8911d6FEb813C65", - "DelayedWETH": "0x02B34E49D429B9499E59034e922b483054785D73", - "DisputeGameFactory": "0xe87372f9e11E5e3050969B81A5C5dc448ae2132e", + "ASRStartingBlockNumber": 41154575, + "ASRStartingOutputRoot": "0x2cb991c0d9e739677fbcc78772c01f2e4639dffb159422b08cfbea1160f8877c", + "AggregateVerifier": "0x4Cf26dE13cC4D64352f6c76b8ef888bEA6a2Acef", + "AnchorStateRegistry": "0x629cf75322989cE321e10BfA4DF2daE43ac19c82", + "DelayedWETH": "0x17626e33878597cbbFc877c9AA47945E64eB6D31", + "DisputeGameFactory": "0x6C9De6Cc269844c3121B2b59F239F79bace2303D", "NitroEnclaveVerifier": "0x77461a6434fFE3435206B19658F33274f3104e07", "TDXRegistrationManager": "0x44E999A5859c2D12378a349882fAe5805DCE71b9", "TDXVerifier": "0x9c38b756B31529Cad02D54A4ba810F3ff1e00054", - "TEEProverRegistry": "0x28Ce5A280A7775ee97587C513C4b4533597fB00b", - "TEEVerifier": "0x268b5AcA6a85f08fD61D3564f012b73D64926F49" -} + "TEEProverRegistry": "0x52F8f01Fb5e2cF6BE15a311EbFB30f24352C7221", + "TEEVerifier": "0xf218d04E5A582deEA9E952E4227C07e865BC8bA1" +} \ No newline at end of file diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index f38846a83..3a8aa17d6 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -54,6 +54,8 @@ contract DeployDevWithTDX is Script { IAnchorStateRegistry public mockAnchorRegistry; address public mockDelayedWETH; address public aggregateVerifier; + Hash public startingAnchorRoot; + uint256 public startingAnchorBlockNumber; function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); @@ -65,12 +67,26 @@ contract DeployDevWithTDX is Script { } function run(address tdxVerifier, address registrationManager) public { + run(tdxVerifier, registrationManager, cfg.multiproofGenesisOutputRoot(), cfg.multiproofGenesisBlockNumber()); + } + + function run( + address tdxVerifier, + address registrationManager, + bytes32 asrStartingOutputRoot, + uint256 asrStartingBlockNumber + ) + public + { nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be set in config"); require(registrationManager != address(0), "registrationManager must be non-zero"); + require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); tdxVerifierAddr = tdxVerifier; tdxRegistrationManager = registrationManager; + startingAnchorRoot = Hash.wrap(asrStartingOutputRoot); + startingAnchorBlockNumber = asrStartingBlockNumber; GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); @@ -83,6 +99,8 @@ contract DeployDevWithTDX is Script { console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); console.log("TDXVerifier:", tdxVerifierAddr); console.log("TDX Registration Manager:", tdxRegistrationManager); + console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); + console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); console.log(""); console.log("NOTE: TDXVerifier owner must be the broadcaster/finalSystemOwner."); @@ -137,12 +155,7 @@ contract DeployDevWithTDX is Script { MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); mockAnchorRegistry = IAnchorStateRegistry(address(asr)); - asr.initialize( - disputeGameFactory, - Hash.wrap(cfg.multiproofGenesisOutputRoot()), - cfg.multiproofGenesisBlockNumber(), - gameType - ); + asr.initialize(disputeGameFactory, startingAnchorRoot, startingAnchorBlockNumber, gameType); } function _deployAggregateVerifier(GameType gameType) internal { @@ -186,6 +199,8 @@ contract DeployDevWithTDX is Script { console.log("\nInfrastructure:"); console.log(" DisputeGameFactory:", disputeGameFactory); console.log(" AnchorStateRegistry (mock):", address(mockAnchorRegistry)); + console.log(" ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); + console.log(" ASR Starting L2 Block:", startingAnchorBlockNumber); console.log(" DelayedWETH (mock):", mockDelayedWETH); console.log("\nGame:"); console.log(" AggregateVerifier:", aggregateVerifier); @@ -212,6 +227,8 @@ contract DeployDevWithTDX is Script { vm.serializeAddress(key, "TEEVerifier", teeVerifier); vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); + vm.serializeBytes32(key, "ASRStartingOutputRoot", startingAnchorRoot.raw()); + vm.serializeUint(key, "ASRStartingBlockNumber", startingAnchorBlockNumber); vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 15987e1d1..4eb4f0e55 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -138,10 +138,10 @@ The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` The TDX path follows the same split as Nitro: expensive attestation verification happens off-chain in a ZK guest, and Solidity verifies the proof plus the on-chain acceptance policy before registering the signer. -| Contract | Purpose | -| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `TDXVerifier` | Verifies a RISC Zero or SP1 proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | -| `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, tracking which TEE type each signer came from for `TEEVerifier`. | +| Contract | Purpose | +| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TDXVerifier` | Verifies a RISC Zero or SP1 proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | +| `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, tracking which TEE type each signer came from for `TEEVerifier`. | The ZK verifier guest is expected to perform the full Intel DCAP verification path: @@ -217,6 +217,8 @@ Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. +The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the resolved output root and L2 block through `run(address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, and `ASR_ANCHOR_BLOCK_LOOKBACK` to anchor a fixed number of L2 blocks behind head. For a fixed anchor, set both `ASR_ANCHOR_OUTPUT_ROOT` and `ASR_ANCHOR_BLOCK_NUMBER`. + ```bash just --justfile scripts/multiproof/justfile deploy-tdx-stack $TDX_VERIFIER ``` @@ -224,10 +226,18 @@ just --justfile scripts/multiproof/justfile deploy-tdx-stack $TDX_VERIFIER To override the manager manually, use: ```bash +export L2_RPC_URL= +export L2_OUTPUT_ROOT_RPC_URL= +ASR_ANCHOR_BLOCK_NUMBER=$(cast block-number --rpc-url $L2_RPC_URL) +ASR_ANCHOR_OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast to-hex $ASR_ANCHOR_BLOCK_NUMBER) \ + --rpc-url $L2_OUTPUT_ROOT_RPC_URL | jq -r '.outputRoot') + forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ - --sig "run(address,address)" \ + --sig "run(address,address,bytes32,uint256)" \ $TDX_VERIFIER \ $TDX_REGISTRATION_MANAGER \ + $ASR_ANCHOR_OUTPUT_ROOT \ + $ASR_ANCHOR_BLOCK_NUMBER \ --rpc-url $L1_RPC_URL \ --broadcast \ --private-key $PRIVATE_KEY diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 2ce1b00d6..4d69f21a6 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -4,7 +4,9 @@ repo_root := justfile_directory() + "/../.." justfile_path := justfile_directory() + "/justfile" default_deploy_config := "deploy-config/sepolia.json" forge_account := "testnet-admin" -l1_rpc_url := "https://ethereum-full-sepolia-k8s-dev.cbhq.net" +l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") +l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:8545") +l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:7545") sepolia_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" sepolia_tdx_verifier_id := "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" @@ -44,31 +46,69 @@ deploy-tdx-verifier risc0_router=sepolia_risc0_verifier_router tdx_verifier_id=s # Print the Sepolia TDX constants used by these recipes. tdx-sepolia-config: + @echo "L1_RPC_URL={{l1_rpc_url}}" @echo "RISC0_VERIFIER_ROUTER={{sepolia_risc0_verifier_router}}" @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" @echo "TDX_IMAGE_HASH={{sepolia_tdx_image_hash}}" @echo "TDX_REGISTRATION_MANAGER={{sepolia_tdx_registration_manager}}" + @echo "L2_RPC_URL={{l2_rpc_url}}" + @echo "L2_OUTPUT_ROOT_RPC_URL={{l2_output_root_rpc_url}}" # Deploy the TDX multiproof dev/test stack using deploy-config/sepolia.json. # # Required env: # L1_RPC_URL +# L2_RPC_URL Optional when using this recipe; defaults to Base Sepolia L2. +# L2_OUTPUT_ROOT_RPC_URL Optional when using this recipe; defaults to Base Sepolia op-node/archive RPC. +# ASR_ANCHOR_BLOCK_LOOKBACK Optional L2 block lookback from head; defaults to 0. +# ASR_ANCHOR_BLOCK_NUMBER Optional explicit L2 block. Requires ASR_ANCHOR_OUTPUT_ROOT. +# ASR_ANCHOR_OUTPUT_ROOT Optional explicit output root. Requires ASR_ANCHOR_BLOCK_NUMBER. # # Args: # tdx_verifier TDXVerifier address from deploy-tdx-verifier # tdx_manager Manager that can call registerTDXSigner() -deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=default_deploy_config: +deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=default_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" owner="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as deployer: $owner" + + if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 + exit 1 + fi + asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" + asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" + else + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + fi + + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi + + echo "Using ASR anchor output root: $asr_anchor_output_root" + echo "Using ASR anchor L2 block: $asr_anchor_block" + DEPLOY_CONFIG_PATH="{{deploy_config}}" \ forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ - --sig "run(address,address)" \ + --sig "run(address,address,bytes32,uint256)" \ "{{tdx_verifier}}" \ "{{tdx_manager}}" \ + "$asr_anchor_output_root" \ + "$asr_anchor_block" \ --rpc-url "{{l1_rpc_url}}" \ --broadcast \ --account "{{forge_account}}" \ @@ -77,6 +117,8 @@ deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deplo # Print the two-step Sepolia command template. tdx-sepolia-help: @echo 'export L1_RPC_URL=' + @echo 'export L2_RPC_URL=' + @echo 'export L2_OUTPUT_ROOT_RPC_URL=' @echo 'just deploy-tdx-verifier' - @echo 'just deploy-tdx-stack [tdx-manager]' + @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' @echo 'just tdx-sepolia-config' diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index d47eb5c7e..09e9b5195 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { Ownable } from "@solady/auth/Ownable.sol"; -import { ISP1Verifier } from "lib/sp1-contracts/contracts/src/ISP1Verifier.sol"; +import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; From accc9230fd4fdbf45d7e8de9a040b5252911eebd Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Wed, 6 May 2026 12:44:34 -0400 Subject: [PATCH 015/146] fix AggregateVerifier flow --- deploy-config/local.json | 2 + deploy-config/sepolia.json | 2 + scripts/deploy/Deploy.s.sol | 3 +- scripts/deploy/DeployConfig.s.sol | 4 + scripts/deploy/DeployImplementations.s.sol | 5 +- scripts/multiproof/DeployDevNoNitro.s.sol | 13 +-- scripts/multiproof/DeployDevWithNitro.s.sol | 5 +- scripts/multiproof/DeployDevWithTDX.s.sol | 5 +- scripts/multiproof/README.md | 18 ++-- snapshots/abi/AggregateVerifier.json | 33 +++++- snapshots/abi/TEEProverRegistry.json | 15 ++- snapshots/abi/TEEVerifier.json | 21 ---- snapshots/semver-lock.json | 18 ++-- src/L1/proofs/AggregateVerifier.sol | 87 +++++++++++---- src/L1/proofs/tee/TEEProverRegistry.sol | 54 ++++++---- src/L1/proofs/tee/TEEVerifier.sol | 53 ++-------- test/L1/proofs/AggregateVerifier.t.sol | 73 +++++++++++-- test/L1/proofs/BaseTest.t.sol | 20 +++- test/L1/proofs/TEEProverRegistry.t.sol | 8 +- test/L1/proofs/TEEProverRegistryTDX.t.sol | 8 +- test/L1/proofs/TEEVerifier.t.sol | 111 +++++++------------- test/mocks/MockDevTEEProverRegistry.sol | 2 +- test/opcm/DeployImplementations.t.sol | 6 +- test/opcm/DeployOPChain.t.sol | 3 +- 24 files changed, 339 insertions(+), 230 deletions(-) diff --git a/deploy-config/local.json b/deploy-config/local.json index b13afac7b..501074350 100644 --- a/deploy-config/local.json +++ b/deploy-config/local.json @@ -67,6 +67,8 @@ "useRevenueShare": false, "chainFeesRecipient": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "teeImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "teeNitroImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "teeTdxImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "multiproofConfigHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "multiproofGameType": 621, "teeProposer": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", diff --git a/deploy-config/sepolia.json b/deploy-config/sepolia.json index fd6db1552..a21340ed4 100644 --- a/deploy-config/sepolia.json +++ b/deploy-config/sepolia.json @@ -59,6 +59,8 @@ "useRevenueShare": true, "chainFeesRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", "teeImageHash": "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6", + "teeNitroImageHash": "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6", + "teeTdxImageHash": "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6", "multiproofConfigHash": "0x12e9c45f19f9817c6d4385fad29e7a70c355502cf0883e76a9a7e478a85d1360", "multiproofGameType": 621, "teeProposer": "0xdb84125f2f4229c81c579f41bc129c71b174eb58", diff --git a/scripts/deploy/Deploy.s.sol b/scripts/deploy/Deploy.s.sol index 38c442abc..7538b1ea8 100644 --- a/scripts/deploy/Deploy.s.sol +++ b/scripts/deploy/Deploy.s.sol @@ -235,7 +235,8 @@ contract Deploy is Deployer { faultGameV2SplitDepth: cfg.faultGameV2SplitDepth(), faultGameV2ClockExtension: cfg.faultGameV2ClockExtension(), faultGameV2MaxClockDuration: cfg.faultGameV2MaxClockDuration(), - teeImageHash: cfg.teeImageHash(), + teeNitroImageHash: cfg.teeNitroImageHash(), + teeTdxImageHash: cfg.teeTdxImageHash(), multiproofConfigHash: cfg.multiproofConfigHash(), multiproofGameType: cfg.multiproofGameType(), nitroEnclaveVerifier: cfg.nitroEnclaveVerifier(), diff --git a/scripts/deploy/DeployConfig.s.sol b/scripts/deploy/DeployConfig.s.sol index a2bb04aed..95f18629b 100644 --- a/scripts/deploy/DeployConfig.s.sol +++ b/scripts/deploy/DeployConfig.s.sol @@ -85,6 +85,8 @@ contract DeployConfig is Script { // Multiproof Configuration bytes32 public teeImageHash; + bytes32 public teeNitroImageHash; + bytes32 public teeTdxImageHash; bytes32 public multiproofConfigHash; uint256 public multiproofGameType; address public teeProposer; @@ -199,6 +201,8 @@ contract DeployConfig is Script { faultGameV2ClockExtension = _readOr(_json, "$.faultGameV2ClockExtension", 10800); faultGameV2MaxClockDuration = _readOr(_json, "$.faultGameV2MaxClockDuration", 302400); teeImageHash = bytes32(_readOr(_json, "$.teeImageHash", 0)); + teeNitroImageHash = bytes32(_readOr(_json, "$.teeNitroImageHash", uint256(teeImageHash))); + teeTdxImageHash = bytes32(_readOr(_json, "$.teeTdxImageHash", uint256(teeImageHash))); multiproofConfigHash = bytes32(_readOr(_json, "$.multiproofConfigHash", 0)); multiproofGameType = _readOr(_json, "$.multiproofGameType", 621); teeProposer = stdJson.readAddress(_json, "$.teeProposer"); diff --git a/scripts/deploy/DeployImplementations.s.sol b/scripts/deploy/DeployImplementations.s.sol index 8db384da5..73d0b434c 100644 --- a/scripts/deploy/DeployImplementations.s.sol +++ b/scripts/deploy/DeployImplementations.s.sol @@ -62,7 +62,8 @@ contract DeployImplementations is Script { uint256 faultGameV2ClockExtension; uint256 faultGameV2MaxClockDuration; // Multiproof parameters - bytes32 teeImageHash; + bytes32 teeNitroImageHash; + bytes32 teeTdxImageHash; bytes32 multiproofConfigHash; uint256 multiproofGameType; address nitroEnclaveVerifier; @@ -634,7 +635,7 @@ contract DeployImplementations is Script { _output.delayedWETHImpl, IVerifier(teeVerifierImpl), IVerifier(zkVerifier), - _input.teeImageHash, + AggregateVerifier.TeeHashes(_input.teeNitroImageHash, _input.teeTdxImageHash), AggregateVerifier.ZkHashes(bytes32(0), bytes32(0)), _input.multiproofConfigHash, _input.l2ChainID, diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 56a379576..d1330c1e3 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -29,11 +29,11 @@ pragma solidity 0.8.15; * After deployment, register one dev Nitro signer and one dev TDX signer: * * cast send $TEE_PROVER_REGISTRY \ - * "addDevSigner(address,bytes32)" $NITRO_SIGNER_ADDRESS $TEE_IMAGE_HASH \ + * "addDevSigner(address,bytes32)" $NITRO_SIGNER_ADDRESS $TEE_NITRO_IMAGE_HASH \ * --private-key $OWNER_KEY --rpc-url $RPC_URL * * cast send $TEE_PROVER_REGISTRY \ - * "addDevTDXSigner(address,bytes32)" $TDX_SIGNER_ADDRESS $TEE_IMAGE_HASH \ + * "addDevTDXSigner(address,bytes32)" $TDX_SIGNER_ADDRESS $TEE_TDX_IMAGE_HASH \ * --private-key $OWNER_KEY --rpc-url $RPC_URL * * No attestation, PCR0 registration, or certificate validation required. @@ -190,7 +190,7 @@ contract DeployDevNoNitro is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - cfg.teeImageHash(), + AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainID(), @@ -219,17 +219,18 @@ contract DeployDevNoNitro is Script { console.log("\nGame:"); console.log(" AggregateVerifier:", aggregateVerifier); console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); + console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); + console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); console.log("\n>>> NEXT STEP - Register dev Nitro and TDX signers (NO ATTESTATION NEEDED) <<<"); console.log("\ncast send", teeProverRegistryProxy); console.log(' "addDevSigner(address,bytes32)" '); - console.log(" ", vm.toString(cfg.teeImageHash())); + console.log(" ", vm.toString(cfg.teeNitroImageHash())); console.log(" --private-key --rpc-url "); console.log("\ncast send", teeProverRegistryProxy); console.log(' "addDevTDXSigner(address,bytes32)" '); - console.log(" ", vm.toString(cfg.teeImageHash())); + console.log(" ", vm.toString(cfg.teeTdxImageHash())); console.log(" --private-key --rpc-url "); console.log("\n========================================\n"); } diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index c76a75018..b8ed52ebb 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -179,7 +179,7 @@ contract DeployDevWithNitro is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - cfg.teeImageHash(), + AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainID(), @@ -209,7 +209,8 @@ contract DeployDevWithNitro is Script { console.log("\nGame:"); console.log(" AggregateVerifier:", aggregateVerifier); console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); + console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); + console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 3a8aa17d6..98b101983 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -172,7 +172,7 @@ contract DeployDevWithTDX is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - cfg.teeImageHash(), + AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainID(), @@ -205,7 +205,8 @@ contract DeployDevWithTDX is Script { console.log("\nGame:"); console.log(" AggregateVerifier:", aggregateVerifier); console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); + console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); + console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 4eb4f0e55..663649bd0 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -43,7 +43,8 @@ Other relevant fields: | Field | Description | | ------------------------------ | --------------------------------------------------------------------------------- | | `teeProposer` | Address to be registered as the TEE proposer | -| `teeImageHash` | PCR0 hash used when registering the dev signer (use `bytes32(0x01...01)` for dev) | +| `teeNitroImageHash` | PCR0 hash used when registering the Nitro dev signer | +| `teeTdxImageHash` | TDX image hash used when registering the TDX dev signer | | `multiproofGameType` | Game type ID for the dispute game | | `multiproofGenesisOutputRoot` | Initial anchor output root | | `multiproofGenesisBlockNumber` | Initial anchor L2 block number | @@ -100,8 +101,9 @@ This returns a raw byte array representing an uncompressed secp256k1 public key Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signer on the deployed `DevTEEProverRegistry`. -> **Note:** PCR0 enforcement is handled by `AggregateVerifier` (which bakes `teeImageHash` into the -> journal the enclave signs). The registry only tracks which signer addresses are valid. +> **Note:** PCR0 / TDX image enforcement is handled by `AggregateVerifier` (which bakes +> `teeNitroImageHash` and `teeTdxImageHash` into the journal the enclaves sign). The registry +> only tracks which signer addresses are valid. ```bash # Replace: @@ -110,15 +112,15 @@ Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signe cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ "addDevSigner(address,bytes32)" \ 0x080f42420846c613158D7b4334257C78bE5A9B90 \ - $TEE_IMAGE_HASH \ + $TEE_NITRO_IMAGE_HASH \ --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" -# Register a TDX dev signer for the same image hash. +# Register a TDX dev signer for the TDX image hash. cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ "addDevTDXSigner(address,bytes32)" \ $TDX_SIGNER_ADDRESS \ - $TEE_IMAGE_HASH \ + $TEE_TDX_IMAGE_HASH \ --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" ``` @@ -154,7 +156,7 @@ CRLs/revocation state TDREPORT field extraction ``` -The Solidity verifier then enforces local policy over the proven journal. The PoC maps TDX measurements into the existing multiproof `TEE_IMAGE_HASH` field as: +The Solidity verifier then enforces local policy over the proven journal. The PoC maps TDX measurements into `TEE_TDX_IMAGE_HASH` as: ```text keccak256(MRTD || RTMR0 || RTMR1 || RTMR2 || RTMR3) @@ -168,7 +170,7 @@ The attested public key must be supplied as an uncompressed 65-byte secp256k1 pu The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 bytes. The last 32 bytes are returned by the verifier as app-specific binding data and emitted by the registry. -`TEEVerifier` is still the proposal-proof verifier, but a TEE proposal proof now requires two signatures over the same journal: one from a Nitro-registered signer and one from a TDX-registered signer. The proof bytes are `proposer || signatureA || signatureB`; either signature order is accepted as long as both registered TEE types are present and both signers match the expected `TEE_IMAGE_HASH`. +`TEEVerifier` is still the proposal-proof verifier, but a TEE proposal proof now requires two signatures over the same journal: one from a Nitro-registered signer and one from a TDX-registered signer. The proof bytes are `proposer || signatureA || signatureB`; either signature order is accepted as long as both registered TEE types are present and both signers match their expected type-specific image hash. > **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining off-chain piece is the actual RISC Zero/SP1 TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. diff --git a/snapshots/abi/AggregateVerifier.json b/snapshots/abi/AggregateVerifier.json index 9c991351e..54a0e5a02 100644 --- a/snapshots/abi/AggregateVerifier.json +++ b/snapshots/abi/AggregateVerifier.json @@ -27,9 +27,21 @@ "type": "address" }, { - "internalType": "bytes32", - "name": "teeImageHash", - "type": "bytes32" + "components": [ + { + "internalType": "bytes32", + "name": "nitroHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "tdxHash", + "type": "bytes32" + } + ], + "internalType": "struct AggregateVerifier.TeeHashes", + "name": "teeHashes", + "type": "tuple" }, { "components": [ @@ -230,7 +242,20 @@ }, { "inputs": [], - "name": "TEE_IMAGE_HASH", + "name": "TEE_NITRO_IMAGE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TEE_TDX_IMAGE_HASH", "outputs": [ { "internalType": "bytes32", diff --git a/snapshots/abi/TEEProverRegistry.json b/snapshots/abi/TEEProverRegistry.json index ceff44a08..44586ef24 100644 --- a/snapshots/abi/TEEProverRegistry.json +++ b/snapshots/abi/TEEProverRegistry.json @@ -100,7 +100,20 @@ }, { "inputs": [], - "name": "getExpectedImageHash", + "name": "getExpectedNitroImageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExpectedTDXImageHash", "outputs": [ { "internalType": "bytes32", diff --git a/snapshots/abi/TEEVerifier.json b/snapshots/abi/TEEVerifier.json index fff275ed7..b491ef7a5 100644 --- a/snapshots/abi/TEEVerifier.json +++ b/snapshots/abi/TEEVerifier.json @@ -116,17 +116,6 @@ "name": "VerifierNullified", "type": "event" }, - { - "inputs": [ - { - "internalType": "address", - "name": "signer", - "type": "address" - } - ], - "name": "DuplicateSigner", - "type": "error" - }, { "inputs": [ { @@ -175,16 +164,6 @@ "name": "InvalidSigner", "type": "error" }, - { - "inputs": [], - "name": "MissingNitroSignature", - "type": "error" - }, - { - "inputs": [], - "name": "MissingTDXSignature", - "type": "error" - }, { "inputs": [], "name": "NotProperGame", diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index d2878448f..ffad91569 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -36,8 +36,8 @@ "sourceCodeHash": "0x5e68ac31e5f3a16a8845c0d1fd6ac4aa03260002b8f232924099e8be3db3a95a" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0x10bb6a60f21de103d1da9ff310a38f571c53a8113d04a029087648debf3f0341", - "sourceCodeHash": "0xd1fa080d716366236f8b963aa80a0c79e474c5603a2b1615dfb14fb5a99dbe6a" + "initCodeHash": "0x6e50ad8219dfc59503395bf63d2a4d91c002bd21a1df7c902debedea9c2e13eb", + "sourceCodeHash": "0x5708e88bac4268a2c1310c3b9aa84335310ae6941a90e41bc10212f356b2060a" }, "src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": { "initCodeHash": "0x6f3afd2d0ef97a82ca3111976322b99343a270e54cd4a405028f2f29c75f7fb1", @@ -55,13 +55,17 @@ "initCodeHash": "0x82f42b4d578bfcf9dc35eaa2c4ada04a45e1eca63021bceceb2ec794b12a9dd6", "sourceCodeHash": "0x172d22287f2131ef06e2fe3b475e802e15024de91b0a0ff8267ee63fc31b32a7" }, + "src/L1/proofs/tee/TDXVerifier.sol:TDXVerifier": { + "initCodeHash": "0xab68cfaae62a40d02a7d096bf1c81ec8ac2c106b19859ffa772a22b477a55af2", + "sourceCodeHash": "0x82a3eff9a7566e483b5f015dc97a88d90bdc4d9d7d49794556106bfc86d55ea0" + }, "src/L1/proofs/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0xfd1942e1c2f59b0aa72b33d698a948a53b6e4cf1040106f173fb5d89f63f57b0", - "sourceCodeHash": "0x23b0045834f94839018f9db307bd387d5abd90d59d3d941b09e69bb9f475e9ba" + "initCodeHash": "0x4b2fb8ba42668b1b9a8ebb99518fc0552703dfda190acde45d771847b450ac65", + "sourceCodeHash": "0xd14971b269de818bcceac3493284196dd0168fad3d21315db965d6d05a2cc050" }, "src/L1/proofs/tee/TEEVerifier.sol:TEEVerifier": { - "initCodeHash": "0x655576cc21cc5c603d55fb4dd2a2f0ef57b11deeaabd3e539b0a70a5f7e2c9af", - "sourceCodeHash": "0xe7d2abb1b2193437ceeecf00f1b2a3a5447867e98bafe033df66d0653c22ff7b" + "initCodeHash": "0x5f42455c6d41c0a49e05681a6fa7dbf21eb22c89705fd11b6030952b3227e559", + "sourceCodeHash": "0x83a09b025e33838859a7b5848463bb7f4d6d76031f5b8b8bc9f3cd5e73ad4464" }, "src/L1/proofs/v2/FaultDisputeGameV2.sol:FaultDisputeGameV2": { "initCodeHash": "0x6fc59e2da083c9e2093e42b0fda705e8215cc216e4dcedbf728c08f69ec2d3bd", @@ -159,4 +163,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} +} \ No newline at end of file diff --git a/src/L1/proofs/AggregateVerifier.sol b/src/L1/proofs/AggregateVerifier.sol index e308c11f3..fb7e90616 100644 --- a/src/L1/proofs/AggregateVerifier.sol +++ b/src/L1/proofs/AggregateVerifier.sol @@ -43,6 +43,23 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { bytes32 aggregateHash; } + /// @notice Hashes for the TEE proving images. + struct TeeHashes { + bytes32 nitroHash; + bytes32 tdxHash; + } + + /// @notice Common public inputs used to compute each TEE signature journal. + struct TeeJournalInputs { + address proposer; + bytes32 l1OriginHash; + bytes32 startingRoot; + uint64 startingL2SequenceNumber; + bytes32 endingRoot; + uint64 endingL2SequenceNumber; + bytes intermediateRoots; + } + //////////////////////////////////////////////////////////////// // Constants // //////////////////////////////////////////////////////////////// @@ -66,8 +83,10 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The minimum number of proofs required to resolve the game. uint256 public constant PROOF_THRESHOLD = 1; - /// @notice TEE proof payload size: nitro signature(65) + tdx signature(65). - uint256 internal constant TEE_PROOF_BYTES_LENGTH = 130; + /// @notice TEE proof payload size: + /// nitro image hash(32) + nitro signature(65) + tdx image hash(32) + tdx signature(65). + uint256 internal constant TEE_PROOF_BYTES_LENGTH = 194; + //////////////////////////////////////////////////////////////// // Immutables // //////////////////////////////////////////////////////////////// @@ -83,8 +102,11 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The TEE prover. IVerifier public immutable TEE_VERIFIER; - /// @notice The hash of the TEE image. - bytes32 public immutable TEE_IMAGE_HASH; + /// @notice The hash of the Nitro TEE image. + bytes32 public immutable TEE_NITRO_IMAGE_HASH; + + /// @notice The hash of the TDX TEE image. + bytes32 public immutable TEE_TDX_IMAGE_HASH; /// @notice The ZK prover. IVerifier public immutable ZK_VERIFIER; @@ -258,7 +280,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @param delayedWETH The delayed WETH contract. /// @param teeVerifier The TEE verifier. /// @param zkVerifier The ZK verifier. - /// @param teeImageHash The hash of the TEE image. + /// @param teeHashes The hashes of the Nitro and TDX TEE images. /// @param zkHashes The hashes of the ZK range and aggregate programs. /// @param configHash The hash of the rollup configuration. /// @param l2ChainId The chain ID of the L2 network. @@ -270,7 +292,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { IDelayedWETH delayedWETH, IVerifier teeVerifier, IVerifier zkVerifier, - bytes32 teeImageHash, + TeeHashes memory teeHashes, ZkHashes memory zkHashes, bytes32 configHash, uint256 l2ChainId, @@ -289,7 +311,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { DELAYED_WETH = delayedWETH; TEE_VERIFIER = teeVerifier; ZK_VERIFIER = zkVerifier; - TEE_IMAGE_HASH = teeImageHash; + TEE_NITRO_IMAGE_HASH = teeHashes.nitroHash; + TEE_TDX_IMAGE_HASH = teeHashes.tdxHash; ZK_RANGE_HASH = zkHashes.rangeHash; ZK_AGGREGATE_HASH = zkHashes.aggregateHash; CONFIG_HASH = configHash; @@ -880,7 +903,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } /// @notice Verifies a TEE proof for the current game. - /// @param proofBytes The proof: nitro signature(65) + tdx signature(65). + /// @param proofBytes The proof: nitro image hash(32) + nitro signature(65) + tdx image hash(32) + /// + tdx signature(65). function _verifyTeeProof( bytes calldata proofBytes, address proposer, @@ -896,23 +920,48 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { { if (proofBytes.length != TEE_PROOF_BYTES_LENGTH) revert InvalidProof(); + bytes32 nitroImageHash = bytes32(proofBytes[:32]); + bytes32 tdxImageHash = bytes32(proofBytes[97:129]); + if (nitroImageHash != TEE_NITRO_IMAGE_HASH || tdxImageHash != TEE_TDX_IMAGE_HASH) revert InvalidProof(); + + TeeJournalInputs memory inputs = TeeJournalInputs({ + proposer: proposer, + l1OriginHash: l1OriginHash, + startingRoot: startingRoot, + startingL2SequenceNumber: startingL2SequenceNumber, + endingRoot: endingRoot, + endingL2SequenceNumber: endingL2SequenceNumber, + intermediateRoots: intermediateRoots + }); + + _verifyTeeSignature(proofBytes[32:97], nitroImageHash, inputs); + _verifyTeeSignature(proofBytes[129:194], tdxImageHash, inputs); + } + + function _verifyTeeSignature( + bytes calldata signature, + bytes32 imageHash, + TeeJournalInputs memory inputs + ) + internal + view + { bytes32 journal = keccak256( abi.encodePacked( - proposer, - l1OriginHash, - startingRoot, - startingL2SequenceNumber, - endingRoot, - endingL2SequenceNumber, - intermediateRoots, + inputs.proposer, + inputs.l1OriginHash, + inputs.startingRoot, + inputs.startingL2SequenceNumber, + inputs.endingRoot, + inputs.endingL2SequenceNumber, + inputs.intermediateRoots, CONFIG_HASH, - TEE_IMAGE_HASH + imageHash ) ); - // Validate the proof. - bytes memory proof = abi.encodePacked(proposer, proofBytes); - if (!TEE_VERIFIER.verify(proof, TEE_IMAGE_HASH, journal)) revert InvalidProof(); + bytes memory verifierProof = abi.encodePacked(inputs.proposer, signature); + if (!TEE_VERIFIER.verify(verifierProof, imageHash, journal)) revert InvalidProof(); } /// @notice Verifies a ZK proof for the current game. diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index de6cc0496..44401f028 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -22,9 +22,9 @@ import { GameType } from "src/libraries/bridge/Types.sol"; /// verified through an external NitroEnclaveVerifier contract (Risc0). TDX signers are registered /// through the TDXVerifier configured on the implementation. /// Registration is PCR0-agnostic: any enclave with a valid attestation can register, -/// enabling pre-registration before hardforks. PCR0 enforcement happens at proof-submission +/// enabling pre-registration before hardforks. PCR0 / image enforcement happens at proof-submission /// time in TEEVerifier, which checks signerImageHash against the AggregateVerifier's -/// TEE_IMAGE_HASH and requires one Nitro signer plus one TDX signer. +/// type-specific TEE image hash and requires one Nitro signer plus one TDX signer. contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { using EnumerableSetLib for EnumerableSetLib.AddressSet; enum TEEType { @@ -47,7 +47,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice The external TDXVerifier contract used for ZK TDX quote verification. ITDXVerifier public immutable TDX_VERIFIER; - /// @notice The DisputeGameFactory used to look up the current AggregateVerifier and its TEE_IMAGE_HASH. + /// @notice The DisputeGameFactory used to look up the current AggregateVerifier and its TEE image hashes. IDisputeGameFactory public immutable DISPUTE_GAME_FACTORY; /// @notice The game type used to look up the AggregateVerifier in the factory. @@ -59,7 +59,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Mapping of signer address to the PCR0 image hash from their attestation. /// @dev Stored at registration time from the ZK-verified attestation document. - /// TEEVerifier checks this against the AggregateVerifier's TEE_IMAGE_HASH at + /// TEEVerifier checks this against the AggregateVerifier's type-specific TEE image hash at /// proof-submission time, so signers automatically become unusable when the /// AggregateVerifier upgrades to a new image hash. isValidSigner also uses /// this for off-chain pre-submission checks. @@ -106,10 +106,10 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Thrown when the dispute game factory is not configured. error DisputeGameFactoryNotSet(); - /// @notice Thrown when reading TEE_IMAGE_HASH from the AggregateVerifier fails. + /// @notice Thrown when reading a TEE image hash from the AggregateVerifier fails. error ImageHashReadFailed(); - /// @notice Thrown when setting a game type whose AggregateVerifier has no TEE_IMAGE_HASH. + /// @notice Thrown when setting a game type whose AggregateVerifier has no type-specific TEE image hash. error InvalidGameType(); /// @notice Thrown when the TDX verifier is not configured. @@ -144,10 +144,13 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } /// @notice Updates the game type used to look up the AggregateVerifier. - /// @dev Validates that the new game type has an AggregateVerifier with a non-zero TEE_IMAGE_HASH. + /// @dev Validates that the new game type has an AggregateVerifier with non-zero type-specific TEE image hashes. /// @param gameType_ The new game type ID. function setGameType(GameType gameType_) external onlyOwner { - if (_getExpectedImageHash(gameType_) == bytes32(0)) revert InvalidGameType(); + if ( + _getExpectedImageHash(gameType_, TEEType.NITRO) == bytes32(0) + || _getExpectedImageHash(gameType_, TEEType.TDX) == bytes32(0) + ) revert InvalidGameType(); gameType = gameType_; emit GameTypeUpdated(gameType_); } @@ -158,9 +161,9 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// 2. Is less than MAX_AGE old /// Registration is PCR0-agnostic: any enclave with a valid attestation can register. /// This enables pre-registration of new-PCR0 enclaves before a hardfork, eliminating - /// proof-generation delay when the on-chain TEE_IMAGE_HASH rotates. The TEEVerifier + /// proof-generation delay when the on-chain Nitro image hash rotates. The TEEVerifier /// enforces PCR0 correctness at proof-submission time by checking signerImageHash - /// against the AggregateVerifier's TEE_IMAGE_HASH, so pre-registered enclaves cannot + /// against the AggregateVerifier's TEE_NITRO_IMAGE_HASH, so pre-registered enclaves cannot /// produce accepted proofs until the hardfork activates. /// @param output The ABI-encoded VerifierJournal from the ZK proof. /// @param proofBytes The Risc0 ZK proof bytes. @@ -211,13 +214,15 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Checks if an address is a valid signer. /// @dev Defense-in-depth: checks both that the signer is registered AND that their - /// registered image hash matches the current AggregateVerifier's TEE_IMAGE_HASH. + /// registered image hash matches the current AggregateVerifier's type-specific TEE image hash. /// This ensures signers automatically become invalid when the AggregateVerifier upgrades. /// @param signer The address to check. /// @return True if the signer is registered with the current image hash, false otherwise. function isValidSigner(address signer) external view returns (bool) { if (!isRegisteredSigner[signer]) return false; - return signerImageHash[signer] == _getExpectedImageHash(gameType); + TEEType teeType = signerTEEType[signer]; + if (teeType == TEEType.NONE) return false; + return signerImageHash[signer] == _getExpectedImageHash(gameType, teeType); } /// @notice Returns all currently registered signer addresses. @@ -228,10 +233,16 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { return _registeredSigners.values(); } - /// @notice Returns the expected TEE image hash from the current AggregateVerifier. - /// @return The TEE_IMAGE_HASH from the AggregateVerifier registered in the factory. - function getExpectedImageHash() external view returns (bytes32) { - return _getExpectedImageHash(gameType); + /// @notice Returns the expected Nitro TEE image hash from the current AggregateVerifier. + /// @return The TEE_NITRO_IMAGE_HASH from the AggregateVerifier registered in the factory. + function getExpectedNitroImageHash() external view returns (bytes32) { + return _getExpectedImageHash(gameType, TEEType.NITRO); + } + + /// @notice Returns the expected TDX TEE image hash from the current AggregateVerifier. + /// @return The TEE_TDX_IMAGE_HASH from the AggregateVerifier registered in the factory. + function getExpectedTDXImageHash() external view returns (bytes32) { + return _getExpectedImageHash(gameType, TEEType.TDX); } /// @notice Initializes the contract with owner, manager, proposers, and game type. @@ -281,11 +292,14 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { emit SignerRegistered(signer); } - /// @dev Reads TEE_IMAGE_HASH from the AggregateVerifier registered in the factory for `gameType_`. - function _getExpectedImageHash(GameType gameType_) internal view returns (bytes32) { + /// @dev Reads a type-specific TEE image hash from the AggregateVerifier registered in the factory for `gameType_`. + function _getExpectedImageHash(GameType gameType_, TEEType teeType) internal view returns (bytes32) { + if (teeType == TEEType.NONE) revert InvalidTEEType(); address impl = address(DISPUTE_GAME_FACTORY.gameImpls(gameType_)); - // AggregateVerifier.TEE_IMAGE_HASH() selector - (bool success, bytes memory data) = impl.staticcall(abi.encodeWithSignature("TEE_IMAGE_HASH()")); + bytes memory callData = teeType == TEEType.NITRO + ? abi.encodeWithSignature("TEE_NITRO_IMAGE_HASH()") + : abi.encodeWithSignature("TEE_TDX_IMAGE_HASH()"); + (bool success, bytes memory data) = impl.staticcall(callData); if (!success || data.length != 32) revert ImageHashReadFailed(); return abi.decode(data, (bytes32)); } diff --git a/src/L1/proofs/tee/TEEVerifier.sol b/src/L1/proofs/tee/TEEVerifier.sol index b8d9eea00..79e777eb0 100644 --- a/src/L1/proofs/tee/TEEVerifier.sol +++ b/src/L1/proofs/tee/TEEVerifier.sol @@ -12,9 +12,7 @@ import { Verifier } from "../Verifier.sol"; /// @title TEEVerifier /// @notice Stateless TEE proof verifier that validates Nitro and TDX signatures against registered signers. /// @dev This contract is designed to be used as the TEE_VERIFIER in the AggregateVerifier. -/// It verifies that proofs are signed by a Nitro signer and a TDX signer registered in -/// TEEProverRegistry. PCR0 / TDX image hash enforcement is handled by -/// AggregateVerifier, which bakes TEE_IMAGE_HASH into the journal that the enclave signs. +/// It verifies one TEE signature at a time against a registered Nitro or TDX signer. /// The contract is intentionally stateless - all state related to output proposals and /// L1 origin verification is managed by the calling contract (e.g., AggregateVerifier). contract TEEVerifier is Verifier, ISemver { @@ -25,8 +23,8 @@ contract TEEVerifier is Verifier, ISemver { /// @notice Size of an ECDSA signature in bytes. uint256 internal constant SIGNATURE_SIZE = 65; - /// @notice Size of a TEE proof: proposer(20) + nitro signature(65) + tdx signature(65). - uint256 internal constant TEE_PROOF_SIZE = 20 + SIGNATURE_SIZE * 2; + /// @notice Size of a TEE proof: proposer(20) + signature(65). + uint256 internal constant TEE_PROOF_SIZE = 20 + SIGNATURE_SIZE; /// @notice Thrown when a recovered signer is not a valid registered signer. error InvalidSigner(address signer); @@ -43,15 +41,6 @@ contract TEEVerifier is Verifier, ISemver { /// @notice Thrown when the proposer is not a valid registered proposer. error InvalidProposer(address proposer); - /// @notice Thrown when both signatures recover to the same signer. - error DuplicateSigner(address signer); - - /// @notice Thrown when neither signature came from a Nitro-registered signer. - error MissingNitroSignature(); - - /// @notice Thrown when neither signature came from a TDX-registered signer. - error MissingTDXSignature(); - /// @notice Constructs the TEEVerifier contract. /// @param teeProverRegistry The TEEProverRegistry contract address. constructor( @@ -64,10 +53,8 @@ contract TEEVerifier is Verifier, ISemver { } /// @notice Verifies a TEE proof for a state transition. - /// @param proofBytes The proof: proposer(20) + two signatures(65 each) = 150 bytes. - /// One signature must recover to a Nitro signer and the other to a TDX signer. - /// @param imageId The claimed TEE image hash (from the calling AggregateVerifier's TEE_IMAGE_HASH). - /// Validated against each signer's registered image hash to prevent cross-game-type attacks. + /// @param proofBytes The proof: proposer(20) + signature(65) = 85 bytes. + /// @param imageId The TEE image hash expected for the recovered signer. /// @param journal The keccak256 hash of the proof's public inputs. /// @return valid Whether the proof is valid. function verify( @@ -88,23 +75,9 @@ contract TEEVerifier is Verifier, ISemver { revert InvalidProposer(proposer); } - bytes calldata firstSignature = proofBytes[20:20 + SIGNATURE_SIZE]; - bytes calldata secondSignature = proofBytes[20 + SIGNATURE_SIZE:TEE_PROOF_SIZE]; - - address firstSigner = _recoverSigner(journal, firstSignature); - address secondSigner = _recoverSigner(journal, secondSignature); - - TEEProverRegistry.TEEType firstTEEType = _validateSigner(firstSigner, imageId); - TEEProverRegistry.TEEType secondTEEType = _validateSigner(secondSigner, imageId); - - if (firstSigner == secondSigner) revert DuplicateSigner(firstSigner); - - if (firstTEEType != TEEProverRegistry.TEEType.NITRO && secondTEEType != TEEProverRegistry.TEEType.NITRO) { - revert MissingNitroSignature(); - } - if (firstTEEType != TEEProverRegistry.TEEType.TDX && secondTEEType != TEEProverRegistry.TEEType.TDX) { - revert MissingTDXSignature(); - } + bytes calldata signature = proofBytes[20:TEE_PROOF_SIZE]; + address signer = _recoverSigner(journal, signature); + _validateSigner(signer, imageId); return true; } @@ -116,15 +89,11 @@ contract TEEVerifier is Verifier, ISemver { if (err != ECDSA.RecoverError.NoError) revert InvalidSignature(); } - function _validateSigner(address signer, bytes32 imageId) - internal - view - returns (TEEProverRegistry.TEEType teeType) - { + function _validateSigner(address signer, bytes32 imageId) internal view { // A registered signer always has a non-NONE TEE type, so this single read also // serves as the registration check (saves an SLOAD versus calling isRegisteredSigner). - teeType = TEE_PROVER_REGISTRY.signerTEEType(signer); - if (teeType == TEEProverRegistry.TEEType.NONE) revert InvalidSigner(signer); + TEEProverRegistry.TEEType signerTEEType = TEE_PROVER_REGISTRY.signerTEEType(signer); + if (signerTEEType == TEEProverRegistry.TEEType.NONE) revert InvalidSigner(signer); // Prevents a signer registered under one enclave image from being used in a game // that expects a different image (e.g., after an upgrade or across game types). diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 7414e039c..a452398bb 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -19,6 +19,53 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); + bytes memory intermediateRoots = + abi.encodePacked(_generateIntermediateRootsExceptLast(currentL2BlockNumber), rootClaim.raw()); + bytes32 l1OriginHash = blockhash(block.number - 1); + bytes32 startingRoot = keccak256(abi.encode(uint256(0))); + bytes32 saltHash = keccak256("tee-proof"); + bytes memory nitroSignature = abi.encodePacked(saltHash, bytes32(0), uint8(27)); + bytes memory tdxSignature = abi.encodePacked(saltHash, bytes32(uint256(1)), uint8(28)); + bytes32 nitroJournal = keccak256( + abi.encodePacked( + TEE_PROVER, + l1OriginHash, + startingRoot, + uint64(0), + rootClaim.raw(), + uint64(currentL2BlockNumber), + intermediateRoots, + CONFIG_HASH, + TEE_NITRO_IMAGE_HASH + ) + ); + bytes32 tdxJournal = keccak256( + abi.encodePacked( + TEE_PROVER, + l1OriginHash, + startingRoot, + uint64(0), + rootClaim.raw(), + uint64(currentL2BlockNumber), + intermediateRoots, + CONFIG_HASH, + TEE_TDX_IMAGE_HASH + ) + ); + + vm.expectCall( + address(teeVerifier), + abi.encodeCall( + IVerifier.verify, (abi.encodePacked(TEE_PROVER, nitroSignature), TEE_NITRO_IMAGE_HASH, nitroJournal) + ) + ); + vm.expectCall( + address(teeVerifier), + abi.encodeCall( + IVerifier.verify, (abi.encodePacked(TEE_PROVER, tdxSignature), TEE_TDX_IMAGE_HASH, tdxJournal) + ) + ); + AggregateVerifier game = _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof ); @@ -268,7 +315,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(194)); vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) @@ -290,7 +337,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(194)); vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); _createAggregateVerifierGame( @@ -325,8 +372,12 @@ contract AggregateVerifierTest is BaseTest { bytes32 l1OriginHash = blockhash(l1OriginNumber); Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); - bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); + bytes memory proofBytes = abi.encodePacked( + uint8(AggregateVerifier.ProofType.TEE), + l1OriginHash, + l1OriginNumber, + _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) + ); _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes @@ -351,8 +402,12 @@ contract AggregateVerifierTest is BaseTest { abi.encode(expectedHash) // returns the blockhash ); - bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), expectedHash, l1OriginNumber, new bytes(130)); + bytes memory proofBytes = abi.encodePacked( + uint8(AggregateVerifier.ProofType.TEE), + expectedHash, + l1OriginNumber, + _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) + ); _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes @@ -370,7 +425,7 @@ contract AggregateVerifierTest is BaseTest { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, @@ -386,7 +441,7 @@ contract AggregateVerifierTest is BaseTest { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, @@ -402,7 +457,7 @@ contract AggregateVerifierTest is BaseTest { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 6c552ef08..2e6e5eca4 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -47,7 +47,8 @@ contract BaseTest is Test { address public immutable ZK_PROVER = makeAddr("zk-prover"); address public immutable ATTACKER = makeAddr("attacker"); - bytes32 public immutable TEE_IMAGE_HASH = keccak256("tee-image"); + bytes32 public immutable TEE_NITRO_IMAGE_HASH = keccak256("tee-nitro-image"); + bytes32 public immutable TEE_TDX_IMAGE_HASH = keccak256("tee-tdx-image"); bytes32 public immutable ZK_RANGE_HASH = keccak256("zk-range"); bytes32 public immutable ZK_AGGREGATE_HASH = keccak256("zk-aggregate"); bytes32 public immutable CONFIG_HASH = keccak256("config"); @@ -130,7 +131,7 @@ contract BaseTest is Test { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, @@ -226,7 +227,7 @@ contract BaseTest is Test { AggregateVerifier.ProofType proofType ) internal - pure + view returns (bytes memory) { return abi.encodePacked(uint8(proofType), _generateProofBody(salt, proofType)); @@ -237,12 +238,21 @@ contract BaseTest is Test { AggregateVerifier.ProofType proofType ) internal - pure + view returns (bytes memory) { if (proofType == AggregateVerifier.ProofType.TEE) { bytes32 saltHash = keccak256(salt); - return abi.encodePacked(saltHash, bytes32(0), uint8(27), saltHash, bytes32(uint256(1)), uint8(28)); + return abi.encodePacked( + TEE_NITRO_IMAGE_HASH, + saltHash, + bytes32(0), + uint8(27), + TEE_TDX_IMAGE_HASH, + saltHash, + bytes32(uint256(1)), + uint8(28) + ); } return abi.encodePacked(salt, bytes32(0), bytes32(0), uint8(27)); } diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index e4c93e5aa..0a95fbc94 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -16,12 +16,14 @@ import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -/// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. +/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. contract MockAggregateVerifierForRegistry { - bytes32 public TEE_IMAGE_HASH; + bytes32 public TEE_NITRO_IMAGE_HASH; + bytes32 public TEE_TDX_IMAGE_HASH; constructor(bytes32 imageHash) { - TEE_IMAGE_HASH = imageHash; + TEE_NITRO_IMAGE_HASH = imageHash; + TEE_TDX_IMAGE_HASH = imageHash; } } diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 742960312..bc8ad918f 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -17,12 +17,14 @@ import { GameType } from "src/libraries/bridge/Types.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -/// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. +/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. contract MockAggregateVerifierForTDXRegistry { - bytes32 public TEE_IMAGE_HASH; + bytes32 public TEE_NITRO_IMAGE_HASH; + bytes32 public TEE_TDX_IMAGE_HASH; constructor(bytes32 imageHash) { - TEE_IMAGE_HASH = imageHash; + TEE_NITRO_IMAGE_HASH = imageHash; + TEE_TDX_IMAGE_HASH = imageHash; } } diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index 7ae88b9bf..df7788e7b 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -18,12 +18,14 @@ import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; -/// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. +/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. contract MockAggregateVerifierForVerifier { - bytes32 public TEE_IMAGE_HASH; + bytes32 public TEE_NITRO_IMAGE_HASH; + bytes32 public TEE_TDX_IMAGE_HASH; - constructor(bytes32 imageHash) { - TEE_IMAGE_HASH = imageHash; + constructor(bytes32 nitroImageHash, bytes32 tdxImageHash) { + TEE_NITRO_IMAGE_HASH = nitroImageHash; + TEE_TDX_IMAGE_HASH = tdxImageHash; } } @@ -53,7 +55,8 @@ contract TEEVerifierTest is Test { address internal nitroSignerAddress; address internal tdxSignerAddress; - bytes32 internal constant IMAGE_ID = keccak256("test-image-id"); + bytes32 internal constant NITRO_IMAGE_ID = keccak256("test-nitro-image-id"); + bytes32 internal constant TDX_IMAGE_ID = keccak256("test-tdx-image-id"); uint32 internal constant TEST_GAME_TYPE = 621; address internal immutable PROPOSER = makeAddr("proposer"); @@ -66,7 +69,8 @@ contract TEEVerifierTest is Test { tdxSignerAddress = vm.addr(TDX_SIGNER_PRIVATE_KEY); // Deploy mock factory and verifier - MockAggregateVerifierForVerifier mockVerifier = new MockAggregateVerifierForVerifier(IMAGE_ID); + MockAggregateVerifierForVerifier mockVerifier = + new MockAggregateVerifierForVerifier(NITRO_IMAGE_ID, TDX_IMAGE_ID); MockDisputeGameFactoryForVerifier mockFactory = new MockDisputeGameFactoryForVerifier(); mockFactory.setImpl(TEST_GAME_TYPE, address(mockVerifier)); @@ -90,8 +94,8 @@ contract TEEVerifierTest is Test { teeProverRegistry = DevTEEProverRegistry(address(proxy)); // Register one Nitro signer and one TDX signer with the correct image hash. - teeProverRegistry.addDevSigner(nitroSignerAddress, IMAGE_ID); - teeProverRegistry.addDevTDXSigner(tdxSignerAddress, IMAGE_ID); + teeProverRegistry.addDevSigner(nitroSignerAddress, NITRO_IMAGE_ID); + teeProverRegistry.addDevTDXSigner(tdxSignerAddress, TDX_IMAGE_ID); // Set the proposer as valid teeProverRegistry.setProposer(PROPOSER, true); @@ -103,10 +107,17 @@ contract TEEVerifierTest is Test { ); } - function testVerifyValidSignatures() public view { + function testVerifyValidNitroSignature() public view { bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _proofBytes(journal); - bool result = verifier.verify(proofBytes, IMAGE_ID, journal); + bytes memory proofBytes = _proofBytes(journal, NITRO_SIGNER_PRIVATE_KEY); + bool result = verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); + assertTrue(result); + } + + function testVerifyValidTDXSignature() public view { + bytes32 journal = keccak256("test-journal"); + bytes memory proofBytes = _proofBytes(journal, TDX_SIGNER_PRIVATE_KEY); + bool result = verifier.verify(proofBytes, TDX_IMAGE_ID, journal); assertTrue(result); } @@ -115,20 +126,18 @@ contract TEEVerifierTest is Test { bytes memory invalidSignature = new bytes(65); invalidSignature[64] = bytes1(uint8(27)); // Set v to 27 - bytes memory proofBytes = _buildProof(PROPOSER, invalidSignature, _signature(TDX_SIGNER_PRIVATE_KEY, journal)); + bytes memory proofBytes = _buildProof(PROPOSER, invalidSignature); vm.expectRevert(TEEVerifier.InvalidSignature.selector); - verifier.verify(proofBytes, IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } function testVerifyFailsWithInvalidProposer() public { bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _buildProof( - address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, journal), _signature(TDX_SIGNER_PRIVATE_KEY, journal) - ); + bytes memory proofBytes = _buildProof(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); - verifier.verify(proofBytes, IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } function testVerifyFailsWithUnregisteredSigner() public { @@ -138,58 +147,28 @@ contract TEEVerifierTest is Test { bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = - _buildProof(PROPOSER, _signature(unregisteredKey, journal), _signature(TDX_SIGNER_PRIVATE_KEY, journal)); + bytes memory proofBytes = _buildProof(PROPOSER, _signature(unregisteredKey, journal)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); - verifier.verify(proofBytes, IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } function testVerifyFailsWithImageIdMismatch() public { bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _proofBytes(journal); - // Different imageId should fail — signer was registered with IMAGE_ID + // Different imageId should fail — Nitro signer was registered with NITRO_IMAGE_ID bytes32 wrongImageId = keccak256("different-image"); - vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, IMAGE_ID, wrongImageId)); + bytes memory proofBytes = _proofBytes(journal, NITRO_SIGNER_PRIVATE_KEY); + vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); verifier.verify(proofBytes, wrongImageId, journal); } - function testVerifyFailsWithDuplicateSigner() public { + function testVerifyFailsWhenTEETypeIsIncludedInProof() public { bytes32 journal = keccak256("test-journal"); - bytes memory nitroSignature = _signature(NITRO_SIGNER_PRIVATE_KEY, journal); - bytes memory proofBytes = _buildProof(PROPOSER, nitroSignature, nitroSignature); - - vm.expectRevert(abi.encodeWithSelector(TEEVerifier.DuplicateSigner.selector, nitroSignerAddress)); - verifier.verify(proofBytes, IMAGE_ID, journal); - } - - function testVerifyFailsWithoutNitroSignature() public { - bytes32 journal = keccak256("test-journal"); - uint256 secondTDXKey = 0x3234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - - vm.prank(owner); - teeProverRegistry.addDevTDXSigner(vm.addr(secondTDXKey), IMAGE_ID); + bytes memory proofBytes = abi.encodePacked(PROPOSER, uint8(3), _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); - bytes memory proofBytes = - _buildProof(PROPOSER, _signature(TDX_SIGNER_PRIVATE_KEY, journal), _signature(secondTDXKey, journal)); - - vm.expectRevert(TEEVerifier.MissingNitroSignature.selector); - verifier.verify(proofBytes, IMAGE_ID, journal); - } - - function testVerifyFailsWithoutTDXSignature() public { - bytes32 journal = keccak256("test-journal"); - uint256 secondNitroKey = 0x4234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - - vm.prank(owner); - teeProverRegistry.addDevSigner(vm.addr(secondNitroKey), IMAGE_ID); - - bytes memory proofBytes = - _buildProof(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal), _signature(secondNitroKey, journal)); - - vm.expectRevert(TEEVerifier.MissingTDXSignature.selector); - verifier.verify(proofBytes, IMAGE_ID, journal); + vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } function testVerifyFailsWithInvalidProofFormat() public { @@ -199,17 +178,15 @@ contract TEEVerifierTest is Test { bytes memory shortProof = new bytes(50); vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); - verifier.verify(shortProof, IMAGE_ID, journal); + verifier.verify(shortProof, NITRO_IMAGE_ID, journal); } function testConstants() public view { assertEq(address(verifier.TEE_PROVER_REGISTRY()), address(teeProverRegistry)); } - function _proofBytes(bytes32 journal) internal view returns (bytes memory) { - return _buildProof( - PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal), _signature(TDX_SIGNER_PRIVATE_KEY, journal) - ); + function _proofBytes(bytes32 journal, uint256 signerPrivateKey) internal view returns (bytes memory) { + return _buildProof(PROPOSER, _signature(signerPrivateKey, journal)); } function _signature(uint256 privateKey, bytes32 journal) internal pure returns (bytes memory) { @@ -217,15 +194,7 @@ contract TEEVerifierTest is Test { return abi.encodePacked(r, s, v); } - function _buildProof( - address proposer, - bytes memory firstSignature, - bytes memory secondSignature - ) - internal - pure - returns (bytes memory) - { - return abi.encodePacked(proposer, firstSignature, secondSignature); + function _buildProof(address proposer, bytes memory signature) internal pure returns (bytes memory) { + return abi.encodePacked(proposer, signature); } } diff --git a/test/mocks/MockDevTEEProverRegistry.sol b/test/mocks/MockDevTEEProverRegistry.sol index f4c6ed10b..66bff358c 100644 --- a/test/mocks/MockDevTEEProverRegistry.sol +++ b/test/mocks/MockDevTEEProverRegistry.sol @@ -23,7 +23,7 @@ contract DevTEEProverRegistry is TEEProverRegistry { /// @notice Registers a Nitro signer for testing (bypasses attestation verification). /// @dev Only callable by owner. For development/testing use only. /// The imageHash parameter is stored so isValidSigner() can validate against - /// the current AggregateVerifier's TEE_IMAGE_HASH. + /// the current AggregateVerifier's TEE_NITRO_IMAGE_HASH. /// @param signer The address of the signer to register. /// @param imageHash The TEE image hash to associate with this signer. function addDevSigner(address signer, bytes32 imageHash) external onlyOwner { diff --git a/test/opcm/DeployImplementations.t.sol b/test/opcm/DeployImplementations.t.sol index d18cd349c..17313846d 100644 --- a/test/opcm/DeployImplementations.t.sol +++ b/test/opcm/DeployImplementations.t.sol @@ -174,7 +174,8 @@ contract DeployImplementations_Test is Test, FeatureFlags { _faultGameV2SplitDepth, // faultGameV2SplitDepth (bounded) _faultGameV2ClockExtension, // faultGameV2ClockExtension (bounded) _faultGameV2MaxClockDuration, // faultGameV2MaxClockDuration (bounded) - bytes32(uint256(1)), // teeImageHash + bytes32(uint256(1)), // teeNitroImageHash + bytes32(uint256(2)), // teeTdxImageHash bytes32(0), // multiproofConfigHash 621, // multiproofGameType address(0), // nitroEnclaveVerifier @@ -404,7 +405,8 @@ contract DeployImplementations_Test is Test, FeatureFlags { 30, // faultGameV2SplitDepth 10800, // faultGameV2ClockExtension 302400, // faultGameV2MaxClockDuration - bytes32(uint256(1)), // teeImageHash + bytes32(uint256(1)), // teeNitroImageHash + bytes32(uint256(2)), // teeTdxImageHash bytes32(0), // multiproofConfigHash 621, // multiproofGameType address(0), // nitroEnclaveVerifier diff --git a/test/opcm/DeployOPChain.t.sol b/test/opcm/DeployOPChain.t.sol index 19a89a3f8..43d82faf1 100644 --- a/test/opcm/DeployOPChain.t.sol +++ b/test/opcm/DeployOPChain.t.sol @@ -87,7 +87,8 @@ contract DeployOPChain_TestBase is Test, FeatureFlags { faultGameV2SplitDepth: 30, faultGameV2ClockExtension: 10800, faultGameV2MaxClockDuration: 302400, - teeImageHash: bytes32(uint256(1)), + teeNitroImageHash: bytes32(uint256(1)), + teeTdxImageHash: bytes32(uint256(2)), multiproofConfigHash: bytes32(0), multiproofGameType: 621, nitroEnclaveVerifier: address(0), From 993bb6526c6620712105d8016cc845001b0df69c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Wed, 6 May 2026 13:01:03 -0400 Subject: [PATCH 016/146] fix semver --- snapshots/semver-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index ba4014842..01ccf8ab8 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -37,7 +37,7 @@ }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { "initCodeHash": "0x6e50ad8219dfc59503395bf63d2a4d91c002bd21a1df7c902debedea9c2e13eb", - "sourceCodeHash": "0x5708e88bac4268a2c1310c3b9aa84335310ae6941a90e41bc10212f356b2060a" + "sourceCodeHash": "0x802a3645c05ae98232b091f779f6321f60230dd22f536b00dcbacccde6af4d3a" }, "src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": { "initCodeHash": "0x6f3afd2d0ef97a82ca3111976322b99343a270e54cd4a405028f2f29c75f7fb1", @@ -61,11 +61,11 @@ }, "src/L1/proofs/tee/TEEProverRegistry.sol:TEEProverRegistry": { "initCodeHash": "0x4b2fb8ba42668b1b9a8ebb99518fc0552703dfda190acde45d771847b450ac65", - "sourceCodeHash": "0xd14971b269de818bcceac3493284196dd0168fad3d21315db965d6d05a2cc050" + "sourceCodeHash": "0x3e763a25f5353756c2ab52f6ae003f8cc7c1da2965aa2fd8b6ec6a0ac0ae6720" }, "src/L1/proofs/tee/TEEVerifier.sol:TEEVerifier": { "initCodeHash": "0x5f42455c6d41c0a49e05681a6fa7dbf21eb22c89705fd11b6030952b3227e559", - "sourceCodeHash": "0x83a09b025e33838859a7b5848463bb7f4d6d76031f5b8b8bc9f3cd5e73ad4464" + "sourceCodeHash": "0xcd8358734ff27b0c17af73713b7f3d2b0f348ba0646efc356345bad9fa8d6a8c" }, "src/L1/proofs/v2/FaultDisputeGameV2.sol:FaultDisputeGameV2": { "initCodeHash": "0x6fc59e2da083c9e2093e42b0fda705e8215cc216e4dcedbf728c08f69ec2d3bd", @@ -163,4 +163,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} +} \ No newline at end of file From 72ce71e22ce7ef92b9d3e7ec5e3afb427257ff64 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Wed, 6 May 2026 16:40:16 -0400 Subject: [PATCH 017/146] another deployment --- deploy-config/sepolia.json | 5 ++--- deployments/11155111-dev-with-tdx.json | 16 ++++++++-------- scripts/multiproof/justfile | 2 -- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/deploy-config/sepolia.json b/deploy-config/sepolia.json index a21340ed4..ceb331ae1 100644 --- a/deploy-config/sepolia.json +++ b/deploy-config/sepolia.json @@ -58,9 +58,8 @@ "useFaultProofs": true, "useRevenueShare": true, "chainFeesRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "teeImageHash": "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6", - "teeNitroImageHash": "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6", - "teeTdxImageHash": "0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6", + "teeNitroImageHash": "0x11fb64617dfa2875d31b0cfb656666fd8cee65eb134fefeca171b9b6b4444a64", + "teeTdxImageHash": "0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66", "multiproofConfigHash": "0x12e9c45f19f9817c6d4385fad29e7a70c355502cf0883e76a9a7e478a85d1360", "multiproofGameType": 621, "teeProposer": "0xdb84125f2f4229c81c579f41bc129c71b174eb58", diff --git a/deployments/11155111-dev-with-tdx.json b/deployments/11155111-dev-with-tdx.json index 1c90ea2b9..6dc233488 100644 --- a/deployments/11155111-dev-with-tdx.json +++ b/deployments/11155111-dev-with-tdx.json @@ -1,13 +1,13 @@ { - "ASRStartingBlockNumber": 41154575, - "ASRStartingOutputRoot": "0x2cb991c0d9e739677fbcc78772c01f2e4639dffb159422b08cfbea1160f8877c", - "AggregateVerifier": "0x4Cf26dE13cC4D64352f6c76b8ef888bEA6a2Acef", - "AnchorStateRegistry": "0x629cf75322989cE321e10BfA4DF2daE43ac19c82", - "DelayedWETH": "0x17626e33878597cbbFc877c9AA47945E64eB6D31", - "DisputeGameFactory": "0x6C9De6Cc269844c3121B2b59F239F79bace2303D", + "ASRStartingBlockNumber": 41162517, + "ASRStartingOutputRoot": "0xb7b34000df977c594da02d331a54c31015b4255998956051bbbcce03e42e9f88", + "AggregateVerifier": "0xfc9D070464370BD6B1a99413f83b574769b0c0B7", + "AnchorStateRegistry": "0xEDA113F9DE36455f8BeCBb2301BCac3CcFFd5D24", + "DelayedWETH": "0xc947c5F371Da8A18CC4374F67775A693dea7fb10", + "DisputeGameFactory": "0xe07A57058b1e7DbD31b2BE1C22476Fbe3eF2B050", "NitroEnclaveVerifier": "0x77461a6434fFE3435206B19658F33274f3104e07", "TDXRegistrationManager": "0x44E999A5859c2D12378a349882fAe5805DCE71b9", "TDXVerifier": "0x9c38b756B31529Cad02D54A4ba810F3ff1e00054", - "TEEProverRegistry": "0x52F8f01Fb5e2cF6BE15a311EbFB30f24352C7221", - "TEEVerifier": "0xf218d04E5A582deEA9E952E4227C07e865BC8bA1" + "TEEProverRegistry": "0xFFABA937771b13F9fCF018C68Dd877826c8F2762", + "TEEVerifier": "0xC7c3a7822DEa94Dde1EBEe567918047dAE305d08" } \ No newline at end of file diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 4d69f21a6..24d2fc4e2 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -11,7 +11,6 @@ l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https:// sepolia_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" sepolia_tdx_verifier_id := "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009" -sepolia_tdx_image_hash := "0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66" sepolia_tdx_registration_manager := "0x44E999A5859c2D12378a349882fAe5805DCE71b9" # List available multiproof deployment recipes. @@ -50,7 +49,6 @@ tdx-sepolia-config: @echo "RISC0_VERIFIER_ROUTER={{sepolia_risc0_verifier_router}}" @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" - @echo "TDX_IMAGE_HASH={{sepolia_tdx_image_hash}}" @echo "TDX_REGISTRATION_MANAGER={{sepolia_tdx_registration_manager}}" @echo "L2_RPC_URL={{l2_rpc_url}}" @echo "L2_OUTPUT_ROOT_RPC_URL={{l2_output_root_rpc_url}}" From 9d6bf3cf5839e40f0d454e4996d80d8119d909e1 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Wed, 6 May 2026 21:47:40 -0400 Subject: [PATCH 018/146] add helper command --- scripts/multiproof/justfile | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 24d2fc4e2..934437b25 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -3,6 +3,7 @@ set dotenv-load repo_root := justfile_directory() + "/../.." justfile_path := justfile_directory() + "/justfile" default_deploy_config := "deploy-config/sepolia.json" +sepolia_tdx_stack_deployments := "deployments/11155111-dev-with-tdx.json" forge_account := "testnet-admin" l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:8545") @@ -46,6 +47,7 @@ deploy-tdx-verifier risc0_router=sepolia_risc0_verifier_router tdx_verifier_id=s # Print the Sepolia TDX constants used by these recipes. tdx-sepolia-config: @echo "L1_RPC_URL={{l1_rpc_url}}" + @jq -r '"TEE_PROVER_REGISTRY=" + (.TEEProverRegistry // "")' "{{repo_root}}/{{sepolia_tdx_stack_deployments}}" @echo "RISC0_VERIFIER_ROUTER={{sepolia_risc0_verifier_router}}" @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" @@ -112,6 +114,58 @@ deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deplo --account "{{forge_account}}" \ --sender "$owner" +# Args: +# registry TEEProverRegistry address. Defaults to deployments/11155111-dev-with-tdx.json. +# rpc L1 Sepolia RPC URL. +# +# Query currently registered TEE signer addresses on Sepolia. +tee-registered-signers registry="" rpc=l1_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + + registry="{{registry}}" + if [[ -z "$registry" ]]; then + registry="$(jq -r '.TEEProverRegistry // empty' "{{sepolia_tdx_stack_deployments}}")" + fi + + if [[ -z "$registry" || "$registry" == "null" ]]; then + echo "Missing TEEProverRegistry address. Pass one explicitly or update {{sepolia_tdx_stack_deployments}}." >&2 + exit 1 + fi + + echo "TEEProverRegistry: $registry" + echo "RPC: {{rpc}}" + + signers=() + while IFS= read -r signer; do + signers+=("$signer") + done < <(cast call "$registry" 'getRegisteredSigners()(address[])' --rpc-url "{{rpc}}" --json | jq -r '.[0][]') + if (( ${#signers[@]} == 0 )); then + echo "No registered TEE signers found." + exit 0 + fi + + printf "%-42s %-6s %-66s %-5s\n" "SIGNER" "TYPE" "IMAGE_HASH" "VALID" + for signer in "${signers[@]}"; do + tee_type_id="$(cast call "$registry" 'signerTEEType(address)(uint8)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" + image_hash="$(cast call "$registry" 'signerImageHash(address)(bytes32)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" + valid_raw="$(cast call "$registry" 'isValidSigner(address)(bool)' "$signer" --rpc-url "{{rpc}}" --json 2>/dev/null || true)" + if [[ -n "$valid_raw" ]]; then + valid="$(echo "$valid_raw" | jq -r '.[0]')" + else + valid="unknown" + fi + + case "$tee_type_id" in + 1) tee_type="NITRO" ;; + 2) tee_type="TDX" ;; + *) tee_type="NONE" ;; + esac + + printf "%-42s %-6s %-66s %-5s\n" "$signer" "$tee_type" "$image_hash" "$valid" + done + # Print the two-step Sepolia command template. tdx-sepolia-help: @echo 'export L1_RPC_URL=' @@ -119,4 +173,5 @@ tdx-sepolia-help: @echo 'export L2_OUTPUT_ROOT_RPC_URL=' @echo 'just deploy-tdx-verifier' @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' + @echo 'just tee-registered-signers' @echo 'just tdx-sepolia-config' From c4c830e3a92d63de04df0357a62c81281d14429c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Thu, 7 May 2026 12:55:27 -0400 Subject: [PATCH 019/146] fix tdx deploy script --- scripts/multiproof/DeployDevWithTDX.s.sol | 4 +++- scripts/multiproof/README.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 98b101983..ad7f3cd5f 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -8,6 +8,7 @@ pragma solidity 0.8.15; * This deploys the same local multiproof testing infrastructure as the existing * dev scripts, but configures TEEProverRegistry for TDX signer registration. Deploy * TDXVerifier first with DeployTDXVerifier.s.sol and pass its address to run(). + * The NitroEnclaveVerifier in the deploy config must also already be deployed. * The default run(address) entrypoint configures DEFAULT_TDX_REGISTRATION_MANAGER * as the registry manager so it can submit TDX signer registrations. */ @@ -102,7 +103,7 @@ contract DeployDevWithTDX is Script { console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); console.log(""); - console.log("NOTE: TDXVerifier owner must be the broadcaster/finalSystemOwner."); + console.log("NOTE: TDXVerifier and NitroEnclaveVerifier owners must be the broadcaster/finalSystemOwner."); vm.startBroadcast(); @@ -139,6 +140,7 @@ contract DeployDevWithTDX is Script { teeProverRegistryProxy = address(registryProxy); ITDXVerifier(tdxVerifierAddr).setProofSubmitter(teeProverRegistryProxy); + INitroEnclaveVerifier(nitroEnclaveVerifierAddr).setProofSubmitter(teeProverRegistryProxy); teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); } diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 13841b32f..b7794a936 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -215,7 +215,7 @@ The script saves output to `deployments/-tdx-verifier.json`. ### Step 2: Deploy the TDX multiproof test stack -Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `TDXVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. +Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates both `TDXVerifier.proofSubmitter` and `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. From aa612e147b3a28aae24490bc5622e2339205dd5b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sat, 9 May 2026 09:14:42 -0400 Subject: [PATCH 020/146] update dev deployment --- deploy-config/zeronet-tdx.json | 78 +++++ deployments/11155111-dev-with-tdx.json | 20 +- deployments/11155111-tee-verifiers.json | 17 + scripts/multiproof/DeployDevWithNitro.s.sol | 10 +- scripts/multiproof/DeployDevWithTDX.s.sol | 19 +- scripts/multiproof/DeployNitroVerifier.s.sol | 201 ++++++++++++ scripts/multiproof/DeployTEEVerifiers.s.sol | 320 +++++++++++++++++++ scripts/multiproof/README.md | 71 +++- scripts/multiproof/justfile | 165 +++++++++- 9 files changed, 868 insertions(+), 33 deletions(-) create mode 100644 deploy-config/zeronet-tdx.json create mode 100644 deployments/11155111-tee-verifiers.json create mode 100644 scripts/multiproof/DeployNitroVerifier.s.sol create mode 100644 scripts/multiproof/DeployTEEVerifiers.s.sol diff --git a/deploy-config/zeronet-tdx.json b/deploy-config/zeronet-tdx.json new file mode 100644 index 000000000..3aad31a1e --- /dev/null +++ b/deploy-config/zeronet-tdx.json @@ -0,0 +1,78 @@ +{ + "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", + "superchainConfigGuardian": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l1StartingBlockTag": "0x48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b", + "l1ChainID": 11155111, + "l2ChainID": 11155420, + "l2BlockTime": 2, + "l1BlockTime": 12, + "maxSequencerDrift": 600, + "sequencerWindowSize": 3600, + "channelTimeout": 300, + "p2pSequencerAddress": "0x57CACBB0d30b01eb2462e5dC940c161aff3230D3", + "batchInboxAddress": "0xff00000000000000000000000000000011155420", + "batchSenderAddress": "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", + "l2OutputOracleSubmissionInterval": 120, + "l2OutputOracleStartingBlockNumber": 0, + "l2OutputOracleStartingTimestamp": 1690493568, + "l2OutputOracleProposer": "0x49277EE36A024120Ee218127354c4a3591dc90A9", + "l2OutputOracleChallenger": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "finalizationPeriodSeconds": 12, + "proxyAdminOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "baseFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l1FeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "sequencerFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "operatorFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "baseFeeVaultWithdrawalNetwork": 0, + "l1FeeVaultWithdrawalNetwork": 0, + "sequencerFeeVaultWithdrawalNetwork": 0, + "operatorFeeVaultWithdrawalNetwork": 0, + "enableGovernance": true, + "governanceTokenSymbol": "OP", + "governanceTokenName": "Optimism", + "governanceTokenOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l2GenesisBlockGasLimit": "0x1c9c380", + "l2GenesisBlockBaseFeePerGas": "0x3b9aca00", + "eip1559Denominator": 50, + "eip1559Elasticity": 6, + "l2GenesisRegolithTimeOffset": "0x0", + "systemConfigStartBlock": 4071248, + "fundDevAccounts": false, + "faultGameAbsolutePrestate": "0x03b7eaa4e3cbce90381921a4b48008f4769871d64f93d113fcadca08ecee503b", + "faultGameMaxDepth": 73, + "faultGameClockExtension": 10800, + "faultGameMaxClockDuration": 302400, + "faultGameGenesisBlock": 9496192, + "faultGameGenesisOutputRoot": "0x63b1cda487c072b020a57c1203f7c2921754005cadbd54bed7f558111b8278d8", + "faultGameSplitDepth": 30, + "faultGameWithdrawalDelay": 604800, + "preimageOracleMinProposalSize": 126000, + "preimageOracleChallengePeriod": 86400, + "proofMaturityDelaySeconds": 604800, + "disputeGameFinalityDelaySeconds": 302400, + "respectedGameType": 0, + "useFaultProofs": true, + "useRevenueShare": true, + "chainFeesRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "teeNitroImageHash": "0x213ad2e8f1594e1c386ef88ed9d2ec0df5a968f5228a5b17f4b0040599726515", + "teeTdxImageHash": "0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66", + "multiproofConfigHash": "0x12e9c45f19f9817c6d4385fad29e7a70c355502cf0883e76a9a7e478a85d1360", + "multiproofGameType": 621, + "teeProposer": "0xdb84125f2f4229c81c579f41bc129c71b174eb58", + "teeChallenger": "0xadc09b63a3ac57a2ce86d946617a18df9db029a1", + "zkRangeHash": "0x51708a6b4a3b800a14607e902c1aad47719905c12698a3e01ca8b3321761bc52", + "zkAggregationHash": "0x005aa369dd9445e172018a9eaa4a0f9767b2c2079ece90ca120422b3c4c65f11", + "nitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", + "tdxVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", + "multiproofGenesisOutputRoot": "0xbc273d5876d1858ecd5aaf4ce4eaf16c73f0187ca4271b774ed5da7d2254ba79", + "multiproofGenesisBlockNumber": 37223829, + "multiproofBlockInterval": 600, + "multiproofIntermediateBlockInterval": 30, + "risc0VerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "risc0SetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9", + "sp1Verifier": "0x397A5f7f3dBd538f23DE225B51f532c34448dA9B" +} diff --git a/deployments/11155111-dev-with-tdx.json b/deployments/11155111-dev-with-tdx.json index 6dc233488..17d4be0a7 100644 --- a/deployments/11155111-dev-with-tdx.json +++ b/deployments/11155111-dev-with-tdx.json @@ -1,13 +1,13 @@ { - "ASRStartingBlockNumber": 41162517, - "ASRStartingOutputRoot": "0xb7b34000df977c594da02d331a54c31015b4255998956051bbbcce03e42e9f88", - "AggregateVerifier": "0xfc9D070464370BD6B1a99413f83b574769b0c0B7", - "AnchorStateRegistry": "0xEDA113F9DE36455f8BeCBb2301BCac3CcFFd5D24", - "DelayedWETH": "0xc947c5F371Da8A18CC4374F67775A693dea7fb10", - "DisputeGameFactory": "0xe07A57058b1e7DbD31b2BE1C22476Fbe3eF2B050", - "NitroEnclaveVerifier": "0x77461a6434fFE3435206B19658F33274f3104e07", + "ASRStartingBlockNumber": 41281684, + "ASRStartingOutputRoot": "0xc9f0f3a76f7c15306635a9990a8d4a3c1e281ca9e1519824679a73ca781f3404", + "AggregateVerifier": "0xA1Fd6E923d8Efa8deebde9D2AE98Df08B9A31C7d", + "AnchorStateRegistry": "0xe3E9ec1a72D8AD322187B4a4B757B2e906f4edF8", + "DelayedWETH": "0x2CCe62a3496aC7bD1A706cf7E1a765fd24e59666", + "DisputeGameFactory": "0x4aae21Fc11669463d37d2B875B52c7D63F794a08", + "NitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", "TDXRegistrationManager": "0x44E999A5859c2D12378a349882fAe5805DCE71b9", - "TDXVerifier": "0x9c38b756B31529Cad02D54A4ba810F3ff1e00054", - "TEEProverRegistry": "0xFFABA937771b13F9fCF018C68Dd877826c8F2762", - "TEEVerifier": "0xC7c3a7822DEa94Dde1EBEe567918047dAE305d08" + "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", + "TEEProverRegistry": "0x45111D4FDF5EC6bD2bDBF9Aed4a5f01140B892D9", + "TEEVerifier": "0x3065B3175F0265212e041Ea119D0001cf751E5a7" } \ No newline at end of file diff --git a/deployments/11155111-tee-verifiers.json b/deployments/11155111-tee-verifiers.json new file mode 100644 index 000000000..ebe25da12 --- /dev/null +++ b/deployments/11155111-tee-verifiers.json @@ -0,0 +1,17 @@ +{ + "IntelRootCaHash": "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009", + "NitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", + "NitroMaxTimeDiff": 3600, + "NitroRiscZeroVerifierRouter": "0xB121B667dd2cf27F95f9F5107137696F56f188f6", + "NitroRootCert": "0x641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68f79bb5b", + "NitroVerifierId": "0x15051db631d6ed382d957c795a558a0abdd00d0d22a1670455721bc2712d3d6e", + "NitroVerifierProofId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "RiscZeroSetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9", + "RiscZeroSetVerifier": "0xef981185595Ed2a49dEC9B2D5073B88Ef9e00Fd4", + "RiscZeroSetVerifierSelector": "0x242f9d5b00000000000000000000000000000000000000000000000000000000", + "RiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "TDXMaxTimeDiff": 3600, + "TDXRiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", + "TDXVerifierId": "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" +} \ No newline at end of file diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index c1eef99b7..492026b63 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -14,8 +14,8 @@ pragma solidity 0.8.15; * You cannot use addDevSigner() - you must go through the full registerSigner() flow. * * PREREQUISITES: - * 1. Deploy the RISC Zero verifier stack AND NitroEnclaveVerifier using - * DeployRiscZeroStack.s.sol (required because NitroEnclaveVerifier and its + * 1. Deploy the RISC Zero verifier route AND NitroEnclaveVerifier using + * DeployNitroVerifier.s.sol (required because NitroEnclaveVerifier and its * dependencies need Solidity ^0.8.20, while this script is pinned to =0.8.15). * 2. Set `nitroEnclaveVerifier` in the deploy config to the deployed address. * @@ -65,7 +65,7 @@ import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; /// @title DeployDevWithNitro /// @notice Development deployment WITH AWS Nitro attestation validation. /// @dev Uses real TEEProverRegistry which requires registerSigner() with valid attestation. -/// NitroEnclaveVerifier must be pre-deployed via DeployRiscZeroStack.s.sol. +/// NitroEnclaveVerifier must be pre-deployed via DeployNitroVerifier.s.sol. contract DeployDevWithNitro is Script { uint256 public constant BLOCK_INTERVAL = 600; uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 30; @@ -91,12 +91,12 @@ contract DeployDevWithNitro is Script { function run() public { GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); - // NitroEnclaveVerifier must be pre-deployed (via DeployRiscZeroStack.s.sol) + // NitroEnclaveVerifier must be pre-deployed (via DeployNitroVerifier.s.sol) nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); tdxVerifierAddr = cfg.tdxVerifier(); require( nitroEnclaveVerifierAddr != address(0), - "nitroEnclaveVerifier must be set in config (deploy via DeployRiscZeroStack.s.sol first)" + "nitroEnclaveVerifier must be set in config (deploy via DeployNitroVerifier.s.sol first)" ); require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index ad7f3cd5f..e600c33c6 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -8,7 +8,8 @@ pragma solidity 0.8.15; * This deploys the same local multiproof testing infrastructure as the existing * dev scripts, but configures TEEProverRegistry for TDX signer registration. Deploy * TDXVerifier first with DeployTDXVerifier.s.sol and pass its address to run(). - * The NitroEnclaveVerifier in the deploy config must also already be deployed. + * The NitroEnclaveVerifier can either come from the deploy config or be passed + * explicitly to the five-argument run() overload. * The default run(address) entrypoint configures DEFAULT_TDX_REGISTRATION_MANAGER * as the registry manager so it can submit TDX signer registrations. */ @@ -79,9 +80,21 @@ contract DeployDevWithTDX is Script { ) public { - nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); + run(cfg.nitroEnclaveVerifier(), tdxVerifier, registrationManager, asrStartingOutputRoot, asrStartingBlockNumber); + } + + function run( + address nitroEnclaveVerifier, + address tdxVerifier, + address registrationManager, + bytes32 asrStartingOutputRoot, + uint256 asrStartingBlockNumber + ) + public + { + nitroEnclaveVerifierAddr = nitroEnclaveVerifier; require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); - require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be set in config"); + require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be non-zero"); require(registrationManager != address(0), "registrationManager must be non-zero"); require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); tdxVerifierAddr = tdxVerifier; diff --git a/scripts/multiproof/DeployNitroVerifier.s.sol b/scripts/multiproof/DeployNitroVerifier.s.sol new file mode 100644 index 000000000..f84289df6 --- /dev/null +++ b/scripts/multiproof/DeployNitroVerifier.s.sol @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title DeployNitroVerifier + * @notice Deploys the RISC Zero set verifier route and NitroEnclaveVerifier used by TEEProverRegistry. + * + * This script is separated from the main multiproof deployment scripts because + * NitroEnclaveVerifier imports verifier interfaces that require Solidity ^0.8.20, + * while the multiproof stack is pinned to Solidity 0.8.15. + * + * Usage: + * + * forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + * --sig "run(address,address,bytes32,bytes32,bytes32)" \ + * \ + * \ + * --rpc-url --broadcast --private-key + * + * If using batched Nitro proofs, use the overload that also supplies + * : + * + * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" + * + * The broadcaster must be the owner because this script calls addVerifyRoute() + * on the freshly deployed NitroEnclaveVerifier. + * + * After running DeployDevWithTDX.s.sol, the Nitro verifier's proofSubmitter is + * updated to the deployed TEEProverRegistry. + */ + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; + +import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; +import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; + +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; + +contract DeployNitroVerifier is Script { + /// @notice Maximum Nitro attestation age accepted by NitroEnclaveVerifier. + uint64 internal constant NITRO_MAX_TIME_DIFF = 3600; + + address public setVerifier; + address public nitroEnclaveVerifier; + + /// @param owner Owner for NitroEnclaveVerifier. Must be the broadcaster for route setup. + /// @param risc0VerifierRouter Existing RISC Zero verifier router. + /// @param setBuilderImageId RISC Zero set builder image ID. + /// @param nitroRootCert SHA-256 hash of the AWS Nitro root certificate. + /// @param nitroVerifierId RISC Zero image ID for the Nitro attestation verifier guest. + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId + ) + public + { + run(owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, bytes32(0)); + } + + /// @param nitroVerifierProofId Optional verifier proof ID used by Nitro batch verification. + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + public + { + bytes4 setVerifierSelector = + _validateInputs(owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId); + + console.log("=== Deploying NitroEnclaveVerifier ==="); + console.log("Owner:", owner); + console.log("RISC Zero Verifier Router:", risc0VerifierRouter); + console.log("Set Builder Image ID:", vm.toString(setBuilderImageId)); + console.log("Set Verifier Selector:", vm.toString(setVerifierSelector)); + console.log("Nitro Root Cert:", vm.toString(nitroRootCert)); + console.log("Nitro Verifier ID:", vm.toString(nitroVerifierId)); + console.log("Nitro Verifier Proof ID:", vm.toString(nitroVerifierProofId)); + console.log("Max Time Diff:", NITRO_MAX_TIME_DIFF); + console.log(""); + console.log("NOTE: proofSubmitter is set to owner as placeholder."); + console.log(" DeployDevWithTDX.s.sol updates it to TEEProverRegistry."); + console.log(""); + + vm.startBroadcast(); + + (setVerifier, nitroEnclaveVerifier) = _deployNitroVerifier( + owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, nitroVerifierProofId + ); + + vm.stopBroadcast(); + + console.log("RiscZeroSetVerifier:", setVerifier); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + console.log(""); + console.log(">>> Use this NitroEnclaveVerifier address in the deploy config <<<"); + + _writeOutput( + setVerifier, + nitroEnclaveVerifier, + risc0VerifierRouter, + setBuilderImageId, + setVerifierSelector, + nitroRootCert, + nitroVerifierId, + nitroVerifierProofId + ); + } + + function _deployNitroVerifier( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + internal + returns (address deployedSetVerifier, address deployedNitroVerifier) + { + deployedSetVerifier = + address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + NitroEnclaveVerifier verifier = new NitroEnclaveVerifier( + owner, + NITRO_MAX_TIME_DIFF, + new bytes32[](0), + new uint64[](0), + nitroRootCert, + owner, + address(0), + ZkCoProcessorType.RiscZero, + zkConfig, + nitroVerifierProofId + ); + deployedNitroVerifier = address(verifier); + + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, deployedSetVerifier); + } + + function _validateInputs( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId + ) + internal + pure + returns (bytes4 setVerifierSelector) + { + require(owner != address(0), "owner must be non-zero"); + require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); + require(setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); + require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); + require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); + + setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + } + + function _writeOutput( + address deployedSetVerifier, + address deployedNitroVerifier, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes4 setVerifierSelector, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + internal + { + string memory key = "deployment"; + vm.serializeAddress(key, "RiscZeroSetVerifier", deployedSetVerifier); + vm.serializeAddress(key, "NitroEnclaveVerifier", deployedNitroVerifier); + vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); + vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); + vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); + vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); + vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); + vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); + string memory json = vm.serializeUint(key, "MaxTimeDiff", NITRO_MAX_TIME_DIFF); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-nitro-verifier.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/DeployTEEVerifiers.s.sol b/scripts/multiproof/DeployTEEVerifiers.s.sol new file mode 100644 index 000000000..46613dfd2 --- /dev/null +++ b/scripts/multiproof/DeployTEEVerifiers.s.sol @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title DeployTEEVerifiers + * @notice Deploys NitroEnclaveVerifier and TDXVerifier in one broadcast. + * + * Usage: + * + * forge script scripts/multiproof/DeployTEEVerifiers.s.sol:DeployTEEVerifiers \ + * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32,bytes32)" \ + * \ + * \ + * \ + * --rpc-url --broadcast --private-key + * + * If using batched Nitro proofs, use the overload that also supplies + * : + * + * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" + * + * If Nitro and TDX use different RISC Zero verifier routers, use: + * + * --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" + * + * The broadcaster must be the owner because this script calls addVerifyRoute() + * on the freshly deployed NitroEnclaveVerifier. Use the longest overload when + * Nitro and TDX proofs use different RISC Zero verifier router contracts. + */ + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; + +import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; +import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; + +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { TDXTcbStatus } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; +import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; + +contract DeployTEEVerifiers is Script { + /// @notice Maximum Nitro attestation age accepted by NitroEnclaveVerifier. + uint64 internal constant NITRO_MAX_TIME_DIFF = 3600; + + /// @notice Maximum TDX quote age accepted by TDXVerifier. + uint64 internal constant TDX_MAX_TIME_DIFF = 3600; + + address public setVerifier; + address public nitroEnclaveVerifier; + address public tdxVerifier; + + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + run( + owner, + risc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + bytes32(0), + tdxVerifierId, + intelRootCaHash + ); + } + + function run( + address owner, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + run( + owner, + nitroRisc0VerifierRouter, + tdxRisc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + bytes32(0), + tdxVerifierId, + intelRootCaHash + ); + } + + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + run( + owner, + risc0VerifierRouter, + risc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + nitroVerifierProofId, + tdxVerifierId, + intelRootCaHash + ); + } + + function run( + address owner, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + bytes4 setVerifierSelector = _validateInputs( + owner, + nitroRisc0VerifierRouter, + tdxRisc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + tdxVerifierId, + intelRootCaHash + ); + + console.log("=== Deploying TEE Verifiers ==="); + console.log("Owner:", owner); + console.log("Nitro RISC Zero Verifier Router:", nitroRisc0VerifierRouter); + console.log("TDX RISC Zero Verifier Router:", tdxRisc0VerifierRouter); + console.log("Set Builder Image ID:", vm.toString(setBuilderImageId)); + console.log("Set Verifier Selector:", vm.toString(setVerifierSelector)); + console.log("Nitro Root Cert:", vm.toString(nitroRootCert)); + console.log("Nitro Verifier ID:", vm.toString(nitroVerifierId)); + console.log("Nitro Verifier Proof ID:", vm.toString(nitroVerifierProofId)); + console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); + console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); + console.log(""); + console.log("NOTE: proofSubmitter is set to owner as placeholder on both verifiers."); + console.log(" DeployDevWithTDX.s.sol updates both to TEEProverRegistry."); + console.log(""); + + vm.startBroadcast(); + + (setVerifier, nitroEnclaveVerifier) = _deployNitroVerifier( + owner, nitroRisc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, nitroVerifierProofId + ); + tdxVerifier = _deployTDXVerifier(owner, tdxRisc0VerifierRouter, tdxVerifierId, intelRootCaHash); + + vm.stopBroadcast(); + + console.log("RiscZeroSetVerifier:", setVerifier); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + console.log("TDXVerifier:", tdxVerifier); + console.log(""); + console.log(">>> Use these verifier addresses for the TDX multiproof deployment <<<"); + + _writeOutput( + setVerifier, + nitroEnclaveVerifier, + tdxVerifier, + nitroRisc0VerifierRouter, + tdxRisc0VerifierRouter, + setBuilderImageId, + setVerifierSelector, + nitroRootCert, + nitroVerifierId, + nitroVerifierProofId, + tdxVerifierId, + intelRootCaHash + ); + } + + function _deployNitroVerifier( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + internal + returns (address deployedSetVerifier, address deployedNitroVerifier) + { + deployedSetVerifier = + address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + NitroEnclaveVerifier verifier = new NitroEnclaveVerifier( + owner, + NITRO_MAX_TIME_DIFF, + new bytes32[](0), + new uint64[](0), + nitroRootCert, + owner, + address(0), + ZkCoProcessorType.RiscZero, + zkConfig, + nitroVerifierProofId + ); + deployedNitroVerifier = address(verifier); + + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, deployedSetVerifier); + } + + function _deployTDXVerifier( + address owner, + address risc0VerifierRouter, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + returns (address deployedTDXVerifier) + { + TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); + allowedStatuses[0] = TDXTcbStatus.UpToDate; + allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: tdxVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + deployedTDXVerifier = address( + new TDXVerifier( + owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, ZkCoProcessorType.RiscZero, zkConfig, allowedStatuses + ) + ); + } + + function _validateInputs( + address owner, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + pure + returns (bytes4 setVerifierSelector) + { + require(owner != address(0), "owner must be non-zero"); + require(nitroRisc0VerifierRouter != address(0), "nitroRisc0VerifierRouter must be non-zero"); + require(tdxRisc0VerifierRouter != address(0), "tdxRisc0VerifierRouter must be non-zero"); + require(setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); + require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); + require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); + require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); + require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); + + setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + } + + function _writeOutput( + address deployedSetVerifier, + address deployedNitroVerifier, + address deployedTDXVerifier, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes4 setVerifierSelector, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + { + string memory key = "deployment"; + vm.serializeAddress(key, "RiscZeroSetVerifier", deployedSetVerifier); + vm.serializeAddress(key, "NitroEnclaveVerifier", deployedNitroVerifier); + vm.serializeAddress(key, "TDXVerifier", deployedTDXVerifier); + vm.serializeAddress(key, "RiscZeroVerifierRouter", tdxRisc0VerifierRouter); + vm.serializeAddress(key, "NitroRiscZeroVerifierRouter", nitroRisc0VerifierRouter); + vm.serializeAddress(key, "TDXRiscZeroVerifierRouter", tdxRisc0VerifierRouter); + vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); + vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); + vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); + vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); + vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); + vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); + vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); + vm.serializeUint(key, "NitroMaxTimeDiff", NITRO_MAX_TIME_DIFF); + string memory json = vm.serializeUint(key, "TDXMaxTimeDiff", TDX_MAX_TIME_DIFF); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-tee-verifiers.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index b7794a936..a48f93383 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -174,36 +174,63 @@ The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 b > **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining off-chain piece is the actual RISC Zero/SP1 TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. -### Step 1: Deploy `TDXVerifier` +### Step 1: Deploy verifier policy contracts -`TDXVerifier` is deployed separately because it depends on verifier interfaces that require Solidity `^0.8.20`, while the rest of the multiproof deployment stack is pinned to Solidity `0.8.15`. +`NitroEnclaveVerifier` and `TDXVerifier` are deployed separately from the main multiproof stack because they depend on verifier interfaces that require Solidity `^0.8.20`, while the rest of the multiproof deployment stack is pinned to Solidity `0.8.15`. For Sepolia TDX testing, `scripts/multiproof/justfile` defaults to: ```bash -RISC0_VERIFIER_ROUTER=0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 -TDX_VERIFIER_ID=0x1f1bc81fae82605af46a4c9a20f641922b7542befd9219644c7e6c59ccdeccab +NITRO_RISC0_VERIFIER_ROUTER=0xB121B667dd2cf27F95f9F5107137696F56f188f6 +TDX_RISC0_VERIFIER_ROUTER=0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 +RISC0_SET_BUILDER_IMAGE_ID=0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9 +TDX_VERIFIER_ID=0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b INTEL_ROOT_CA_HASH=0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009 -TDX_IMAGE_HASH=0xa227306080459e4bcf1324b229b344af4469846f9861aa8aff450f35046df9d6 -TDX_SIGNER=0x6A1f38f20044e8e69EAC755144F14f973e7b8d6E -TDX_REGISTRATION_MANAGER=0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7 +TDX_IMAGE_HASH=0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66 +TDX_REGISTRATION_MANAGER=0x44E999A5859c2D12378a349882fAe5805DCE71b9 ``` -`deploy-config/sepolia.json` uses the same `RISC0_VERIFIER_ROUTER` and `TDX_IMAGE_HASH`. The `TEEProverRegistry` +`deploy-config/sepolia.json` uses the same TDX RISC Zero verifier router and `TDX_IMAGE_HASH`. The `TEEProverRegistry` constructor also requires a non-zero `tdxVerifier`; for config-driven deployments, set `tdxVerifier` in the deploy config to the deployed `TDXVerifier` address. +Deploy both TEE verifier policy contracts with one command: + +```bash +just --justfile scripts/multiproof/justfile deploy-tee-verifiers $NITRO_ROOT_CERT $NITRO_VERIFIER_ID +``` + +This saves output to `deployments/-tee-verifiers.json`. + +To deploy a fresh Nitro verifier, a fresh TDX verifier, and then the TDX +multiproof stack against those exact verifier addresses without editing +`deploy-config/sepolia.json`, use: + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-system $NITRO_ROOT_CERT $NITRO_VERIFIER_ID +``` + +To deploy Nitro separately: + +```bash +just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID +``` + +This saves output to `deployments/-nitro-verifier.json`. + +To deploy TDX separately: + ```bash just --justfile scripts/multiproof/justfile deploy-tdx-verifier ``` -To override any verifier input manually, pass all three verifier args: +To override any TDX verifier input manually, pass all three verifier args: ```bash forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ --sig "run(address,address,bytes32,bytes32)" \ $OWNER \ - $RISC0_VERIFIER_ROUTER \ + $TDX_RISC0_VERIFIER_ROUTER \ $TDX_VERIFIER_ID \ $INTEL_ROOT_CA_HASH \ --rpc-url $L1_RPC_URL \ @@ -213,6 +240,23 @@ forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ The script saves output to `deployments/-tdx-verifier.json`. +To override Nitro inputs manually: + +```bash +forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + --sig "run(address,address,bytes32,bytes32,bytes32)" \ + $OWNER \ + $NITRO_RISC0_VERIFIER_ROUTER \ + $RISC0_SET_BUILDER_IMAGE_ID \ + $NITRO_ROOT_CERT \ + $NITRO_VERIFIER_ID \ + --rpc-url $L1_RPC_URL \ + --broadcast \ + --private-key $PRIVATE_KEY +``` + +Use `DeployTEEVerifiers.s.sol` with `run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)` to override all Nitro and TDX inputs while still deploying both in one broadcast. + ### Step 2: Deploy the TDX multiproof test stack Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates both `TDXVerifier.proofSubmitter` and `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. @@ -225,6 +269,13 @@ The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking ` just --justfile scripts/multiproof/justfile deploy-tdx-stack $TDX_VERIFIER ``` +If the Nitro verifier in `deploy-config/sepolia.json` is owned by another account, do not edit the shared Sepolia deploy +config. Pass both verifier addresses explicitly instead: + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-stack-with-verifiers $NITRO_VERIFIER $TDX_VERIFIER +``` + To override the manager manually, use: ```bash diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 934437b25..238d8eaf0 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -3,16 +3,21 @@ set dotenv-load repo_root := justfile_directory() + "/../.." justfile_path := justfile_directory() + "/justfile" default_deploy_config := "deploy-config/sepolia.json" +zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" sepolia_tdx_stack_deployments := "deployments/11155111-dev-with-tdx.json" +sepolia_tee_verifiers_deployments := "deployments/11155111-tee-verifiers.json" forge_account := "testnet-admin" l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:8545") l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:7545") -sepolia_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" +sepolia_nitro_risc0_verifier_router := "0xB121B667dd2cf27F95f9F5107137696F56f188f6" +sepolia_tdx_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" +sepolia_risc0_set_builder_image_id := "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9" sepolia_tdx_verifier_id := "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009" sepolia_tdx_registration_manager := "0x44E999A5859c2D12378a349882fAe5805DCE71b9" +zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000000" # List available multiproof deployment recipes. default: @@ -27,7 +32,7 @@ default: # risc0_router RISC Zero verifier router # tdx_verifier_id RISC Zero image ID for the TDX DCAP verifier guest # intel_root_ca_hash Trusted Intel root CA hash consumed by the guest -deploy-tdx-verifier risc0_router=sepolia_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: +deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" @@ -44,18 +49,78 @@ deploy-tdx-verifier risc0_router=sepolia_risc0_verifier_router tdx_verifier_id=s --account "{{forge_account}}" \ --sender "$owner" +# Deploy the NitroEnclaveVerifier policy contract and its local RISC Zero set verifier route. +# +# Required args: +# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate +# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest +# +# Optional args: +# risc0_router RISC Zero verifier router +# set_builder_image_id RISC Zero set builder image ID +# nitro_verifier_proof_id Verifier proof ID for batched Nitro proofs; defaults to zero +deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nitro_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as owner/deployer: $owner" + forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ + "$owner" \ + "{{risc0_router}}" \ + "{{set_builder_image_id}}" \ + "{{nitro_root_cert}}" \ + "{{nitro_verifier_id}}" \ + "{{nitro_verifier_proof_id}}" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Deploy both TEE verifier policy contracts in one broadcast. +# +# Required args: +# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate +# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest +# +# Optional args default to the Sepolia TDX testing values in this justfile. +deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepolia_nitro_risc0_verifier_router tdx_risc0_router=sepolia_tdx_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32 tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as owner/deployer: $owner" + forge script scripts/multiproof/DeployTEEVerifiers.s.sol:DeployTEEVerifiers \ + --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" \ + "$owner" \ + "{{nitro_risc0_router}}" \ + "{{tdx_risc0_router}}" \ + "{{set_builder_image_id}}" \ + "{{nitro_root_cert}}" \ + "{{nitro_verifier_id}}" \ + "{{nitro_verifier_proof_id}}" \ + "{{tdx_verifier_id}}" \ + "{{intel_root_ca_hash}}" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + # Print the Sepolia TDX constants used by these recipes. tdx-sepolia-config: @echo "L1_RPC_URL={{l1_rpc_url}}" @jq -r '"TEE_PROVER_REGISTRY=" + (.TEEProverRegistry // "")' "{{repo_root}}/{{sepolia_tdx_stack_deployments}}" - @echo "RISC0_VERIFIER_ROUTER={{sepolia_risc0_verifier_router}}" + @echo "NITRO_RISC0_VERIFIER_ROUTER={{sepolia_nitro_risc0_verifier_router}}" + @echo "TDX_RISC0_VERIFIER_ROUTER={{sepolia_tdx_risc0_verifier_router}}" + @echo "RISC0_SET_BUILDER_IMAGE_ID={{sepolia_risc0_set_builder_image_id}}" @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" @echo "TDX_REGISTRATION_MANAGER={{sepolia_tdx_registration_manager}}" @echo "L2_RPC_URL={{l2_rpc_url}}" @echo "L2_OUTPUT_ROOT_RPC_URL={{l2_output_root_rpc_url}}" -# Deploy the TDX multiproof dev/test stack using deploy-config/sepolia.json. +# Deploy the TDX multiproof dev/test stack using the zeronet TDX config. # # Required env: # L1_RPC_URL @@ -68,7 +133,7 @@ tdx-sepolia-config: # Args: # tdx_verifier TDXVerifier address from deploy-tdx-verifier # tdx_manager Manager that can call registerTDXSigner() -deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=default_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: +deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" @@ -114,6 +179,92 @@ deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deplo --account "{{forge_account}}" \ --sender "$owner" +# Deploy the TDX multiproof dev/test stack with both verifier addresses supplied explicitly. +# +# Use this when you want to bind the deployed stack to explicit verifier addresses. +# +# Args: +# nitro_verifier NitroEnclaveVerifier address from deploy-nitro-verifier or deploy-tee-verifiers +# tdx_verifier TDXVerifier address from deploy-tdx-verifier or deploy-tee-verifiers +# tdx_manager Manager that can call registerTDXSigner() +deploy-tdx-stack-with-verifiers nitro_verifier tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as deployer: $owner" + + if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 + exit 1 + fi + asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" + asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" + else + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + fi + + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi + + echo "Using NitroEnclaveVerifier: {{nitro_verifier}}" + echo "Using TDXVerifier: {{tdx_verifier}}" + echo "Using ASR anchor output root: $asr_anchor_output_root" + echo "Using ASR anchor L2 block: $asr_anchor_block" + + DEPLOY_CONFIG_PATH="{{deploy_config}}" \ + forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ + --sig "run(address,address,address,bytes32,uint256)" \ + "{{nitro_verifier}}" \ + "{{tdx_verifier}}" \ + "{{tdx_manager}}" \ + "$asr_anchor_output_root" \ + "$asr_anchor_block" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Deploy fresh Nitro and TDX verifier policy contracts, then deploy the TDX stack +# against those verifier addresses without editing shared Sepolia deploy config. +# +# Required args: +# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate +# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest +deploy-tdx-system nitro_root_cert nitro_verifier_id tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + + just --justfile "{{justfile_path}}" deploy-tee-verifiers "{{nitro_root_cert}}" "{{nitro_verifier_id}}" + + nitro_verifier="$(jq -r '.NitroEnclaveVerifier // empty' "{{sepolia_tee_verifiers_deployments}}")" + tdx_verifier="$(jq -r '.TDXVerifier // empty' "{{sepolia_tee_verifiers_deployments}}")" + + if [[ -z "$nitro_verifier" || -z "$tdx_verifier" ]]; then + echo "Missing verifier address in {{sepolia_tee_verifiers_deployments}}" >&2 + exit 1 + fi + + just --justfile "{{justfile_path}}" deploy-tdx-stack-with-verifiers \ + "$nitro_verifier" \ + "$tdx_verifier" \ + "{{tdx_manager}}" \ + "{{deploy_config}}" \ + "{{l2_rpc}}" \ + "{{l2_output_root_rpc}}" + # Args: # registry TEEProverRegistry address. Defaults to deployments/11155111-dev-with-tdx.json. # rpc L1 Sepolia RPC URL. @@ -171,7 +322,11 @@ tdx-sepolia-help: @echo 'export L1_RPC_URL=' @echo 'export L2_RPC_URL=' @echo 'export L2_OUTPUT_ROOT_RPC_URL=' + @echo 'just deploy-nitro-verifier ' @echo 'just deploy-tdx-verifier' + @echo 'just deploy-tee-verifiers ' + @echo 'just deploy-tdx-system ' @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' + @echo 'just deploy-tdx-stack-with-verifiers [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' @echo 'just tee-registered-signers' @echo 'just tdx-sepolia-config' From 5f4d06b064436760f728808ab1b3cf533b4cb7dd Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Thu, 25 Jun 2026 08:26:40 -0400 Subject: [PATCH 021/146] fix build --- interfaces/L1/proofs/IAggregateVerifier.sol | 3 +- interfaces/L1/proofs/tee/ITDXVerifier.sol | 3 + scripts/deploy/DeployConfig.s.sol | 8 +- scripts/deploy/SystemDeploy.s.sol | 31 ++++- scripts/multiproof/DeployDevBase.s.sol | 2 +- scripts/multiproof/DeployDevNoNitro.s.sol | 140 +++----------------- scripts/multiproof/DeployDevWithNitro.s.sol | 107 +-------------- scripts/multiproof/DeployDevWithTDX.s.sol | 2 +- src/L1/proofs/tee/TDXVerifier.sol | 2 +- test/L1/OptimismPortal2.t.sol | 2 +- test/L1/proofs/AggregateVerifier.t.sol | 78 ++++++----- test/L1/proofs/BaseTest.t.sol | 1 + test/L1/proofs/Challenge.t.sol | 10 +- test/L1/proofs/DisputeGameFactory.t.sol | 2 +- test/L1/proofs/Nullify.t.sol | 2 + test/L1/proofs/TEEProverRegistry.t.sol | 46 ++----- test/L1/proofs/TEEProverRegistryTDX.t.sol | 5 +- test/L1/proofs/TEEVerifier.t.sol | 15 +-- test/deploy/SystemDeploy.t.sol | 22 ++- test/deploy/SystemDeployAssertions.sol | 6 +- 20 files changed, 158 insertions(+), 329 deletions(-) diff --git a/interfaces/L1/proofs/IAggregateVerifier.sol b/interfaces/L1/proofs/IAggregateVerifier.sol index dd588e4ec..532b4a394 100644 --- a/interfaces/L1/proofs/IAggregateVerifier.sol +++ b/interfaces/L1/proofs/IAggregateVerifier.sol @@ -18,7 +18,8 @@ interface IAggregateVerifier is IDisputeGame { function DISPUTE_GAME_FACTORY() external view returns (IDisputeGameFactory); function DELAYED_WETH() external view returns (IDelayedWETH); function TEE_VERIFIER() external view returns (IVerifier); - function TEE_IMAGE_HASH() external view returns (bytes32); + function TEE_NITRO_IMAGE_HASH() external view returns (bytes32); + function TEE_TDX_IMAGE_HASH() external view returns (bytes32); function ZK_VERIFIER() external view returns (IVerifier); function ZK_RANGE_HASH() external view returns (bytes32); function ZK_AGGREGATE_HASH() external view returns (bytes32); diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index de7ae0f05..aa64d98be 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -68,6 +68,9 @@ struct TDXVerifierJournal { /// @title ITDXVerifier /// @notice Interface for Intel TDX quote verification used by TDX-aware TEE prover registries. interface ITDXVerifier { + /// @notice Address authorized to submit verified proofs. + function proofSubmitter() external view returns (address); + /// @notice Verifies a ZK proof of Intel TDX DCAP quote verification and returns attested signer metadata. /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. /// @param zkCoprocessor ZK proving system used to generate the proof. diff --git a/scripts/deploy/DeployConfig.s.sol b/scripts/deploy/DeployConfig.s.sol index 7fe484580..031a3ce97 100644 --- a/scripts/deploy/DeployConfig.s.sol +++ b/scripts/deploy/DeployConfig.s.sol @@ -22,6 +22,7 @@ contract DeployConfig is Script { address public sp1Verifier; address public superchainConfigGuardian; address public superchainConfigIncidentResponder; + address public tdxVerifier; address public teeChallenger; address public teeProposer; @@ -31,6 +32,8 @@ contract DeployConfig is Script { bytes32 public multiproofConfigHash; bytes32 public multiproofGenesisOutputRoot; bytes32 public teeImageHash; + bytes32 public teeNitroImageHash; + bytes32 public teeTdxImageHash; bytes32 public zkAggregationHash; bytes32 public zkRangeHash; @@ -75,6 +78,7 @@ contract DeployConfig is Script { sp1Verifier = _json.readAddress("$.sp1Verifier"); superchainConfigGuardian = _json.readAddress("$.superchainConfigGuardian"); superchainConfigIncidentResponder = _json.readAddress("$.superchainConfigIncidentResponder"); + tdxVerifier = _json.readAddressOr("$.tdxVerifier", address(0)); teeChallenger = _json.readAddress("$.teeChallenger"); teeProposer = _json.readAddress("$.teeProposer"); @@ -82,7 +86,9 @@ contract DeployConfig is Script { multiproofConfigHash = _json.readBytes32("$.multiproofConfigHash"); multiproofGenesisOutputRoot = _json.readBytes32("$.multiproofGenesisOutputRoot"); - teeImageHash = _json.readBytes32("$.teeImageHash"); + teeImageHash = _json.readBytes32Or("$.teeImageHash", bytes32(0)); + teeNitroImageHash = _json.readBytes32Or("$.teeNitroImageHash", teeImageHash); + teeTdxImageHash = _json.readBytes32Or("$.teeTdxImageHash", teeImageHash); zkAggregationHash = _json.readBytes32("$.zkAggregationHash"); zkRangeHash = _json.readBytes32("$.zkRangeHash"); diff --git a/scripts/deploy/SystemDeploy.s.sol b/scripts/deploy/SystemDeploy.s.sol index 227d123d6..1901c37f0 100644 --- a/scripts/deploy/SystemDeploy.s.sol +++ b/scripts/deploy/SystemDeploy.s.sol @@ -37,6 +37,7 @@ import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; import { ZKVerifier } from "src/L1/proofs/zk/ZKVerifier.sol"; import { Constants } from "src/libraries/Constants.sol"; @@ -72,12 +73,14 @@ contract SystemDeploy is Script { uint256 withdrawalDelaySeconds; uint256 proofMaturityDelaySeconds; uint256 disputeGameFinalityDelaySeconds; - bytes32 teeImageHash; + bytes32 teeNitroImageHash; + bytes32 teeTdxImageHash; bytes32 zkRangeHash; bytes32 zkAggregationHash; bytes32 multiproofConfigHash; uint256 multiproofGameType; address nitroEnclaveVerifier; + address tdxVerifier; uint256 multiproofBlockInterval; uint256 multiproofIntermediateBlockInterval; ISP1Verifier sp1Verifier; @@ -120,7 +123,8 @@ contract SystemDeploy is Script { IDelayedWETH delayedWETH; IVerifier teeVerifier; IVerifier zkVerifier; - bytes32 teeImageHash; + bytes32 teeNitroImageHash; + bytes32 teeTdxImageHash; bytes32 zkRangeHash; bytes32 zkAggregationHash; bytes32 multiproofConfigHash; @@ -257,12 +261,14 @@ contract SystemDeploy is Script { withdrawalDelaySeconds: cfg.delayedWETHWithdrawalDelay(), proofMaturityDelaySeconds: cfg.proofMaturityDelaySeconds(), disputeGameFinalityDelaySeconds: cfg.disputeGameFinalityDelaySeconds(), - teeImageHash: cfg.teeImageHash(), + teeNitroImageHash: cfg.teeNitroImageHash(), + teeTdxImageHash: cfg.teeTdxImageHash(), zkRangeHash: cfg.zkRangeHash(), zkAggregationHash: cfg.zkAggregationHash(), multiproofConfigHash: cfg.multiproofConfigHash(), multiproofGameType: cfg.multiproofGameType(), nitroEnclaveVerifier: cfg.nitroEnclaveVerifier(), + tdxVerifier: cfg.tdxVerifier(), multiproofBlockInterval: cfg.multiproofBlockInterval(), multiproofIntermediateBlockInterval: cfg.multiproofIntermediateBlockInterval(), sp1Verifier: ISP1Verifier(cfg.sp1Verifier()), @@ -961,7 +967,9 @@ contract SystemDeploy is Script { vm.broadcast(msg.sender); output_.teeProverRegistryImpl = new TEEProverRegistry( - INitroEnclaveVerifier(_input.nitroEnclaveVerifier), _output.disputeGameFactoryProxy + INitroEnclaveVerifier(_input.nitroEnclaveVerifier), + ITDXVerifier(_input.tdxVerifier), + _output.disputeGameFactoryProxy ); output_.teeProverRegistryProxy = @@ -989,6 +997,11 @@ contract SystemDeploy is Script { vm.broadcast(msg.sender); nitroVerifier.setProofSubmitter(address(output_.teeProverRegistryProxy)); } + ITDXVerifier tdxVerifier = ITDXVerifier(_input.tdxVerifier); + if (tdxVerifier.proofSubmitter() != address(output_.teeProverRegistryProxy)) { + vm.broadcast(msg.sender); + tdxVerifier.setProofSubmitter(address(output_.teeProverRegistryProxy)); + } vm.broadcast(msg.sender); output_.teeVerifier = @@ -1003,7 +1016,8 @@ contract SystemDeploy is Script { delayedWETH: _output.delayedWETHProxy, teeVerifier: output_.teeVerifier, zkVerifier: output_.zkVerifier, - teeImageHash: _input.teeImageHash, + teeNitroImageHash: _input.teeNitroImageHash, + teeTdxImageHash: _input.teeTdxImageHash, zkRangeHash: _input.zkRangeHash, zkAggregationHash: _input.zkAggregationHash, multiproofConfigHash: _input.multiproofConfigHash, @@ -1033,7 +1047,7 @@ contract SystemDeploy is Script { _input.delayedWETH, _input.teeVerifier, _input.zkVerifier, - _input.teeImageHash, + AggregateVerifier.TeeHashes(_input.teeNitroImageHash, _input.teeTdxImageHash), AggregateVerifier.ZkHashes(_input.zkRangeHash, _input.zkAggregationHash), _input.multiproofConfigHash, _input.l2ChainId, @@ -1066,14 +1080,17 @@ contract SystemDeploy is Script { } function _assertValidMultiproofInput(ImplementationInput memory _input) internal view { - require(_input.teeImageHash != bytes32(0), "SystemDeploy: teeImageHash not set"); + require(_input.teeNitroImageHash != bytes32(0), "SystemDeploy: teeNitroImageHash not set"); + require(_input.teeTdxImageHash != bytes32(0), "SystemDeploy: teeTdxImageHash not set"); require(_input.zkRangeHash != bytes32(0), "SystemDeploy: zkRangeHash not set"); require(_input.zkAggregationHash != bytes32(0), "SystemDeploy: zkAggregationHash not set"); require(_input.multiproofConfigHash != bytes32(0), "SystemDeploy: multiproofConfigHash not set"); require(_input.multiproofGameType != 0, "SystemDeploy: multiproofGameType not set"); require(_input.nitroEnclaveVerifier != address(0), "SystemDeploy: nitroEnclaveVerifier not set"); + require(_input.tdxVerifier != address(0), "SystemDeploy: tdxVerifier not set"); require(address(_input.sp1Verifier) != address(0), "SystemDeploy: sp1Verifier not set"); DeployUtils.assertValidContractAddress(_input.nitroEnclaveVerifier); + DeployUtils.assertValidContractAddress(_input.tdxVerifier); DeployUtils.assertValidContractAddress(address(_input.sp1Verifier)); require(_input.multiproofBlockInterval != 0, "SystemDeploy: multiproof block interval not set"); require( diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 31d009270..2d6da2d8c 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -111,7 +111,7 @@ abstract contract DeployDevBase is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - cfg.teeImageHash(), + AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainId(), diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 5c48e03b9..4602c4d4f 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -3,12 +3,10 @@ pragma solidity 0.8.15; import { console2 as console } from "lib/forge-std/src/console2.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; -import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; +import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; +import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { DeployDevBase } from "./DeployDevBase.s.sol"; @@ -20,21 +18,10 @@ contract DeployDevNoNitro is DeployDevBase { uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 10; uint256 public constant INIT_BOND = 0.001 ether; - DeployConfig public constant cfg = - DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); - address public tdxVerifierAddr; - address public teeProverRegistryProxy; - address public teeVerifier; - address public disputeGameFactory; - IAnchorStateRegistry public mockAnchorRegistry; - address public mockDelayedWETH; - address public aggregateVerifier; - function setUp() public { - DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); - cfg.read(Config.deployConfigPath()); - tdxVerifierAddr = cfg.tdxVerifier(); + function _blockInterval() internal pure override returns (uint256) { + return BLOCK_INTERVAL; } function _intermediateBlockInterval() internal pure override returns (uint256) { @@ -49,108 +36,38 @@ contract DeployDevNoNitro is DeployDevBase { return "-dev-no-nitro.json"; } - function _deployTEERegistryImpl() internal override returns (address) { - return - address( - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(disputeGameFactory)) - ); - } - - function _logHeader() internal view override { - console.log("=== Deploying Dev Infrastructure (NO NITRO) ==="); - console.log("Chain ID:", block.chainid); - console.log("Owner:", cfg.finalSystemOwner()); - console.log("TEE Proposer:", cfg.teeProposer()); - console.log("TEE Challenger:", cfg.teeChallenger()); - console.log("Game Type:", cfg.multiproofGameType()); - console.log("TDXVerifier:", tdxVerifierAddr); - console.log(""); - console.log("NOTE: Using DevTEEProverRegistry - NO attestation required."); + function _preflight() internal override { + tdxVerifierAddr = cfg.tdxVerifier(); require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); - - vm.startBroadcast(); - - _deployInfrastructure(gameType); - _deployTEEContracts(gameType); - _deployAggregateVerifier(gameType); - - vm.stopBroadcast(); - - _printSummary(); - _writeOutput(); } - function _deployTEEContracts(GameType gameType) internal { - address owner = cfg.finalSystemOwner(); - address teeRegistryImpl = address( + function _deployTEERegistryImpl() internal override returns (address) { + return address( new DevTEEProverRegistry( INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifierAddr), IDisputeGameFactory(disputeGameFactory) ) ); - address[] memory initialProposers = new address[](2); - initialProposers[0] = cfg.teeProposer(); - initialProposers[1] = cfg.teeChallenger(); - Proxy teeProxy = new Proxy(msg.sender); - teeProxy.upgradeToAndCall( - teeRegistryImpl, abi.encodeCall(TEEProverRegistry.initialize, (owner, owner, initialProposers, gameType)) - ); - teeProxy.changeAdmin(address(0xdead)); - teeProverRegistryProxy = address(teeProxy); - - teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); } - function _deployInfrastructure(GameType gameType) internal { - address factoryImpl = address(new DisputeGameFactory()); - MinimalProxyAdmin proxyAdmin = new MinimalProxyAdmin(cfg.finalSystemOwner()); - - Proxy proxy = new Proxy(msg.sender); - proxy.upgradeTo(factoryImpl); - proxy.changeAdmin(address(proxyAdmin)); - DisputeGameFactory(address(proxy)).initialize(cfg.finalSystemOwner()); - disputeGameFactory = address(proxy); - - MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); - mockAnchorRegistry = IAnchorStateRegistry(address(asr)); - asr.initialize( - disputeGameFactory, - Hash.wrap(cfg.multiproofGenesisOutputRoot()), - cfg.multiproofGenesisBlockNumber(), - gameType - ); + function _serializeExtra(string memory key) internal override { + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); } - function _deployAggregateVerifier(GameType gameType) internal { - address zkVerifier = address(new MockVerifier(mockAnchorRegistry)); - mockDelayedWETH = address(new MockDelayedWETH()); - - AggregateVerifier.ZkHashes memory zkHashes = - AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }); - - aggregateVerifier = address( - new AggregateVerifier( - gameType, - mockAnchorRegistry, - IDelayedWETH(payable(mockDelayedWETH)), - IVerifier(teeVerifier), - IVerifier(zkVerifier), - AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), - zkHashes, - cfg.multiproofConfigHash(), - cfg.l2ChainID(), - BLOCK_INTERVAL, - INTERMEDIATE_BLOCK_INTERVAL - ) - ); - - DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); - factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); - factory.setInitBond(gameType, INIT_BOND); + function _logHeader() internal view override { + console.log("=== Deploying Dev Infrastructure (NO NITRO) ==="); + console.log("Chain ID:", block.chainid); + console.log("Owner:", cfg.finalSystemOwner()); + console.log("TEE Proposer:", cfg.teeProposer()); + console.log("TEE Challenger:", cfg.teeChallenger()); + console.log("Game Type:", cfg.multiproofGameType()); + console.log("TDXVerifier:", tdxVerifierAddr); + console.log(""); + console.log("NOTE: Using DevTEEProverRegistry - NO attestation required."); } - function _printSummary() internal view { + function _printSummary() internal view override { console.log("\n========================================"); console.log(" DEV DEPLOYMENT COMPLETE (NO NITRO)"); console.log("========================================"); @@ -180,19 +97,4 @@ contract DeployDevNoNitro is DeployDevBase { console.log(" --private-key --rpc-url "); console.log("\n========================================\n"); } - - function _writeOutput() internal { - string memory key = "deployment"; - vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); - vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); - vm.serializeAddress(key, "TEEVerifier", teeVerifier); - vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); - vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); - vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); - string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); - - string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-dev-no-nitro.json"); - vm.writeJson(json, outPath); - console.log("Deployment saved to:", outPath); - } } diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index 52597cb22..ec40a6463 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -3,8 +3,9 @@ pragma solidity 0.8.15; import { console2 as console } from "lib/forge-std/src/console2.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; +import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { DeployDevBase } from "./DeployDevBase.s.sol"; @@ -13,11 +14,6 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @notice Development deployment WITH AWS Nitro attestation validation. Uses the real /// TEEProverRegistry, so signer registration requires a ZK proof of a valid AWS /// Nitro attestation (no addDevSigner bypass). -/// @dev Prerequisite: deploy the RISC Zero verifier stack and NitroEnclaveVerifier via -/// DeployRiscZeroStack.s.sol first (those contracts need Solidity ^0.8.20, while this -/// script is pinned to =0.8.15), then set `nitroEnclaveVerifier` in the deploy config. -/// Note: AWS Nitro attestations are only valid for 60 minutes — generate the ZK proof -/// and submit registerSigner() within that window. contract DeployDevWithNitro is DeployDevBase { uint256 public constant BLOCK_INTERVAL = 600; uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 30; @@ -25,12 +21,6 @@ contract DeployDevWithNitro is DeployDevBase { address public nitroEnclaveVerifierAddr; address public tdxVerifierAddr; - address public teeProverRegistryProxy; - address public teeVerifier; - address public disputeGameFactory; - IAnchorStateRegistry public mockAnchorRegistry; - address public mockDelayedWETH; - address public aggregateVerifier; function _blockInterval() internal pure override returns (uint256) { return BLOCK_INTERVAL; @@ -61,13 +51,16 @@ contract DeployDevWithNitro is DeployDevBase { function _deployTEERegistryImpl() internal override returns (address) { return address( new TEEProverRegistry( - INitroEnclaveVerifier(nitroEnclaveVerifierAddr), IDisputeGameFactory(disputeGameFactory) + INitroEnclaveVerifier(nitroEnclaveVerifierAddr), + ITDXVerifier(tdxVerifierAddr), + IDisputeGameFactory(disputeGameFactory) ) ); } function _serializeExtra(string memory key) internal override { vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); } function _logHeader() internal view override { @@ -83,77 +76,7 @@ contract DeployDevWithNitro is DeployDevBase { console.log("NOTE: Using REAL TEEProverRegistry - ZK attestation proof REQUIRED."); } - function _deployTEEContracts(GameType gameType) internal { - address owner = cfg.finalSystemOwner(); - address teeRegistryImpl = address( - new TEEProverRegistry( - INitroEnclaveVerifier(nitroEnclaveVerifierAddr), - ITDXVerifier(tdxVerifierAddr), - IDisputeGameFactory(disputeGameFactory) - ) - ); - address[] memory initialProposers = new address[](2); - initialProposers[0] = cfg.teeProposer(); - initialProposers[1] = cfg.teeChallenger(); - Proxy teeProxy = new Proxy(msg.sender); - teeProxy.upgradeToAndCall( - teeRegistryImpl, abi.encodeCall(TEEProverRegistry.initialize, (owner, owner, initialProposers, gameType)) - ); - teeProxy.changeAdmin(address(0xdead)); - teeProverRegistryProxy = address(teeProxy); - - teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); - } - - function _deployInfrastructure(GameType gameType) internal { - address factoryImpl = address(new DisputeGameFactory()); - MinimalProxyAdmin proxyAdmin = new MinimalProxyAdmin(cfg.finalSystemOwner()); - - Proxy proxy = new Proxy(msg.sender); - proxy.upgradeTo(factoryImpl); - proxy.changeAdmin(address(proxyAdmin)); - DisputeGameFactory(address(proxy)).initialize(cfg.finalSystemOwner()); - disputeGameFactory = address(proxy); - - MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); - mockAnchorRegistry = IAnchorStateRegistry(address(asr)); - asr.initialize( - disputeGameFactory, - Hash.wrap(cfg.multiproofGenesisOutputRoot()), - cfg.multiproofGenesisBlockNumber(), - gameType - ); - } - - function _deployAggregateVerifier(GameType gameType) internal { - address zkVerifier = address(new MockVerifier(mockAnchorRegistry)); - mockDelayedWETH = address(new MockDelayedWETH()); - - AggregateVerifier.ZkHashes memory zkHashes = - AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }); - - aggregateVerifier = address( - new AggregateVerifier( - gameType, - mockAnchorRegistry, - IDelayedWETH(payable(mockDelayedWETH)), - IVerifier(teeVerifier), - IVerifier(zkVerifier), - AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), - zkHashes, - cfg.multiproofConfigHash(), - cfg.l2ChainID(), - BLOCK_INTERVAL, - INTERMEDIATE_BLOCK_INTERVAL - ) - ); - - DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); - factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); - factory.setInitBond(gameType, INIT_BOND); - } - - function _printSummary() internal view { + function _printSummary() internal view override { console.log("\n========================================"); console.log(" DEV DEPLOYMENT COMPLETE (WITH NITRO)"); console.log("========================================"); @@ -182,20 +105,4 @@ contract DeployDevWithNitro is DeployDevBase { console.log(" --private-key --rpc-url "); console.log("\n========================================\n"); } - - function _writeOutput() internal { - string memory key = "deployment"; - vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); - vm.serializeAddress(key, "TEEVerifier", teeVerifier); - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); - vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); - vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); - vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); - vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); - string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); - - string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-dev-with-nitro.json"); - vm.writeJson(json, outPath); - console.log("Deployment saved to:", outPath); - } } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index e600c33c6..f62066f4b 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -190,7 +190,7 @@ contract DeployDevWithTDX is Script { AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), zkHashes, cfg.multiproofConfigHash(), - cfg.l2ChainID(), + cfg.l2ChainId(), cfg.multiproofBlockInterval(), cfg.multiproofIntermediateBlockInterval() ) diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index 09e9b5195..351f672b0 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Ownable } from "@solady/auth/Ownable.sol"; +import { Ownable } from "lib/solady/src/auth/Ownable.sol"; import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; diff --git a/test/L1/OptimismPortal2.t.sol b/test/L1/OptimismPortal2.t.sol index bf93e25f4..a2e375d05 100644 --- a/test/L1/OptimismPortal2.t.sol +++ b/test/L1/OptimismPortal2.t.sol @@ -85,7 +85,7 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { delayedWeth, IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - bytes32(uint256(1)), + AggregateVerifier.TeeHashes(bytes32(uint256(1)), bytes32(uint256(1))), AggregateVerifier.ZkHashes(bytes32(uint256(2)), bytes32(uint256(3))), bytes32(uint256(4)), deploy.cfg().l2ChainId(), diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 88604aec2..6fc7caca5 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -78,9 +78,7 @@ contract AggregateVerifierTest is BaseTest { ) ); - AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof - ); + _createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof); } function testInitializeWithZKProof() public { @@ -425,46 +423,32 @@ contract AggregateVerifierTest is BaseTest { return abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); } - function _expectDeployWithInvalidBlockIntervalsReverts( - uint256 blockInterval, - uint256 intermediateBlockInterval + function _generateIntermediateRootsExceptLast(uint256 l2BlockNumber) private pure returns (bytes memory) { + uint256 rootsCount = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL; + bytes32[] memory intermediateRoots = new bytes32[](rootsCount - 1); + uint256 startingL2BlockNumber = l2BlockNumber - BLOCK_INTERVAL; + for (uint256 i = 1; i < rootsCount; i++) { + intermediateRoots[i - 1] = keccak256(abi.encode(startingL2BlockNumber + INTERMEDIATE_BLOCK_INTERVAL * i)); + } + return abi.encodePacked(intermediateRoots); + } + + function _deployAggregateVerifierCloneWithoutFactoryRegistration( + address creator, + Claim rootClaim, + uint256 l2BlockNumber, + address parentAddress, + bytes memory proof ) private + returns (AggregateVerifier) { - vm.expectRevert( - abi.encodeWithSelector(AggregateVerifier.InvalidBlockInterval.selector, 0, INTERMEDIATE_BLOCK_INTERVAL) - ); - new AggregateVerifier( - AGGREGATE_VERIFIER_GAME_TYPE, - IAnchorStateRegistry(address(anchorStateRegistry)), - IDelayedWETH(payable(address(delayedWETH))), - IVerifier(address(teeVerifier)), - IVerifier(address(zkVerifier)), - AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), - AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), - CONFIG_HASH, - L2_CHAIN_ID, - 0, - INTERMEDIATE_BLOCK_INTERVAL - ); - _deployAggregateVerifierWithIntervals(blockInterval, intermediateBlockInterval); - } - - // Case 2: INTERMEDIATE_BLOCK_INTERVAL is 0 - vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.InvalidBlockInterval.selector, BLOCK_INTERVAL, 0)); - new AggregateVerifier( - AGGREGATE_VERIFIER_GAME_TYPE, - IAnchorStateRegistry(address(anchorStateRegistry)), - IDelayedWETH(payable(address(delayedWETH))), - IVerifier(address(teeVerifier)), - IVerifier(address(zkVerifier)), - AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), - AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), - CONFIG_HASH, - L2_CHAIN_ID, - BLOCK_INTERVAL, - 0 - ); + bytes memory extraData = _aggregateVerifierExtraData(rootClaim, l2BlockNumber, parentAddress); + Hash uuid = factory.getGameUUID(GameTypes.AGGREGATE_VERIFIER, rootClaim, extraData); + address clone = address(aggregateVerifierImpl) + .cloneDeterministic( + abi.encodePacked(creator, rootClaim, blockhash(block.number - 1), extraData), Hash.unwrap(uuid) + ); vm.deal(creator, INIT_BOND); vm.prank(creator); @@ -473,6 +457,20 @@ contract AggregateVerifierTest is BaseTest { return AggregateVerifier(payable(clone)); } + function _expectDeployWithInvalidBlockIntervalsReverts( + uint256 blockInterval, + uint256 intermediateBlockInterval + ) + private + { + vm.expectRevert( + abi.encodeWithSelector( + AggregateVerifier.InvalidBlockInterval.selector, blockInterval, intermediateBlockInterval + ) + ); + _deployAggregateVerifierWithIntervals(blockInterval, intermediateBlockInterval); + } + function _deployAggregateVerifierWithIntervals( uint256 blockInterval, uint256 intermediateBlockInterval diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index a951969e6..e82f6ec7c 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -163,6 +163,7 @@ contract BaseTest is Test { returns (bytes memory) { uint256 l1OriginNumber = block.number - 1; + bytes32 l1OriginHash = blockhash(l1OriginNumber); return abi.encodePacked(uint8(proofType), l1OriginHash, l1OriginNumber, _generateProofBody(salt, proofType)); } diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index d9ae28c0b..ebf66d9a5 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -40,9 +40,6 @@ contract ChallengeTest is BaseTest { TEE_PROVER, "tee1", "tee-proof-1", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) ); - Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); - vm.expectRevert(AggregateVerifier.InvalidProofType.selector); _challenge(game, AggregateVerifier.ProofType.TEE, bytes32(0)); } @@ -92,10 +89,7 @@ contract ChallengeTest is BaseTest { game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); - // challenge game — TEE proof was nullified, so MissingProof(TEE) is expected - Claim rootClaim3 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "zk"))); - bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); - + // challenge game: TEE proof was nullified, so MissingProof(TEE) is expected vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.MissingProof.selector, AggregateVerifier.ProofType.TEE) ); @@ -146,7 +140,7 @@ contract ChallengeTest is BaseTest { _assertChallengeRecorded(gameA); AggregateVerifier gameB = - _createAggregateVerifierGame(TEE_PROVER, rootClaimB, currentL2BlockNumber, address(gameA), teeProofB); + _createGame(TEE_PROVER, "tee-nullify-b", "tee-proof-b", AggregateVerifier.ProofType.TEE, address(gameA)); Claim rootNullifyB = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee-nullify-b"))); bytes memory teeNullifyB = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); diff --git a/test/L1/proofs/DisputeGameFactory.t.sol b/test/L1/proofs/DisputeGameFactory.t.sol index 6cab78c26..65172d2db 100644 --- a/test/L1/proofs/DisputeGameFactory.t.sol +++ b/test/L1/proofs/DisputeGameFactory.t.sol @@ -225,7 +225,7 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_TestInit { delayedWeth, IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - bytes32(uint256(1)), + AggregateVerifier.TeeHashes(bytes32(uint256(1)), bytes32(uint256(1))), AggregateVerifier.ZkHashes(bytes32(uint256(2)), bytes32(uint256(3))), bytes32(uint256(4)), L2_CHAIN_ID, diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index c1a706f54..51181da84 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -20,6 +20,8 @@ contract NullifyTest is BaseTest { Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + game.nullify(teeProof2, LAST_INTERMEDIATE_ROOT_INDEX, rootClaim2.raw()); + _assertNullifiedToNoProofs(game, TEE_PROVER); vm.warp(block.timestamp + NO_PROOF_CREDIT_CLAIM_DELAY); _claimCreditAfterDelay(game); diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index d78834e91..22b3a7b78 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -27,14 +27,14 @@ contract MockAggregateVerifierForRegistry { } contract MockDisputeGameFactoryForRegistry { - IDisputeGame internal immutable _impl; + mapping(uint32 => address) internal _impls; - constructor(address impl) { - _impl = IDisputeGame(impl); + function setImpl(GameType gameType, address impl) external { + _impls[GameType.unwrap(gameType)] = impl; } - function gameImpls(GameType) external view returns (IDisputeGame) { - return _impl; + function gameImpls(GameType gameType) external view returns (IDisputeGame) { + return IDisputeGame(_impls[GameType.unwrap(gameType)]); } } @@ -62,37 +62,17 @@ contract TEEProverRegistryTest is Test { manager = makeAddr("manager"); unauthorized = makeAddr("unauthorized"); - // Deploy mock factory and verifier - mockVerifier = new MockAggregateVerifierForRegistry(TEST_IMAGE_HASH); - mockFactory = new MockDisputeGameFactoryForRegistry(); - mockFactory.setImpl(TEST_GAME_TYPE, address(mockVerifier)); - - // Deploy implementation (using DevTEEProverRegistry for test flexibility) - DevTEEProverRegistry impl = new DevTEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(mockFactory)) - ); - - // Deploy proxy admin - proxyAdmin = new ProxyAdmin(address(this)); - - // Deploy proxy - TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( - address(impl), - address(proxyAdmin), - abi.encodeCall( - TEEProverRegistry.initialize, (owner, manager, new address[](0), GameType.wrap(TEST_GAME_TYPE)) - ) - ); - - teeProverRegistry = DevTEEProverRegistry(address(proxy)); + teeProverRegistry = _deployRegistry(new address[](0), TEST_GAME_TYPE); } function _deployRegistry(address[] memory proposers, GameType gameType) internal returns (DevTEEProverRegistry) { MockAggregateVerifierForRegistry verifier = new MockAggregateVerifierForRegistry(TEST_IMAGE_HASH); - MockDisputeGameFactoryForRegistry factory = new MockDisputeGameFactoryForRegistry(address(verifier)); + MockDisputeGameFactoryForRegistry factory = new MockDisputeGameFactoryForRegistry(); + factory.setImpl(gameType, address(verifier)); - DevTEEProverRegistry impl = - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(address(factory))); + DevTEEProverRegistry impl = new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(factory)) + ); address proxyAdmin = makeAddr("proxy-admin"); Proxy proxy = new Proxy(proxyAdmin); @@ -145,10 +125,6 @@ contract TEEProverRegistryTest is Test { address proposer1 = makeAddr("proposer1"); address proposer2 = makeAddr("proposer2"); address proposer3 = makeAddr("proposer3"); - DevTEEProverRegistry impl2 = new DevTEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(1)) - ); - ProxyAdmin proxyAdmin2 = new ProxyAdmin(address(this)); address[] memory proposers = new address[](3); proposers[0] = proposer1; proposers[1] = proposer2; diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index bc8ad918f..1fb3df4ae 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -43,6 +43,7 @@ contract MockDisputeGameFactoryForTDXRegistry { contract MockTDXVerifierForRegistry is ITDXVerifier { TDXVerifierJournal internal _journal; + address public proofSubmitter; function setJournal(TDXVerifierJournal memory journal) external { _journal = journal; @@ -68,7 +69,9 @@ contract MockTDXVerifierForRegistry is ITDXVerifier { return true; } - function setProofSubmitter(address) external { } + function setProofSubmitter(address newProofSubmitter) external { + proofSubmitter = newProofSubmitter; + } } contract TEEProverRegistryTDXTest is Test { diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index b96138425..cd66ff72d 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -29,14 +29,14 @@ contract MockAggregateVerifierForVerifier { } contract MockDisputeGameFactoryForVerifier { - IDisputeGame internal immutable _impl; + mapping(uint32 => address) internal _impls; - constructor(address impl) { - _impl = IDisputeGame(impl); + function setImpl(uint32 gameType, address impl) external { + _impls[gameType] = impl; } - function gameImpls(GameType) external view returns (IDisputeGame) { - return _impl; + function gameImpls(GameType gameType) external view returns (IDisputeGame) { + return IDisputeGame(_impls[GameType.unwrap(gameType)]); } } @@ -57,8 +57,6 @@ contract TEEVerifierTest is Test { address internal immutable PROPOSER = makeAddr("proposer"); function setUp() public { - owner = address(this); - nitroSignerAddress = vm.addr(NITRO_SIGNER_PRIVATE_KEY); tdxSignerAddress = vm.addr(TDX_SIGNER_PRIVATE_KEY); @@ -79,7 +77,8 @@ contract TEEVerifierTest is Test { proxy.upgradeToAndCall( address(impl), abi.encodeCall( - TEEProverRegistry.initialize, (address(this), address(this), new address[](0), TEST_GAME_TYPE) + TEEProverRegistry.initialize, + (address(this), address(this), new address[](0), GameType.wrap(TEST_GAME_TYPE)) ) ); diff --git a/test/deploy/SystemDeploy.t.sol b/test/deploy/SystemDeploy.t.sol index 6a94439d6..a8279c744 100644 --- a/test/deploy/SystemDeploy.t.sol +++ b/test/deploy/SystemDeploy.t.sol @@ -23,6 +23,14 @@ contract MockNitroEnclaveVerifier { } } +contract MockTDXVerifier { + address public proofSubmitter; + + function setProofSubmitter(address _proofSubmitter) external { + proofSubmitter = _proofSubmitter; + } +} + contract MockSP1Verifier { function verifyProof(bytes32, bytes calldata, bytes calldata) external pure { } } @@ -40,6 +48,7 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { address internal proposer = makeAddr("proposer"); address internal challenger = makeAddr("challenger"); MockNitroEnclaveVerifier internal nitroEnclaveVerifier; + MockTDXVerifier internal tdxVerifier; MockSP1Verifier internal sp1Verifier; uint256 internal l2ChainId = 901; @@ -47,6 +56,7 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { function setUp() public { systemDeploy = new SystemDeploy(); nitroEnclaveVerifier = new MockNitroEnclaveVerifier(); + tdxVerifier = new MockTDXVerifier(); sp1Verifier = new MockSP1Verifier(); } @@ -184,12 +194,14 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { withdrawalDelaySeconds: 100, proofMaturityDelaySeconds: 400, disputeGameFinalityDelaySeconds: 500, - teeImageHash: bytes32(uint256(1)), + teeNitroImageHash: bytes32(uint256(1)), + teeTdxImageHash: bytes32(uint256(11)), zkRangeHash: bytes32(uint256(2)), zkAggregationHash: bytes32(uint256(3)), multiproofConfigHash: bytes32(uint256(4)), multiproofGameType: 621, nitroEnclaveVerifier: address(nitroEnclaveVerifier), + tdxVerifier: address(tdxVerifier), multiproofBlockInterval: 100, multiproofIntermediateBlockInterval: 10, sp1Verifier: ISP1Verifier(address(sp1Verifier)), @@ -253,6 +265,11 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { teeProverRegistryProxyAddr, "nitro proof submitter" ); + assertEq( + MockTDXVerifier(_input.implementationsInput.tdxVerifier).proofSubmitter(), + teeProverRegistryProxyAddr, + "tdx proof submitter" + ); assertEq( address(teeProverRegistry.DISPUTE_GAME_FACTORY()), address(_output.opChain.disputeGameFactoryProxy), @@ -288,7 +305,8 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { ethLockbox: _output.opChain.ethLockboxProxy, proxyAdminOwner: _input.opChainInput.roles.opChainProxyAdminOwner, multiproofGameType: GameType.wrap(uint32(_input.implementationsInput.multiproofGameType)), - teeImageHash: _input.implementationsInput.teeImageHash, + teeNitroImageHash: _input.implementationsInput.teeNitroImageHash, + teeTdxImageHash: _input.implementationsInput.teeTdxImageHash, zkRangeHash: _input.implementationsInput.zkRangeHash, zkAggregationHash: _input.implementationsInput.zkAggregationHash, multiproofConfigHash: _input.implementationsInput.multiproofConfigHash, diff --git a/test/deploy/SystemDeployAssertions.sol b/test/deploy/SystemDeployAssertions.sol index fb3aa1c1e..b92d0fa5c 100644 --- a/test/deploy/SystemDeployAssertions.sol +++ b/test/deploy/SystemDeployAssertions.sol @@ -39,7 +39,8 @@ abstract contract SystemDeployAssertions is Test { IETHLockbox ethLockbox; address proxyAdminOwner; GameType multiproofGameType; - bytes32 teeImageHash; + bytes32 teeNitroImageHash; + bytes32 teeTdxImageHash; bytes32 zkRangeHash; bytes32 zkAggregationHash; bytes32 multiproofConfigHash; @@ -250,7 +251,8 @@ abstract contract SystemDeployAssertions is Test { assertEq(address(_aggregateVerifier.DELAYED_WETH()), address(_expected.delayedWETH), "AV-60"); assertEq(address(_aggregateVerifier.TEE_VERIFIER()), _expected.implementations.teeVerifierImpl, "AV-70"); assertEq(address(_aggregateVerifier.ZK_VERIFIER()), _expected.implementations.zkVerifierImpl, "AV-80"); - assertEq(_aggregateVerifier.TEE_IMAGE_HASH(), _expected.teeImageHash, "AV-90"); + assertEq(_aggregateVerifier.TEE_NITRO_IMAGE_HASH(), _expected.teeNitroImageHash, "AV-90"); + assertEq(_aggregateVerifier.TEE_TDX_IMAGE_HASH(), _expected.teeTdxImageHash, "AV-95"); assertEq(_aggregateVerifier.ZK_RANGE_HASH(), _expected.zkRangeHash, "AV-100"); assertEq(_aggregateVerifier.ZK_AGGREGATE_HASH(), _expected.zkAggregationHash, "AV-110"); assertEq(_aggregateVerifier.CONFIG_HASH(), _expected.multiproofConfigHash, "AV-120"); From d36603a2ec8c0ad1868063f7441511b5efc592c9 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 28 Jun 2026 09:20:43 -0400 Subject: [PATCH 022/146] add local zeronet deploy config --- deploy-config/hoodi-zeronet-tdx-local.json | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 deploy-config/hoodi-zeronet-tdx-local.json diff --git a/deploy-config/hoodi-zeronet-tdx-local.json b/deploy-config/hoodi-zeronet-tdx-local.json new file mode 100644 index 000000000..b726171d8 --- /dev/null +++ b/deploy-config/hoodi-zeronet-tdx-local.json @@ -0,0 +1,48 @@ +{ + "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "baseFeeVaultRecipient": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "baseFeeVaultWithdrawalNetwork": 0, + "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + "disputeGameFinalityDelaySeconds": 302400, + "delayedWETHWithdrawalDelay": 302400, + "finalSystemOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "fundDevAccounts": true, + "gasPriceOracleBaseFeeScalar": 1368, + "gasPriceOracleBlobBaseFeeScalar": 810949, + "l1ChainId": 560048, + "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "l1FeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788", + "l1FeeVaultWithdrawalNetwork": 0, + "l2ChainId": 763360, + "l2GenesisBlockGasLimit": "0x17D7840", + "l2OutputOracleStartingBlockNumber": 1, + "l2OutputOracleStartingTimestamp": 1, + "multiproofBlockInterval": 100, + "multiproofConfigHash": "0xd14ddabfc0ad1dd737d6e5917cf271fd479bd539c9b3d85a602589c679a9983a", + "multiproofGameType": 621, + "multiproofGenesisBlockNumber": 0, + "multiproofIntermediateBlockInterval": 10, + "multiproofGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", + "nitroEnclaveVerifier": "0x0000000000000000000000000000000000000000", + "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "operatorFeeVaultRecipient": "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec", + "operatorFeeVaultWithdrawalNetwork": 0, + "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "proofMaturityDelaySeconds": 604800, + "proxyAdminOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "respectedGameType": 621, + "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "sequencerFeeVaultRecipient": "0xfabb0ac9d68b0b445fb7357272ff202c5651694a", + "sequencerFeeVaultWithdrawalNetwork": 0, + "sp1Verifier": "0x0000000000000000000000000000000000000000", + "superchainConfigGuardian": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "superchainConfigIncidentResponder": "0x0000000000000000000000000000000000000000", + "tdxVerifier": "0x0000000000000000000000000000000000000001", + "teeChallenger": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", + "teeImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "teeNitroImageHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "teeTdxImageHash": "0x278ba4ed4251d187fb7215be37d8ee2ccb36906c71611dd89b655a81ed2445dc", + "teeProposer": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "zkAggregationHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "zkRangeHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} From 31ef2a88a8a2774d50ed6dd7f285d8d0c5209a74 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 28 Jun 2026 09:28:43 -0400 Subject: [PATCH 023/146] add deploy command --- scripts/multiproof/justfile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 238d8eaf0..8f823dccd 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -4,10 +4,12 @@ repo_root := justfile_directory() + "/../.." justfile_path := justfile_directory() + "/justfile" default_deploy_config := "deploy-config/sepolia.json" zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" +hoodi_zeronet_tdx_local_deploy_config := "deploy-config/hoodi-zeronet-tdx-local.json" sepolia_tdx_stack_deployments := "deployments/11155111-dev-with-tdx.json" sepolia_tee_verifiers_deployments := "deployments/11155111-tee-verifiers.json" forge_account := "testnet-admin" l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") +hoodi_l1_rpc_url := env_var_or_default("HOODI_L1_RPC_URL", "https://c3-chainproxy-eth-hoodi-full-dev.cbhq.net") l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:8545") l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:7545") @@ -120,6 +122,21 @@ tdx-sepolia-config: @echo "L2_RPC_URL={{l2_rpc_url}}" @echo "L2_OUTPUT_ROOT_RPC_URL={{l2_output_root_rpc_url}}" +# Deploy the no-attestation TDX local testing stack on Hoodi. +deploy-hoodi-zeronet-tdx-local rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + deployer="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as deployer: $deployer" + echo "Using deploy config: {{deploy_config}}" + DEPLOY_CONFIG_PATH="{{deploy_config}}" \ + forge script scripts/multiproof/DeployDevNoNitro.s.sol:DeployDevNoNitro \ + --rpc-url "{{rpc}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$deployer" + # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. # # Required env: @@ -328,5 +345,6 @@ tdx-sepolia-help: @echo 'just deploy-tdx-system ' @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' @echo 'just deploy-tdx-stack-with-verifiers [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' + @echo 'just deploy-hoodi-zeronet-tdx-local' @echo 'just tee-registered-signers' @echo 'just tdx-sepolia-config' From a5d7b34a7a90f74a49fc2e759fefde0e6c2ba1dc Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 28 Jun 2026 09:36:39 -0400 Subject: [PATCH 024/146] fix deploy scripts --- scripts/multiproof/DeployDevBase.s.sol | 5 ++++- scripts/multiproof/DeployDevWithTDX.s.sol | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 2d6da2d8c..58e9ca38c 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -65,8 +65,8 @@ abstract contract DeployDevBase is Script { Proxy proxy = new Proxy(msg.sender); proxy.upgradeTo(factoryImpl); + DisputeGameFactory(address(proxy)).initialize(msg.sender); proxy.changeAdmin(address(proxyAdmin)); - DisputeGameFactory(address(proxy)).initialize(owner); disputeGameFactory = address(proxy); MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); @@ -123,6 +123,9 @@ abstract contract DeployDevBase is Script { DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); factory.setInitBond(gameType, _initBond()); + if (factory.owner() != cfg.finalSystemOwner()) { + factory.transferOwnership(cfg.finalSystemOwner()); + } } function _writeOutput() internal { diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index f62066f4b..b0fa85792 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -164,8 +164,8 @@ contract DeployDevWithTDX is Script { Proxy proxy = new Proxy(msg.sender); proxy.upgradeTo(factoryImpl); + DisputeGameFactory(address(proxy)).initialize(msg.sender); proxy.changeAdmin(address(proxyAdmin)); - DisputeGameFactory(address(proxy)).initialize(cfg.finalSystemOwner()); disputeGameFactory = address(proxy); MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); @@ -199,6 +199,9 @@ contract DeployDevWithTDX is Script { DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); factory.setInitBond(gameType, INIT_BOND); + if (factory.owner() != cfg.finalSystemOwner()) { + factory.transferOwnership(cfg.finalSystemOwner()); + } } function _printSummary() internal view { From 911998b71aeb1ddc818496d5d45b306574ebecfb Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 28 Jun 2026 09:42:07 -0400 Subject: [PATCH 025/146] deploy to hoodi --- deployments/560048-dev-no-nitro.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 deployments/560048-dev-no-nitro.json diff --git a/deployments/560048-dev-no-nitro.json b/deployments/560048-dev-no-nitro.json new file mode 100644 index 000000000..c3e016770 --- /dev/null +++ b/deployments/560048-dev-no-nitro.json @@ -0,0 +1,9 @@ +{ + "AggregateVerifier": "0x23212a77311C1145FA6CE2e88554Cb697cE9E60E", + "AnchorStateRegistry": "0x3365567988f788F7e878377CF211CC98A3505E15", + "DelayedWETH": "0x3eD4f0892020a9C57586c33554032be00fE379E4", + "DisputeGameFactory": "0xFce77110df39c54681a0769EB515cCE862d074d9", + "TDXVerifier": "0x0000000000000000000000000000000000000001", + "TEEProverRegistry": "0x3fCAc0DECe96A5480e1e5F2d2911F3caB1415F8b", + "TEEVerifier": "0x295f73c663843302591290B7dd4520BF23E6A5f0" +} \ No newline at end of file From c7288977ae37553ec970405e057b42943096e5aa Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Sun, 28 Jun 2026 17:03:26 -0400 Subject: [PATCH 026/146] add local deploy helpers --- deploy-config/hoodi-zeronet-tdx-local.json | 4 +- deployments/560048-dev-no-nitro.json | 14 ++-- scripts/multiproof/DeployDevBase.s.sol | 19 +++-- scripts/multiproof/justfile | 82 ++++++++++++++++++- .../mocks/MockAnchorStateRegistry.sol | 10 ++- test/L1/proofs/TEEVerifier.t.sol | 7 +- 6 files changed, 119 insertions(+), 17 deletions(-) diff --git a/deploy-config/hoodi-zeronet-tdx-local.json b/deploy-config/hoodi-zeronet-tdx-local.json index b726171d8..edcb821c3 100644 --- a/deploy-config/hoodi-zeronet-tdx-local.json +++ b/deploy-config/hoodi-zeronet-tdx-local.json @@ -5,7 +5,7 @@ "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", "disputeGameFinalityDelaySeconds": 302400, "delayedWETHWithdrawalDelay": 302400, - "finalSystemOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", "fundDevAccounts": true, "gasPriceOracleBaseFeeScalar": 1368, "gasPriceOracleBlobBaseFeeScalar": 810949, @@ -42,7 +42,7 @@ "teeImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "teeNitroImageHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "teeTdxImageHash": "0x278ba4ed4251d187fb7215be37d8ee2ccb36906c71611dd89b655a81ed2445dc", - "teeProposer": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "teeProposer": "0x0ce54eFf9256b1da3b67656278EbAD2A9dfCFd4F", "zkAggregationHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "zkRangeHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } diff --git a/deployments/560048-dev-no-nitro.json b/deployments/560048-dev-no-nitro.json index c3e016770..ddcdabe6a 100644 --- a/deployments/560048-dev-no-nitro.json +++ b/deployments/560048-dev-no-nitro.json @@ -1,9 +1,11 @@ { - "AggregateVerifier": "0x23212a77311C1145FA6CE2e88554Cb697cE9E60E", - "AnchorStateRegistry": "0x3365567988f788F7e878377CF211CC98A3505E15", - "DelayedWETH": "0x3eD4f0892020a9C57586c33554032be00fE379E4", - "DisputeGameFactory": "0xFce77110df39c54681a0769EB515cCE862d074d9", + "ASRStartingBlockNumber": 4353541, + "ASRStartingOutputRoot": "0xb63f4c2646f83e684e14f6a217332ef8c372e95281269384cdb2aa93efd3109a", + "AggregateVerifier": "0x2aB6e58257e9F2689F8a17464450e1cf3870BAD1", + "AnchorStateRegistry": "0xc14F7E178BfE6209d30c431a8C69b4EcE6d15334", + "DelayedWETH": "0x4fc52f45202cfa48Cb84308B05A0D2A6adBAbBF0", + "DisputeGameFactory": "0x8e993853C303288f4fcd138E180E31a3c798E4F9", "TDXVerifier": "0x0000000000000000000000000000000000000001", - "TEEProverRegistry": "0x3fCAc0DECe96A5480e1e5F2d2911F3caB1415F8b", - "TEEVerifier": "0x295f73c663843302591290B7dd4520BF23E6A5f0" + "TEEProverRegistry": "0x8BE44CA11B9F81111242D0f854Db9171B3978Fc7", + "TEEVerifier": "0x09F9E99d379A9963Fe13814b31B90ba81bf9a74f" } \ No newline at end of file diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 58e9ca38c..9da5ac2ef 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -34,6 +34,8 @@ abstract contract DeployDevBase is Script { IAnchorStateRegistry public mockAnchorRegistry; address public mockDelayedWETH; address public aggregateVerifier; + Hash public startingAnchorRoot; + uint256 public startingAnchorBlockNumber; function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); @@ -41,6 +43,14 @@ abstract contract DeployDevBase is Script { } function run() public { + run(cfg.multiproofGenesisOutputRoot(), cfg.multiproofGenesisBlockNumber()); + } + + function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public { + require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); + startingAnchorRoot = Hash.wrap(asrStartingOutputRoot); + startingAnchorBlockNumber = asrStartingBlockNumber; + GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); _preflight(); @@ -71,12 +81,7 @@ abstract contract DeployDevBase is Script { MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); mockAnchorRegistry = IAnchorStateRegistry(address(asr)); - asr.initialize( - disputeGameFactory, - Hash.wrap(cfg.multiproofGenesisOutputRoot()), - cfg.multiproofGenesisBlockNumber(), - gameType - ); + asr.initialize(disputeGameFactory, startingAnchorRoot, startingAnchorBlockNumber, gameType); } function _deployTEEContracts(GameType gameType) internal { @@ -135,6 +140,8 @@ abstract contract DeployDevBase is Script { _serializeExtra(key); vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); + vm.serializeBytes32(key, "ASRStartingOutputRoot", startingAnchorRoot.raw()); + vm.serializeUint(key, "ASRStartingBlockNumber", startingAnchorBlockNumber); vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 8f823dccd..d8f8dcf6e 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -1,15 +1,20 @@ set dotenv-load repo_root := justfile_directory() + "/../.." +workspace_root := repo_root + "/.." justfile_path := justfile_directory() + "/justfile" default_deploy_config := "deploy-config/sepolia.json" zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" hoodi_zeronet_tdx_local_deploy_config := "deploy-config/hoodi-zeronet-tdx-local.json" +hoodi_zeronet_tdx_local_deployments := "deployments/560048-dev-no-nitro.json" sepolia_tdx_stack_deployments := "deployments/11155111-dev-with-tdx.json" sepolia_tee_verifiers_deployments := "deployments/11155111-tee-verifiers.json" forge_account := "testnet-admin" l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") hoodi_l1_rpc_url := env_var_or_default("HOODI_L1_RPC_URL", "https://c3-chainproxy-eth-hoodi-full-dev.cbhq.net") +hoodi_l2_rpc_url := env_var_or_default("HOODI_L2_RPC_URL", "https://base-zeronet-reth-proofs-donotuse.cbhq.net:8545") +hoodi_rollup_rpc_url := env_var_or_default("HOODI_ROLLUP_RPC_URL", "https://base-zeronet-reth-rpc-donotuse.cbhq.net:7545") +hoodi_l2_output_root_rpc_url := env_var_or_default("HOODI_L2_OUTPUT_ROOT_RPC_URL", hoodi_rollup_rpc_url) l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:8545") l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:7545") @@ -123,20 +128,94 @@ tdx-sepolia-config: @echo "L2_OUTPUT_ROOT_RPC_URL={{l2_output_root_rpc_url}}" # Deploy the no-attestation TDX local testing stack on Hoodi. -deploy-hoodi-zeronet-tdx-local rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config: +deploy-hoodi-zeronet-tdx-local rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config l2_rpc=hoodi_l2_rpc_url l2_output_root_rpc=hoodi_l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" deployer="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as deployer: $deployer" echo "Using deploy config: {{deploy_config}}" + + if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 + exit 1 + fi + asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" + asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" + else + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + fi + + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi + + echo "Using ASR anchor output root: $asr_anchor_output_root" + echo "Using ASR anchor L2 block: $asr_anchor_block" + DEPLOY_CONFIG_PATH="{{deploy_config}}" \ forge script scripts/multiproof/DeployDevNoNitro.s.sol:DeployDevNoNitro \ + --sig "run(bytes32,uint256)" \ + "$asr_anchor_output_root" \ + "$asr_anchor_block" \ --rpc-url "{{rpc}}" \ --broadcast \ --account "{{forge_account}}" \ --sender "$deployer" +# Register a TDX signer in the Hoodi no-attestation local testing registry. +register-dev-tdx-signer signer registry="" image_hash="" rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config deployments=hoodi_zeronet_tdx_local_deployments: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + + registry="{{registry}}" + if [[ -z "$registry" ]]; then + registry="$(jq -r '.TEEProverRegistry // empty' "{{repo_root}}/{{deployments}}")" + fi + + image_hash="{{image_hash}}" + if [[ -z "$image_hash" ]]; then + image_hash="$(jq -r '.teeTdxImageHash // empty' "{{repo_root}}/{{deploy_config}}")" + fi + + if [[ -z "$registry" || "$registry" == "null" ]]; then + echo "Missing TEEProverRegistry address. Pass one explicitly or deploy {{deployments}}." >&2 + exit 1 + fi + if [[ -z "$image_hash" || "$image_hash" == "null" ]]; then + echo "Missing teeTdxImageHash. Pass one explicitly or update {{deploy_config}}." >&2 + exit 1 + fi + + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as owner: $owner" + echo "TEEProverRegistry: $registry" + echo "TDX signer: {{signer}}" + echo "TDX image hash: $image_hash" + + cast send "$registry" \ + "addDevTDXSigner(address,bytes32)" \ + "{{signer}}" \ + "$image_hash" \ + --rpc-url "{{rpc}}" \ + --account "{{forge_account}}" \ + --from "$owner" + +# Run the local offchain services needed to submit dual TEE proposals to Hoodi. +run-hoodi-zeronet-tdx-local-offchain: + cd "{{workspace_root}}/base" && just hoodi-zeronet-tdx-local-offchain + # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. # # Required env: @@ -346,5 +425,6 @@ tdx-sepolia-help: @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' @echo 'just deploy-tdx-stack-with-verifiers [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' @echo 'just deploy-hoodi-zeronet-tdx-local' + @echo 'just register-dev-tdx-signer ' @echo 'just tee-registered-signers' @echo 'just tdx-sepolia-config' diff --git a/scripts/multiproof/mocks/MockAnchorStateRegistry.sol b/scripts/multiproof/mocks/MockAnchorStateRegistry.sol index bb4282d59..59451f3cb 100644 --- a/scripts/multiproof/mocks/MockAnchorStateRegistry.sol +++ b/scripts/multiproof/mocks/MockAnchorStateRegistry.sol @@ -15,6 +15,7 @@ import { GameType, Hash, Proposal } from "src/libraries/bridge/Types.sol"; contract MockAnchorStateRegistry { Hash public anchorRoot; uint256 public anchorL2BlockNumber; + IDisputeGame public anchorGame; address public factory; GameType public respectedGameType; @@ -33,6 +34,10 @@ contract MockAnchorStateRegistry { } function getAnchorRoot() external view returns (Hash, uint256) { + if (address(anchorGame) != address(0)) { + return (Hash.wrap(anchorGame.rootClaim().raw()), anchorGame.l2SequenceNumber()); + } + return (anchorRoot, anchorL2BlockNumber); } @@ -45,6 +50,7 @@ contract MockAnchorStateRegistry { } function setAnchorState(Hash newAnchorRoot, uint256 newAnchorL2BlockNumber) external { + anchorGame = IDisputeGame(address(0)); anchorRoot = newAnchorRoot; anchorL2BlockNumber = newAnchorL2BlockNumber; } @@ -79,5 +85,7 @@ contract MockAnchorStateRegistry { return _game.resolvedAt().raw() != 0; } - function setAnchorState(IDisputeGame) external { } + function setAnchorState(IDisputeGame game) external { + anchorGame = game; + } } diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index cd66ff72d..b8dc32abd 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -43,6 +43,7 @@ contract MockDisputeGameFactoryForVerifier { contract TEEVerifierTest is Test { TEEVerifier public verifier; DevTEEProverRegistry public teeProverRegistry; + MockAnchorStateRegistry public anchorStateRegistry; uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; @@ -91,12 +92,16 @@ contract TEEVerifierTest is Test { // Set the proposer as valid teeProverRegistry.setProposer(PROPOSER, true); - MockAnchorStateRegistry anchorStateRegistry = new MockAnchorStateRegistry(); + anchorStateRegistry = new MockAnchorStateRegistry(); verifier = new TEEVerifier( TEEProverRegistry(address(teeProverRegistry)), IAnchorStateRegistry(address(anchorStateRegistry)) ); } + function testMockAnchorGameStartsUnset() public view { + assertEq(address(anchorStateRegistry.anchorGame()), address(0)); + } + function testVerifyValidNitroSignature() public view { bytes32 journal = keccak256("test-journal"); bytes memory proofBytes = _proofBytes(journal, NITRO_SIGNER_PRIVATE_KEY); From b81321d4778a6fb8f61d5d6d36664dd232f73195 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 10:42:06 -0400 Subject: [PATCH 027/146] deploy dev contracts --- deploy-config/zeronet-tdx.json | 107 ++++++++-------------- deployments/11155111-dev-with-tdx.json | 16 ++-- scripts/multiproof/DeployDevWithTDX.s.sol | 3 +- scripts/multiproof/README.md | 20 ++-- scripts/multiproof/justfile | 33 +++++-- 5 files changed, 84 insertions(+), 95 deletions(-) diff --git a/deploy-config/zeronet-tdx.json b/deploy-config/zeronet-tdx.json index 3aad31a1e..5833504ca 100644 --- a/deploy-config/zeronet-tdx.json +++ b/deploy-config/zeronet-tdx.json @@ -1,78 +1,47 @@ { - "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", - "superchainConfigGuardian": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "l1StartingBlockTag": "0x48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b", - "l1ChainID": 11155111, - "l2ChainID": 11155420, - "l2BlockTime": 2, - "l1BlockTime": 12, - "maxSequencerDrift": 600, - "sequencerWindowSize": 3600, - "channelTimeout": 300, - "p2pSequencerAddress": "0x57CACBB0d30b01eb2462e5dC940c161aff3230D3", - "batchInboxAddress": "0xff00000000000000000000000000000011155420", - "batchSenderAddress": "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", - "l2OutputOracleSubmissionInterval": 120, - "l2OutputOracleStartingBlockNumber": 0, - "l2OutputOracleStartingTimestamp": 1690493568, - "l2OutputOracleProposer": "0x49277EE36A024120Ee218127354c4a3591dc90A9", - "l2OutputOracleChallenger": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "finalizationPeriodSeconds": 12, - "proxyAdminOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "baseFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "l1FeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "sequencerFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "operatorFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", - "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", - "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", - "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "baseFeeVaultRecipient": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "baseFeeVaultWithdrawalNetwork": 0, - "l1FeeVaultWithdrawalNetwork": 0, - "sequencerFeeVaultWithdrawalNetwork": 0, - "operatorFeeVaultWithdrawalNetwork": 0, - "enableGovernance": true, - "governanceTokenSymbol": "OP", - "governanceTokenName": "Optimism", - "governanceTokenOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "l2GenesisBlockGasLimit": "0x1c9c380", - "l2GenesisBlockBaseFeePerGas": "0x3b9aca00", - "eip1559Denominator": 50, - "eip1559Elasticity": 6, - "l2GenesisRegolithTimeOffset": "0x0", - "systemConfigStartBlock": 4071248, - "fundDevAccounts": false, - "faultGameAbsolutePrestate": "0x03b7eaa4e3cbce90381921a4b48008f4769871d64f93d113fcadca08ecee503b", - "faultGameMaxDepth": 73, - "faultGameClockExtension": 10800, - "faultGameMaxClockDuration": 302400, - "faultGameGenesisBlock": 9496192, - "faultGameGenesisOutputRoot": "0x63b1cda487c072b020a57c1203f7c2921754005cadbd54bed7f558111b8278d8", - "faultGameSplitDepth": 30, - "faultGameWithdrawalDelay": 604800, - "preimageOracleMinProposalSize": 126000, - "preimageOracleChallengePeriod": 86400, - "proofMaturityDelaySeconds": 604800, + "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", "disputeGameFinalityDelaySeconds": 302400, - "respectedGameType": 0, - "useFaultProofs": true, - "useRevenueShare": true, - "chainFeesRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "teeNitroImageHash": "0x213ad2e8f1594e1c386ef88ed9d2ec0df5a968f5228a5b17f4b0040599726515", - "teeTdxImageHash": "0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66", + "delayedWETHWithdrawalDelay": 302400, + "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", + "fundDevAccounts": true, + "gasPriceOracleBaseFeeScalar": 1368, + "gasPriceOracleBlobBaseFeeScalar": 810949, + "l1ChainId": 11155111, + "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "l1FeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788", + "l1FeeVaultWithdrawalNetwork": 0, + "l2ChainId": 84532, + "l2GenesisBlockGasLimit": "0x17D7840", + "l2OutputOracleStartingBlockNumber": 1, + "l2OutputOracleStartingTimestamp": 1, + "multiproofBlockInterval": 100, "multiproofConfigHash": "0x12e9c45f19f9817c6d4385fad29e7a70c355502cf0883e76a9a7e478a85d1360", "multiproofGameType": 621, - "teeProposer": "0xdb84125f2f4229c81c579f41bc129c71b174eb58", - "teeChallenger": "0xadc09b63a3ac57a2ce86d946617a18df9db029a1", - "zkRangeHash": "0x51708a6b4a3b800a14607e902c1aad47719905c12698a3e01ca8b3321761bc52", - "zkAggregationHash": "0x005aa369dd9445e172018a9eaa4a0f9767b2c2079ece90ca120422b3c4c65f11", + "multiproofGenesisBlockNumber": 0, + "multiproofIntermediateBlockInterval": 10, + "multiproofGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", "nitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", + "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "operatorFeeVaultRecipient": "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec", + "operatorFeeVaultWithdrawalNetwork": 0, + "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "proofMaturityDelaySeconds": 604800, + "proxyAdminOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "respectedGameType": 621, + "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "sequencerFeeVaultRecipient": "0xfabb0ac9d68b0b445fb7357272ff202c5651694a", + "sequencerFeeVaultWithdrawalNetwork": 0, + "sp1Verifier": "0x397A5f7f3dBd538f23DE225B51f532c34448dA9B", + "superchainConfigGuardian": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "superchainConfigIncidentResponder": "0x0000000000000000000000000000000000000000", "tdxVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", - "multiproofGenesisOutputRoot": "0xbc273d5876d1858ecd5aaf4ce4eaf16c73f0187ca4271b774ed5da7d2254ba79", - "multiproofGenesisBlockNumber": 37223829, - "multiproofBlockInterval": 600, - "multiproofIntermediateBlockInterval": 30, - "risc0VerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", - "risc0SetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9", - "sp1Verifier": "0x397A5f7f3dBd538f23DE225B51f532c34448dA9B" + "teeChallenger": "0xadc09b63a3ac57a2ce86d946617a18df9db029a1", + "teeNitroImageHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "teeTdxImageHash": "0x278ba4ed4251d187fb7215be37d8ee2ccb36906c71611dd89b655a81ed2445dc", + "teeProposer": "0x0ce54eFf9256b1da3b67656278EbAD2A9dfCFd4F", + "zkAggregationHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "zkRangeHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } diff --git a/deployments/11155111-dev-with-tdx.json b/deployments/11155111-dev-with-tdx.json index 17d4be0a7..679f83234 100644 --- a/deployments/11155111-dev-with-tdx.json +++ b/deployments/11155111-dev-with-tdx.json @@ -1,13 +1,13 @@ { - "ASRStartingBlockNumber": 41281684, - "ASRStartingOutputRoot": "0xc9f0f3a76f7c15306635a9990a8d4a3c1e281ca9e1519824679a73ca781f3404", - "AggregateVerifier": "0xA1Fd6E923d8Efa8deebde9D2AE98Df08B9A31C7d", - "AnchorStateRegistry": "0xe3E9ec1a72D8AD322187B4a4B757B2e906f4edF8", - "DelayedWETH": "0x2CCe62a3496aC7bD1A706cf7E1a765fd24e59666", - "DisputeGameFactory": "0x4aae21Fc11669463d37d2B875B52c7D63F794a08", + "ASRStartingBlockNumber": 43486445, + "ASRStartingOutputRoot": "0x41c199b47625de52f6dda0ca9e412a72ab331540f802aa7de6fabf7b6f33381e", + "AggregateVerifier": "0x59937F72aa2a018d1e14E7763266F87f7737dF1f", + "AnchorStateRegistry": "0x465bf36F5a3832b3bD077552e7Ef3C54b9885718", + "DelayedWETH": "0x3c094F0d46A37054aD1620Ff57080835224f46b9", + "DisputeGameFactory": "0xB22CD1c3c0532ce0D0275F05CEBdFf99DBB7A348", "NitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", "TDXRegistrationManager": "0x44E999A5859c2D12378a349882fAe5805DCE71b9", "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", - "TEEProverRegistry": "0x45111D4FDF5EC6bD2bDBF9Aed4a5f01140B892D9", - "TEEVerifier": "0x3065B3175F0265212e041Ea119D0001cf751E5a7" + "TEEProverRegistry": "0x98e565A0e98fB33f7E605221dF647C9C43Ef98C8", + "TEEVerifier": "0xdd36bB9254c65fE8D616E41DECC8D0Bc1B79efcC" } \ No newline at end of file diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index b0fa85792..6de055df1 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -35,6 +35,7 @@ import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; import { MockVerifier } from "test/mocks/MockVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; +import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; @@ -133,7 +134,7 @@ contract DeployDevWithTDX is Script { function _deployTDXContracts(GameType gameType) internal { address owner = cfg.finalSystemOwner(); address registryImpl = address( - new TEEProverRegistry( + new DevTEEProverRegistry( INitroEnclaveVerifier(nitroEnclaveVerifierAddr), ITDXVerifier(tdxVerifierAddr), IDisputeGameFactory(disputeGameFactory) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index a48f93383..b0dbd3db9 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -40,14 +40,14 @@ Ensure `finalSystemOwner` is set to the address you will deploy from (i.e. the a Other relevant fields: -| Field | Description | -| ------------------------------ | --------------------------------------------------------------------------------- | -| `teeProposer` | Address to be registered as the TEE proposer | -| `teeNitroImageHash` | PCR0 hash used when registering the Nitro dev signer | -| `teeTdxImageHash` | TDX image hash used when registering the TDX dev signer | -| `multiproofGameType` | Game type ID for the dispute game | -| `multiproofGenesisOutputRoot` | Initial anchor output root | -| `multiproofGenesisBlockNumber` | Initial anchor L2 block number | +| Field | Description | +| ------------------------------ | ------------------------------------------------------- | +| `teeProposer` | Address to be registered as the TEE proposer | +| `teeNitroImageHash` | PCR0 hash used when registering the Nitro dev signer | +| `teeTdxImageHash` | TDX image hash used when registering the TDX dev signer | +| `multiproofGameType` | Game type ID for the dispute game | +| `multiproofGenesisOutputRoot` | Initial anchor output root | +| `multiproofGenesisBlockNumber` | Initial anchor L2 block number | ### Step 2: Deploy contracts @@ -63,10 +63,10 @@ The proving system needs a recent anchor state to catch up to chain tip. Set thi ```bash # 1. Get the latest L2 block number -BLOCK=$(cast block-number --rpc-url https://base-sepolia-archive-k8s-dev.cbhq.net:8545) +BLOCK=$(cast block-number --rpc-url https://base-sepolia-reth-proofs-k8s-donotuse.cbhq.net:8545) # 2. Get the output root at that block -OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast 2h $BLOCK) --rpc-url https://base-sepolia-archive-k8s-dev.cbhq.net:7545 | jq -r '.outputRoot') +OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast 2h $BLOCK) --rpc-url https://base-sepolia-reth-internal-rpc-donotuse.cbhq.net:7545 | jq -r '.outputRoot') # 3. Set the anchor state on the deployed MockAnchorStateRegistry # Replace 0x983b... with the AnchorStateRegistry address from your deployment output diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index d8f8dcf6e..69e387c82 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -15,8 +15,8 @@ hoodi_l1_rpc_url := env_var_or_default("HOODI_L1_RPC_URL", "https://c3-chainprox hoodi_l2_rpc_url := env_var_or_default("HOODI_L2_RPC_URL", "https://base-zeronet-reth-proofs-donotuse.cbhq.net:8545") hoodi_rollup_rpc_url := env_var_or_default("HOODI_ROLLUP_RPC_URL", "https://base-zeronet-reth-rpc-donotuse.cbhq.net:7545") hoodi_l2_output_root_rpc_url := env_var_or_default("HOODI_L2_OUTPUT_ROOT_RPC_URL", hoodi_rollup_rpc_url) -l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:8545") -l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:7545") +l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-reth-proofs-k8s-donotuse.cbhq.net:8545") +l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-reth-internal-rpc-donotuse.cbhq.net:7545") sepolia_nitro_risc0_verifier_router := "0xB121B667dd2cf27F95f9F5107137696F56f188f6" sepolia_tdx_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" @@ -173,6 +173,25 @@ deploy-hoodi-zeronet-tdx-local rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_ --account "{{forge_account}}" \ --sender "$deployer" +# Deploy the TDX multiproof dev/test stack on Sepolia. +deploy-sepolia-tdx-dev tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + + tdx_verifier="$(jq -r '.tdxVerifier // empty' "{{deploy_config}}")" + if [[ -z "$tdx_verifier" || "$tdx_verifier" == "null" ]]; then + echo "Missing tdxVerifier in {{deploy_config}}" >&2 + exit 1 + fi + + just --justfile "{{justfile_path}}" deploy-tdx-stack \ + "$tdx_verifier" \ + "{{tdx_manager}}" \ + "{{deploy_config}}" \ + "{{l2_rpc}}" \ + "{{l2_output_root_rpc}}" + # Register a TDX signer in the Hoodi no-attestation local testing registry. register-dev-tdx-signer signer registry="" image_hash="" rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config deployments=hoodi_zeronet_tdx_local_deployments: #!/usr/bin/env bash @@ -212,9 +231,9 @@ register-dev-tdx-signer signer registry="" image_hash="" rpc=hoodi_l1_rpc_url de --account "{{forge_account}}" \ --from "$owner" -# Run the local offchain services needed to submit dual TEE proposals to Hoodi. -run-hoodi-zeronet-tdx-local-offchain: - cd "{{workspace_root}}/base" && just hoodi-zeronet-tdx-local-offchain +# Run the local offchain services needed to submit dual TEE proposals on Base Sepolia. +run-sepolia-tdx-dev-offchain: + cd "{{workspace_root}}/base" && just sepolia-tdx-dev-offchain # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. # @@ -422,9 +441,9 @@ tdx-sepolia-help: @echo 'just deploy-tdx-verifier' @echo 'just deploy-tee-verifiers ' @echo 'just deploy-tdx-system ' + @echo 'just deploy-sepolia-tdx-dev' + @echo 'just run-sepolia-tdx-dev-offchain' @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' @echo 'just deploy-tdx-stack-with-verifiers [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' - @echo 'just deploy-hoodi-zeronet-tdx-local' - @echo 'just register-dev-tdx-signer ' @echo 'just tee-registered-signers' @echo 'just tdx-sepolia-config' From bc79c9f026a863637c44f1a1248b37f78a45ab3f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 11:07:11 -0400 Subject: [PATCH 028/146] clean up TDXVerifier --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 9 +- scripts/multiproof/DeployTDXVerifier.s.sol | 14 +- scripts/multiproof/DeployTEEVerifiers.s.sol | 13 +- scripts/multiproof/README.md | 12 +- snapshots/abi/TDXVerifier.json | 250 ++++++-------------- snapshots/semver-lock.json | 14 +- snapshots/storageLayout/TDXVerifier.json | 15 +- src/L1/proofs/tee/TDXVerifier.sol | 82 +++---- src/L1/proofs/tee/TEEProverRegistry.sol | 10 +- test/L1/proofs/TDXVerifier.t.sol | 120 ++++------ test/L1/proofs/TEEProverRegistryTDX.t.sol | 15 +- 11 files changed, 178 insertions(+), 376 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index aa64d98be..1ef88a3fa 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { ZkCoProcessorType, ZkCoProcessorConfig } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; - /// @notice Statuses that a TDX quote/collateral verifier may emit. /// @dev Unknown is index 0 so uninitialized values fail closed. enum TDXVerificationResult { @@ -33,7 +31,7 @@ enum TDXTcbStatus { Revoked } -/// @notice Public journal emitted by the off-chain/ZK TDX DCAP verifier. +/// @notice Public journal emitted by the offchain/ZK TDX DCAP verifier. /// @param result Overall verification result after quote and collateral validation in the guest. /// @param tcbStatus Intel TDX TCB status for the platform. /// @param timestamp Quote timestamp in milliseconds since Unix epoch. @@ -73,20 +71,15 @@ interface ITDXVerifier { /// @notice Verifies a ZK proof of Intel TDX DCAP quote verification and returns attested signer metadata. /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. - /// @param zkCoprocessor ZK proving system used to generate the proof. /// @param proofBytes ZK proof bytes. /// @return journal Verified TDX attestation metadata. function verify( bytes calldata output, - ZkCoProcessorType zkCoprocessor, bytes calldata proofBytes ) external returns (TDXVerifierJournal memory journal); - /// @notice Retrieves the configuration for a specific coprocessor. - function getZkConfig(ZkCoProcessorType zkCoprocessor) external view returns (ZkCoProcessorConfig memory); - /// @notice Returns whether a TCB status is accepted by verifier policy. function allowedTcbStatuses(TDXTcbStatus status) external view returns (bool); diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index f083a527f..9a3b7d625 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -23,8 +23,6 @@ pragma solidity ^0.8.20; import { Script } from "forge-std/Script.sol"; import { console2 as console } from "forge-std/console2.sol"; -import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { TDXTcbStatus } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract DeployTDXVerifier is Script { @@ -41,14 +39,6 @@ contract DeployTDXVerifier is Script { require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); - TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); - allowedStatuses[0] = TDXTcbStatus.UpToDate; - allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; - - ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ - verifierId: tdxVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter - }); - console.log("=== Deploying TDXVerifier ==="); console.log("Owner:", owner); console.log("RISC Zero Verifier Router:", risc0VerifierRouter); @@ -60,9 +50,7 @@ contract DeployTDXVerifier is Script { vm.startBroadcast(); address tdxVerifier = address( - new TDXVerifier( - owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, ZkCoProcessorType.RiscZero, zkConfig, allowedStatuses - ) + new TDXVerifier(owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, risc0VerifierRouter, tdxVerifierId) ); vm.stopBroadcast(); diff --git a/scripts/multiproof/DeployTEEVerifiers.s.sol b/scripts/multiproof/DeployTEEVerifiers.s.sol index 46613dfd2..6cc1ff695 100644 --- a/scripts/multiproof/DeployTEEVerifiers.s.sol +++ b/scripts/multiproof/DeployTEEVerifiers.s.sol @@ -35,7 +35,6 @@ import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVer import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { TDXTcbStatus } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; @@ -239,18 +238,8 @@ contract DeployTEEVerifiers is Script { internal returns (address deployedTDXVerifier) { - TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); - allowedStatuses[0] = TDXTcbStatus.UpToDate; - allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; - - ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ - verifierId: tdxVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter - }); - deployedTDXVerifier = address( - new TDXVerifier( - owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, ZkCoProcessorType.RiscZero, zkConfig, allowedStatuses - ) + new TDXVerifier(owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, risc0VerifierRouter, tdxVerifierId) ); } diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index b0dbd3db9..acbc10856 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -137,12 +137,12 @@ The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` ## Path 3: TDX (Production-Path PoC) -The TDX path follows the same split as Nitro: expensive attestation verification happens off-chain in a ZK guest, -and Solidity verifies the proof plus the on-chain acceptance policy before registering the signer. +The TDX path follows the same split as Nitro: expensive attestation verification happens offchain in a ZK guest, +and Solidity verifies the proof plus the onchain acceptance policy before registering the signer. | Contract | Purpose | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `TDXVerifier` | Verifies a RISC Zero or SP1 proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | +| `TDXVerifier` | Verifies a RISC Zero proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | | `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, tracking which TEE type each signer came from for `TEEVerifier`. | The ZK verifier guest is expected to perform the full Intel DCAP verification path: @@ -172,7 +172,7 @@ The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 b `TEEVerifier` is still the proposal-proof verifier, but a TEE proposal proof now requires two signatures over the same journal: one from a Nitro-registered signer and one from a TDX-registered signer. The proof bytes are `proposer || signatureA || signatureB`; either signature order is accepted as long as both registered TEE types are present and both signers match their expected type-specific image hash. -> **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining off-chain piece is the actual RISC Zero/SP1 TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. +> **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining offchain piece is the actual RISC Zero TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. ### Step 1: Deploy verifier policy contracts @@ -370,7 +370,7 @@ mv roots.json deployments/roots.json ### Step 4: Run the seeding script -Create all games on-chain. Each game is chained to the previous one (game 0's parent is the `AnchorStateRegistry`, game N's parent is game N-1). The account running this needs enough ETH for bonds and gas (500 games at 0.00001 ETH bond = 0.005 ETH + gas). +Create all games onchain. Each game is chained to the previous one (game 0's parent is the `AnchorStateRegistry`, game N's parent is game N-1). The account running this needs enough ETH for bonds and gas (500 games at 0.00001 ETH bond = 0.005 ETH + gas). ```bash ROOTS_FILE=./deployments/roots.json \ @@ -389,7 +389,7 @@ Optional env vars: | `GAME_COUNT` | 500 | Number of games to create | | `ROOTS_FILE` | `roots.json` | Path to the output roots JSON | -### Step 5: Verify on-chain +### Step 5: Verify onchain ```bash # Check total game count diff --git a/snapshots/abi/TDXVerifier.json b/snapshots/abi/TDXVerifier.json index bda2cd07b..50b1676b0 100644 --- a/snapshots/abi/TDXVerifier.json +++ b/snapshots/abi/TDXVerifier.json @@ -22,36 +22,14 @@ "type": "address" }, { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoprocessor", - "type": "uint8" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "verifierId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "aggregatorId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "zkVerifier", - "type": "address" - } - ], - "internalType": "struct ZkCoProcessorConfig", - "name": "config", - "type": "tuple" + "internalType": "address", + "name": "initialRiscZeroVerifier", + "type": "address" }, { - "internalType": "enum TDXTcbStatus[]", - "name": "initialAllowedTcbStatuses", - "type": "uint8[]" + "internalType": "bytes32", + "name": "initialVerifierId", + "type": "bytes32" } ], "stateMutability": "nonpayable", @@ -96,42 +74,6 @@ "stateMutability": "payable", "type": "function" }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoprocessor", - "type": "uint8" - } - ], - "name": "getZkConfig", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "verifierId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "aggregatorId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "zkVerifier", - "type": "address" - } - ], - "internalType": "struct ZkCoProcessorConfig", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [], "name": "maxTimeDiff", @@ -204,6 +146,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [], + "name": "riscZeroVerifier", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "rootCaHash", @@ -245,13 +200,18 @@ }, { "inputs": [ + { + "internalType": "address", + "name": "newRiscZeroVerifier", + "type": "address" + }, { "internalType": "bytes32", - "name": "newRootCaHash", + "name": "newVerifierId", "type": "bytes32" } ], - "name": "setRootCaHash", + "name": "setRiscZeroConfiguration", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -259,17 +219,12 @@ { "inputs": [ { - "internalType": "enum TDXTcbStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "allowed", - "type": "bool" + "internalType": "bytes32", + "name": "newRootCaHash", + "type": "bytes32" } ], - "name": "setTcbStatusAllowed", + "name": "setRootCaHash", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -277,34 +232,17 @@ { "inputs": [ { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoprocessor", + "internalType": "enum TDXTcbStatus", + "name": "status", "type": "uint8" }, { - "components": [ - { - "internalType": "bytes32", - "name": "verifierId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "aggregatorId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "zkVerifier", - "type": "address" - } - ], - "internalType": "struct ZkCoProcessorConfig", - "name": "config", - "type": "tuple" + "internalType": "bool", + "name": "allowed", + "type": "bool" } ], - "name": "setZkConfiguration", + "name": "setTcbStatusAllowed", "outputs": [], "stateMutability": "nonpayable", "type": "function" @@ -322,6 +260,19 @@ "stateMutability": "payable", "type": "function" }, + { + "inputs": [], + "name": "verifierId", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -329,11 +280,6 @@ "name": "output", "type": "bytes" }, - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoprocessor", - "type": "uint8" - }, { "internalType": "bytes", "name": "proofBytes", @@ -436,35 +382,6 @@ "stateMutability": "pure", "type": "function" }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "", - "type": "uint8" - } - ], - "name": "zkConfig", - "outputs": [ - { - "internalType": "bytes32", - "name": "verifierId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "aggregatorId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "zkVerifier", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "anonymous": false, "inputs": [ @@ -584,12 +501,18 @@ "inputs": [ { "indexed": true, + "internalType": "address", + "name": "riscZeroVerifier", + "type": "address" + }, + { + "indexed": false, "internalType": "bytes32", - "name": "rootCaHash", + "name": "verifierId", "type": "bytes32" } ], - "name": "RootCaHashUpdated", + "name": "RiscZeroConfigurationUpdated", "type": "event" }, { @@ -597,18 +520,12 @@ "inputs": [ { "indexed": true, - "internalType": "enum TDXTcbStatus", - "name": "status", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bool", - "name": "allowed", - "type": "bool" + "internalType": "bytes32", + "name": "rootCaHash", + "type": "bytes32" } ], - "name": "TcbStatusPolicyUpdated", + "name": "RootCaHashUpdated", "type": "event" }, { @@ -616,35 +533,18 @@ "inputs": [ { "indexed": true, - "internalType": "enum ZkCoProcessorType", - "name": "zkCoprocessor", + "internalType": "enum TDXTcbStatus", + "name": "status", "type": "uint8" }, { - "components": [ - { - "internalType": "bytes32", - "name": "verifierId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "aggregatorId", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "zkVerifier", - "type": "address" - } - ], "indexed": false, - "internalType": "struct ZkCoProcessorConfig", - "name": "config", - "type": "tuple" + "internalType": "bool", + "name": "allowed", + "type": "bool" } ], - "name": "ZKConfigurationUpdated", + "name": "TcbStatusPolicyUpdated", "type": "event" }, { @@ -776,17 +676,17 @@ }, { "inputs": [], - "name": "UnknownZkCoprocessor", + "name": "ZeroMaxTimeDiff", "type": "error" }, { "inputs": [], - "name": "ZeroMaxTimeDiff", + "name": "ZeroProofSubmitter", "type": "error" }, { "inputs": [], - "name": "ZeroProofSubmitter", + "name": "ZeroRiscZeroVerifier", "type": "error" }, { @@ -795,14 +695,8 @@ "type": "error" }, { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoprocessor", - "type": "uint8" - } - ], - "name": "ZkVerifierNotConfigured", + "inputs": [], + "name": "ZeroVerifierId", "type": "error" } ] \ No newline at end of file diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index d23adf3c1..1a406e7e7 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -28,8 +28,8 @@ "sourceCodeHash": "0xf122a50487efe9bd5a620262ba20ef4adbca14eeec2af7fd32e6e16739001596" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0x6e50ad8219dfc59503395bf63d2a4d91c002bd21a1df7c902debedea9c2e13eb", - "sourceCodeHash": "0x802a3645c05ae98232b091f779f6321f60230dd22f536b00dcbacccde6af4d3a" + "initCodeHash": "0x222bb03bc906afac854b7c073ca984c63880c945a0f8d21db94cc5fe2896e952", + "sourceCodeHash": "0xceda0cd8df108723b38dc67c097d0d188da976fd0d0b15843be5182c015e0299" }, "src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": { "initCodeHash": "0x6f3afd2d0ef97a82ca3111976322b99343a270e54cd4a405028f2f29c75f7fb1", @@ -48,12 +48,12 @@ "sourceCodeHash": "0x03c164216b27f82ee13064ace6079d5e24e187d888f41bd01c8daffa60c575d6" }, "src/L1/proofs/tee/TDXVerifier.sol:TDXVerifier": { - "initCodeHash": "0xab68cfaae62a40d02a7d096bf1c81ec8ac2c106b19859ffa772a22b477a55af2", - "sourceCodeHash": "0x82a3eff9a7566e483b5f015dc97a88d90bdc4d9d7d49794556106bfc86d55ea0" + "initCodeHash": "0xf7c689996242db58682e81e76bde2d8355520d5d5012d491d0c88c77c333c017", + "sourceCodeHash": "0x59c7bfbc91a32cce592f9bea996f3e2a30de02eaa4aa8ceb9a9bf725394c9a8d" }, "src/L1/proofs/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0x4b2fb8ba42668b1b9a8ebb99518fc0552703dfda190acde45d771847b450ac65", - "sourceCodeHash": "0x3e763a25f5353756c2ab52f6ae003f8cc7c1da2965aa2fd8b6ec6a0ac0ae6720" + "initCodeHash": "0xc7efbb8f5e4c1d75c0f88b62003df0dc984e3a00689e718699e2cb13c836a87e", + "sourceCodeHash": "0xcdbe2ba1aeac4e3dc6a4e138bda7bd3dd9d072b7d419f5a8af4f3e9a4a045217" }, "src/L1/proofs/tee/TEEVerifier.sol:TEEVerifier": { "initCodeHash": "0x5f42455c6d41c0a49e05681a6fa7dbf21eb22c89705fd11b6030952b3227e559", @@ -139,4 +139,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} +} \ No newline at end of file diff --git a/snapshots/storageLayout/TDXVerifier.json b/snapshots/storageLayout/TDXVerifier.json index 2f13d341b..55f930c47 100644 --- a/snapshots/storageLayout/TDXVerifier.json +++ b/snapshots/storageLayout/TDXVerifier.json @@ -21,17 +21,24 @@ "type": "bytes32" }, { - "bytes": "32", - "label": "zkConfig", + "bytes": "20", + "label": "riscZeroVerifier", "offset": 0, "slot": "2", - "type": "mapping(enum ZkCoProcessorType => struct ZkCoProcessorConfig)" + "type": "address" }, { "bytes": "32", - "label": "allowedTcbStatuses", + "label": "verifierId", "offset": 0, "slot": "3", + "type": "bytes32" + }, + { + "bytes": "32", + "label": "allowedTcbStatuses", + "offset": 0, + "slot": "4", "type": "mapping(enum TDXTcbStatus => bool)" } ] \ No newline at end of file diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index 351f672b0..01aedda3a 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; import { Ownable } from "lib/solady/src/auth/Ownable.sol"; -import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; @@ -10,16 +9,14 @@ import { ITDXVerifier, TDXTcbStatus, TDXVerificationResult, - TDXVerifierJournal, - ZkCoProcessorType, - ZkCoProcessorConfig + TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; /// @title TDXVerifier /// @notice Production-shape Intel TDX DCAP verifier for multiproof signer registration. /// @dev The heavy TDX work is expected to happen in a ZK guest: quote signature verification, PCK chain /// validation, TCB info validation, QE identity validation, CRL checks, and extraction of TDREPORT fields. -/// This contract verifies the ZK proof and enforces on-chain policy over the verified journal. +/// This contract verifies the ZK proof and enforces onchain policy over the verified journal. contract TDXVerifier is Ownable, ITDXVerifier, ISemver { /// @notice Conversion factor from milliseconds to seconds. uint256 private constant MS_PER_SECOND = 1000; @@ -33,8 +30,11 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. bytes32 public rootCaHash; - /// @notice Configuration mapping for each supported ZK coprocessor type. - mapping(ZkCoProcessorType => ZkCoProcessorConfig) public zkConfig; + /// @notice RISC Zero verifier router. + address public riscZeroVerifier; + + /// @notice RISC Zero image ID for the TDX DCAP verifier guest. + bytes32 public verifierId; /// @inheritdoc ITDXVerifier mapping(TDXTcbStatus => bool) public allowedTcbStatuses; @@ -51,8 +51,8 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { /// @notice Emitted when a TCB status policy changes. event TcbStatusPolicyUpdated(TDXTcbStatus indexed status, bool allowed); - /// @notice Emitted when ZK configuration changes. - event ZKConfigurationUpdated(ZkCoProcessorType indexed zkCoprocessor, ZkCoProcessorConfig config); + /// @notice Emitted when RISC Zero verification configuration changes. + event RiscZeroConfigurationUpdated(address indexed riscZeroVerifier, bytes32 verifierId); /// @notice Emitted after a TDX attestation journal is accepted. event AttestationSubmitted( @@ -76,11 +76,11 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { /// @notice Thrown when the caller is not the configured proof submitter. error CallerNotProofSubmitter(); - /// @notice Thrown when the ZK coprocessor type is unknown. - error UnknownZkCoprocessor(); + /// @notice Thrown when a zero RISC Zero verifier address is provided. + error ZeroRiscZeroVerifier(); - /// @notice Thrown when the configured ZK verifier address is zero. - error ZkVerifierNotConfigured(ZkCoProcessorType zkCoprocessor); + /// @notice Thrown when a zero verifier ID is provided. + error ZeroVerifierId(); /// @notice Thrown when the TDX verifier guest did not report success. error TDXVerificationFailed(TDXVerificationResult result); @@ -111,24 +111,16 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { uint64 initialMaxTimeDiff, bytes32 initialRootCaHash, address initialProofSubmitter, - ZkCoProcessorType zkCoprocessor, - ZkCoProcessorConfig memory config, - TDXTcbStatus[] memory initialAllowedTcbStatuses + address initialRiscZeroVerifier, + bytes32 initialVerifierId ) { _initializeOwner(owner); _setMaxTimeDiff(initialMaxTimeDiff); _setRootCaHash(initialRootCaHash); _setProofSubmitter(initialProofSubmitter); - _setZkConfiguration(zkCoprocessor, config); - - for (uint256 i = 0; i < initialAllowedTcbStatuses.length; i++) { - _setTcbStatusAllowed(initialAllowedTcbStatuses[i], true); - } - } - - /// @inheritdoc ITDXVerifier - function getZkConfig(ZkCoProcessorType zkCoprocessor) external view returns (ZkCoProcessorConfig memory) { - return zkConfig[zkCoprocessor]; + _setRiscZeroConfiguration(initialRiscZeroVerifier, initialVerifierId); + _setTcbStatusAllowed(TDXTcbStatus.UpToDate, true); + _setTcbStatusAllowed(TDXTcbStatus.SwHardeningNeeded, true); } /// @notice Sets the trusted Intel root CA hash. @@ -151,15 +143,14 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { _setTcbStatusAllowed(status, allowed); } - /// @notice Configures a ZK verifier/program for a coprocessor. - function setZkConfiguration(ZkCoProcessorType zkCoprocessor, ZkCoProcessorConfig memory config) external onlyOwner { - _setZkConfiguration(zkCoprocessor, config); + /// @notice Configures the RISC Zero verifier/program. + function setRiscZeroConfiguration(address newRiscZeroVerifier, bytes32 newVerifierId) external onlyOwner { + _setRiscZeroConfiguration(newRiscZeroVerifier, newVerifierId); } /// @inheritdoc ITDXVerifier function verify( bytes calldata output, - ZkCoProcessorType zkCoprocessor, bytes calldata proofBytes ) external @@ -167,7 +158,7 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { { if (msg.sender != proofSubmitter) revert CallerNotProofSubmitter(); - _verifyZk(zkCoprocessor, output, proofBytes); + IRiscZeroVerifier(riscZeroVerifier).verify(proofBytes, verifierId, sha256(output)); journal = abi.decode(output, (TDXVerifierJournal)); _verifyJournal(journal); @@ -209,26 +200,6 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { } } - function _verifyZk( - ZkCoProcessorType zkCoprocessor, - bytes calldata output, - bytes calldata proofBytes - ) - internal - view - { - ZkCoProcessorConfig memory config = zkConfig[zkCoprocessor]; - if (config.zkVerifier == address(0)) revert ZkVerifierNotConfigured(zkCoprocessor); - - if (zkCoprocessor == ZkCoProcessorType.RiscZero) { - IRiscZeroVerifier(config.zkVerifier).verify(proofBytes, config.verifierId, sha256(output)); - } else if (zkCoprocessor == ZkCoProcessorType.Succinct) { - ISP1Verifier(config.zkVerifier).verifyProof(config.verifierId, output, proofBytes); - } else { - revert UnknownZkCoprocessor(); - } - } - function _derivePublicKeyHash(bytes memory publicKey) internal pure returns (bytes32 publicKeyHash) { if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); // Skip the 32-byte length word and the 0x04 uncompressed prefix; hash the 64-byte X||Y. @@ -260,8 +231,11 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { emit TcbStatusPolicyUpdated(status, allowed); } - function _setZkConfiguration(ZkCoProcessorType zkCoprocessor, ZkCoProcessorConfig memory config) internal { - zkConfig[zkCoprocessor] = config; - emit ZKConfigurationUpdated(zkCoprocessor, config); + function _setRiscZeroConfiguration(address newRiscZeroVerifier, bytes32 newVerifierId) internal { + if (newRiscZeroVerifier == address(0)) revert ZeroRiscZeroVerifier(); + if (newVerifierId == bytes32(0)) revert ZeroVerifierId(); + riscZeroVerifier = newRiscZeroVerifier; + verifierId = newVerifierId; + emit RiscZeroConfigurationUpdated(newRiscZeroVerifier, newVerifierId); } } diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index 61139f901..aabed1aec 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -62,7 +62,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// TEEVerifier checks this against the AggregateVerifier's type-specific TEE image hash at /// proof-submission time, so signers automatically become unusable when the /// AggregateVerifier upgrades to a new image hash. isValidSigner also uses - /// this for off-chain pre-submission checks. + /// this for offchain pre-submission checks. mapping(address => bytes32) public signerImageHash; /// @notice Mapping of whether an address is a valid proposer. @@ -70,7 +70,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Enumerable set of all currently registered signer addresses. /// @dev Kept in sync with `isRegisteredSigner`: add on register, remove on deregister. - /// Enables O(1) on-chain enumeration via `getRegisteredSigners()`. + /// Enables O(1) onchain enumeration via `getRegisteredSigners()`. EnumerableSetLib.AddressSet internal _registeredSigners; /// @notice Mapping of signer address to the attestation type used to register it. @@ -161,7 +161,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// 2. Is less than MAX_AGE old /// Registration is PCR0-agnostic: any enclave with a valid attestation can register. /// This enables pre-registration of new-PCR0 enclaves before a hardfork, eliminating - /// proof-generation delay when the on-chain Nitro image hash rotates. The TEEVerifier + /// proof-generation delay when the onchain Nitro image hash rotates. The TEEVerifier /// enforces PCR0 correctness at proof-submission time by checking signerImageHash /// against the AggregateVerifier's TEE_NITRO_IMAGE_HASH, so pre-registered enclaves cannot /// produce accepted proofs until the hardfork activates. @@ -195,7 +195,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. /// @param proofBytes ZK proof bytes. function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { - TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, ZkCoProcessorType.RiscZero, proofBytes); + TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, proofBytes); _registerSigner(journal.signer, journal.imageHash, TEEType.TDX); @@ -226,7 +226,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } /// @notice Returns all currently registered signer addresses. - /// @dev Reads directly from the on-chain enumerable set — no event scanning required. + /// @dev Reads directly from the onchain enumerable set — no event scanning required. /// The order of addresses in the returned array is not guaranteed. /// @return An array of all registered signer addresses. function getRegisteredSigners() external view returns (address[] memory) { diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index 83c74afbd..f51fa3e85 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; import { Test } from "forge-std/Test.sol"; import { TDXTcbStatus, TDXVerificationResult, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; @@ -14,12 +13,10 @@ contract TDXVerifierTest is Test { address internal owner; address internal proofSubmitter; address internal mockRiscZeroVerifier; - address internal mockSP1Verifier; bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); bytes32 internal constant WRONG_ROOT_CA_HASH = keccak256("wrong-root-ca"); bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); - bytes32 internal constant AGGREGATOR_ID = keccak256("tdx-aggregator-id"); bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); bytes32 internal constant MRTD_HASH = keccak256("mrtd"); bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); @@ -33,48 +30,35 @@ contract TDXVerifierTest is Test { owner = address(this); proofSubmitter = address(this); mockRiscZeroVerifier = makeAddr("mock-risc-zero"); - mockSP1Verifier = makeAddr("mock-sp1"); - - TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); - allowedStatuses[0] = TDXTcbStatus.UpToDate; - allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; - - verifier = new TDXVerifier( - owner, - MAX_TIME_DIFF, - ROOT_CA_HASH, - proofSubmitter, - ZkCoProcessorType.Succinct, - ZkCoProcessorConfig({ verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockSP1Verifier }), - allowedStatuses - ); + + verifier = + new TDXVerifier(owner, MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, mockRiscZeroVerifier, VERIFIER_ID); } - function testVerifySucceedsWithSP1ProofAndAllowedJournal() public { + function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public { TDXVerifierJournal memory journal = _successJournal(); bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + TDXVerifierJournal memory result = verifier.verify(output, proofBytes); assertEq(result.signer, journal.signer); assertEq(result.imageHash, IMAGE_HASH); assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.UpToDate)); } - function testVerifySucceedsWithRiscZeroProof() public { - ZkCoProcessorConfig memory config = ZkCoProcessorConfig({ - verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockRiscZeroVerifier - }); - verifier.setZkConfiguration(ZkCoProcessorType.RiscZero, config); + function testOwnerCanUpdateRiscZeroConfiguration() public { + address newVerifier = makeAddr("new-risc-zero"); + bytes32 newVerifierId = keccak256("new-tdx-verifier-id"); + verifier.setRiscZeroConfiguration(newVerifier, newVerifierId); TDXVerifierJournal memory journal = _successJournal(); bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"5678"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(newVerifier, newVerifierId, output, proofBytes); - TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); + TDXVerifierJournal memory result = verifier.verify(output, proofBytes); assertEq(result.signer, journal.signer); assertEq(result.imageHash, IMAGE_HASH); @@ -85,27 +69,12 @@ contract TDXVerifierTest is Test { vm.prank(makeAddr("not-submitter")); vm.expectRevert(TDXVerifier.CallerNotProofSubmitter.selector); - verifier.verify(output, ZkCoProcessorType.Succinct, ""); + verifier.verify(output, ""); } - function testVerifyRevertsIfZkVerifierNotConfigured() public { - bytes memory output = abi.encode(_successJournal()); - - vm.expectRevert( - abi.encodeWithSelector(TDXVerifier.ZkVerifierNotConfigured.selector, ZkCoProcessorType.RiscZero) - ); - verifier.verify(output, ZkCoProcessorType.RiscZero, ""); - } - - function testVerifyRevertsForUnknownCoprocessor() public { - ZkCoProcessorConfig memory config = - ZkCoProcessorConfig({ verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockSP1Verifier }); - verifier.setZkConfiguration(ZkCoProcessorType.Unknown, config); - - bytes memory output = abi.encode(_successJournal()); - - vm.expectRevert(TDXVerifier.UnknownZkCoprocessor.selector); - verifier.verify(output, ZkCoProcessorType.Unknown, ""); + function testSetRiscZeroConfigurationRevertsIfZeroVerifier() public { + vm.expectRevert(TDXVerifier.ZeroRiscZeroVerifier.selector); + verifier.setRiscZeroConfiguration(address(0), VERIFIER_ID); } function testVerifyRevertsWhenGuestReportsFailure() public { @@ -113,10 +82,10 @@ contract TDXVerifierTest is Test { journal.result = TDXVerificationResult.PckCertChainInvalid; bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TDXVerificationFailed.selector, journal.result)); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function testVerifyRevertsWhenRootCaHashMismatches() public { @@ -124,12 +93,12 @@ contract TDXVerifierTest is Test { journal.rootCaHash = WRONG_ROOT_CA_HASH; bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert( abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, WRONG_ROOT_CA_HASH) ); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function testVerifyRevertsWhenTcbStatusIsNotAllowed() public { @@ -137,10 +106,10 @@ contract TDXVerifierTest is Test { journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TcbStatusNotAllowed.selector, journal.tcbStatus)); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function testOwnerCanAllowAdditionalTcbStatus() public { @@ -150,9 +119,9 @@ contract TDXVerifierTest is Test { journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + TDXVerifierJournal memory result = verifier.verify(output, proofBytes); assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.ConfigurationNeeded)); } @@ -162,10 +131,10 @@ contract TDXVerifierTest is Test { journal.collateralExpiration = uint64(block.timestamp); bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert(abi.encodeWithSelector(TDXVerifier.CollateralExpired.selector, journal.collateralExpiration)); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function testVerifyRevertsWhenTimestampTooOld() public { @@ -173,14 +142,14 @@ contract TDXVerifierTest is Test { journal.timestamp = uint64(block.timestamp - MAX_TIME_DIFF) * 1000; bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert( abi.encodeWithSelector( TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp - MAX_TIME_DIFF), block.timestamp ) ); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function testVerifyRevertsWhenTimestampIsFromFuture() public { @@ -188,12 +157,12 @@ contract TDXVerifierTest is Test { journal.timestamp = uint64(block.timestamp) * 1000; bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert( abi.encodeWithSelector(TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp), block.timestamp) ); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function testVerifyRevertsWhenReportDataDoesNotBindPublicKey() public { @@ -202,12 +171,12 @@ contract TDXVerifierTest is Test { journal.reportDataPrefix = keccak256("wrong-report-data"); bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert( abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, expected, journal.reportDataPrefix) ); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function testVerifyRevertsWhenSignerDoesNotMatchPublicKey() public { @@ -216,10 +185,10 @@ contract TDXVerifierTest is Test { journal.signer = makeAddr("wrong-signer"); bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; - _mockSP1Verify(VERIFIER_ID, output, proofBytes); + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); vm.expectRevert(abi.encodeWithSelector(TDXVerifier.SignerMismatch.selector, expected, journal.signer)); - verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + verifier.verify(output, proofBytes); } function _successJournal() internal view returns (TDXVerifierJournal memory journal) { @@ -256,20 +225,21 @@ contract TDXVerifierTest is Test { } function _mockRiscZeroVerify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { - vm.mockCall( - mockRiscZeroVerifier, - abi.encodeWithSelector( - bytes4(keccak256("verify(bytes,bytes32,bytes32)")), proofBytes, programId, sha256(output) - ), - "" - ); + _mockRiscZeroVerify(mockRiscZeroVerifier, programId, output, proofBytes); } - function _mockSP1Verify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { + function _mockRiscZeroVerify( + address verifierAddress, + bytes32 programId, + bytes memory output, + bytes memory proofBytes + ) + internal + { vm.mockCall( - mockSP1Verifier, + verifierAddress, abi.encodeWithSelector( - bytes4(keccak256("verifyProof(bytes32,bytes,bytes)")), programId, output, proofBytes + bytes4(keccak256("verify(bytes,bytes32,bytes32)")), proofBytes, programId, sha256(output) ), "" ); diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 1fb3df4ae..0cdf7ad8c 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -12,7 +12,6 @@ import { TDXVerificationResult, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { GameType } from "src/libraries/bridge/Types.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; @@ -49,22 +48,10 @@ contract MockTDXVerifierForRegistry is ITDXVerifier { _journal = journal; } - function verify( - bytes calldata, - ZkCoProcessorType, - bytes calldata - ) - external - view - returns (TDXVerifierJournal memory) - { + function verify(bytes calldata, bytes calldata) external view returns (TDXVerifierJournal memory) { return _journal; } - function getZkConfig(ZkCoProcessorType) external pure returns (ZkCoProcessorConfig memory) { - return ZkCoProcessorConfig({ verifierId: bytes32(0), aggregatorId: bytes32(0), zkVerifier: address(0) }); - } - function allowedTcbStatuses(TDXTcbStatus) external pure returns (bool) { return true; } From ea2cf9d354bccd5c7936dbd83fea60263b011c3b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 11:35:23 -0400 Subject: [PATCH 029/146] clean up TDXVerifier --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 1 + snapshots/abi/TDXVerifier.json | 175 +--------------------- snapshots/semver-lock.json | 8 +- snapshots/storageLayout/TDXVerifier.json | 37 +---- src/L1/proofs/tee/TDXVerifier.sol | 118 +++------------ test/L1/proofs/TDXVerifier.t.sol | 40 +---- 6 files changed, 39 insertions(+), 340 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index 1ef88a3fa..b29b377ab 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -78,6 +78,7 @@ interface ITDXVerifier { bytes calldata proofBytes ) external + view returns (TDXVerifierJournal memory journal); /// @notice Returns whether a TCB status is accepted by verifier policy. diff --git a/snapshots/abi/TDXVerifier.json b/snapshots/abi/TDXVerifier.json index 50b1676b0..ed4b77a00 100644 --- a/snapshots/abi/TDXVerifier.json +++ b/snapshots/abi/TDXVerifier.json @@ -39,7 +39,7 @@ "inputs": [ { "internalType": "enum TDXTcbStatus", - "name": "", + "name": "status", "type": "uint8" } ], @@ -51,7 +51,7 @@ "type": "bool" } ], - "stateMutability": "view", + "stateMutability": "pure", "type": "function" }, { @@ -172,19 +172,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint64", - "name": "newMaxTimeDiff", - "type": "uint64" - } - ], - "name": "setMaxTimeDiff", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -198,55 +185,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address", - "name": "newRiscZeroVerifier", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "newVerifierId", - "type": "bytes32" - } - ], - "name": "setRiscZeroConfiguration", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "newRootCaHash", - "type": "bytes32" - } - ], - "name": "setRootCaHash", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "enum TDXTcbStatus", - "name": "status", - "type": "uint8" - }, - { - "internalType": "bool", - "name": "allowed", - "type": "bool" - } - ], - "name": "setTcbStatusAllowed", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -366,7 +304,7 @@ "type": "tuple" } ], - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -382,62 +320,6 @@ "stateMutability": "pure", "type": "function" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "signer", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "imageHash", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "enum TDXTcbStatus", - "name": "tcbStatus", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "pckCertHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "tcbInfoHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "qeIdentityHash", - "type": "bytes32" - } - ], - "name": "AttestationSubmitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "maxTimeDiff", - "type": "uint64" - } - ], - "name": "MaxTimeDiffUpdated", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -496,57 +378,6 @@ "name": "ProofSubmitterChanged", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "riscZeroVerifier", - "type": "address" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "verifierId", - "type": "bytes32" - } - ], - "name": "RiscZeroConfigurationUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "rootCaHash", - "type": "bytes32" - } - ], - "name": "RootCaHashUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "enum TDXTcbStatus", - "name": "status", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bool", - "name": "allowed", - "type": "bool" - } - ], - "name": "TcbStatusPolicyUpdated", - "type": "event" - }, { "inputs": [], "name": "AlreadyInitialized", diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 1a406e7e7..a2d0f1823 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -28,7 +28,7 @@ "sourceCodeHash": "0xf122a50487efe9bd5a620262ba20ef4adbca14eeec2af7fd32e6e16739001596" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0x222bb03bc906afac854b7c073ca984c63880c945a0f8d21db94cc5fe2896e952", + "initCodeHash": "0xa393180e221f3083135ddfe56e054dd989e335b13bbfc04df8ee01ba0b860291", "sourceCodeHash": "0xceda0cd8df108723b38dc67c097d0d188da976fd0d0b15843be5182c015e0299" }, "src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": { @@ -48,11 +48,11 @@ "sourceCodeHash": "0x03c164216b27f82ee13064ace6079d5e24e187d888f41bd01c8daffa60c575d6" }, "src/L1/proofs/tee/TDXVerifier.sol:TDXVerifier": { - "initCodeHash": "0xf7c689996242db58682e81e76bde2d8355520d5d5012d491d0c88c77c333c017", - "sourceCodeHash": "0x59c7bfbc91a32cce592f9bea996f3e2a30de02eaa4aa8ceb9a9bf725394c9a8d" + "initCodeHash": "0x4f217be5fd1e333ef50a49014af962b3e4c6df978b7aa050555f7a764afef8fa", + "sourceCodeHash": "0xebbc9e5b57730e6c1a10aa87b10901efbbecb92452a9fbd2bb5827285c30dd7d" }, "src/L1/proofs/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0xc7efbb8f5e4c1d75c0f88b62003df0dc984e3a00689e718699e2cb13c836a87e", + "initCodeHash": "0x8f835cc538a6aca33adfc9f66e17c06822e518587d34b8cadd77de10d041b462", "sourceCodeHash": "0xcdbe2ba1aeac4e3dc6a4e138bda7bd3dd9d072b7d419f5a8af4f3e9a4a045217" }, "src/L1/proofs/tee/TEEVerifier.sol:TEEVerifier": { diff --git a/snapshots/storageLayout/TDXVerifier.json b/snapshots/storageLayout/TDXVerifier.json index 55f930c47..d97bafbd1 100644 --- a/snapshots/storageLayout/TDXVerifier.json +++ b/snapshots/storageLayout/TDXVerifier.json @@ -1,44 +1,9 @@ [ - { - "bytes": "8", - "label": "maxTimeDiff", - "offset": 0, - "slot": "0", - "type": "uint64" - }, { "bytes": "20", "label": "proofSubmitter", - "offset": 8, - "slot": "0", - "type": "address" - }, - { - "bytes": "32", - "label": "rootCaHash", - "offset": 0, - "slot": "1", - "type": "bytes32" - }, - { - "bytes": "20", - "label": "riscZeroVerifier", "offset": 0, - "slot": "2", + "slot": "0", "type": "address" - }, - { - "bytes": "32", - "label": "verifierId", - "offset": 0, - "slot": "3", - "type": "bytes32" - }, - { - "bytes": "32", - "label": "allowedTcbStatuses", - "offset": 0, - "slot": "4", - "type": "mapping(enum TDXTcbStatus => bool)" } ] \ No newline at end of file diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index 01aedda3a..4de550eda 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -18,52 +18,24 @@ import { /// validation, TCB info validation, QE identity validation, CRL checks, and extraction of TDREPORT fields. /// This contract verifies the ZK proof and enforces onchain policy over the verified journal. contract TDXVerifier is Ownable, ITDXVerifier, ISemver { - /// @notice Conversion factor from milliseconds to seconds. - uint256 private constant MS_PER_SECOND = 1000; - /// @notice Maximum accepted age of a TDX quote, in seconds. - uint64 public maxTimeDiff; + uint64 public immutable maxTimeDiff; /// @notice Address authorized to submit TDX proofs, expected to be the TDX-aware registry. address public proofSubmitter; /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. - bytes32 public rootCaHash; + bytes32 public immutable rootCaHash; /// @notice RISC Zero verifier router. - address public riscZeroVerifier; + address public immutable riscZeroVerifier; /// @notice RISC Zero image ID for the TDX DCAP verifier guest. - bytes32 public verifierId; - - /// @inheritdoc ITDXVerifier - mapping(TDXTcbStatus => bool) public allowedTcbStatuses; - - /// @notice Emitted when the trusted Intel root CA hash changes. - event RootCaHashUpdated(bytes32 indexed rootCaHash); + bytes32 public immutable verifierId; /// @notice Emitted when the proof submitter changes. event ProofSubmitterChanged(address indexed proofSubmitter); - /// @notice Emitted when the quote timestamp tolerance changes. - event MaxTimeDiffUpdated(uint64 maxTimeDiff); - - /// @notice Emitted when a TCB status policy changes. - event TcbStatusPolicyUpdated(TDXTcbStatus indexed status, bool allowed); - - /// @notice Emitted when RISC Zero verification configuration changes. - event RiscZeroConfigurationUpdated(address indexed riscZeroVerifier, bytes32 verifierId); - - /// @notice Emitted after a TDX attestation journal is accepted. - event AttestationSubmitted( - address indexed signer, - bytes32 indexed imageHash, - TDXTcbStatus indexed tcbStatus, - bytes32 pckCertHash, - bytes32 tcbInfoHash, - bytes32 qeIdentityHash - ); - /// @notice Thrown when a zero maxTimeDiff is provided. error ZeroMaxTimeDiff(); @@ -115,17 +87,15 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { bytes32 initialVerifierId ) { _initializeOwner(owner); - _setMaxTimeDiff(initialMaxTimeDiff); - _setRootCaHash(initialRootCaHash); + if (initialMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); + if (initialRootCaHash == bytes32(0)) revert ZeroRootCaHash(); + if (initialRiscZeroVerifier == address(0)) revert ZeroRiscZeroVerifier(); + if (initialVerifierId == bytes32(0)) revert ZeroVerifierId(); + maxTimeDiff = initialMaxTimeDiff; + rootCaHash = initialRootCaHash; + riscZeroVerifier = initialRiscZeroVerifier; + verifierId = initialVerifierId; _setProofSubmitter(initialProofSubmitter); - _setRiscZeroConfiguration(initialRiscZeroVerifier, initialVerifierId); - _setTcbStatusAllowed(TDXTcbStatus.UpToDate, true); - _setTcbStatusAllowed(TDXTcbStatus.SwHardeningNeeded, true); - } - - /// @notice Sets the trusted Intel root CA hash. - function setRootCaHash(bytes32 newRootCaHash) external onlyOwner { - _setRootCaHash(newRootCaHash); } /// @notice Sets the proof submitter, expected to be the TDX-aware registry. @@ -133,19 +103,9 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { _setProofSubmitter(newProofSubmitter); } - /// @notice Sets maximum allowed quote age, in seconds. - function setMaxTimeDiff(uint64 newMaxTimeDiff) external onlyOwner { - _setMaxTimeDiff(newMaxTimeDiff); - } - - /// @notice Sets whether a TDX TCB status is accepted. - function setTcbStatusAllowed(TDXTcbStatus status, bool allowed) external onlyOwner { - _setTcbStatusAllowed(status, allowed); - } - - /// @notice Configures the RISC Zero verifier/program. - function setRiscZeroConfiguration(address newRiscZeroVerifier, bytes32 newVerifierId) external onlyOwner { - _setRiscZeroConfiguration(newRiscZeroVerifier, newVerifierId); + /// @inheritdoc ITDXVerifier + function allowedTcbStatuses(TDXTcbStatus status) public pure returns (bool) { + return status == TDXTcbStatus.UpToDate || status == TDXTcbStatus.SwHardeningNeeded; } /// @inheritdoc ITDXVerifier @@ -154,6 +114,7 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { bytes calldata proofBytes ) external + view returns (TDXVerifierJournal memory journal) { if (msg.sender != proofSubmitter) revert CallerNotProofSubmitter(); @@ -161,33 +122,23 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { IRiscZeroVerifier(riscZeroVerifier).verify(proofBytes, verifierId, sha256(output)); journal = abi.decode(output, (TDXVerifierJournal)); _verifyJournal(journal); - - emit AttestationSubmitted( - journal.signer, - journal.imageHash, - journal.tcbStatus, - journal.pckCertHash, - journal.tcbInfoHash, - journal.qeIdentityHash - ); } /// @notice Semantic version. - /// @custom:semver 0.2.0 + /// @custom:semver 0.3.0 function version() public pure virtual returns (string memory) { - return "0.2.0"; + return "0.3.0"; } function _verifyJournal(TDXVerifierJournal memory journal) internal view { if (journal.result != TDXVerificationResult.Success) revert TDXVerificationFailed(journal.result); - bytes32 expectedRootCaHash = rootCaHash; - if (journal.rootCaHash != expectedRootCaHash) { - revert RootCaHashMismatch(expectedRootCaHash, journal.rootCaHash); + if (journal.rootCaHash != rootCaHash) { + revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); } - if (!allowedTcbStatuses[journal.tcbStatus]) revert TcbStatusNotAllowed(journal.tcbStatus); + if (!allowedTcbStatuses(journal.tcbStatus)) revert TcbStatusNotAllowed(journal.tcbStatus); if (journal.collateralExpiration <= block.timestamp) revert CollateralExpired(journal.collateralExpiration); - uint64 timestamp = journal.timestamp / uint64(MS_PER_SECOND); + uint64 timestamp = journal.timestamp / 1000; if (timestamp + maxTimeDiff <= block.timestamp || timestamp >= block.timestamp) { revert InvalidTimestamp(timestamp, block.timestamp); } @@ -208,34 +159,9 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { } } - function _setRootCaHash(bytes32 newRootCaHash) internal { - if (newRootCaHash == bytes32(0)) revert ZeroRootCaHash(); - rootCaHash = newRootCaHash; - emit RootCaHashUpdated(newRootCaHash); - } - function _setProofSubmitter(address newProofSubmitter) internal { if (newProofSubmitter == address(0)) revert ZeroProofSubmitter(); proofSubmitter = newProofSubmitter; emit ProofSubmitterChanged(newProofSubmitter); } - - function _setMaxTimeDiff(uint64 newMaxTimeDiff) internal { - if (newMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); - maxTimeDiff = newMaxTimeDiff; - emit MaxTimeDiffUpdated(newMaxTimeDiff); - } - - function _setTcbStatusAllowed(TDXTcbStatus status, bool allowed) internal { - allowedTcbStatuses[status] = allowed; - emit TcbStatusPolicyUpdated(status, allowed); - } - - function _setRiscZeroConfiguration(address newRiscZeroVerifier, bytes32 newVerifierId) internal { - if (newRiscZeroVerifier == address(0)) revert ZeroRiscZeroVerifier(); - if (newVerifierId == bytes32(0)) revert ZeroVerifierId(); - riscZeroVerifier = newRiscZeroVerifier; - verifierId = newVerifierId; - emit RiscZeroConfigurationUpdated(newRiscZeroVerifier, newVerifierId); - } } diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index f51fa3e85..a85f40a6e 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -48,22 +48,6 @@ contract TDXVerifierTest is Test { assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.UpToDate)); } - function testOwnerCanUpdateRiscZeroConfiguration() public { - address newVerifier = makeAddr("new-risc-zero"); - bytes32 newVerifierId = keccak256("new-tdx-verifier-id"); - verifier.setRiscZeroConfiguration(newVerifier, newVerifierId); - - TDXVerifierJournal memory journal = _successJournal(); - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"5678"; - _mockRiscZeroVerify(newVerifier, newVerifierId, output, proofBytes); - - TDXVerifierJournal memory result = verifier.verify(output, proofBytes); - - assertEq(result.signer, journal.signer); - assertEq(result.imageHash, IMAGE_HASH); - } - function testVerifyRevertsIfNotProofSubmitter() public { bytes memory output = abi.encode(_successJournal()); @@ -72,9 +56,15 @@ contract TDXVerifierTest is Test { verifier.verify(output, ""); } - function testSetRiscZeroConfigurationRevertsIfZeroVerifier() public { + function testConstructorRevertsIfZeroRiscZeroVerifier() public { vm.expectRevert(TDXVerifier.ZeroRiscZeroVerifier.selector); - verifier.setRiscZeroConfiguration(address(0), VERIFIER_ID); + new TDXVerifier(owner, MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, address(0), VERIFIER_ID); + } + + function testAllowedTcbStatusesAreFixed() public view { + assertTrue(verifier.allowedTcbStatuses(TDXTcbStatus.UpToDate)); + assertTrue(verifier.allowedTcbStatuses(TDXTcbStatus.SwHardeningNeeded)); + assertFalse(verifier.allowedTcbStatuses(TDXTcbStatus.ConfigurationNeeded)); } function testVerifyRevertsWhenGuestReportsFailure() public { @@ -112,20 +102,6 @@ contract TDXVerifierTest is Test { verifier.verify(output, proofBytes); } - function testOwnerCanAllowAdditionalTcbStatus() public { - verifier.setTcbStatusAllowed(TDXTcbStatus.ConfigurationNeeded, true); - - TDXVerifierJournal memory journal = _successJournal(); - journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - - TDXVerifierJournal memory result = verifier.verify(output, proofBytes); - - assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.ConfigurationNeeded)); - } - function testVerifyRevertsWhenCollateralExpired() public { TDXVerifierJournal memory journal = _successJournal(); journal.collateralExpiration = uint64(block.timestamp); From 8eebf5be50c9cbb1c7f16ac35143bbdd1b219fca Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 11:54:25 -0400 Subject: [PATCH 030/146] clean up TDXVerifier --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 14 --------- scripts/deploy/SystemDeploy.s.sol | 5 ---- scripts/multiproof/DeployDevWithTDX.s.sol | 4 +-- scripts/multiproof/DeployTDXVerifier.s.sol | 26 ++++++++++------ scripts/multiproof/DeployTEEVerifiers.s.sol | 8 ++--- scripts/multiproof/README.md | 4 +-- scripts/multiproof/justfile | 10 +++++-- src/L1/proofs/tee/TDXVerifier.sol | 33 +++++---------------- test/L1/proofs/TDXVerifier.t.sol | 19 ++++-------- test/L1/proofs/TEEProverRegistryTDX.t.sol | 12 -------- test/deploy/SystemDeploy.t.sol | 9 ------ 11 files changed, 45 insertions(+), 99 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index b29b377ab..618cac192 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -37,13 +37,9 @@ enum TDXTcbStatus { /// @param timestamp Quote timestamp in milliseconds since Unix epoch. /// @param collateralExpiration Earliest expiration timestamp in seconds across accepted collateral. /// @param rootCaHash Hash of the Intel root CA used to validate the PCK/collateral signing chains. -/// @param pckCertHash Hash of the PCK leaf certificate that signed the quote attestation key chain. -/// @param tcbInfoHash Hash of the TCB info collateral consumed by the guest. -/// @param qeIdentityHash Hash of the QE identity collateral consumed by the guest. /// @param publicKey Uncompressed secp256k1 public key: 0x04 || x || y. /// @param signer Ethereum address derived from publicKey. /// @param imageHash Multiproof-compatible image hash derived from MRTD and RTMR0-3. -/// @param mrTdHash keccak256 hash of the 48-byte MRTD measurement. /// @param reportDataPrefix First 32 bytes of TDREPORT.REPORTDATA. /// @param reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA, available for app-specific binding. struct TDXVerifierJournal { @@ -52,13 +48,9 @@ struct TDXVerifierJournal { uint64 timestamp; uint64 collateralExpiration; bytes32 rootCaHash; - bytes32 pckCertHash; - bytes32 tcbInfoHash; - bytes32 qeIdentityHash; bytes publicKey; address signer; bytes32 imageHash; - bytes32 mrTdHash; bytes32 reportDataPrefix; bytes32 reportDataSuffix; } @@ -80,10 +72,4 @@ interface ITDXVerifier { external view returns (TDXVerifierJournal memory journal); - - /// @notice Returns whether a TCB status is accepted by verifier policy. - function allowedTcbStatuses(TDXTcbStatus status) external view returns (bool); - - /// @notice Updates the address authorized to submit verified proofs. - function setProofSubmitter(address newProofSubmitter) external; } diff --git a/scripts/deploy/SystemDeploy.s.sol b/scripts/deploy/SystemDeploy.s.sol index 1901c37f0..935a0ad3a 100644 --- a/scripts/deploy/SystemDeploy.s.sol +++ b/scripts/deploy/SystemDeploy.s.sol @@ -997,11 +997,6 @@ contract SystemDeploy is Script { vm.broadcast(msg.sender); nitroVerifier.setProofSubmitter(address(output_.teeProverRegistryProxy)); } - ITDXVerifier tdxVerifier = ITDXVerifier(_input.tdxVerifier); - if (tdxVerifier.proofSubmitter() != address(output_.teeProverRegistryProxy)) { - vm.broadcast(msg.sender); - tdxVerifier.setProofSubmitter(address(output_.teeProverRegistryProxy)); - } vm.broadcast(msg.sender); output_.teeVerifier = diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 6de055df1..4c97f7def 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -117,7 +117,8 @@ contract DeployDevWithTDX is Script { console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); console.log(""); - console.log("NOTE: TDXVerifier and NitroEnclaveVerifier owners must be the broadcaster/finalSystemOwner."); + console.log("NOTE: TDXVerifier proofSubmitter must already be this TEEProverRegistry."); + console.log(" NitroEnclaveVerifier owner must be the broadcaster/finalSystemOwner."); vm.startBroadcast(); @@ -153,7 +154,6 @@ contract DeployDevWithTDX is Script { registryProxy.changeAdmin(address(0xdead)); teeProverRegistryProxy = address(registryProxy); - ITDXVerifier(tdxVerifierAddr).setProofSubmitter(teeProverRegistryProxy); INitroEnclaveVerifier(nitroEnclaveVerifierAddr).setProofSubmitter(teeProverRegistryProxy); teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index 9a3b7d625..95c879fce 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -13,11 +13,10 @@ pragma solidity ^0.8.20; * * forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ * --sig "run(address,address,bytes32,bytes32)" \ - * \ + * \ * --rpc-url --broadcast --private-key * - * After running DeployDevWithTDX.s.sol, the TDX verifier's proofSubmitter is - * updated to the deployed TEEProverRegistry. + * proofSubmitter is immutable and must be the TEEProverRegistry that will call verify(). */ import { Script } from "forge-std/Script.sol"; @@ -29,18 +28,25 @@ contract DeployTDXVerifier is Script { /// @notice Maximum TDX quote age accepted by TDXVerifier. uint64 internal constant TDX_MAX_TIME_DIFF = 3600; - /// @param owner Owner for TDXVerifier. + /// @param proofSubmitter Address authorized to submit proofs. /// @param risc0VerifierRouter Existing RISC Zero verifier router. /// @param tdxVerifierId RISC Zero image ID for the TDX DCAP verifier guest. /// @param intelRootCaHash Hash of the trusted Intel root CA consumed by the guest. - function run(address owner, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) public { - require(owner != address(0), "owner must be non-zero"); + function run( + address proofSubmitter, + address risc0VerifierRouter, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + require(proofSubmitter != address(0), "proofSubmitter must be non-zero"); require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); console.log("=== Deploying TDXVerifier ==="); - console.log("Owner:", owner); + console.log("Proof Submitter:", proofSubmitter); console.log("RISC Zero Verifier Router:", risc0VerifierRouter); console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); @@ -50,7 +56,7 @@ contract DeployTDXVerifier is Script { vm.startBroadcast(); address tdxVerifier = address( - new TDXVerifier(owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, risc0VerifierRouter, tdxVerifierId) + new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, proofSubmitter, risc0VerifierRouter, tdxVerifierId) ); vm.stopBroadcast(); @@ -59,11 +65,12 @@ contract DeployTDXVerifier is Script { console.log(""); console.log(">>> Use this address as the DeployDevWithTDX.s.sol argument <<<"); - _writeOutput(tdxVerifier, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); + _writeOutput(tdxVerifier, proofSubmitter, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); } function _writeOutput( address tdxVerifier, + address proofSubmitter, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash @@ -72,6 +79,7 @@ contract DeployTDXVerifier is Script { { string memory key = "deployment"; vm.serializeAddress(key, "TDXVerifier", tdxVerifier); + vm.serializeAddress(key, "ProofSubmitter", proofSubmitter); vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); diff --git a/scripts/multiproof/DeployTEEVerifiers.s.sol b/scripts/multiproof/DeployTEEVerifiers.s.sol index 6cc1ff695..4e92e2a29 100644 --- a/scripts/multiproof/DeployTEEVerifiers.s.sol +++ b/scripts/multiproof/DeployTEEVerifiers.s.sol @@ -158,8 +158,8 @@ contract DeployTEEVerifiers is Script { console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); console.log(""); - console.log("NOTE: proofSubmitter is set to owner as placeholder on both verifiers."); - console.log(" DeployDevWithTDX.s.sol updates both to TEEProverRegistry."); + console.log("NOTE: Nitro proofSubmitter is set to owner as placeholder."); + console.log(" TDX proofSubmitter is immutable and set to owner by this script."); console.log(""); vm.startBroadcast(); @@ -230,7 +230,7 @@ contract DeployTEEVerifiers is Script { } function _deployTDXVerifier( - address owner, + address proofSubmitter, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash @@ -239,7 +239,7 @@ contract DeployTEEVerifiers is Script { returns (address deployedTDXVerifier) { deployedTDXVerifier = address( - new TDXVerifier(owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, risc0VerifierRouter, tdxVerifierId) + new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, proofSubmitter, risc0VerifierRouter, tdxVerifierId) ); } diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index acbc10856..569554e04 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -229,7 +229,7 @@ To override any TDX verifier input manually, pass all three verifier args: ```bash forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ --sig "run(address,address,bytes32,bytes32)" \ - $OWNER \ + $TEE_PROVER_REGISTRY \ $TDX_RISC0_VERIFIER_ROUTER \ $TDX_VERIFIER_ID \ $INTEL_ROOT_CA_HASH \ @@ -259,7 +259,7 @@ Use `DeployTEEVerifiers.s.sol` with `run(address,address,address,bytes32,bytes32 ### Step 2: Deploy the TDX multiproof test stack -Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates both `TDXVerifier.proofSubmitter` and `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. +Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. The `TDXVerifier` must be deployed with `proofSubmitter` set to the `TEEProverRegistry`; `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 69e387c82..89a509cec 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -36,18 +36,24 @@ default: # L1_RPC_URL # # Args: +# proof_submitter TEEProverRegistry allowed to call verify(); defaults to owner # risc0_router RISC Zero verifier router # tdx_verifier_id RISC Zero image ID for the TDX DCAP verifier guest # intel_root_ca_hash Trusted Intel root CA hash consumed by the guest -deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: +deploy-tdx-verifier proof_submitter="" risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" owner="$(cast wallet address --account "{{forge_account}}")" + proof_submitter="{{proof_submitter}}" + if [[ -z "$proof_submitter" ]]; then + proof_submitter="$owner" + fi echo "Using {{forge_account}} as owner/deployer: $owner" + echo "Using proof submitter: $proof_submitter" forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ --sig "run(address,address,bytes32,bytes32)" \ - "$owner" \ + "$proof_submitter" \ "{{risc0_router}}" \ "{{tdx_verifier_id}}" \ "{{intel_root_ca_hash}}" \ diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index 4de550eda..c3fd25dcd 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Ownable } from "lib/solady/src/auth/Ownable.sol"; import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; @@ -17,12 +16,12 @@ import { /// @dev The heavy TDX work is expected to happen in a ZK guest: quote signature verification, PCK chain /// validation, TCB info validation, QE identity validation, CRL checks, and extraction of TDREPORT fields. /// This contract verifies the ZK proof and enforces onchain policy over the verified journal. -contract TDXVerifier is Ownable, ITDXVerifier, ISemver { +contract TDXVerifier is ITDXVerifier, ISemver { /// @notice Maximum accepted age of a TDX quote, in seconds. uint64 public immutable maxTimeDiff; /// @notice Address authorized to submit TDX proofs, expected to be the TDX-aware registry. - address public proofSubmitter; + address public immutable proofSubmitter; /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. bytes32 public immutable rootCaHash; @@ -33,9 +32,6 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { /// @notice RISC Zero image ID for the TDX DCAP verifier guest. bytes32 public immutable verifierId; - /// @notice Emitted when the proof submitter changes. - event ProofSubmitterChanged(address indexed proofSubmitter); - /// @notice Thrown when a zero maxTimeDiff is provided. error ZeroMaxTimeDiff(); @@ -79,33 +75,22 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { error ReportDataMismatch(bytes32 expected, bytes32 actual); constructor( - address owner, uint64 initialMaxTimeDiff, bytes32 initialRootCaHash, address initialProofSubmitter, address initialRiscZeroVerifier, bytes32 initialVerifierId ) { - _initializeOwner(owner); if (initialMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); if (initialRootCaHash == bytes32(0)) revert ZeroRootCaHash(); + if (initialProofSubmitter == address(0)) revert ZeroProofSubmitter(); if (initialRiscZeroVerifier == address(0)) revert ZeroRiscZeroVerifier(); if (initialVerifierId == bytes32(0)) revert ZeroVerifierId(); maxTimeDiff = initialMaxTimeDiff; rootCaHash = initialRootCaHash; + proofSubmitter = initialProofSubmitter; riscZeroVerifier = initialRiscZeroVerifier; verifierId = initialVerifierId; - _setProofSubmitter(initialProofSubmitter); - } - - /// @notice Sets the proof submitter, expected to be the TDX-aware registry. - function setProofSubmitter(address newProofSubmitter) external onlyOwner { - _setProofSubmitter(newProofSubmitter); - } - - /// @inheritdoc ITDXVerifier - function allowedTcbStatuses(TDXTcbStatus status) public pure returns (bool) { - return status == TDXTcbStatus.UpToDate || status == TDXTcbStatus.SwHardeningNeeded; } /// @inheritdoc ITDXVerifier @@ -135,7 +120,9 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { if (journal.rootCaHash != rootCaHash) { revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); } - if (!allowedTcbStatuses(journal.tcbStatus)) revert TcbStatusNotAllowed(journal.tcbStatus); + if (journal.tcbStatus != TDXTcbStatus.UpToDate && journal.tcbStatus != TDXTcbStatus.SwHardeningNeeded) { + revert TcbStatusNotAllowed(journal.tcbStatus); + } if (journal.collateralExpiration <= block.timestamp) revert CollateralExpired(journal.collateralExpiration); uint64 timestamp = journal.timestamp / 1000; @@ -158,10 +145,4 @@ contract TDXVerifier is Ownable, ITDXVerifier, ISemver { publicKeyHash := keccak256(add(publicKey, 0x21), 64) } } - - function _setProofSubmitter(address newProofSubmitter) internal { - if (newProofSubmitter == address(0)) revert ZeroProofSubmitter(); - proofSubmitter = newProofSubmitter; - emit ProofSubmitterChanged(newProofSubmitter); - } } diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index a85f40a6e..dd9ea2770 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -10,7 +10,6 @@ import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract TDXVerifierTest is Test { TDXVerifier internal verifier; - address internal owner; address internal proofSubmitter; address internal mockRiscZeroVerifier; @@ -18,7 +17,6 @@ contract TDXVerifierTest is Test { bytes32 internal constant WRONG_ROOT_CA_HASH = keccak256("wrong-root-ca"); bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); - bytes32 internal constant MRTD_HASH = keccak256("mrtd"); bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); uint64 internal constant MAX_TIME_DIFF = 3600; @@ -27,12 +25,10 @@ contract TDXVerifierTest is Test { function setUp() public { vm.warp(NOW); - owner = address(this); proofSubmitter = address(this); mockRiscZeroVerifier = makeAddr("mock-risc-zero"); - verifier = - new TDXVerifier(owner, MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, mockRiscZeroVerifier, VERIFIER_ID); + verifier = new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, mockRiscZeroVerifier, VERIFIER_ID); } function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public { @@ -58,13 +54,12 @@ contract TDXVerifierTest is Test { function testConstructorRevertsIfZeroRiscZeroVerifier() public { vm.expectRevert(TDXVerifier.ZeroRiscZeroVerifier.selector); - new TDXVerifier(owner, MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, address(0), VERIFIER_ID); + new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, address(0), VERIFIER_ID); } - function testAllowedTcbStatusesAreFixed() public view { - assertTrue(verifier.allowedTcbStatuses(TDXTcbStatus.UpToDate)); - assertTrue(verifier.allowedTcbStatuses(TDXTcbStatus.SwHardeningNeeded)); - assertFalse(verifier.allowedTcbStatuses(TDXTcbStatus.ConfigurationNeeded)); + function testConstructorRevertsIfZeroProofSubmitter() public { + vm.expectRevert(TDXVerifier.ZeroProofSubmitter.selector); + new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, address(0), mockRiscZeroVerifier, VERIFIER_ID); } function testVerifyRevertsWhenGuestReportsFailure() public { @@ -180,13 +175,9 @@ contract TDXVerifierTest is Test { timestamp: uint64(block.timestamp - 1) * 1000, collateralExpiration: uint64(block.timestamp + 1 days), rootCaHash: ROOT_CA_HASH, - pckCertHash: keccak256("pck-cert"), - tcbInfoHash: keccak256("tcb-info"), - qeIdentityHash: keccak256("qe-identity"), publicKey: publicKey, signer: address(uint160(uint256(publicKeyHash))), imageHash: IMAGE_HASH, - mrTdHash: MRTD_HASH, reportDataPrefix: publicKeyHash, reportDataSuffix: REPORT_DATA_SUFFIX }); diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 0cdf7ad8c..e351f95b2 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -51,14 +51,6 @@ contract MockTDXVerifierForRegistry is ITDXVerifier { function verify(bytes calldata, bytes calldata) external view returns (TDXVerifierJournal memory) { return _journal; } - - function allowedTcbStatuses(TDXTcbStatus) external pure returns (bool) { - return true; - } - - function setProofSubmitter(address newProofSubmitter) external { - proofSubmitter = newProofSubmitter; - } } contract TEEProverRegistryTDXTest is Test { @@ -105,13 +97,9 @@ contract TEEProverRegistryTDXTest is Test { timestamp: 0, collateralExpiration: 0, rootCaHash: bytes32(0), - pckCertHash: bytes32(0), - tcbInfoHash: bytes32(0), - qeIdentityHash: bytes32(0), publicKey: "", signer: signer, imageHash: IMAGE_HASH, - mrTdHash: bytes32(0), reportDataPrefix: bytes32(0), reportDataSuffix: REPORT_DATA_SUFFIX }); diff --git a/test/deploy/SystemDeploy.t.sol b/test/deploy/SystemDeploy.t.sol index a8279c744..8175d8af8 100644 --- a/test/deploy/SystemDeploy.t.sol +++ b/test/deploy/SystemDeploy.t.sol @@ -25,10 +25,6 @@ contract MockNitroEnclaveVerifier { contract MockTDXVerifier { address public proofSubmitter; - - function setProofSubmitter(address _proofSubmitter) external { - proofSubmitter = _proofSubmitter; - } } contract MockSP1Verifier { @@ -265,11 +261,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { teeProverRegistryProxyAddr, "nitro proof submitter" ); - assertEq( - MockTDXVerifier(_input.implementationsInput.tdxVerifier).proofSubmitter(), - teeProverRegistryProxyAddr, - "tdx proof submitter" - ); assertEq( address(teeProverRegistry.DISPUTE_GAME_FACTORY()), address(_output.opChain.disputeGameFactoryProxy), From e46039c6aaada072f3008e38b8d9cd50194d504f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 12:07:33 -0400 Subject: [PATCH 031/146] clean up TDXVerifier --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 24 +- scripts/multiproof/DeployDevWithTDX.s.sol | 3 +- scripts/multiproof/DeployTDXVerifier.s.sol | 27 +- scripts/multiproof/DeployTEEVerifiers.s.sol | 9 +- scripts/multiproof/README.md | 5 +- scripts/multiproof/justfile | 11 +- snapshots/abi/TDXVerifier.json | 487 +++++--------------- snapshots/semver-lock.json | 6 +- snapshots/storageLayout/TDXVerifier.json | 10 +- src/L1/proofs/tee/TDXVerifier.sol | 46 +- test/L1/proofs/TDXVerifier.t.sol | 31 +- test/L1/proofs/TEEProverRegistryTDX.t.sol | 10 +- 12 files changed, 151 insertions(+), 518 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index 618cac192..897c2061c 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -1,23 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -/// @notice Statuses that a TDX quote/collateral verifier may emit. -/// @dev Unknown is index 0 so uninitialized values fail closed. -enum TDXVerificationResult { - Unknown, - Success, - InvalidQuote, - QuoteSignatureInvalid, - RootCaNotTrusted, - PckCertChainInvalid, - TcbInfoInvalid, - QeIdentityInvalid, - TcbStatusNotAllowed, - CollateralExpired, - InvalidTimestamp, - ReportDataMismatch -} - /// @notice Intel TDX TCB status reduced to the statuses this contract's policy needs. /// @dev Unknown is index 0 so uninitialized values fail closed. enum TDXTcbStatus { @@ -32,7 +15,7 @@ enum TDXTcbStatus { } /// @notice Public journal emitted by the offchain/ZK TDX DCAP verifier. -/// @param result Overall verification result after quote and collateral validation in the guest. +/// @param success Whether quote and collateral validation succeeded in the guest. /// @param tcbStatus Intel TDX TCB status for the platform. /// @param timestamp Quote timestamp in milliseconds since Unix epoch. /// @param collateralExpiration Earliest expiration timestamp in seconds across accepted collateral. @@ -43,7 +26,7 @@ enum TDXTcbStatus { /// @param reportDataPrefix First 32 bytes of TDREPORT.REPORTDATA. /// @param reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA, available for app-specific binding. struct TDXVerifierJournal { - TDXVerificationResult result; + bool success; TDXTcbStatus tcbStatus; uint64 timestamp; uint64 collateralExpiration; @@ -58,9 +41,6 @@ struct TDXVerifierJournal { /// @title ITDXVerifier /// @notice Interface for Intel TDX quote verification used by TDX-aware TEE prover registries. interface ITDXVerifier { - /// @notice Address authorized to submit verified proofs. - function proofSubmitter() external view returns (address); - /// @notice Verifies a ZK proof of Intel TDX DCAP quote verification and returns attested signer metadata. /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. /// @param proofBytes ZK proof bytes. diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 4c97f7def..b5417dab5 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -117,8 +117,7 @@ contract DeployDevWithTDX is Script { console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); console.log(""); - console.log("NOTE: TDXVerifier proofSubmitter must already be this TEEProverRegistry."); - console.log(" NitroEnclaveVerifier owner must be the broadcaster/finalSystemOwner."); + console.log("NOTE: NitroEnclaveVerifier owner must be the broadcaster/finalSystemOwner."); vm.startBroadcast(); diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index 95c879fce..e8e70e808 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -12,11 +12,9 @@ pragma solidity ^0.8.20; * Usage: * * forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ - * --sig "run(address,address,bytes32,bytes32)" \ - * \ + * --sig "run(address,bytes32,bytes32)" \ + * \ * --rpc-url --broadcast --private-key - * - * proofSubmitter is immutable and must be the TEEProverRegistry that will call verify(). */ import { Script } from "forge-std/Script.sol"; @@ -28,25 +26,15 @@ contract DeployTDXVerifier is Script { /// @notice Maximum TDX quote age accepted by TDXVerifier. uint64 internal constant TDX_MAX_TIME_DIFF = 3600; - /// @param proofSubmitter Address authorized to submit proofs. /// @param risc0VerifierRouter Existing RISC Zero verifier router. /// @param tdxVerifierId RISC Zero image ID for the TDX DCAP verifier guest. /// @param intelRootCaHash Hash of the trusted Intel root CA consumed by the guest. - function run( - address proofSubmitter, - address risc0VerifierRouter, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - public - { - require(proofSubmitter != address(0), "proofSubmitter must be non-zero"); + function run(address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) public { require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); console.log("=== Deploying TDXVerifier ==="); - console.log("Proof Submitter:", proofSubmitter); console.log("RISC Zero Verifier Router:", risc0VerifierRouter); console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); @@ -55,9 +43,8 @@ contract DeployTDXVerifier is Script { vm.startBroadcast(); - address tdxVerifier = address( - new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, proofSubmitter, risc0VerifierRouter, tdxVerifierId) - ); + address tdxVerifier = + address(new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, risc0VerifierRouter, tdxVerifierId)); vm.stopBroadcast(); @@ -65,12 +52,11 @@ contract DeployTDXVerifier is Script { console.log(""); console.log(">>> Use this address as the DeployDevWithTDX.s.sol argument <<<"); - _writeOutput(tdxVerifier, proofSubmitter, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); + _writeOutput(tdxVerifier, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); } function _writeOutput( address tdxVerifier, - address proofSubmitter, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash @@ -79,7 +65,6 @@ contract DeployTDXVerifier is Script { { string memory key = "deployment"; vm.serializeAddress(key, "TDXVerifier", tdxVerifier); - vm.serializeAddress(key, "ProofSubmitter", proofSubmitter); vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); diff --git a/scripts/multiproof/DeployTEEVerifiers.s.sol b/scripts/multiproof/DeployTEEVerifiers.s.sol index 4e92e2a29..71877291f 100644 --- a/scripts/multiproof/DeployTEEVerifiers.s.sol +++ b/scripts/multiproof/DeployTEEVerifiers.s.sol @@ -159,7 +159,6 @@ contract DeployTEEVerifiers is Script { console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); console.log(""); console.log("NOTE: Nitro proofSubmitter is set to owner as placeholder."); - console.log(" TDX proofSubmitter is immutable and set to owner by this script."); console.log(""); vm.startBroadcast(); @@ -167,7 +166,7 @@ contract DeployTEEVerifiers is Script { (setVerifier, nitroEnclaveVerifier) = _deployNitroVerifier( owner, nitroRisc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, nitroVerifierProofId ); - tdxVerifier = _deployTDXVerifier(owner, tdxRisc0VerifierRouter, tdxVerifierId, intelRootCaHash); + tdxVerifier = _deployTDXVerifier(tdxRisc0VerifierRouter, tdxVerifierId, intelRootCaHash); vm.stopBroadcast(); @@ -230,7 +229,6 @@ contract DeployTEEVerifiers is Script { } function _deployTDXVerifier( - address proofSubmitter, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash @@ -238,9 +236,8 @@ contract DeployTEEVerifiers is Script { internal returns (address deployedTDXVerifier) { - deployedTDXVerifier = address( - new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, proofSubmitter, risc0VerifierRouter, tdxVerifierId) - ); + deployedTDXVerifier = + address(new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, risc0VerifierRouter, tdxVerifierId)); } function _validateInputs( diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 569554e04..874993817 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -228,8 +228,7 @@ To override any TDX verifier input manually, pass all three verifier args: ```bash forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ - --sig "run(address,address,bytes32,bytes32)" \ - $TEE_PROVER_REGISTRY \ + --sig "run(address,bytes32,bytes32)" \ $TDX_RISC0_VERIFIER_ROUTER \ $TDX_VERIFIER_ID \ $INTEL_ROOT_CA_HASH \ @@ -259,7 +258,7 @@ Use `DeployTEEVerifiers.s.sol` with `run(address,address,address,bytes32,bytes32 ### Step 2: Deploy the TDX multiproof test stack -Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. The `TDXVerifier` must be deployed with `proofSubmitter` set to the `TEEProverRegistry`; `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. +Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 89a509cec..300816509 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -36,24 +36,17 @@ default: # L1_RPC_URL # # Args: -# proof_submitter TEEProverRegistry allowed to call verify(); defaults to owner # risc0_router RISC Zero verifier router # tdx_verifier_id RISC Zero image ID for the TDX DCAP verifier guest # intel_root_ca_hash Trusted Intel root CA hash consumed by the guest -deploy-tdx-verifier proof_submitter="" risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: +deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" owner="$(cast wallet address --account "{{forge_account}}")" - proof_submitter="{{proof_submitter}}" - if [[ -z "$proof_submitter" ]]; then - proof_submitter="$owner" - fi echo "Using {{forge_account}} as owner/deployer: $owner" - echo "Using proof submitter: $proof_submitter" forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ - --sig "run(address,address,bytes32,bytes32)" \ - "$proof_submitter" \ + --sig "run(address,bytes32,bytes32)" \ "{{risc0_router}}" \ "{{tdx_verifier_id}}" \ "{{intel_root_ca_hash}}" \ diff --git a/snapshots/abi/TDXVerifier.json b/snapshots/abi/TDXVerifier.json index ed4b77a00..d1eaad43a 100644 --- a/snapshots/abi/TDXVerifier.json +++ b/snapshots/abi/TDXVerifier.json @@ -1,533 +1,270 @@ [ { + "type": "constructor", "inputs": [ { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "uint64", "name": "initialMaxTimeDiff", - "type": "uint64" + "type": "uint64", + "internalType": "uint64" }, { - "internalType": "bytes32", "name": "initialRootCaHash", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "initialProofSubmitter", - "type": "address" + "type": "bytes32", + "internalType": "bytes32" }, { - "internalType": "address", "name": "initialRiscZeroVerifier", - "type": "address" + "type": "address", + "internalType": "address" }, { - "internalType": "bytes32", "name": "initialVerifierId", - "type": "bytes32" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "enum TDXTcbStatus", - "name": "status", - "type": "uint8" - } - ], - "name": "allowedTcbStatuses", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [], - "name": "cancelOwnershipHandover", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pendingOwner", - "type": "address" + "type": "bytes32", + "internalType": "bytes32" } ], - "name": "completeOwnershipHandover", - "outputs": [], - "stateMutability": "payable", - "type": "function" + "stateMutability": "nonpayable" }, { - "inputs": [], + "type": "function", "name": "maxTimeDiff", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "result", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pendingOwner", - "type": "address" - } - ], - "name": "ownershipHandoverExpiresAt", - "outputs": [ - { - "internalType": "uint256", - "name": "result", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { "inputs": [], - "name": "proofSubmitter", "outputs": [ { - "internalType": "address", "name": "", - "type": "address" + "type": "uint64", + "internalType": "uint64" } ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "payable", - "type": "function" + "stateMutability": "view" }, { - "inputs": [], - "name": "requestOwnershipHandover", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], + "type": "function", "name": "riscZeroVerifier", + "inputs": [], "outputs": [ { - "internalType": "address", "name": "", - "type": "address" + "type": "address", + "internalType": "address" } ], - "stateMutability": "view", - "type": "function" + "stateMutability": "view" }, { - "inputs": [], + "type": "function", "name": "rootCaHash", + "inputs": [], "outputs": [ { - "internalType": "bytes32", "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newProofSubmitter", - "type": "address" + "type": "bytes32", + "internalType": "bytes32" } ], - "name": "setProofSubmitter", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" + "stateMutability": "view" }, { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [], + "type": "function", "name": "verifierId", + "inputs": [], "outputs": [ { - "internalType": "bytes32", "name": "", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" } ], - "stateMutability": "view", - "type": "function" + "stateMutability": "view" }, { + "type": "function", + "name": "verify", "inputs": [ { - "internalType": "bytes", "name": "output", - "type": "bytes" + "type": "bytes", + "internalType": "bytes" }, { - "internalType": "bytes", "name": "proofBytes", - "type": "bytes" + "type": "bytes", + "internalType": "bytes" } ], - "name": "verify", "outputs": [ { + "name": "journal", + "type": "tuple", + "internalType": "struct TDXVerifierJournal", "components": [ { - "internalType": "enum TDXVerificationResult", - "name": "result", - "type": "uint8" + "name": "success", + "type": "bool", + "internalType": "bool" }, { - "internalType": "enum TDXTcbStatus", "name": "tcbStatus", - "type": "uint8" + "type": "uint8", + "internalType": "enum TDXTcbStatus" }, { - "internalType": "uint64", "name": "timestamp", - "type": "uint64" + "type": "uint64", + "internalType": "uint64" }, { - "internalType": "uint64", "name": "collateralExpiration", - "type": "uint64" + "type": "uint64", + "internalType": "uint64" }, { - "internalType": "bytes32", "name": "rootCaHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "pckCertHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "tcbInfoHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "qeIdentityHash", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" }, { - "internalType": "bytes", "name": "publicKey", - "type": "bytes" + "type": "bytes", + "internalType": "bytes" }, { - "internalType": "address", "name": "signer", - "type": "address" + "type": "address", + "internalType": "address" }, { - "internalType": "bytes32", "name": "imageHash", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" }, { - "internalType": "bytes32", - "name": "mrTdHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", "name": "reportDataPrefix", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" }, { - "internalType": "bytes32", "name": "reportDataSuffix", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" } - ], - "internalType": "struct TDXVerifierJournal", - "name": "journal", - "type": "tuple" + ] } ], - "stateMutability": "view", - "type": "function" + "stateMutability": "view" }, { - "inputs": [], + "type": "function", "name": "version", + "inputs": [], "outputs": [ { - "internalType": "string", "name": "", - "type": "string" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pendingOwner", - "type": "address" - } - ], - "name": "OwnershipHandoverCanceled", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pendingOwner", - "type": "address" - } - ], - "name": "OwnershipHandoverRequested", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "oldOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "proofSubmitter", - "type": "address" + "type": "string", + "internalType": "string" } ], - "name": "ProofSubmitterChanged", - "type": "event" - }, - { - "inputs": [], - "name": "AlreadyInitialized", - "type": "error" - }, - { - "inputs": [], - "name": "CallerNotProofSubmitter", - "type": "error" + "stateMutability": "pure" }, { + "type": "error", + "name": "CollateralExpired", "inputs": [ { - "internalType": "uint64", "name": "collateralExpiration", - "type": "uint64" + "type": "uint64", + "internalType": "uint64" } - ], - "name": "CollateralExpired", - "type": "error" + ] }, { - "inputs": [], + "type": "error", "name": "InvalidPublicKey", - "type": "error" + "inputs": [] }, { + "type": "error", + "name": "InvalidTimestamp", "inputs": [ { - "internalType": "uint64", "name": "timestampSeconds", - "type": "uint64" + "type": "uint64", + "internalType": "uint64" }, { - "internalType": "uint256", "name": "currentTimestamp", - "type": "uint256" + "type": "uint256", + "internalType": "uint256" } - ], - "name": "InvalidTimestamp", - "type": "error" - }, - { - "inputs": [], - "name": "NewOwnerIsZeroAddress", - "type": "error" - }, - { - "inputs": [], - "name": "NoHandoverRequest", - "type": "error" + ] }, { + "type": "error", + "name": "ReportDataMismatch", "inputs": [ { - "internalType": "bytes32", "name": "expected", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" }, { - "internalType": "bytes32", "name": "actual", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" } - ], - "name": "ReportDataMismatch", - "type": "error" + ] }, { + "type": "error", + "name": "RootCaHashMismatch", "inputs": [ { - "internalType": "bytes32", "name": "expected", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" }, { - "internalType": "bytes32", "name": "actual", - "type": "bytes32" + "type": "bytes32", + "internalType": "bytes32" } - ], - "name": "RootCaHashMismatch", - "type": "error" + ] }, { + "type": "error", + "name": "SignerMismatch", "inputs": [ { - "internalType": "address", "name": "expected", - "type": "address" + "type": "address", + "internalType": "address" }, { - "internalType": "address", "name": "actual", - "type": "address" + "type": "address", + "internalType": "address" } - ], - "name": "SignerMismatch", - "type": "error" + ] }, { - "inputs": [ - { - "internalType": "enum TDXVerificationResult", - "name": "result", - "type": "uint8" - } - ], + "type": "error", "name": "TDXVerificationFailed", - "type": "error" + "inputs": [] }, { + "type": "error", + "name": "TcbStatusNotAllowed", "inputs": [ { - "internalType": "enum TDXTcbStatus", "name": "status", - "type": "uint8" + "type": "uint8", + "internalType": "enum TDXTcbStatus" } - ], - "name": "TcbStatusNotAllowed", - "type": "error" - }, - { - "inputs": [], - "name": "Unauthorized", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroMaxTimeDiff", - "type": "error" + ] }, { - "inputs": [], - "name": "ZeroProofSubmitter", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroRiscZeroVerifier", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroRootCaHash", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroVerifierId", - "type": "error" + "type": "error", + "name": "ZeroInput", + "inputs": [] } -] \ No newline at end of file +] diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index a2d0f1823..7ebf1c622 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -48,11 +48,11 @@ "sourceCodeHash": "0x03c164216b27f82ee13064ace6079d5e24e187d888f41bd01c8daffa60c575d6" }, "src/L1/proofs/tee/TDXVerifier.sol:TDXVerifier": { - "initCodeHash": "0x4f217be5fd1e333ef50a49014af962b3e4c6df978b7aa050555f7a764afef8fa", - "sourceCodeHash": "0xebbc9e5b57730e6c1a10aa87b10901efbbecb92452a9fbd2bb5827285c30dd7d" + "initCodeHash": "0x14ec4c0f99e2c957fa5dc7284fd74c9e24347c68557b01719f0709f08f4bda0b", + "sourceCodeHash": "0x28bb714117e586feaafdc8846c82615fa3b106d256426fef12133f8c4a3dcd70" }, "src/L1/proofs/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0x8f835cc538a6aca33adfc9f66e17c06822e518587d34b8cadd77de10d041b462", + "initCodeHash": "0xa21dc34428175e2781ba786b87542da46bb39318f7001b31606387df8a94fde9", "sourceCodeHash": "0xcdbe2ba1aeac4e3dc6a4e138bda7bd3dd9d072b7d419f5a8af4f3e9a4a045217" }, "src/L1/proofs/tee/TEEVerifier.sol:TEEVerifier": { diff --git a/snapshots/storageLayout/TDXVerifier.json b/snapshots/storageLayout/TDXVerifier.json index d97bafbd1..fe51488c7 100644 --- a/snapshots/storageLayout/TDXVerifier.json +++ b/snapshots/storageLayout/TDXVerifier.json @@ -1,9 +1 @@ -[ - { - "bytes": "20", - "label": "proofSubmitter", - "offset": 0, - "slot": "0", - "type": "address" - } -] \ No newline at end of file +[] diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index c3fd25dcd..29b4c9b50 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -4,12 +4,7 @@ pragma solidity ^0.8.0; import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; -import { - ITDXVerifier, - TDXTcbStatus, - TDXVerificationResult, - TDXVerifierJournal -} from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ITDXVerifier, TDXTcbStatus, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; /// @title TDXVerifier /// @notice Production-shape Intel TDX DCAP verifier for multiproof signer registration. @@ -20,9 +15,6 @@ contract TDXVerifier is ITDXVerifier, ISemver { /// @notice Maximum accepted age of a TDX quote, in seconds. uint64 public immutable maxTimeDiff; - /// @notice Address authorized to submit TDX proofs, expected to be the TDX-aware registry. - address public immutable proofSubmitter; - /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. bytes32 public immutable rootCaHash; @@ -32,26 +24,11 @@ contract TDXVerifier is ITDXVerifier, ISemver { /// @notice RISC Zero image ID for the TDX DCAP verifier guest. bytes32 public immutable verifierId; - /// @notice Thrown when a zero maxTimeDiff is provided. - error ZeroMaxTimeDiff(); - - /// @notice Thrown when a zero address is provided for the proof submitter. - error ZeroProofSubmitter(); - - /// @notice Thrown when a zero root CA hash is provided. - error ZeroRootCaHash(); - - /// @notice Thrown when the caller is not the configured proof submitter. - error CallerNotProofSubmitter(); - - /// @notice Thrown when a zero RISC Zero verifier address is provided. - error ZeroRiscZeroVerifier(); - - /// @notice Thrown when a zero verifier ID is provided. - error ZeroVerifierId(); + /// @notice Thrown when a zero constructor input is provided. + error ZeroInput(); /// @notice Thrown when the TDX verifier guest did not report success. - error TDXVerificationFailed(TDXVerificationResult result); + error TDXVerificationFailed(); /// @notice Thrown when the journal root does not match the trusted Intel root. error RootCaHashMismatch(bytes32 expected, bytes32 actual); @@ -77,18 +54,15 @@ contract TDXVerifier is ITDXVerifier, ISemver { constructor( uint64 initialMaxTimeDiff, bytes32 initialRootCaHash, - address initialProofSubmitter, address initialRiscZeroVerifier, bytes32 initialVerifierId ) { - if (initialMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); - if (initialRootCaHash == bytes32(0)) revert ZeroRootCaHash(); - if (initialProofSubmitter == address(0)) revert ZeroProofSubmitter(); - if (initialRiscZeroVerifier == address(0)) revert ZeroRiscZeroVerifier(); - if (initialVerifierId == bytes32(0)) revert ZeroVerifierId(); + if ( + initialMaxTimeDiff == 0 || initialRootCaHash == bytes32(0) || initialRiscZeroVerifier == address(0) + || initialVerifierId == bytes32(0) + ) revert ZeroInput(); maxTimeDiff = initialMaxTimeDiff; rootCaHash = initialRootCaHash; - proofSubmitter = initialProofSubmitter; riscZeroVerifier = initialRiscZeroVerifier; verifierId = initialVerifierId; } @@ -102,8 +76,6 @@ contract TDXVerifier is ITDXVerifier, ISemver { view returns (TDXVerifierJournal memory journal) { - if (msg.sender != proofSubmitter) revert CallerNotProofSubmitter(); - IRiscZeroVerifier(riscZeroVerifier).verify(proofBytes, verifierId, sha256(output)); journal = abi.decode(output, (TDXVerifierJournal)); _verifyJournal(journal); @@ -116,7 +88,7 @@ contract TDXVerifier is ITDXVerifier, ISemver { } function _verifyJournal(TDXVerifierJournal memory journal) internal view { - if (journal.result != TDXVerificationResult.Success) revert TDXVerificationFailed(journal.result); + if (!journal.success) revert TDXVerificationFailed(); if (journal.rootCaHash != rootCaHash) { revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); } diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index dd9ea2770..f69f23608 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -3,14 +3,13 @@ pragma solidity ^0.8.0; import { Test } from "forge-std/Test.sol"; -import { TDXTcbStatus, TDXVerificationResult, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { TDXTcbStatus, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract TDXVerifierTest is Test { TDXVerifier internal verifier; - address internal proofSubmitter; address internal mockRiscZeroVerifier; bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); @@ -25,10 +24,9 @@ contract TDXVerifierTest is Test { function setUp() public { vm.warp(NOW); - proofSubmitter = address(this); mockRiscZeroVerifier = makeAddr("mock-risc-zero"); - verifier = new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, mockRiscZeroVerifier, VERIFIER_ID); + verifier = new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, mockRiscZeroVerifier, VERIFIER_ID); } function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public { @@ -44,32 +42,19 @@ contract TDXVerifierTest is Test { assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.UpToDate)); } - function testVerifyRevertsIfNotProofSubmitter() public { - bytes memory output = abi.encode(_successJournal()); - - vm.prank(makeAddr("not-submitter")); - vm.expectRevert(TDXVerifier.CallerNotProofSubmitter.selector); - verifier.verify(output, ""); - } - - function testConstructorRevertsIfZeroRiscZeroVerifier() public { - vm.expectRevert(TDXVerifier.ZeroRiscZeroVerifier.selector); - new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, proofSubmitter, address(0), VERIFIER_ID); - } - - function testConstructorRevertsIfZeroProofSubmitter() public { - vm.expectRevert(TDXVerifier.ZeroProofSubmitter.selector); - new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, address(0), mockRiscZeroVerifier, VERIFIER_ID); + function testConstructorRevertsIfZeroInput() public { + vm.expectRevert(TDXVerifier.ZeroInput.selector); + new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, address(0), VERIFIER_ID); } function testVerifyRevertsWhenGuestReportsFailure() public { TDXVerifierJournal memory journal = _successJournal(); - journal.result = TDXVerificationResult.PckCertChainInvalid; + journal.success = false; bytes memory output = abi.encode(journal); bytes memory proofBytes = hex"1234"; _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TDXVerificationFailed.selector, journal.result)); + vm.expectRevert(TDXVerifier.TDXVerificationFailed.selector); verifier.verify(output, proofBytes); } @@ -170,7 +155,7 @@ contract TDXVerifierTest is Test { } journal = TDXVerifierJournal({ - result: TDXVerificationResult.Success, + success: true, tcbStatus: TDXTcbStatus.UpToDate, timestamp: uint64(block.timestamp - 1) * 1000, collateralExpiration: uint64(block.timestamp + 1 days), diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index e351f95b2..3e437fa6b 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -6,12 +6,7 @@ import { Test } from "forge-std/Test.sol"; import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { - ITDXVerifier, - TDXTcbStatus, - TDXVerificationResult, - TDXVerifierJournal -} from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ITDXVerifier, TDXTcbStatus, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { GameType } from "src/libraries/bridge/Types.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; @@ -42,7 +37,6 @@ contract MockDisputeGameFactoryForTDXRegistry { contract MockTDXVerifierForRegistry is ITDXVerifier { TDXVerifierJournal internal _journal; - address public proofSubmitter; function setJournal(TDXVerifierJournal memory journal) external { _journal = journal; @@ -92,7 +86,7 @@ contract TEEProverRegistryTDXTest is Test { address signer = address(0x1234); journal = TDXVerifierJournal({ - result: TDXVerificationResult.Success, + success: true, tcbStatus: TDXTcbStatus.UpToDate, timestamp: 0, collateralExpiration: 0, From 8d2894a35e01fc4b38c2ee70fbbe6c631676de30 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 12:10:25 -0400 Subject: [PATCH 032/146] clean up TDXVerifier --- src/L1/proofs/tee/TDXVerifier.sol | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index 29b4c9b50..dcb16fc9b 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -78,16 +78,7 @@ contract TDXVerifier is ITDXVerifier, ISemver { { IRiscZeroVerifier(riscZeroVerifier).verify(proofBytes, verifierId, sha256(output)); journal = abi.decode(output, (TDXVerifierJournal)); - _verifyJournal(journal); - } - - /// @notice Semantic version. - /// @custom:semver 0.3.0 - function version() public pure virtual returns (string memory) { - return "0.3.0"; - } - function _verifyJournal(TDXVerifierJournal memory journal) internal view { if (!journal.success) revert TDXVerificationFailed(); if (journal.rootCaHash != rootCaHash) { revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); @@ -110,6 +101,12 @@ contract TDXVerifier is ITDXVerifier, ISemver { } } + /// @notice Semantic version. + /// @custom:semver 0.3.0 + function version() public pure virtual returns (string memory) { + return "0.3.0"; + } + function _derivePublicKeyHash(bytes memory publicKey) internal pure returns (bytes32 publicKeyHash) { if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); // Skip the 32-byte length word and the 0x04 uncompressed prefix; hash the 64-byte X||Y. From e154b141f2b3ed927b35bfa8c59ccdb5f6003fc9 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 12:14:37 -0400 Subject: [PATCH 033/146] clean up TDXVerifier --- src/L1/proofs/tee/TDXVerifier.sol | 22 ++++++++-------------- test/L1/proofs/TDXVerifier.t.sol | 7 ++++--- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index dcb16fc9b..202c4e9f1 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -45,9 +45,6 @@ contract TDXVerifier is ITDXVerifier, ISemver { /// @notice Thrown when the public key is not an uncompressed secp256k1 public key. error InvalidPublicKey(); - /// @notice Thrown when the journal signer does not match the supplied public key. - error SignerMismatch(address expected, address actual); - /// @notice Thrown when TDREPORT.REPORTDATA does not bind the supplied public key. error ReportDataMismatch(bytes32 expected, bytes32 actual); @@ -93,12 +90,17 @@ contract TDXVerifier is ITDXVerifier, ISemver { revert InvalidTimestamp(timestamp, block.timestamp); } - bytes32 publicKeyHash = _derivePublicKeyHash(journal.publicKey); - address signer = address(uint160(uint256(publicKeyHash))); - if (journal.signer != signer) revert SignerMismatch(signer, journal.signer); + bytes memory publicKey = journal.publicKey; + if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); + bytes32 publicKeyHash; + assembly { + publicKeyHash := keccak256(add(publicKey, 0x21), 64) + } if (journal.reportDataPrefix != publicKeyHash) { revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); } + + journal.signer = address(uint160(uint256(publicKeyHash))); } /// @notice Semantic version. @@ -106,12 +108,4 @@ contract TDXVerifier is ITDXVerifier, ISemver { function version() public pure virtual returns (string memory) { return "0.3.0"; } - - function _derivePublicKeyHash(bytes memory publicKey) internal pure returns (bytes32 publicKeyHash) { - if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); - // Skip the 32-byte length word and the 0x04 uncompressed prefix; hash the 64-byte X||Y. - assembly { - publicKeyHash := keccak256(add(publicKey, 0x21), 64) - } - } } diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index f69f23608..0c2076c91 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -135,7 +135,7 @@ contract TDXVerifierTest is Test { verifier.verify(output, proofBytes); } - function testVerifyRevertsWhenSignerDoesNotMatchPublicKey() public { + function testVerifyDerivesSignerFromPublicKey() public { TDXVerifierJournal memory journal = _successJournal(); address expected = journal.signer; journal.signer = makeAddr("wrong-signer"); @@ -143,8 +143,9 @@ contract TDXVerifierTest is Test { bytes memory proofBytes = hex"1234"; _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - vm.expectRevert(abi.encodeWithSelector(TDXVerifier.SignerMismatch.selector, expected, journal.signer)); - verifier.verify(output, proofBytes); + TDXVerifierJournal memory result = verifier.verify(output, proofBytes); + + assertEq(result.signer, expected); } function _successJournal() internal view returns (TDXVerifierJournal memory journal) { From cfbf44514cdc1673c357ad9b0a455f2c4053917d Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 12:18:52 -0400 Subject: [PATCH 034/146] clean up TDXVerifier --- src/L1/proofs/tee/TDXVerifier.sol | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index 202c4e9f1..c16737209 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -19,7 +19,7 @@ contract TDXVerifier is ITDXVerifier, ISemver { bytes32 public immutable rootCaHash; /// @notice RISC Zero verifier router. - address public immutable riscZeroVerifier; + IRiscZeroVerifier public immutable riscZeroVerifier; /// @notice RISC Zero image ID for the TDX DCAP verifier guest. bytes32 public immutable verifierId; @@ -60,7 +60,7 @@ contract TDXVerifier is ITDXVerifier, ISemver { ) revert ZeroInput(); maxTimeDiff = initialMaxTimeDiff; rootCaHash = initialRootCaHash; - riscZeroVerifier = initialRiscZeroVerifier; + riscZeroVerifier = IRiscZeroVerifier(initialRiscZeroVerifier); verifierId = initialVerifierId; } @@ -73,13 +73,11 @@ contract TDXVerifier is ITDXVerifier, ISemver { view returns (TDXVerifierJournal memory journal) { - IRiscZeroVerifier(riscZeroVerifier).verify(proofBytes, verifierId, sha256(output)); + riscZeroVerifier.verify(proofBytes, verifierId, sha256(output)); journal = abi.decode(output, (TDXVerifierJournal)); if (!journal.success) revert TDXVerificationFailed(); - if (journal.rootCaHash != rootCaHash) { - revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); - } + if (journal.rootCaHash != rootCaHash) revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); if (journal.tcbStatus != TDXTcbStatus.UpToDate && journal.tcbStatus != TDXTcbStatus.SwHardeningNeeded) { revert TcbStatusNotAllowed(journal.tcbStatus); } @@ -96,9 +94,8 @@ contract TDXVerifier is ITDXVerifier, ISemver { assembly { publicKeyHash := keccak256(add(publicKey, 0x21), 64) } - if (journal.reportDataPrefix != publicKeyHash) { - revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); - } + bytes32 reportDataPrefix = journal.reportDataPrefix; + if (reportDataPrefix != publicKeyHash) revert ReportDataMismatch(publicKeyHash, reportDataPrefix); journal.signer = address(uint160(uint256(publicKeyHash))); } From 10b7ec3cc522427982b648a9058e4f07ed2d505c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 12:22:04 -0400 Subject: [PATCH 035/146] clean up TDXVerifier --- src/L1/proofs/tee/TDXVerifier.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index c16737209..c5048fc7c 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -94,8 +94,7 @@ contract TDXVerifier is ITDXVerifier, ISemver { assembly { publicKeyHash := keccak256(add(publicKey, 0x21), 64) } - bytes32 reportDataPrefix = journal.reportDataPrefix; - if (reportDataPrefix != publicKeyHash) revert ReportDataMismatch(publicKeyHash, reportDataPrefix); + if (journal.reportDataPrefix != publicKeyHash) revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); journal.signer = address(uint160(uint256(publicKeyHash))); } From 5342b19147e37badc6753e23c0e43f7967ed4708 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 12:31:21 -0400 Subject: [PATCH 036/146] clean up TDXVerifier --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 14 ++++--- src/L1/proofs/tee/TDXVerifier.sol | 33 ++++------------ src/L1/proofs/tee/TEEProverRegistry.sol | 8 ++-- test/L1/proofs/TDXVerifier.t.sol | 45 ++++++++-------------- test/L1/proofs/TEEProverRegistryTDX.t.sol | 47 ++++++++--------------- 5 files changed, 52 insertions(+), 95 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index 897c2061c..4dad00deb 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -20,8 +20,8 @@ enum TDXTcbStatus { /// @param timestamp Quote timestamp in milliseconds since Unix epoch. /// @param collateralExpiration Earliest expiration timestamp in seconds across accepted collateral. /// @param rootCaHash Hash of the Intel root CA used to validate the PCK/collateral signing chains. -/// @param publicKey Uncompressed secp256k1 public key: 0x04 || x || y. -/// @param signer Ethereum address derived from publicKey. +/// @param publicKeyX secp256k1 public key x-coordinate. +/// @param publicKeyY secp256k1 public key y-coordinate. /// @param imageHash Multiproof-compatible image hash derived from MRTD and RTMR0-3. /// @param reportDataPrefix First 32 bytes of TDREPORT.REPORTDATA. /// @param reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA, available for app-specific binding. @@ -31,8 +31,8 @@ struct TDXVerifierJournal { uint64 timestamp; uint64 collateralExpiration; bytes32 rootCaHash; - bytes publicKey; - address signer; + bytes32 publicKeyX; + bytes32 publicKeyY; bytes32 imageHash; bytes32 reportDataPrefix; bytes32 reportDataSuffix; @@ -44,12 +44,14 @@ interface ITDXVerifier { /// @notice Verifies a ZK proof of Intel TDX DCAP quote verification and returns attested signer metadata. /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. /// @param proofBytes ZK proof bytes. - /// @return journal Verified TDX attestation metadata. + /// @return signer Ethereum address derived from the attested public key. + /// @return imageHash Multiproof-compatible image hash derived from MRTD and RTMR0-3. + /// @return reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA. function verify( bytes calldata output, bytes calldata proofBytes ) external view - returns (TDXVerifierJournal memory journal); + returns (address signer, bytes32 imageHash, bytes32 reportDataSuffix); } diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index c5048fc7c..a34a2ab8e 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -24,28 +24,12 @@ contract TDXVerifier is ITDXVerifier, ISemver { /// @notice RISC Zero image ID for the TDX DCAP verifier guest. bytes32 public immutable verifierId; - /// @notice Thrown when a zero constructor input is provided. error ZeroInput(); - - /// @notice Thrown when the TDX verifier guest did not report success. error TDXVerificationFailed(); - - /// @notice Thrown when the journal root does not match the trusted Intel root. error RootCaHashMismatch(bytes32 expected, bytes32 actual); - - /// @notice Thrown when the journal's TCB status is not allowed. error TcbStatusNotAllowed(TDXTcbStatus status); - - /// @notice Thrown when collateral consumed by the ZK guest is expired. error CollateralExpired(uint64 collateralExpiration); - - /// @notice Thrown when the quote timestamp is outside policy. error InvalidTimestamp(uint64 timestampSeconds, uint256 currentTimestamp); - - /// @notice Thrown when the public key is not an uncompressed secp256k1 public key. - error InvalidPublicKey(); - - /// @notice Thrown when TDREPORT.REPORTDATA does not bind the supplied public key. error ReportDataMismatch(bytes32 expected, bytes32 actual); constructor( @@ -71,10 +55,10 @@ contract TDXVerifier is ITDXVerifier, ISemver { ) external view - returns (TDXVerifierJournal memory journal) + returns (address signer, bytes32 imageHash, bytes32 reportDataSuffix) { riscZeroVerifier.verify(proofBytes, verifierId, sha256(output)); - journal = abi.decode(output, (TDXVerifierJournal)); + TDXVerifierJournal memory journal = abi.decode(output, (TDXVerifierJournal)); if (!journal.success) revert TDXVerificationFailed(); if (journal.rootCaHash != rootCaHash) revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); @@ -88,15 +72,14 @@ contract TDXVerifier is ITDXVerifier, ISemver { revert InvalidTimestamp(timestamp, block.timestamp); } - bytes memory publicKey = journal.publicKey; - if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); - bytes32 publicKeyHash; - assembly { - publicKeyHash := keccak256(add(publicKey, 0x21), 64) + bytes32 publicKeyHash = keccak256(abi.encodePacked(journal.publicKeyX, journal.publicKeyY)); + if (journal.reportDataPrefix != publicKeyHash) { + revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); } - if (journal.reportDataPrefix != publicKeyHash) revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); - journal.signer = address(uint160(uint256(publicKeyHash))); + signer = address(uint160(uint256(publicKeyHash))); + imageHash = journal.imageHash; + reportDataSuffix = journal.reportDataSuffix; } /// @notice Semantic version. diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index aabed1aec..7c3e67167 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -9,7 +9,7 @@ import { Pcr, Bytes48 } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { OwnableManagedUpgradeable } from "src/universal/OwnableManagedUpgradeable.sol"; @@ -195,11 +195,11 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. /// @param proofBytes ZK proof bytes. function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { - TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, proofBytes); + (address signer, bytes32 imageHash, bytes32 reportDataSuffix) = TDX_VERIFIER.verify(output, proofBytes); - _registerSigner(journal.signer, journal.imageHash, TEEType.TDX); + _registerSigner(signer, imageHash, TEEType.TDX); - emit TDXSignerRegistered(journal.signer, journal.imageHash, journal.reportDataSuffix); + emit TDXSignerRegistered(signer, imageHash, reportDataSuffix); } /// @notice Deregisters a signer. diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index 0c2076c91..e70fd68c6 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -17,6 +17,8 @@ contract TDXVerifierTest is Test { bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); + bytes32 internal constant PUBLIC_KEY_X = hex"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; + bytes32 internal constant PUBLIC_KEY_Y = hex"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40"; uint64 internal constant MAX_TIME_DIFF = 3600; uint256 internal constant NOW = 1_700_000_000; @@ -35,11 +37,11 @@ contract TDXVerifierTest is Test { bytes memory proofBytes = hex"1234"; _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - TDXVerifierJournal memory result = verifier.verify(output, proofBytes); + (address signer, bytes32 imageHash, bytes32 reportDataSuffix) = verifier.verify(output, proofBytes); - assertEq(result.signer, journal.signer); - assertEq(result.imageHash, IMAGE_HASH); - assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.UpToDate)); + assertEq(signer, _signer()); + assertEq(imageHash, IMAGE_HASH); + assertEq(reportDataSuffix, REPORT_DATA_SUFFIX); } function testConstructorRevertsIfZeroInput() public { @@ -135,25 +137,8 @@ contract TDXVerifierTest is Test { verifier.verify(output, proofBytes); } - function testVerifyDerivesSignerFromPublicKey() public { - TDXVerifierJournal memory journal = _successJournal(); - address expected = journal.signer; - journal.signer = makeAddr("wrong-signer"); - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - - TDXVerifierJournal memory result = verifier.verify(output, proofBytes); - - assertEq(result.signer, expected); - } - function _successJournal() internal view returns (TDXVerifierJournal memory journal) { - bytes memory publicKey = _publicKey(); - bytes32 publicKeyHash; - assembly { - publicKeyHash := keccak256(add(publicKey, 0x21), 64) - } + bytes32 publicKeyHash = _publicKeyHash(); journal = TDXVerifierJournal({ success: true, @@ -161,20 +146,20 @@ contract TDXVerifierTest is Test { timestamp: uint64(block.timestamp - 1) * 1000, collateralExpiration: uint64(block.timestamp + 1 days), rootCaHash: ROOT_CA_HASH, - publicKey: publicKey, - signer: address(uint160(uint256(publicKeyHash))), + publicKeyX: PUBLIC_KEY_X, + publicKeyY: PUBLIC_KEY_Y, imageHash: IMAGE_HASH, reportDataPrefix: publicKeyHash, reportDataSuffix: REPORT_DATA_SUFFIX }); } - function _publicKey() internal pure returns (bytes memory publicKey) { - publicKey = new bytes(65); - publicKey[0] = 0x04; - for (uint256 i = 1; i < publicKey.length; i++) { - publicKey[i] = bytes1(uint8(i)); - } + function _publicKeyHash() internal pure returns (bytes32) { + return keccak256(abi.encodePacked(PUBLIC_KEY_X, PUBLIC_KEY_Y)); + } + + function _signer() internal pure returns (address) { + return address(uint160(uint256(_publicKeyHash()))); } function _mockRiscZeroVerify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 3e437fa6b..0aa5b7780 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol"; import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier, TDXTcbStatus, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { GameType } from "src/libraries/bridge/Types.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; @@ -36,14 +36,18 @@ contract MockDisputeGameFactoryForTDXRegistry { } contract MockTDXVerifierForRegistry is ITDXVerifier { - TDXVerifierJournal internal _journal; - - function setJournal(TDXVerifierJournal memory journal) external { - _journal = journal; + address internal _signer; + bytes32 internal _imageHash; + bytes32 internal _reportDataSuffix; + + function setResult(address signer, bytes32 imageHash, bytes32 reportDataSuffix) external { + _signer = signer; + _imageHash = imageHash; + _reportDataSuffix = reportDataSuffix; } - function verify(bytes calldata, bytes calldata) external view returns (TDXVerifierJournal memory) { - return _journal; + function verify(bytes calldata, bytes calldata) external view returns (address, bytes32, bytes32) { + return (_signer, _imageHash, _reportDataSuffix); } } @@ -52,9 +56,9 @@ contract TEEProverRegistryTDXTest is Test { bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); function testRegisterTDXSignerStoresImageHash() public { - TDXVerifierJournal memory journal = _successJournal(); + address signer = address(0x1234); MockTDXVerifierForRegistry verifier = new MockTDXVerifierForRegistry(); - verifier.setJournal(journal); + verifier.setResult(signer, IMAGE_HASH, REPORT_DATA_SUFFIX); MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); @@ -66,10 +70,10 @@ contract TEEProverRegistryTDXTest is Test { vm.prank(address(0xdEaD)); registry.registerTDXSigner("", ""); - assertTrue(registry.isRegisteredSigner(journal.signer)); - assertEq(registry.signerImageHash(journal.signer), IMAGE_HASH); - assertEq(uint8(registry.signerTEEType(journal.signer)), uint8(TEEProverRegistry.TEEType.TDX)); - assertTrue(registry.isValidSigner(journal.signer)); + assertTrue(registry.isRegisteredSigner(signer)); + assertEq(registry.signerImageHash(signer), IMAGE_HASH); + assertEq(uint8(registry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); + assertTrue(registry.isValidSigner(signer)); } function testConstructorRevertsIfTDXVerifierNotSet() public { @@ -81,21 +85,4 @@ contract TEEProverRegistryTDXTest is Test { INitroEnclaveVerifier(address(0)), ITDXVerifier(address(0)), IDisputeGameFactory(address(factory)) ); } - - function _successJournal() internal pure returns (TDXVerifierJournal memory journal) { - address signer = address(0x1234); - - journal = TDXVerifierJournal({ - success: true, - tcbStatus: TDXTcbStatus.UpToDate, - timestamp: 0, - collateralExpiration: 0, - rootCaHash: bytes32(0), - publicKey: "", - signer: signer, - imageHash: IMAGE_HASH, - reportDataPrefix: bytes32(0), - reportDataSuffix: REPORT_DATA_SUFFIX - }); - } } From 734d77d321f3b9bf92b9d2f7b0dd045a648fec11 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 14:02:25 -0400 Subject: [PATCH 037/146] clean up TDXVerifier --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 5 +---- src/L1/proofs/tee/TDXVerifier.sol | 8 ++------ src/L1/proofs/tee/TEEProverRegistry.sol | 6 +++--- test/L1/proofs/TDXVerifier.t.sol | 15 +-------------- test/L1/proofs/TEEProverRegistryTDX.t.sol | 11 ++++------- 5 files changed, 11 insertions(+), 34 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index 4dad00deb..3a580b141 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -15,7 +15,6 @@ enum TDXTcbStatus { } /// @notice Public journal emitted by the offchain/ZK TDX DCAP verifier. -/// @param success Whether quote and collateral validation succeeded in the guest. /// @param tcbStatus Intel TDX TCB status for the platform. /// @param timestamp Quote timestamp in milliseconds since Unix epoch. /// @param collateralExpiration Earliest expiration timestamp in seconds across accepted collateral. @@ -26,7 +25,6 @@ enum TDXTcbStatus { /// @param reportDataPrefix First 32 bytes of TDREPORT.REPORTDATA. /// @param reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA, available for app-specific binding. struct TDXVerifierJournal { - bool success; TDXTcbStatus tcbStatus; uint64 timestamp; uint64 collateralExpiration; @@ -46,12 +44,11 @@ interface ITDXVerifier { /// @param proofBytes ZK proof bytes. /// @return signer Ethereum address derived from the attested public key. /// @return imageHash Multiproof-compatible image hash derived from MRTD and RTMR0-3. - /// @return reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA. function verify( bytes calldata output, bytes calldata proofBytes ) external view - returns (address signer, bytes32 imageHash, bytes32 reportDataSuffix); + returns (address signer, bytes32 imageHash); } diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol index a34a2ab8e..b24f503fb 100644 --- a/src/L1/proofs/tee/TDXVerifier.sol +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -25,7 +25,6 @@ contract TDXVerifier is ITDXVerifier, ISemver { bytes32 public immutable verifierId; error ZeroInput(); - error TDXVerificationFailed(); error RootCaHashMismatch(bytes32 expected, bytes32 actual); error TcbStatusNotAllowed(TDXTcbStatus status); error CollateralExpired(uint64 collateralExpiration); @@ -55,12 +54,11 @@ contract TDXVerifier is ITDXVerifier, ISemver { ) external view - returns (address signer, bytes32 imageHash, bytes32 reportDataSuffix) + returns (address signer, bytes32 imageHash) { riscZeroVerifier.verify(proofBytes, verifierId, sha256(output)); TDXVerifierJournal memory journal = abi.decode(output, (TDXVerifierJournal)); - if (!journal.success) revert TDXVerificationFailed(); if (journal.rootCaHash != rootCaHash) revert RootCaHashMismatch(rootCaHash, journal.rootCaHash); if (journal.tcbStatus != TDXTcbStatus.UpToDate && journal.tcbStatus != TDXTcbStatus.SwHardeningNeeded) { revert TcbStatusNotAllowed(journal.tcbStatus); @@ -77,9 +75,7 @@ contract TDXVerifier is ITDXVerifier, ISemver { revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); } - signer = address(uint160(uint256(publicKeyHash))); - imageHash = journal.imageHash; - reportDataSuffix = journal.reportDataSuffix; + return (address(uint160(uint256(publicKeyHash))), journal.imageHash); } /// @notice Semantic version. diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index 7c3e67167..55fea12d2 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -80,7 +80,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { event SignerRegistered(address indexed signer); /// @notice Emitted when a TDX signer is registered. - event TDXSignerRegistered(address indexed signer, bytes32 indexed imageHash, bytes32 reportDataSuffix); + event TDXSignerRegistered(address indexed signer, bytes32 indexed imageHash); /// @notice Emitted when a signer is deregistered. event SignerDeregistered(address indexed signer); @@ -195,11 +195,11 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. /// @param proofBytes ZK proof bytes. function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { - (address signer, bytes32 imageHash, bytes32 reportDataSuffix) = TDX_VERIFIER.verify(output, proofBytes); + (address signer, bytes32 imageHash) = TDX_VERIFIER.verify(output, proofBytes); _registerSigner(signer, imageHash, TEEType.TDX); - emit TDXSignerRegistered(signer, imageHash, reportDataSuffix); + emit TDXSignerRegistered(signer, imageHash); } /// @notice Deregisters a signer. diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index e70fd68c6..fd35ad459 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -37,11 +37,10 @@ contract TDXVerifierTest is Test { bytes memory proofBytes = hex"1234"; _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - (address signer, bytes32 imageHash, bytes32 reportDataSuffix) = verifier.verify(output, proofBytes); + (address signer, bytes32 imageHash) = verifier.verify(output, proofBytes); assertEq(signer, _signer()); assertEq(imageHash, IMAGE_HASH); - assertEq(reportDataSuffix, REPORT_DATA_SUFFIX); } function testConstructorRevertsIfZeroInput() public { @@ -49,17 +48,6 @@ contract TDXVerifierTest is Test { new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, address(0), VERIFIER_ID); } - function testVerifyRevertsWhenGuestReportsFailure() public { - TDXVerifierJournal memory journal = _successJournal(); - journal.success = false; - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - - vm.expectRevert(TDXVerifier.TDXVerificationFailed.selector); - verifier.verify(output, proofBytes); - } - function testVerifyRevertsWhenRootCaHashMismatches() public { TDXVerifierJournal memory journal = _successJournal(); journal.rootCaHash = WRONG_ROOT_CA_HASH; @@ -141,7 +129,6 @@ contract TDXVerifierTest is Test { bytes32 publicKeyHash = _publicKeyHash(); journal = TDXVerifierJournal({ - success: true, tcbStatus: TDXTcbStatus.UpToDate, timestamp: uint64(block.timestamp - 1) * 1000, collateralExpiration: uint64(block.timestamp + 1 days), diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 0aa5b7780..13e4466ce 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -38,27 +38,24 @@ contract MockDisputeGameFactoryForTDXRegistry { contract MockTDXVerifierForRegistry is ITDXVerifier { address internal _signer; bytes32 internal _imageHash; - bytes32 internal _reportDataSuffix; - function setResult(address signer, bytes32 imageHash, bytes32 reportDataSuffix) external { + function setResult(address signer, bytes32 imageHash) external { _signer = signer; _imageHash = imageHash; - _reportDataSuffix = reportDataSuffix; } - function verify(bytes calldata, bytes calldata) external view returns (address, bytes32, bytes32) { - return (_signer, _imageHash, _reportDataSuffix); + function verify(bytes calldata, bytes calldata) external view returns (address, bytes32) { + return (_signer, _imageHash); } } contract TEEProverRegistryTDXTest is Test { bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); - bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); function testRegisterTDXSignerStoresImageHash() public { address signer = address(0x1234); MockTDXVerifierForRegistry verifier = new MockTDXVerifierForRegistry(); - verifier.setResult(signer, IMAGE_HASH, REPORT_DATA_SUFFIX); + verifier.setResult(signer, IMAGE_HASH); MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); From c36db9401c178f14665de69307ca82aea47b5ddb Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 14:06:34 -0400 Subject: [PATCH 038/146] clean up TDXVerifier --- interfaces/L1/proofs/tee/ITDXVerifier.sol | 2 -- test/L1/proofs/TDXVerifier.t.sol | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol index 3a580b141..8b5248bcf 100644 --- a/interfaces/L1/proofs/tee/ITDXVerifier.sol +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -23,7 +23,6 @@ enum TDXTcbStatus { /// @param publicKeyY secp256k1 public key y-coordinate. /// @param imageHash Multiproof-compatible image hash derived from MRTD and RTMR0-3. /// @param reportDataPrefix First 32 bytes of TDREPORT.REPORTDATA. -/// @param reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA, available for app-specific binding. struct TDXVerifierJournal { TDXTcbStatus tcbStatus; uint64 timestamp; @@ -33,7 +32,6 @@ struct TDXVerifierJournal { bytes32 publicKeyY; bytes32 imageHash; bytes32 reportDataPrefix; - bytes32 reportDataSuffix; } /// @title ITDXVerifier diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index fd35ad459..90aa5b1f4 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -16,7 +16,6 @@ contract TDXVerifierTest is Test { bytes32 internal constant WRONG_ROOT_CA_HASH = keccak256("wrong-root-ca"); bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); - bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); bytes32 internal constant PUBLIC_KEY_X = hex"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; bytes32 internal constant PUBLIC_KEY_Y = hex"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40"; @@ -136,8 +135,7 @@ contract TDXVerifierTest is Test { publicKeyX: PUBLIC_KEY_X, publicKeyY: PUBLIC_KEY_Y, imageHash: IMAGE_HASH, - reportDataPrefix: publicKeyHash, - reportDataSuffix: REPORT_DATA_SUFFIX + reportDataPrefix: publicKeyHash }); } From c1dcb2fc30b785ade5dcec0c3ab171eccadf2941 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 14:46:29 -0400 Subject: [PATCH 039/146] clean up TEEProverRegistry --- snapshots/abi/TEEProverRegistry.json | 32 +------------------------ src/L1/proofs/tee/TEEProverRegistry.sol | 10 -------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/snapshots/abi/TEEProverRegistry.json b/snapshots/abi/TEEProverRegistry.json index 44586ef24..a56ceb630 100644 --- a/snapshots/abi/TEEProverRegistry.json +++ b/snapshots/abi/TEEProverRegistry.json @@ -515,31 +515,6 @@ "name": "SignerRegistered", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "signer", - "type": "address" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "imageHash", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "reportDataSuffix", - "type": "bytes32" - } - ], - "name": "TDXSignerRegistered", - "type": "event" - }, { "inputs": [], "name": "AttestationTooOld", @@ -570,11 +545,6 @@ "name": "InvalidPublicKey", "type": "error" }, - { - "inputs": [], - "name": "InvalidTEEType", - "type": "error" - }, { "inputs": [], "name": "PCR0NotFound", @@ -606,4 +576,4 @@ "name": "TDXVerifierNotSet", "type": "error" } -] \ No newline at end of file +] diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index 55fea12d2..8778dd6f5 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -79,9 +79,6 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Emitted when a signer is registered. event SignerRegistered(address indexed signer); - /// @notice Emitted when a TDX signer is registered. - event TDXSignerRegistered(address indexed signer, bytes32 indexed imageHash); - /// @notice Emitted when a signer is deregistered. event SignerDeregistered(address indexed signer); @@ -115,9 +112,6 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Thrown when the TDX verifier is not configured. error TDXVerifierNotSet(); - /// @notice Thrown when attempting to register a signer with no TEE type. - error InvalidTEEType(); - /// @notice Thrown when a signer is already registered under another TEE type. error SignerTEETypeMismatch(address signer, TEEType existingTEEType, TEEType newTEEType); @@ -198,8 +192,6 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { (address signer, bytes32 imageHash) = TDX_VERIFIER.verify(output, proofBytes); _registerSigner(signer, imageHash, TEEType.TDX); - - emit TDXSignerRegistered(signer, imageHash); } /// @notice Deregisters a signer. @@ -279,7 +271,6 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @dev Registers a signer and stores the image hash enforced by TEEVerifier at proof-submission time. function _registerSigner(address signer, bytes32 imageHash, TEEType teeType) internal { - if (teeType == TEEType.NONE) revert InvalidTEEType(); TEEType existingTEEType = signerTEEType[signer]; if (existingTEEType != TEEType.NONE && existingTEEType != teeType) { revert SignerTEETypeMismatch(signer, existingTEEType, teeType); @@ -294,7 +285,6 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @dev Reads a type-specific TEE image hash from the AggregateVerifier registered in the factory for `gameType_`. function _getExpectedImageHash(GameType gameType_, TEEType teeType) internal view returns (bytes32) { - if (teeType == TEEType.NONE) revert InvalidTEEType(); address impl = address(DISPUTE_GAME_FACTORY.gameImpls(gameType_)); bytes memory callData = teeType == TEEType.NITRO ? abi.encodeWithSignature("TEE_NITRO_IMAGE_HASH()") From dd66e493e31039817ba65d646c71ef2d66702769 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 14:52:33 -0400 Subject: [PATCH 040/146] clean up TEEProverRegistry --- snapshots/abi/TEEProverRegistry.json | 21 --------------------- src/L1/proofs/tee/TEEProverRegistry.sol | 8 -------- test/L1/proofs/TEEProverRegistry.t.sol | 13 ++++--------- 3 files changed, 4 insertions(+), 38 deletions(-) diff --git a/snapshots/abi/TEEProverRegistry.json b/snapshots/abi/TEEProverRegistry.json index a56ceb630..0b5d450ca 100644 --- a/snapshots/abi/TEEProverRegistry.json +++ b/snapshots/abi/TEEProverRegistry.json @@ -550,27 +550,6 @@ "name": "PCR0NotFound", "type": "error" }, - { - "inputs": [ - { - "internalType": "address", - "name": "signer", - "type": "address" - }, - { - "internalType": "enum TEEProverRegistry.TEEType", - "name": "existingTEEType", - "type": "uint8" - }, - { - "internalType": "enum TEEProverRegistry.TEEType", - "name": "newTEEType", - "type": "uint8" - } - ], - "name": "SignerTEETypeMismatch", - "type": "error" - }, { "inputs": [], "name": "TDXVerifierNotSet", diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index 8778dd6f5..2176e119c 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -112,9 +112,6 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Thrown when the TDX verifier is not configured. error TDXVerifierNotSet(); - /// @notice Thrown when a signer is already registered under another TEE type. - error SignerTEETypeMismatch(address signer, TEEType existingTEEType, TEEType newTEEType); - constructor(INitroEnclaveVerifier nitroVerifier, ITDXVerifier tdxVerifier, IDisputeGameFactory factory) { if (address(factory) == address(0)) revert DisputeGameFactoryNotSet(); if (address(tdxVerifier) == address(0)) revert TDXVerifierNotSet(); @@ -271,11 +268,6 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @dev Registers a signer and stores the image hash enforced by TEEVerifier at proof-submission time. function _registerSigner(address signer, bytes32 imageHash, TEEType teeType) internal { - TEEType existingTEEType = signerTEEType[signer]; - if (existingTEEType != TEEType.NONE && existingTEEType != teeType) { - revert SignerTEETypeMismatch(signer, existingTEEType, teeType); - } - isRegisteredSigner[signer] = true; signerImageHash[signer] = imageHash; signerTEEType[signer] = teeType; diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index 22b3a7b78..1770f5d1b 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -218,22 +218,17 @@ contract TEEProverRegistryTest is Test { assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); } - function testRegisteringSameSignerWithDifferentTEETypeFails() public { + function testRegisteringSameSignerWithDifferentTEETypeOverwritesTEEType() public { address signer = makeAddr("dev-signer"); vm.prank(owner); teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); vm.prank(owner); - vm.expectRevert( - abi.encodeWithSelector( - TEEProverRegistry.SignerTEETypeMismatch.selector, - signer, - TEEProverRegistry.TEEType.NITRO, - TEEProverRegistry.TEEType.TDX - ) - ); teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); + + assertTrue(teeProverRegistry.isRegisteredSigner(signer)); + assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); } function testMaxAgeConstant() public view { From 3589af32090c044542a299fda0613f0b034b121f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 14:59:13 -0400 Subject: [PATCH 041/146] clean up TEEProverRegistry --- interfaces/L1/proofs/tee/ITEEProverRegistry.sol | 2 +- snapshots/abi/TEEProverRegistry.json | 17 +++++------------ src/L1/proofs/tee/TEEProverRegistry.sol | 13 +++---------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/interfaces/L1/proofs/tee/ITEEProverRegistry.sol b/interfaces/L1/proofs/tee/ITEEProverRegistry.sol index ea38c36ed..0b45e722b 100644 --- a/interfaces/L1/proofs/tee/ITEEProverRegistry.sol +++ b/interfaces/L1/proofs/tee/ITEEProverRegistry.sol @@ -21,7 +21,7 @@ interface ITEEProverRegistry { function deregisterSigner(address signer) external; function isValidSigner(address signer) external view returns (bool); function getRegisteredSigners() external view returns (address[] memory); - function getExpectedImageHash() external view returns (bytes32); + function getExpectedImageHash(uint8 teeType) external view returns (bytes32); function initialize( address initialOwner, address initialManager, diff --git a/snapshots/abi/TEEProverRegistry.json b/snapshots/abi/TEEProverRegistry.json index 0b5d450ca..5fcb635b8 100644 --- a/snapshots/abi/TEEProverRegistry.json +++ b/snapshots/abi/TEEProverRegistry.json @@ -99,21 +99,14 @@ "type": "function" }, { - "inputs": [], - "name": "getExpectedNitroImageHash", - "outputs": [ + "inputs": [ { - "internalType": "bytes32", - "name": "", - "type": "bytes32" + "internalType": "enum TEEProverRegistry.TEEType", + "name": "teeType", + "type": "uint8" } ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getExpectedTDXImageHash", + "name": "getExpectedImageHash", "outputs": [ { "internalType": "bytes32", diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index 2176e119c..55c8858d6 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -222,16 +222,9 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { return _registeredSigners.values(); } - /// @notice Returns the expected Nitro TEE image hash from the current AggregateVerifier. - /// @return The TEE_NITRO_IMAGE_HASH from the AggregateVerifier registered in the factory. - function getExpectedNitroImageHash() external view returns (bytes32) { - return _getExpectedImageHash(gameType, TEEType.NITRO); - } - - /// @notice Returns the expected TDX TEE image hash from the current AggregateVerifier. - /// @return The TEE_TDX_IMAGE_HASH from the AggregateVerifier registered in the factory. - function getExpectedTDXImageHash() external view returns (bytes32) { - return _getExpectedImageHash(gameType, TEEType.TDX); + /// @notice Returns the expected TEE image hash for the given TEE type from the current AggregateVerifier. + function getExpectedImageHash(TEEType teeType) external view returns (bytes32) { + return _getExpectedImageHash(gameType, teeType); } /// @notice Initializes the contract with owner, manager, proposers, and game type. From a902cde56164faf35e905b10f3f9bf60ffab3d4b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 15:04:10 -0400 Subject: [PATCH 042/146] clean up TEEProverRegistry --- src/L1/proofs/tee/TEEProverRegistry.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index 55c8858d6..228a80810 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -208,10 +208,8 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @param signer The address to check. /// @return True if the signer is registered with the current image hash, false otherwise. function isValidSigner(address signer) external view returns (bool) { - if (!isRegisteredSigner[signer]) return false; TEEType teeType = signerTEEType[signer]; - if (teeType == TEEType.NONE) return false; - return signerImageHash[signer] == _getExpectedImageHash(gameType, teeType); + return teeType != TEEType.NONE && signerImageHash[signer] == _getExpectedImageHash(gameType, teeType); } /// @notice Returns all currently registered signer addresses. From e7c236a6b4fdd775eac6954788497fb1d4f1ff54 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 15:12:35 -0400 Subject: [PATCH 043/146] clean up TEEVerifier --- src/L1/proofs/tee/TEEVerifier.sol | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/L1/proofs/tee/TEEVerifier.sol b/src/L1/proofs/tee/TEEVerifier.sol index 92428cf7d..4bfcfaa57 100644 --- a/src/L1/proofs/tee/TEEVerifier.sol +++ b/src/L1/proofs/tee/TEEVerifier.sol @@ -20,11 +20,8 @@ contract TEEVerifier is Verifier, ISemver { /// @dev Signers are registered via Nitro or TDX attestation in TEEProverRegistry. TEEProverRegistry public immutable TEE_PROVER_REGISTRY; - /// @notice Size of an ECDSA signature in bytes. - uint256 internal constant SIGNATURE_SIZE = 65; - /// @notice Size of a TEE proof: proposer(20) + signature(65). - uint256 internal constant TEE_PROOF_SIZE = 20 + SIGNATURE_SIZE; + uint256 internal constant TEE_PROOF_SIZE = 85; /// @notice Thrown when a recovered signer is not a valid registered signer. error InvalidSigner(address signer); @@ -75,21 +72,11 @@ contract TEEVerifier is Verifier, ISemver { revert InvalidProposer(proposer); } - bytes calldata signature = proofBytes[20:TEE_PROOF_SIZE]; - address signer = _recoverSigner(journal, signature); - _validateSigner(signer, imageId); - - return true; - } - - function _recoverSigner(bytes32 journal, bytes calldata signature) internal pure returns (address signer) { // The signature should be over the journal hash directly (not eth-signed-message prefixed). - ECDSA.RecoverError err; - (signer, err) = ECDSA.tryRecover(journal, signature); + bytes calldata signature = proofBytes[20:TEE_PROOF_SIZE]; + (address signer, ECDSA.RecoverError err) = ECDSA.tryRecover(journal, signature); if (err != ECDSA.RecoverError.NoError) revert InvalidSignature(); - } - function _validateSigner(address signer, bytes32 imageId) internal view { // A registered signer always has a non-NONE TEE type, so this single read also // serves as the registration check (saves an SLOAD versus calling isRegisteredSigner). TEEProverRegistry.TEEType signerTEEType = TEE_PROVER_REGISTRY.signerTEEType(signer); @@ -101,6 +88,8 @@ contract TEEVerifier is Verifier, ISemver { if (registeredImageHash != imageId) { revert ImageIdMismatch(registeredImageHash, imageId); } + + return true; } /// @notice Semantic version. From e9d6d9852f53853c25b0fe2da274dfe1641a0c1c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 15:16:48 -0400 Subject: [PATCH 044/146] clean up TEEVerifier --- src/L1/proofs/tee/TEEVerifier.sol | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/L1/proofs/tee/TEEVerifier.sol b/src/L1/proofs/tee/TEEVerifier.sol index 4bfcfaa57..e4aceb88c 100644 --- a/src/L1/proofs/tee/TEEVerifier.sol +++ b/src/L1/proofs/tee/TEEVerifier.sol @@ -20,9 +20,6 @@ contract TEEVerifier is Verifier, ISemver { /// @dev Signers are registered via Nitro or TDX attestation in TEEProverRegistry. TEEProverRegistry public immutable TEE_PROVER_REGISTRY; - /// @notice Size of a TEE proof: proposer(20) + signature(65). - uint256 internal constant TEE_PROOF_SIZE = 85; - /// @notice Thrown when a recovered signer is not a valid registered signer. error InvalidSigner(address signer); @@ -65,7 +62,7 @@ contract TEEVerifier is Verifier, ISemver { notNullified returns (bool) { - if (proofBytes.length != TEE_PROOF_SIZE) revert InvalidProofFormat(); + if (proofBytes.length != 85) revert InvalidProofFormat(); address proposer = address(bytes20(proofBytes[0:20])); if (!TEE_PROVER_REGISTRY.isValidProposer(proposer)) { @@ -73,14 +70,11 @@ contract TEEVerifier is Verifier, ISemver { } // The signature should be over the journal hash directly (not eth-signed-message prefixed). - bytes calldata signature = proofBytes[20:TEE_PROOF_SIZE]; + bytes calldata signature = proofBytes[20:85]; (address signer, ECDSA.RecoverError err) = ECDSA.tryRecover(journal, signature); if (err != ECDSA.RecoverError.NoError) revert InvalidSignature(); - // A registered signer always has a non-NONE TEE type, so this single read also - // serves as the registration check (saves an SLOAD versus calling isRegisteredSigner). - TEEProverRegistry.TEEType signerTEEType = TEE_PROVER_REGISTRY.signerTEEType(signer); - if (signerTEEType == TEEProverRegistry.TEEType.NONE) revert InvalidSigner(signer); + if (!TEE_PROVER_REGISTRY.isRegisteredSigner(signer)) revert InvalidSigner(signer); // Prevents a signer registered under one enclave image from being used in a game // that expects a different image (e.g., after an upgrade or across game types). From 49b0a5205d05981d2c7618b49fcf55994d1e02c4 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 15:27:07 -0400 Subject: [PATCH 045/146] clean up AggregateVerifier --- src/L1/proofs/AggregateVerifier.sol | 71 ++++++++++------------------- 1 file changed, 24 insertions(+), 47 deletions(-) diff --git a/src/L1/proofs/AggregateVerifier.sol b/src/L1/proofs/AggregateVerifier.sol index 61edfef01..62e866e98 100644 --- a/src/L1/proofs/AggregateVerifier.sol +++ b/src/L1/proofs/AggregateVerifier.sol @@ -50,17 +50,6 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { bytes32 tdxHash; } - /// @notice Common public inputs used to compute each TEE signature journal. - struct TeeJournalInputs { - address proposer; - bytes32 l1OriginHash; - bytes32 startingRoot; - uint64 startingL2SequenceNumber; - bytes32 endingRoot; - uint64 endingL2SequenceNumber; - bytes intermediateRoots; - } - //////////////////////////////////////////////////////////////// // Constants // //////////////////////////////////////////////////////////////// @@ -922,44 +911,32 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { bytes32 tdxImageHash = bytes32(proofBytes[97:129]); if (nitroImageHash != TEE_NITRO_IMAGE_HASH || tdxImageHash != TEE_TDX_IMAGE_HASH) revert InvalidProof(); - TeeJournalInputs memory inputs = TeeJournalInputs({ - proposer: proposer, - l1OriginHash: l1OriginHash, - startingRoot: startingRoot, - startingL2SequenceNumber: startingL2SequenceNumber, - endingRoot: endingRoot, - endingL2SequenceNumber: endingL2SequenceNumber, - intermediateRoots: intermediateRoots - }); - - _verifyTeeSignature(proofBytes[32:97], nitroImageHash, inputs); - _verifyTeeSignature(proofBytes[129:194], tdxImageHash, inputs); - } - - function _verifyTeeSignature( - bytes calldata signature, - bytes32 imageHash, - TeeJournalInputs memory inputs - ) - internal - view - { - bytes32 journal = keccak256( - abi.encodePacked( - inputs.proposer, - inputs.l1OriginHash, - inputs.startingRoot, - inputs.startingL2SequenceNumber, - inputs.endingRoot, - inputs.endingL2SequenceNumber, - inputs.intermediateRoots, - CONFIG_HASH, - imageHash - ) + bytes memory journalPrefix = abi.encodePacked( + proposer, + l1OriginHash, + startingRoot, + startingL2SequenceNumber, + endingRoot, + endingL2SequenceNumber, + intermediateRoots, + CONFIG_HASH ); - bytes memory verifierProof = abi.encodePacked(inputs.proposer, signature); - if (!TEE_VERIFIER.verify(verifierProof, imageHash, journal)) revert InvalidProof(); + if (!TEE_VERIFIER.verify( + abi.encodePacked(proposer, proofBytes[32:97]), + nitroImageHash, + keccak256(abi.encodePacked(journalPrefix, nitroImageHash)) + )) { + revert InvalidProof(); + } + + if (!TEE_VERIFIER.verify( + abi.encodePacked(proposer, proofBytes[129:194]), + tdxImageHash, + keccak256(abi.encodePacked(journalPrefix, tdxImageHash)) + )) { + revert InvalidProof(); + } } /// @notice Verifies a ZK proof for the current game. From 359e3199b669e37d206e3aee836d8bc161f916a5 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 15:39:49 -0400 Subject: [PATCH 046/146] clean up AggregateVerifier --- scripts/deploy/SystemDeploy.s.sol | 3 +- scripts/multiproof/DeployDevBase.s.sol | 3 +- scripts/multiproof/DeployDevWithTDX.s.sol | 3 +- snapshots/abi/AggregateVerifier.json | 23 +++++-------- snapshots/semver-lock.json | 6 ++-- src/L1/proofs/AggregateVerifier.sol | 40 +++++++++-------------- test/L1/OptimismPortal2.t.sol | 3 +- test/L1/proofs/AggregateVerifier.t.sol | 7 ++-- test/L1/proofs/BaseTest.t.sol | 18 +++------- test/L1/proofs/DisputeGameFactory.t.sol | 3 +- 10 files changed, 45 insertions(+), 64 deletions(-) diff --git a/scripts/deploy/SystemDeploy.s.sol b/scripts/deploy/SystemDeploy.s.sol index 935a0ad3a..b69679f7c 100644 --- a/scripts/deploy/SystemDeploy.s.sol +++ b/scripts/deploy/SystemDeploy.s.sol @@ -1042,7 +1042,8 @@ contract SystemDeploy is Script { _input.delayedWETH, _input.teeVerifier, _input.zkVerifier, - AggregateVerifier.TeeHashes(_input.teeNitroImageHash, _input.teeTdxImageHash), + _input.teeNitroImageHash, + _input.teeTdxImageHash, AggregateVerifier.ZkHashes(_input.zkRangeHash, _input.zkAggregationHash), _input.multiproofConfigHash, _input.l2ChainId, diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 9da5ac2ef..24d47095a 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -116,7 +116,8 @@ abstract contract DeployDevBase is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), + cfg.teeNitroImageHash(), + cfg.teeTdxImageHash(), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainId(), diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index b5417dab5..a48988039 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -187,7 +187,8 @@ contract DeployDevWithTDX is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), + cfg.teeNitroImageHash(), + cfg.teeTdxImageHash(), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainId(), diff --git a/snapshots/abi/AggregateVerifier.json b/snapshots/abi/AggregateVerifier.json index 1f6be8ed7..65e82fb17 100644 --- a/snapshots/abi/AggregateVerifier.json +++ b/snapshots/abi/AggregateVerifier.json @@ -27,21 +27,14 @@ "type": "address" }, { - "components": [ - { - "internalType": "bytes32", - "name": "nitroHash", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "tdxHash", - "type": "bytes32" - } - ], - "internalType": "struct AggregateVerifier.TeeHashes", - "name": "teeHashes", - "type": "tuple" + "internalType": "bytes32", + "name": "teeNitroImageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "teeTdxImageHash", + "type": "bytes32" }, { "components": [ diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 7ebf1c622..accd591f6 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -28,8 +28,8 @@ "sourceCodeHash": "0xf122a50487efe9bd5a620262ba20ef4adbca14eeec2af7fd32e6e16739001596" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0xa393180e221f3083135ddfe56e054dd989e335b13bbfc04df8ee01ba0b860291", - "sourceCodeHash": "0xceda0cd8df108723b38dc67c097d0d188da976fd0d0b15843be5182c015e0299" + "initCodeHash": "0xa01943ea100f6b04897860ebf21bb54fda9ec3b9b08b9aedc5496e6739a46920", + "sourceCodeHash": "0x6962d4835853937f304681679e43929a9a58f5873c80b658b0c3975e696736be" }, "src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": { "initCodeHash": "0x6f3afd2d0ef97a82ca3111976322b99343a270e54cd4a405028f2f29c75f7fb1", @@ -139,4 +139,4 @@ "initCodeHash": "0x2bfce526f82622288333d53ca3f43a0a94306ba1bab99241daa845f8f4b18bd4", "sourceCodeHash": "0xf49d7b0187912a6bb67926a3222ae51121e9239495213c975b3b4b217ee57a1b" } -} \ No newline at end of file +} diff --git a/src/L1/proofs/AggregateVerifier.sol b/src/L1/proofs/AggregateVerifier.sol index 62e866e98..6e787120f 100644 --- a/src/L1/proofs/AggregateVerifier.sol +++ b/src/L1/proofs/AggregateVerifier.sol @@ -44,12 +44,6 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { bytes32 aggregateHash; } - /// @notice Hashes for the TEE proving images. - struct TeeHashes { - bytes32 nitroHash; - bytes32 tdxHash; - } - //////////////////////////////////////////////////////////////// // Constants // //////////////////////////////////////////////////////////////// @@ -73,9 +67,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The minimum number of proofs required to resolve the game. uint256 public constant PROOF_THRESHOLD = 1; - /// @notice TEE proof payload size: - /// nitro image hash(32) + nitro signature(65) + tdx image hash(32) + tdx signature(65). - uint256 internal constant TEE_PROOF_BYTES_LENGTH = 194; + /// @notice TEE proof payload size: nitro signature(65) + tdx signature(65). + uint256 internal constant TEE_PROOF_BYTES_LENGTH = 130; //////////////////////////////////////////////////////////////// // Immutables // @@ -267,7 +260,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @param delayedWETH The delayed WETH contract. /// @param teeVerifier The TEE verifier. /// @param zkVerifier The ZK verifier. - /// @param teeHashes The hashes of the Nitro and TDX TEE images. + /// @param teeNitroImageHash The hash of the Nitro TEE image. + /// @param teeTdxImageHash The hash of the TDX TEE image. /// @param zkHashes The hashes of the ZK range and aggregate programs. /// @param configHash The hash of the rollup configuration. /// @param l2ChainId The chain ID of the L2 network. @@ -279,7 +273,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { IDelayedWETH delayedWETH, IVerifier teeVerifier, IVerifier zkVerifier, - TeeHashes memory teeHashes, + bytes32 teeNitroImageHash, + bytes32 teeTdxImageHash, ZkHashes memory zkHashes, bytes32 configHash, uint256 l2ChainId, @@ -298,8 +293,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { DELAYED_WETH = delayedWETH; TEE_VERIFIER = teeVerifier; ZK_VERIFIER = zkVerifier; - TEE_NITRO_IMAGE_HASH = teeHashes.nitroHash; - TEE_TDX_IMAGE_HASH = teeHashes.tdxHash; + TEE_NITRO_IMAGE_HASH = teeNitroImageHash; + TEE_TDX_IMAGE_HASH = teeTdxImageHash; ZK_RANGE_HASH = zkHashes.rangeHash; ZK_AGGREGATE_HASH = zkHashes.aggregateHash; CONFIG_HASH = configHash; @@ -890,8 +885,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } /// @notice Verifies a TEE proof for the current game. - /// @param proofBytes The proof: nitro image hash(32) + nitro signature(65) + tdx image hash(32) - /// + tdx signature(65). + /// @param proofBytes The proof: nitro signature(65) + tdx signature(65). function _verifyTeeProof( bytes calldata proofBytes, address proposer, @@ -907,10 +901,6 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { { if (proofBytes.length != TEE_PROOF_BYTES_LENGTH) revert InvalidProof(); - bytes32 nitroImageHash = bytes32(proofBytes[:32]); - bytes32 tdxImageHash = bytes32(proofBytes[97:129]); - if (nitroImageHash != TEE_NITRO_IMAGE_HASH || tdxImageHash != TEE_TDX_IMAGE_HASH) revert InvalidProof(); - bytes memory journalPrefix = abi.encodePacked( proposer, l1OriginHash, @@ -923,17 +913,17 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { ); if (!TEE_VERIFIER.verify( - abi.encodePacked(proposer, proofBytes[32:97]), - nitroImageHash, - keccak256(abi.encodePacked(journalPrefix, nitroImageHash)) + abi.encodePacked(proposer, proofBytes[:65]), + TEE_NITRO_IMAGE_HASH, + keccak256(abi.encodePacked(journalPrefix, TEE_NITRO_IMAGE_HASH)) )) { revert InvalidProof(); } if (!TEE_VERIFIER.verify( - abi.encodePacked(proposer, proofBytes[129:194]), - tdxImageHash, - keccak256(abi.encodePacked(journalPrefix, tdxImageHash)) + abi.encodePacked(proposer, proofBytes[65:130]), + TEE_TDX_IMAGE_HASH, + keccak256(abi.encodePacked(journalPrefix, TEE_TDX_IMAGE_HASH)) )) { revert InvalidProof(); } diff --git a/test/L1/OptimismPortal2.t.sol b/test/L1/OptimismPortal2.t.sol index a2e375d05..062c7c6fb 100644 --- a/test/L1/OptimismPortal2.t.sol +++ b/test/L1/OptimismPortal2.t.sol @@ -85,7 +85,8 @@ abstract contract OptimismPortal2_TestInit is DisputeGameFactory_TestInit { delayedWeth, IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - AggregateVerifier.TeeHashes(bytes32(uint256(1)), bytes32(uint256(1))), + bytes32(uint256(1)), + bytes32(uint256(1)), AggregateVerifier.ZkHashes(bytes32(uint256(2)), bytes32(uint256(3))), bytes32(uint256(4)), deploy.cfg().l2ChainId(), diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 6fc7caca5..4eafac2f7 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -235,7 +235,7 @@ contract AggregateVerifierTest is BaseTest { bytes32 l1OriginHash = bytes32(uint256(1)); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(194)); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); _expectCreateGameRevertsForTeeProof( rootClaim, @@ -253,7 +253,7 @@ contract AggregateVerifierTest is BaseTest { bytes32 l1OriginHash = bytes32(uint256(1)); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(194)); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); _expectCreateGameRevertsForTeeProof( rootClaim, @@ -484,7 +484,8 @@ contract AggregateVerifierTest is BaseTest { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), + TEE_NITRO_IMAGE_HASH, + TEE_TDX_IMAGE_HASH, AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index e82f6ec7c..80ccaf06b 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -113,7 +113,8 @@ contract BaseTest is Test { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), + TEE_NITRO_IMAGE_HASH, + TEE_TDX_IMAGE_HASH, AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, @@ -172,7 +173,7 @@ contract BaseTest is Test { AggregateVerifier.ProofType proofType ) internal - view + pure returns (bytes memory) { return abi.encodePacked(uint8(proofType), _generateProofBody(salt, proofType)); @@ -183,21 +184,12 @@ contract BaseTest is Test { AggregateVerifier.ProofType proofType ) internal - view + pure returns (bytes memory) { if (proofType == AggregateVerifier.ProofType.TEE) { bytes32 saltHash = keccak256(salt); - return abi.encodePacked( - TEE_NITRO_IMAGE_HASH, - saltHash, - bytes32(0), - uint8(27), - TEE_TDX_IMAGE_HASH, - saltHash, - bytes32(uint256(1)), - uint8(28) - ); + return abi.encodePacked(saltHash, bytes32(0), uint8(27), saltHash, bytes32(uint256(1)), uint8(28)); } return abi.encodePacked(salt, bytes32(0), bytes32(0), uint8(27)); } diff --git a/test/L1/proofs/DisputeGameFactory.t.sol b/test/L1/proofs/DisputeGameFactory.t.sol index 65172d2db..835ee9f8b 100644 --- a/test/L1/proofs/DisputeGameFactory.t.sol +++ b/test/L1/proofs/DisputeGameFactory.t.sol @@ -225,7 +225,8 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_TestInit { delayedWeth, IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - AggregateVerifier.TeeHashes(bytes32(uint256(1)), bytes32(uint256(1))), + bytes32(uint256(1)), + bytes32(uint256(1)), AggregateVerifier.ZkHashes(bytes32(uint256(2)), bytes32(uint256(3))), bytes32(uint256(4)), L2_CHAIN_ID, From 8b5cee632028565a04024eb543b34be82c418706 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 15:42:30 -0400 Subject: [PATCH 047/146] clean up AggregateVerifier --- src/L1/proofs/AggregateVerifier.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/L1/proofs/AggregateVerifier.sol b/src/L1/proofs/AggregateVerifier.sol index 6e787120f..1ce56c675 100644 --- a/src/L1/proofs/AggregateVerifier.sol +++ b/src/L1/proofs/AggregateVerifier.sol @@ -67,9 +67,6 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The minimum number of proofs required to resolve the game. uint256 public constant PROOF_THRESHOLD = 1; - /// @notice TEE proof payload size: nitro signature(65) + tdx signature(65). - uint256 internal constant TEE_PROOF_BYTES_LENGTH = 130; - //////////////////////////////////////////////////////////////// // Immutables // //////////////////////////////////////////////////////////////// @@ -899,7 +896,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { internal view { - if (proofBytes.length != TEE_PROOF_BYTES_LENGTH) revert InvalidProof(); + if (proofBytes.length != 130) revert InvalidProof(); bytes memory journalPrefix = abi.encodePacked( proposer, From 52cf0f8a818df539f4c1999d2a83f5b534552151 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 15:54:36 -0400 Subject: [PATCH 048/146] clean up DeployTEEVerifiers --- scripts/multiproof/DeployTEEVerifiers.s.sol | 311 +++++--------------- 1 file changed, 80 insertions(+), 231 deletions(-) diff --git a/scripts/multiproof/DeployTEEVerifiers.s.sol b/scripts/multiproof/DeployTEEVerifiers.s.sol index 71877291f..214cff61e 100644 --- a/scripts/multiproof/DeployTEEVerifiers.s.sol +++ b/scripts/multiproof/DeployTEEVerifiers.s.sol @@ -8,24 +8,14 @@ pragma solidity ^0.8.20; * Usage: * * forge script scripts/multiproof/DeployTEEVerifiers.s.sol:DeployTEEVerifiers \ - * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32,bytes32)" \ - * \ + * --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" \ + * \ * \ - * \ + * \ * --rpc-url --broadcast --private-key * - * If using batched Nitro proofs, use the overload that also supplies - * : - * - * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" - * - * If Nitro and TDX use different RISC Zero verifier routers, use: - * - * --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" - * * The broadcaster must be the owner because this script calls addVerifyRoute() - * on the freshly deployed NitroEnclaveVerifier. Use the longest overload when - * Nitro and TDX proofs use different RISC Zero verifier router contracts. + * on the freshly deployed NitroEnclaveVerifier. */ import { Script } from "forge-std/Script.sol"; @@ -39,37 +29,19 @@ import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract DeployTEEVerifiers is Script { - /// @notice Maximum Nitro attestation age accepted by NitroEnclaveVerifier. - uint64 internal constant NITRO_MAX_TIME_DIFF = 3600; - - /// @notice Maximum TDX quote age accepted by TDXVerifier. - uint64 internal constant TDX_MAX_TIME_DIFF = 3600; - - address public setVerifier; - address public nitroEnclaveVerifier; - address public tdxVerifier; - - function run( - address owner, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - public - { - run( - owner, - risc0VerifierRouter, - setBuilderImageId, - nitroRootCert, - nitroVerifierId, - bytes32(0), - tdxVerifierId, - intelRootCaHash - ); + /// @notice Maximum Nitro attestation and TDX quote age accepted by the verifiers. + uint64 internal constant MAX_TIME_DIFF = 3600; + + struct Input { + address owner; + address nitroRisc0VerifierRouter; + address tdxRisc0VerifierRouter; + bytes32 setBuilderImageId; + bytes32 nitroRootCert; + bytes32 nitroVerifierId; + bytes32 nitroVerifierProofId; + bytes32 tdxVerifierId; + bytes32 intelRootCaHash; } function run( @@ -79,225 +51,102 @@ contract DeployTEEVerifiers is Script { bytes32 setBuilderImageId, bytes32 nitroRootCert, bytes32 nitroVerifierId, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - public - { - run( - owner, - nitroRisc0VerifierRouter, - tdxRisc0VerifierRouter, - setBuilderImageId, - nitroRootCert, - nitroVerifierId, - bytes32(0), - tdxVerifierId, - intelRootCaHash - ); - } - - function run( - address owner, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, bytes32 nitroVerifierProofId, bytes32 tdxVerifierId, bytes32 intelRootCaHash ) public { - run( - owner, - risc0VerifierRouter, - risc0VerifierRouter, - setBuilderImageId, - nitroRootCert, - nitroVerifierId, - nitroVerifierProofId, - tdxVerifierId, - intelRootCaHash - ); - } + Input memory input = Input({ + owner: owner, + nitroRisc0VerifierRouter: nitroRisc0VerifierRouter, + tdxRisc0VerifierRouter: tdxRisc0VerifierRouter, + setBuilderImageId: setBuilderImageId, + nitroRootCert: nitroRootCert, + nitroVerifierId: nitroVerifierId, + nitroVerifierProofId: nitroVerifierProofId, + tdxVerifierId: tdxVerifierId, + intelRootCaHash: intelRootCaHash + }); - function run( - address owner, - address nitroRisc0VerifierRouter, - address tdxRisc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 nitroVerifierProofId, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - public - { - bytes4 setVerifierSelector = _validateInputs( - owner, - nitroRisc0VerifierRouter, - tdxRisc0VerifierRouter, - setBuilderImageId, - nitroRootCert, - nitroVerifierId, - tdxVerifierId, - intelRootCaHash - ); + require(input.owner != address(0), "owner must be non-zero"); + require(input.nitroRisc0VerifierRouter != address(0), "nitroRisc0VerifierRouter must be non-zero"); + require(input.tdxRisc0VerifierRouter != address(0), "tdxRisc0VerifierRouter must be non-zero"); + require(input.setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); + require(input.nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); + require(input.nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); + require(input.tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); + require(input.intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); + + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(input.setBuilderImageId); console.log("=== Deploying TEE Verifiers ==="); - console.log("Owner:", owner); - console.log("Nitro RISC Zero Verifier Router:", nitroRisc0VerifierRouter); - console.log("TDX RISC Zero Verifier Router:", tdxRisc0VerifierRouter); - console.log("Set Builder Image ID:", vm.toString(setBuilderImageId)); + console.log("Owner:", input.owner); + console.log("Nitro RISC Zero Verifier Router:", input.nitroRisc0VerifierRouter); + console.log("TDX RISC Zero Verifier Router:", input.tdxRisc0VerifierRouter); + console.log("Set Builder Image ID:", vm.toString(input.setBuilderImageId)); console.log("Set Verifier Selector:", vm.toString(setVerifierSelector)); - console.log("Nitro Root Cert:", vm.toString(nitroRootCert)); - console.log("Nitro Verifier ID:", vm.toString(nitroVerifierId)); - console.log("Nitro Verifier Proof ID:", vm.toString(nitroVerifierProofId)); - console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); - console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); + console.log("Nitro Root Cert:", vm.toString(input.nitroRootCert)); + console.log("Nitro Verifier ID:", vm.toString(input.nitroVerifierId)); + console.log("Nitro Verifier Proof ID:", vm.toString(input.nitroVerifierProofId)); + console.log("TDX Verifier ID:", vm.toString(input.tdxVerifierId)); + console.log("Intel Root CA Hash:", vm.toString(input.intelRootCaHash)); console.log(""); console.log("NOTE: Nitro proofSubmitter is set to owner as placeholder."); console.log(""); vm.startBroadcast(); - (setVerifier, nitroEnclaveVerifier) = _deployNitroVerifier( - owner, nitroRisc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, nitroVerifierProofId - ); - tdxVerifier = _deployTDXVerifier(tdxRisc0VerifierRouter, tdxVerifierId, intelRootCaHash); - - vm.stopBroadcast(); - - console.log("RiscZeroSetVerifier:", setVerifier); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); - console.log("TDXVerifier:", tdxVerifier); - console.log(""); - console.log(">>> Use these verifier addresses for the TDX multiproof deployment <<<"); - - _writeOutput( - setVerifier, - nitroEnclaveVerifier, - tdxVerifier, - nitroRisc0VerifierRouter, - tdxRisc0VerifierRouter, - setBuilderImageId, - setVerifierSelector, - nitroRootCert, - nitroVerifierId, - nitroVerifierProofId, - tdxVerifierId, - intelRootCaHash + address setVerifier = address( + new RiscZeroSetVerifier(IRiscZeroVerifier(input.nitroRisc0VerifierRouter), input.setBuilderImageId, "") ); - } - - function _deployNitroVerifier( - address owner, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 nitroVerifierProofId - ) - internal - returns (address deployedSetVerifier, address deployedNitroVerifier) - { - deployedSetVerifier = - address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ - verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + verifierId: input.nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: input.nitroRisc0VerifierRouter }); - NitroEnclaveVerifier verifier = new NitroEnclaveVerifier( - owner, - NITRO_MAX_TIME_DIFF, + NitroEnclaveVerifier nitroEnclaveVerifierContract = new NitroEnclaveVerifier( + input.owner, + MAX_TIME_DIFF, new bytes32[](0), new uint64[](0), - nitroRootCert, - owner, + input.nitroRootCert, + input.owner, address(0), ZkCoProcessorType.RiscZero, zkConfig, - nitroVerifierProofId + input.nitroVerifierProofId ); - deployedNitroVerifier = address(verifier); + address nitroEnclaveVerifier = address(nitroEnclaveVerifierContract); + nitroEnclaveVerifierContract.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, setVerifier); - bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, deployedSetVerifier); - } - - function _deployTDXVerifier( - address risc0VerifierRouter, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - internal - returns (address deployedTDXVerifier) - { - deployedTDXVerifier = - address(new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, risc0VerifierRouter, tdxVerifierId)); - } + address tdxVerifier = address( + new TDXVerifier(MAX_TIME_DIFF, input.intelRootCaHash, input.tdxRisc0VerifierRouter, input.tdxVerifierId) + ); - function _validateInputs( - address owner, - address nitroRisc0VerifierRouter, - address tdxRisc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - internal - pure - returns (bytes4 setVerifierSelector) - { - require(owner != address(0), "owner must be non-zero"); - require(nitroRisc0VerifierRouter != address(0), "nitroRisc0VerifierRouter must be non-zero"); - require(tdxRisc0VerifierRouter != address(0), "tdxRisc0VerifierRouter must be non-zero"); - require(setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); - require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); - require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); - require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); - require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); + vm.stopBroadcast(); - setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); - } + console.log("RiscZeroSetVerifier:", setVerifier); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + console.log("TDXVerifier:", tdxVerifier); + console.log(""); + console.log(">>> Use these verifier addresses for the TDX multiproof deployment <<<"); - function _writeOutput( - address deployedSetVerifier, - address deployedNitroVerifier, - address deployedTDXVerifier, - address nitroRisc0VerifierRouter, - address tdxRisc0VerifierRouter, - bytes32 setBuilderImageId, - bytes4 setVerifierSelector, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 nitroVerifierProofId, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - internal - { string memory key = "deployment"; - vm.serializeAddress(key, "RiscZeroSetVerifier", deployedSetVerifier); - vm.serializeAddress(key, "NitroEnclaveVerifier", deployedNitroVerifier); - vm.serializeAddress(key, "TDXVerifier", deployedTDXVerifier); - vm.serializeAddress(key, "RiscZeroVerifierRouter", tdxRisc0VerifierRouter); - vm.serializeAddress(key, "NitroRiscZeroVerifierRouter", nitroRisc0VerifierRouter); - vm.serializeAddress(key, "TDXRiscZeroVerifierRouter", tdxRisc0VerifierRouter); - vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); + vm.serializeAddress(key, "RiscZeroSetVerifier", setVerifier); + vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifier); + vm.serializeAddress(key, "TDXVerifier", tdxVerifier); + vm.serializeAddress(key, "NitroRiscZeroVerifierRouter", input.nitroRisc0VerifierRouter); + vm.serializeAddress(key, "TDXRiscZeroVerifierRouter", input.tdxRisc0VerifierRouter); + vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", input.setBuilderImageId); vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); - vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); - vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); - vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); - vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); - vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); - vm.serializeUint(key, "NitroMaxTimeDiff", NITRO_MAX_TIME_DIFF); - string memory json = vm.serializeUint(key, "TDXMaxTimeDiff", TDX_MAX_TIME_DIFF); + vm.serializeBytes32(key, "NitroRootCert", input.nitroRootCert); + vm.serializeBytes32(key, "NitroVerifierId", input.nitroVerifierId); + vm.serializeBytes32(key, "NitroVerifierProofId", input.nitroVerifierProofId); + vm.serializeBytes32(key, "TDXVerifierId", input.tdxVerifierId); + vm.serializeBytes32(key, "IntelRootCaHash", input.intelRootCaHash); + vm.serializeUint(key, "NitroMaxTimeDiff", MAX_TIME_DIFF); + string memory json = vm.serializeUint(key, "TDXMaxTimeDiff", MAX_TIME_DIFF); string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-tee-verifiers.json"); vm.writeJson(json, outPath); From 5e30f2b7b52a628fb8b64da2e920a71efc62ce30 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 16:00:24 -0400 Subject: [PATCH 049/146] clean up DeployTEEVerifiers --- deployments/11155111-tee-verifiers.json | 17 +-- scripts/multiproof/DeployTEEVerifiers.s.sol | 155 -------------------- scripts/multiproof/README.md | 2 +- scripts/multiproof/justfile | 41 ++++-- 4 files changed, 29 insertions(+), 186 deletions(-) delete mode 100644 scripts/multiproof/DeployTEEVerifiers.s.sol diff --git a/deployments/11155111-tee-verifiers.json b/deployments/11155111-tee-verifiers.json index ebe25da12..d9688a14f 100644 --- a/deployments/11155111-tee-verifiers.json +++ b/deployments/11155111-tee-verifiers.json @@ -1,17 +1,4 @@ { - "IntelRootCaHash": "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009", "NitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", - "NitroMaxTimeDiff": 3600, - "NitroRiscZeroVerifierRouter": "0xB121B667dd2cf27F95f9F5107137696F56f188f6", - "NitroRootCert": "0x641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68f79bb5b", - "NitroVerifierId": "0x15051db631d6ed382d957c795a558a0abdd00d0d22a1670455721bc2712d3d6e", - "NitroVerifierProofId": "0x0000000000000000000000000000000000000000000000000000000000000000", - "RiscZeroSetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9", - "RiscZeroSetVerifier": "0xef981185595Ed2a49dEC9B2D5073B88Ef9e00Fd4", - "RiscZeroSetVerifierSelector": "0x242f9d5b00000000000000000000000000000000000000000000000000000000", - "RiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", - "TDXMaxTimeDiff": 3600, - "TDXRiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", - "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", - "TDXVerifierId": "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" -} \ No newline at end of file + "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092" +} diff --git a/scripts/multiproof/DeployTEEVerifiers.s.sol b/scripts/multiproof/DeployTEEVerifiers.s.sol deleted file mode 100644 index 214cff61e..000000000 --- a/scripts/multiproof/DeployTEEVerifiers.s.sol +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/** - * @title DeployTEEVerifiers - * @notice Deploys NitroEnclaveVerifier and TDXVerifier in one broadcast. - * - * Usage: - * - * forge script scripts/multiproof/DeployTEEVerifiers.s.sol:DeployTEEVerifiers \ - * --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" \ - * \ - * \ - * \ - * --rpc-url --broadcast --private-key - * - * The broadcaster must be the owner because this script calls addVerifyRoute() - * on the freshly deployed NitroEnclaveVerifier. - */ - -import { Script } from "forge-std/Script.sol"; -import { console2 as console } from "forge-std/console2.sol"; - -import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; -import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; - -import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; -import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; - -contract DeployTEEVerifiers is Script { - /// @notice Maximum Nitro attestation and TDX quote age accepted by the verifiers. - uint64 internal constant MAX_TIME_DIFF = 3600; - - struct Input { - address owner; - address nitroRisc0VerifierRouter; - address tdxRisc0VerifierRouter; - bytes32 setBuilderImageId; - bytes32 nitroRootCert; - bytes32 nitroVerifierId; - bytes32 nitroVerifierProofId; - bytes32 tdxVerifierId; - bytes32 intelRootCaHash; - } - - function run( - address owner, - address nitroRisc0VerifierRouter, - address tdxRisc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 nitroVerifierProofId, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - public - { - Input memory input = Input({ - owner: owner, - nitroRisc0VerifierRouter: nitroRisc0VerifierRouter, - tdxRisc0VerifierRouter: tdxRisc0VerifierRouter, - setBuilderImageId: setBuilderImageId, - nitroRootCert: nitroRootCert, - nitroVerifierId: nitroVerifierId, - nitroVerifierProofId: nitroVerifierProofId, - tdxVerifierId: tdxVerifierId, - intelRootCaHash: intelRootCaHash - }); - - require(input.owner != address(0), "owner must be non-zero"); - require(input.nitroRisc0VerifierRouter != address(0), "nitroRisc0VerifierRouter must be non-zero"); - require(input.tdxRisc0VerifierRouter != address(0), "tdxRisc0VerifierRouter must be non-zero"); - require(input.setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); - require(input.nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); - require(input.nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); - require(input.tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); - require(input.intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); - - bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(input.setBuilderImageId); - - console.log("=== Deploying TEE Verifiers ==="); - console.log("Owner:", input.owner); - console.log("Nitro RISC Zero Verifier Router:", input.nitroRisc0VerifierRouter); - console.log("TDX RISC Zero Verifier Router:", input.tdxRisc0VerifierRouter); - console.log("Set Builder Image ID:", vm.toString(input.setBuilderImageId)); - console.log("Set Verifier Selector:", vm.toString(setVerifierSelector)); - console.log("Nitro Root Cert:", vm.toString(input.nitroRootCert)); - console.log("Nitro Verifier ID:", vm.toString(input.nitroVerifierId)); - console.log("Nitro Verifier Proof ID:", vm.toString(input.nitroVerifierProofId)); - console.log("TDX Verifier ID:", vm.toString(input.tdxVerifierId)); - console.log("Intel Root CA Hash:", vm.toString(input.intelRootCaHash)); - console.log(""); - console.log("NOTE: Nitro proofSubmitter is set to owner as placeholder."); - console.log(""); - - vm.startBroadcast(); - - address setVerifier = address( - new RiscZeroSetVerifier(IRiscZeroVerifier(input.nitroRisc0VerifierRouter), input.setBuilderImageId, "") - ); - - ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ - verifierId: input.nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: input.nitroRisc0VerifierRouter - }); - - NitroEnclaveVerifier nitroEnclaveVerifierContract = new NitroEnclaveVerifier( - input.owner, - MAX_TIME_DIFF, - new bytes32[](0), - new uint64[](0), - input.nitroRootCert, - input.owner, - address(0), - ZkCoProcessorType.RiscZero, - zkConfig, - input.nitroVerifierProofId - ); - address nitroEnclaveVerifier = address(nitroEnclaveVerifierContract); - nitroEnclaveVerifierContract.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, setVerifier); - - address tdxVerifier = address( - new TDXVerifier(MAX_TIME_DIFF, input.intelRootCaHash, input.tdxRisc0VerifierRouter, input.tdxVerifierId) - ); - - vm.stopBroadcast(); - - console.log("RiscZeroSetVerifier:", setVerifier); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); - console.log("TDXVerifier:", tdxVerifier); - console.log(""); - console.log(">>> Use these verifier addresses for the TDX multiproof deployment <<<"); - - string memory key = "deployment"; - vm.serializeAddress(key, "RiscZeroSetVerifier", setVerifier); - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifier); - vm.serializeAddress(key, "TDXVerifier", tdxVerifier); - vm.serializeAddress(key, "NitroRiscZeroVerifierRouter", input.nitroRisc0VerifierRouter); - vm.serializeAddress(key, "TDXRiscZeroVerifierRouter", input.tdxRisc0VerifierRouter); - vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", input.setBuilderImageId); - vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); - vm.serializeBytes32(key, "NitroRootCert", input.nitroRootCert); - vm.serializeBytes32(key, "NitroVerifierId", input.nitroVerifierId); - vm.serializeBytes32(key, "NitroVerifierProofId", input.nitroVerifierProofId); - vm.serializeBytes32(key, "TDXVerifierId", input.tdxVerifierId); - vm.serializeBytes32(key, "IntelRootCaHash", input.intelRootCaHash); - vm.serializeUint(key, "NitroMaxTimeDiff", MAX_TIME_DIFF); - string memory json = vm.serializeUint(key, "TDXMaxTimeDiff", MAX_TIME_DIFF); - - string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-tee-verifiers.json"); - vm.writeJson(json, outPath); - console.log("Deployment saved to:", outPath); - } -} diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 874993817..8de82f43c 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -254,7 +254,7 @@ forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ --private-key $PRIVATE_KEY ``` -Use `DeployTEEVerifiers.s.sol` with `run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)` to override all Nitro and TDX inputs while still deploying both in one broadcast. +To override all Nitro and TDX inputs while still deploying both, pass the optional args to `deploy-tee-verifiers`. ### Step 2: Deploy the TDX multiproof test stack diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 300816509..158879f5c 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -84,7 +84,7 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nit --account "{{forge_account}}" \ --sender "$owner" -# Deploy both TEE verifier policy contracts in one broadcast. +# Deploy both TEE verifier policy contracts with the existing single-verifier scripts. # # Required args: # nitro_root_cert SHA-256 hash of the AWS Nitro root certificate @@ -95,23 +95,34 @@ deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepoli #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" - owner="$(cast wallet address --account "{{forge_account}}")" - echo "Using {{forge_account}} as owner/deployer: $owner" - forge script scripts/multiproof/DeployTEEVerifiers.s.sol:DeployTEEVerifiers \ - --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" \ - "$owner" \ - "{{nitro_risc0_router}}" \ - "{{tdx_risc0_router}}" \ - "{{set_builder_image_id}}" \ + + just --justfile "{{justfile_path}}" deploy-nitro-verifier \ "{{nitro_root_cert}}" \ "{{nitro_verifier_id}}" \ - "{{nitro_verifier_proof_id}}" \ + "{{nitro_risc0_router}}" \ + "{{set_builder_image_id}}" \ + "{{nitro_verifier_proof_id}}" + + just --justfile "{{justfile_path}}" deploy-tdx-verifier \ + "{{tdx_risc0_router}}" \ "{{tdx_verifier_id}}" \ - "{{intel_root_ca_hash}}" \ - --rpc-url "{{l1_rpc_url}}" \ - --broadcast \ - --account "{{forge_account}}" \ - --sender "$owner" + "{{intel_root_ca_hash}}" + + chain_id="$(cast chain-id --rpc-url "{{l1_rpc_url}}")" + nitro_path="deployments/${chain_id}-nitro-verifier.json" + tdx_path="deployments/${chain_id}-tdx-verifier.json" + out_path="deployments/${chain_id}-tee-verifiers.json" + nitro_verifier="$(jq -r '.NitroEnclaveVerifier // empty' "$nitro_path")" + tdx_verifier="$(jq -r '.TDXVerifier // empty' "$tdx_path")" + + if [[ -z "$nitro_verifier" || -z "$tdx_verifier" ]]; then + echo "Missing verifier address in $nitro_path or $tdx_path" >&2 + exit 1 + fi + + jq -n --arg nitro "$nitro_verifier" --arg tdx "$tdx_verifier" \ + '{NitroEnclaveVerifier: $nitro, TDXVerifier: $tdx}' > "$out_path" + echo "Deployment saved to: $out_path" # Print the Sepolia TDX constants used by these recipes. tdx-sepolia-config: From 4b5009813dbd1d2b7ab47bee4d9e2b16067eaa85 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 16:32:28 -0400 Subject: [PATCH 050/146] clean up DeployTDXVerifier --- scripts/multiproof/DeployTDXVerifier.s.sol | 25 ---------------------- 1 file changed, 25 deletions(-) diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index e8e70e808..910383f0c 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -30,17 +30,6 @@ contract DeployTDXVerifier is Script { /// @param tdxVerifierId RISC Zero image ID for the TDX DCAP verifier guest. /// @param intelRootCaHash Hash of the trusted Intel root CA consumed by the guest. function run(address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) public { - require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); - require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); - require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); - - console.log("=== Deploying TDXVerifier ==="); - console.log("RISC Zero Verifier Router:", risc0VerifierRouter); - console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); - console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); - console.log("Max Time Diff:", TDX_MAX_TIME_DIFF); - console.log(""); - vm.startBroadcast(); address tdxVerifier = @@ -49,20 +38,6 @@ contract DeployTDXVerifier is Script { vm.stopBroadcast(); console.log("TDXVerifier:", tdxVerifier); - console.log(""); - console.log(">>> Use this address as the DeployDevWithTDX.s.sol argument <<<"); - - _writeOutput(tdxVerifier, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); - } - - function _writeOutput( - address tdxVerifier, - address risc0VerifierRouter, - bytes32 tdxVerifierId, - bytes32 intelRootCaHash - ) - internal - { string memory key = "deployment"; vm.serializeAddress(key, "TDXVerifier", tdxVerifier); vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); From e64855ada0bf59cf591cddb4f4f3b6ead0f90991 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 16:47:00 -0400 Subject: [PATCH 051/146] clean up DeployTDXVerifier --- scripts/multiproof/DeployTDXVerifier.s.sol | 24 ++-------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol index 910383f0c..c0d8a8832 100644 --- a/scripts/multiproof/DeployTDXVerifier.s.sol +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -8,45 +8,25 @@ pragma solidity ^0.8.20; * This script is separated from DeployDevWithTDX.s.sol because TDXVerifier imports * verifier interfaces that require Solidity ^0.8.20, while the multiproof stack is * pinned to Solidity 0.8.15. - * - * Usage: - * - * forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ - * --sig "run(address,bytes32,bytes32)" \ - * \ - * --rpc-url --broadcast --private-key */ import { Script } from "forge-std/Script.sol"; -import { console2 as console } from "forge-std/console2.sol"; import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract DeployTDXVerifier is Script { - /// @notice Maximum TDX quote age accepted by TDXVerifier. - uint64 internal constant TDX_MAX_TIME_DIFF = 3600; - /// @param risc0VerifierRouter Existing RISC Zero verifier router. /// @param tdxVerifierId RISC Zero image ID for the TDX DCAP verifier guest. /// @param intelRootCaHash Hash of the trusted Intel root CA consumed by the guest. function run(address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) public { vm.startBroadcast(); - address tdxVerifier = - address(new TDXVerifier(TDX_MAX_TIME_DIFF, intelRootCaHash, risc0VerifierRouter, tdxVerifierId)); + address tdxVerifier = address(new TDXVerifier(1 hours, intelRootCaHash, risc0VerifierRouter, tdxVerifierId)); vm.stopBroadcast(); - console.log("TDXVerifier:", tdxVerifier); - string memory key = "deployment"; - vm.serializeAddress(key, "TDXVerifier", tdxVerifier); - vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); - vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); - vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); - string memory json = vm.serializeUint(key, "MaxTimeDiff", TDX_MAX_TIME_DIFF); - + string memory json = vm.serializeAddress("deployment", "TDXVerifier", tdxVerifier); string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-tdx-verifier.json"); vm.writeJson(json, outPath); - console.log("Deployment saved to:", outPath); } } From 879c57180a185314bd2a6e423ae2501bf5e7d310 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 17:05:59 -0400 Subject: [PATCH 052/146] clean up DeployNitroVerifier --- scripts/multiproof/DeployNitroVerifier.s.sol | 136 +++++-------------- scripts/multiproof/README.md | 3 +- 2 files changed, 33 insertions(+), 106 deletions(-) diff --git a/scripts/multiproof/DeployNitroVerifier.s.sol b/scripts/multiproof/DeployNitroVerifier.s.sol index f84289df6..1c336a069 100644 --- a/scripts/multiproof/DeployNitroVerifier.s.sol +++ b/scripts/multiproof/DeployNitroVerifier.s.sol @@ -12,15 +12,12 @@ pragma solidity ^0.8.20; * Usage: * * forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ - * --sig "run(address,address,bytes32,bytes32,bytes32)" \ + * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ * \ - * \ + * \ * --rpc-url --broadcast --private-key * - * If using batched Nitro proofs, use the overload that also supplies - * : - * - * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" + * Use bytes32(0) for unless using batched Nitro proofs. * * The broadcaster must be the owner because this script calls addVerifyRoute() * on the freshly deployed NitroEnclaveVerifier. @@ -40,28 +37,13 @@ import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol contract DeployNitroVerifier is Script { /// @notice Maximum Nitro attestation age accepted by NitroEnclaveVerifier. - uint64 internal constant NITRO_MAX_TIME_DIFF = 3600; - - address public setVerifier; - address public nitroEnclaveVerifier; + uint64 internal constant NITRO_MAX_TIME_DIFF = 1 hours; /// @param owner Owner for NitroEnclaveVerifier. Must be the broadcaster for route setup. /// @param risc0VerifierRouter Existing RISC Zero verifier router. /// @param setBuilderImageId RISC Zero set builder image ID. /// @param nitroRootCert SHA-256 hash of the AWS Nitro root certificate. /// @param nitroVerifierId RISC Zero image ID for the Nitro attestation verifier guest. - function run( - address owner, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId - ) - public - { - run(owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, bytes32(0)); - } - /// @param nitroVerifierProofId Optional verifier proof ID used by Nitro batch verification. function run( address owner, @@ -73,8 +55,13 @@ contract DeployNitroVerifier is Script { ) public { - bytes4 setVerifierSelector = - _validateInputs(owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId); + require(owner != address(0), "owner must be non-zero"); + require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); + require(setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); + require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); + require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); + + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); console.log("=== Deploying NitroEnclaveVerifier ==="); console.log("Owner:", owner); @@ -90,49 +77,22 @@ contract DeployNitroVerifier is Script { console.log(" DeployDevWithTDX.s.sol updates it to TEEProverRegistry."); console.log(""); - vm.startBroadcast(); - - (setVerifier, nitroEnclaveVerifier) = _deployNitroVerifier( - owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, nitroVerifierProofId - ); - - vm.stopBroadcast(); - - console.log("RiscZeroSetVerifier:", setVerifier); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); - console.log(""); - console.log(">>> Use this NitroEnclaveVerifier address in the deploy config <<<"); - - _writeOutput( - setVerifier, - nitroEnclaveVerifier, - risc0VerifierRouter, - setBuilderImageId, - setVerifierSelector, - nitroRootCert, - nitroVerifierId, - nitroVerifierProofId - ); - } - - function _deployNitroVerifier( - address owner, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 nitroVerifierProofId - ) - internal - returns (address deployedSetVerifier, address deployedNitroVerifier) - { - deployedSetVerifier = - address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); + string memory key = "deployment"; + vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); + vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); + vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); + vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); + vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); + vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter }); + vm.startBroadcast(); + + address setVerifier = + address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); NitroEnclaveVerifier verifier = new NitroEnclaveVerifier( owner, NITRO_MAX_TIME_DIFF, @@ -145,53 +105,19 @@ contract DeployNitroVerifier is Script { zkConfig, nitroVerifierProofId ); - deployedNitroVerifier = address(verifier); + address nitroEnclaveVerifier = address(verifier); - bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, deployedSetVerifier); - } + verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, setVerifier); - function _validateInputs( - address owner, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId - ) - internal - pure - returns (bytes4 setVerifierSelector) - { - require(owner != address(0), "owner must be non-zero"); - require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); - require(setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); - require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); - require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); + vm.stopBroadcast(); - setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); - } + console.log("RiscZeroSetVerifier:", setVerifier); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + console.log(""); + console.log(">>> Use this NitroEnclaveVerifier address in the deploy config <<<"); - function _writeOutput( - address deployedSetVerifier, - address deployedNitroVerifier, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes4 setVerifierSelector, - bytes32 nitroRootCert, - bytes32 nitroVerifierId, - bytes32 nitroVerifierProofId - ) - internal - { - string memory key = "deployment"; - vm.serializeAddress(key, "RiscZeroSetVerifier", deployedSetVerifier); - vm.serializeAddress(key, "NitroEnclaveVerifier", deployedNitroVerifier); - vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); - vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); - vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); - vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); - vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); - vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); + vm.serializeAddress(key, "RiscZeroSetVerifier", setVerifier); + vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifier); string memory json = vm.serializeUint(key, "MaxTimeDiff", NITRO_MAX_TIME_DIFF); string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-nitro-verifier.json"); diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 8de82f43c..f246e68cf 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -243,12 +243,13 @@ To override Nitro inputs manually: ```bash forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ - --sig "run(address,address,bytes32,bytes32,bytes32)" \ + --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ $OWNER \ $NITRO_RISC0_VERIFIER_ROUTER \ $RISC0_SET_BUILDER_IMAGE_ID \ $NITRO_ROOT_CERT \ $NITRO_VERIFIER_ID \ + $NITRO_VERIFIER_PROOF_ID \ --rpc-url $L1_RPC_URL \ --broadcast \ --private-key $PRIVATE_KEY From 57d10d0fa382b1f26064f4463ebb029b402d276b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 17:12:28 -0400 Subject: [PATCH 053/146] clean up DeployNitroVerifier --- scripts/multiproof/DeployNitroVerifier.s.sol | 52 ++------------------ 1 file changed, 4 insertions(+), 48 deletions(-) diff --git a/scripts/multiproof/DeployNitroVerifier.s.sol b/scripts/multiproof/DeployNitroVerifier.s.sol index 1c336a069..cfd7f7df7 100644 --- a/scripts/multiproof/DeployNitroVerifier.s.sol +++ b/scripts/multiproof/DeployNitroVerifier.s.sol @@ -8,22 +8,6 @@ pragma solidity ^0.8.20; * This script is separated from the main multiproof deployment scripts because * NitroEnclaveVerifier imports verifier interfaces that require Solidity ^0.8.20, * while the multiproof stack is pinned to Solidity 0.8.15. - * - * Usage: - * - * forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ - * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ - * \ - * \ - * --rpc-url --broadcast --private-key - * - * Use bytes32(0) for unless using batched Nitro proofs. - * - * The broadcaster must be the owner because this script calls addVerifyRoute() - * on the freshly deployed NitroEnclaveVerifier. - * - * After running DeployDevWithTDX.s.sol, the Nitro verifier's proofSubmitter is - * updated to the deployed TEEProverRegistry. */ import { Script } from "forge-std/Script.sol"; @@ -36,9 +20,6 @@ import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; contract DeployNitroVerifier is Script { - /// @notice Maximum Nitro attestation age accepted by NitroEnclaveVerifier. - uint64 internal constant NITRO_MAX_TIME_DIFF = 1 hours; - /// @param owner Owner for NitroEnclaveVerifier. Must be the broadcaster for route setup. /// @param risc0VerifierRouter Existing RISC Zero verifier router. /// @param setBuilderImageId RISC Zero set builder image ID. @@ -63,28 +44,6 @@ contract DeployNitroVerifier is Script { bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); - console.log("=== Deploying NitroEnclaveVerifier ==="); - console.log("Owner:", owner); - console.log("RISC Zero Verifier Router:", risc0VerifierRouter); - console.log("Set Builder Image ID:", vm.toString(setBuilderImageId)); - console.log("Set Verifier Selector:", vm.toString(setVerifierSelector)); - console.log("Nitro Root Cert:", vm.toString(nitroRootCert)); - console.log("Nitro Verifier ID:", vm.toString(nitroVerifierId)); - console.log("Nitro Verifier Proof ID:", vm.toString(nitroVerifierProofId)); - console.log("Max Time Diff:", NITRO_MAX_TIME_DIFF); - console.log(""); - console.log("NOTE: proofSubmitter is set to owner as placeholder."); - console.log(" DeployDevWithTDX.s.sol updates it to TEEProverRegistry."); - console.log(""); - - string memory key = "deployment"; - vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); - vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); - vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); - vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); - vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); - vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); - ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter }); @@ -95,7 +54,7 @@ contract DeployNitroVerifier is Script { address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); NitroEnclaveVerifier verifier = new NitroEnclaveVerifier( owner, - NITRO_MAX_TIME_DIFF, + 1 hours, new bytes32[](0), new uint64[](0), nitroRootCert, @@ -105,20 +64,17 @@ contract DeployNitroVerifier is Script { zkConfig, nitroVerifierProofId ); - address nitroEnclaveVerifier = address(verifier); verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, setVerifier); vm.stopBroadcast(); console.log("RiscZeroSetVerifier:", setVerifier); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); - console.log(""); - console.log(">>> Use this NitroEnclaveVerifier address in the deploy config <<<"); + console.log("NitroEnclaveVerifier:", address(verifier)); + string memory key = "deployment"; vm.serializeAddress(key, "RiscZeroSetVerifier", setVerifier); - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifier); - string memory json = vm.serializeUint(key, "MaxTimeDiff", NITRO_MAX_TIME_DIFF); + string memory json = vm.serializeAddress(key, "NitroEnclaveVerifier", address(verifier)); string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-nitro-verifier.json"); vm.writeJson(json, outPath); From 0b2e1f5a2a675bc31f4bc13f2fc5ef65e643cc2e Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 17:19:45 -0400 Subject: [PATCH 054/146] clean up DeployNitroVerifier --- scripts/multiproof/DeployNitroVerifier.s.sol | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/scripts/multiproof/DeployNitroVerifier.s.sol b/scripts/multiproof/DeployNitroVerifier.s.sol index cfd7f7df7..dbf434725 100644 --- a/scripts/multiproof/DeployNitroVerifier.s.sol +++ b/scripts/multiproof/DeployNitroVerifier.s.sol @@ -11,7 +11,6 @@ pragma solidity ^0.8.20; */ import { Script } from "forge-std/Script.sol"; -import { console2 as console } from "forge-std/console2.sol"; import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; @@ -42,8 +41,6 @@ contract DeployNitroVerifier is Script { require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); - bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); - ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter }); @@ -65,19 +62,16 @@ contract DeployNitroVerifier is Script { nitroVerifierProofId ); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, setVerifier); + verifier.addVerifyRoute( + ZkCoProcessorType.RiscZero, RiscZeroSetVerifierLib.selector(setBuilderImageId), setVerifier + ); vm.stopBroadcast(); - console.log("RiscZeroSetVerifier:", setVerifier); - console.log("NitroEnclaveVerifier:", address(verifier)); - - string memory key = "deployment"; - vm.serializeAddress(key, "RiscZeroSetVerifier", setVerifier); - string memory json = vm.serializeAddress(key, "NitroEnclaveVerifier", address(verifier)); + vm.serializeAddress("deployment", "RiscZeroSetVerifier", setVerifier); + string memory json = vm.serializeAddress("deployment", "NitroEnclaveVerifier", address(verifier)); string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-nitro-verifier.json"); vm.writeJson(json, outPath); - console.log("Deployment saved to:", outPath); } } From bf1b3f267b41481feab417bef5bce5e8a1bd435f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 17:31:34 -0400 Subject: [PATCH 055/146] clean up DeployDevWithTDX --- scripts/multiproof/DeployDevBase.s.sol | 16 +- scripts/multiproof/DeployDevWithTDX.s.sol | 224 +++++----------------- 2 files changed, 62 insertions(+), 178 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 24d47095a..1cde3977f 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -94,11 +94,16 @@ abstract contract DeployDevBase is Script { Proxy teeProxy = new Proxy(msg.sender); teeProxy.upgradeToAndCall( - teeRegistryImpl, abi.encodeCall(TEEProverRegistry.initialize, (owner, owner, initialProposers, gameType)) + teeRegistryImpl, + abi.encodeCall( + TEEProverRegistry.initialize, (owner, _teeRegistrationManager(owner), initialProposers, gameType) + ) ); teeProxy.changeAdmin(address(0xdead)); teeProverRegistryProxy = address(teeProxy); + _afterTEERegistryDeploy(); + teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); } @@ -151,8 +156,8 @@ abstract contract DeployDevBase is Script { console.log("Deployment saved to:", outPath); } - function _blockInterval() internal pure virtual returns (uint256); - function _intermediateBlockInterval() internal pure virtual returns (uint256); + function _blockInterval() internal view virtual returns (uint256); + function _intermediateBlockInterval() internal view virtual returns (uint256); function _initBond() internal pure virtual returns (uint256); function _outputSuffix() internal pure virtual returns (string memory); function _deployTEERegistryImpl() internal virtual returns (address); @@ -161,4 +166,9 @@ abstract contract DeployDevBase is Script { function _preflight() internal virtual { } function _serializeExtra(string memory key) internal virtual { } + + function _teeRegistrationManager(address owner) internal view virtual returns (address) { + return owner; + } + function _afterTEERegistryDeploy() internal virtual { } } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index a48988039..7d537485f 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -1,77 +1,22 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -/** - * @title DeployDevWithTDX - * @notice Development deployment using the TDX signer-registration path. - * - * This deploys the same local multiproof testing infrastructure as the existing - * dev scripts, but configures TEEProverRegistry for TDX signer registration. Deploy - * TDXVerifier first with DeployTDXVerifier.s.sol and pass its address to run(). - * The NitroEnclaveVerifier can either come from the deploy config or be passed - * explicitly to the five-argument run() overload. - * The default run(address) entrypoint configures DEFAULT_TDX_REGISTRATION_MANAGER - * as the registry manager so it can submit TDX signer registrations. - */ +import { console2 as console } from "lib/forge-std/src/console2.sol"; +import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { Proxy } from "src/universal/Proxy.sol"; -import { Script } from "forge-std/Script.sol"; -import { console2 as console } from "forge-std/console2.sol"; -import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; -import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; -import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; -import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { DisputeGameFactory } from "src/L1/proofs/DisputeGameFactory.sol"; -import { GameType, Hash } from "src/libraries/bridge/Types.sol"; - -import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; -import { Config } from "scripts/libraries/Config.sol"; -import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; - -import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; -import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; -import { MockVerifier } from "test/mocks/MockVerifier.sol"; -import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; -import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; -import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; -import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; +import { DeployDevBase } from "./DeployDevBase.s.sol"; -contract DeployDevWithTDX is Script { +/// @notice Development deployment using the TDX signer-registration path. +contract DeployDevWithTDX is DeployDevBase { uint256 public constant INIT_BOND = 0.00001 ether; - address public constant DEFAULT_TDX_REGISTRATION_MANAGER = 0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7; - - DeployConfig public constant cfg = - DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); address public nitroEnclaveVerifierAddr; address public tdxVerifierAddr; address public tdxRegistrationManager; - address public teeProverRegistryProxy; - address public teeVerifier; - address public disputeGameFactory; - IAnchorStateRegistry public mockAnchorRegistry; - address public mockDelayedWETH; - address public aggregateVerifier; - Hash public startingAnchorRoot; - uint256 public startingAnchorBlockNumber; - - function setUp() public { - DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); - cfg.read(Config.deployConfigPath()); - } - - function run(address tdxVerifier) public { - run(tdxVerifier, DEFAULT_TDX_REGISTRATION_MANAGER); - } - - function run(address tdxVerifier, address registrationManager) public { - run(tdxVerifier, registrationManager, cfg.multiproofGenesisOutputRoot(), cfg.multiproofGenesisBlockNumber()); - } function run( address tdxVerifier, @@ -94,118 +39,74 @@ contract DeployDevWithTDX is Script { public { nitroEnclaveVerifierAddr = nitroEnclaveVerifier; - require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); - require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be non-zero"); - require(registrationManager != address(0), "registrationManager must be non-zero"); - require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); tdxVerifierAddr = tdxVerifier; tdxRegistrationManager = registrationManager; - startingAnchorRoot = Hash.wrap(asrStartingOutputRoot); - startingAnchorBlockNumber = asrStartingBlockNumber; - - GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); + run(asrStartingOutputRoot, asrStartingBlockNumber); + } - console.log("=== Deploying Dev Infrastructure (WITH TDX) ==="); - console.log("Chain ID:", block.chainid); - console.log("Owner:", cfg.finalSystemOwner()); - console.log("TEE Proposer:", cfg.teeProposer()); - console.log("TEE Challenger:", cfg.teeChallenger()); - console.log("Game Type:", cfg.multiproofGameType()); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); - console.log("TDXVerifier:", tdxVerifierAddr); - console.log("TDX Registration Manager:", tdxRegistrationManager); - console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); - console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); - console.log(""); - console.log("NOTE: NitroEnclaveVerifier owner must be the broadcaster/finalSystemOwner."); + function _blockInterval() internal view override returns (uint256) { + return cfg.multiproofBlockInterval(); + } - vm.startBroadcast(); + function _intermediateBlockInterval() internal view override returns (uint256) { + return cfg.multiproofIntermediateBlockInterval(); + } - _deployInfrastructure(gameType); - _deployTDXContracts(gameType); - _deployAggregateVerifier(gameType); + function _initBond() internal pure override returns (uint256) { + return INIT_BOND; + } - vm.stopBroadcast(); + function _outputSuffix() internal pure override returns (string memory) { + return "-dev-with-tdx.json"; + } - _printSummary(); - _writeOutput(); + function _preflight() internal view override { + require(tdxVerifierAddr != address(0), "tdxVerifier must be non-zero"); + require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be non-zero"); + require(tdxRegistrationManager != address(0), "registrationManager must be non-zero"); } - function _deployTDXContracts(GameType gameType) internal { - address owner = cfg.finalSystemOwner(); - address registryImpl = address( + function _deployTEERegistryImpl() internal override returns (address) { + return address( new DevTEEProverRegistry( INitroEnclaveVerifier(nitroEnclaveVerifierAddr), ITDXVerifier(tdxVerifierAddr), IDisputeGameFactory(disputeGameFactory) ) ); + } - address[] memory initialProposers = new address[](2); - initialProposers[0] = cfg.teeProposer(); - initialProposers[1] = cfg.teeChallenger(); - - Proxy registryProxy = new Proxy(msg.sender); - registryProxy.upgradeToAndCall( - registryImpl, - abi.encodeCall(TEEProverRegistry.initialize, (owner, tdxRegistrationManager, initialProposers, gameType)) - ); - registryProxy.changeAdmin(address(0xdead)); - teeProverRegistryProxy = address(registryProxy); + function _teeRegistrationManager(address) internal view override returns (address) { + return tdxRegistrationManager; + } + function _afterTEERegistryDeploy() internal override { INitroEnclaveVerifier(nitroEnclaveVerifierAddr).setProofSubmitter(teeProverRegistryProxy); - - teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); } - function _deployInfrastructure(GameType gameType) internal { - address factoryImpl = address(new DisputeGameFactory()); - MinimalProxyAdmin proxyAdmin = new MinimalProxyAdmin(cfg.finalSystemOwner()); - - Proxy proxy = new Proxy(msg.sender); - proxy.upgradeTo(factoryImpl); - DisputeGameFactory(address(proxy)).initialize(msg.sender); - proxy.changeAdmin(address(proxyAdmin)); - disputeGameFactory = address(proxy); - - MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); - mockAnchorRegistry = IAnchorStateRegistry(address(asr)); - asr.initialize(disputeGameFactory, startingAnchorRoot, startingAnchorBlockNumber, gameType); + function _serializeExtra(string memory key) internal override { + vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); + vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); } - function _deployAggregateVerifier(GameType gameType) internal { - address zkVerifier = address(new MockVerifier(mockAnchorRegistry)); - mockDelayedWETH = address(new MockDelayedWETH()); - - AggregateVerifier.ZkHashes memory zkHashes = - AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }); - - aggregateVerifier = address( - new AggregateVerifier( - gameType, - mockAnchorRegistry, - IDelayedWETH(payable(mockDelayedWETH)), - IVerifier(teeVerifier), - IVerifier(zkVerifier), - cfg.teeNitroImageHash(), - cfg.teeTdxImageHash(), - zkHashes, - cfg.multiproofConfigHash(), - cfg.l2ChainId(), - cfg.multiproofBlockInterval(), - cfg.multiproofIntermediateBlockInterval() - ) - ); - - DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); - factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); - factory.setInitBond(gameType, INIT_BOND); - if (factory.owner() != cfg.finalSystemOwner()) { - factory.transferOwnership(cfg.finalSystemOwner()); - } + function _logHeader() internal view override { + console.log("=== Deploying Dev Infrastructure (WITH TDX) ==="); + console.log("Chain ID:", block.chainid); + console.log("Owner:", cfg.finalSystemOwner()); + console.log("TEE Proposer:", cfg.teeProposer()); + console.log("TEE Challenger:", cfg.teeChallenger()); + console.log("Game Type:", cfg.multiproofGameType()); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); + console.log("TDXVerifier:", tdxVerifierAddr); + console.log("TDX Registration Manager:", tdxRegistrationManager); + console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); + console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); + console.log(""); + console.log("NOTE: NitroEnclaveVerifier owner must be the broadcaster/finalSystemOwner."); } - function _printSummary() internal view { + function _printSummary() internal view override { console.log("\n========================================"); console.log(" DEV DEPLOYMENT COMPLETE (TDX)"); console.log("========================================"); @@ -228,32 +129,5 @@ contract DeployDevWithTDX is Script { console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); - console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); - console.log("\n cast send", teeProverRegistryProxy); - console.log(' "registerSigner(bytes,bytes)" '); - console.log(" --private-key --rpc-url "); - console.log("\n cast send", teeProverRegistryProxy); - console.log(' "registerTDXSigner(bytes,bytes)" '); - console.log(" --private-key --rpc-url "); - console.log("\n========================================\n"); - } - - function _writeOutput() internal { - string memory key = "deployment"; - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); - vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); - vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); - vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); - vm.serializeAddress(key, "TEEVerifier", teeVerifier); - vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); - vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); - vm.serializeBytes32(key, "ASRStartingOutputRoot", startingAnchorRoot.raw()); - vm.serializeUint(key, "ASRStartingBlockNumber", startingAnchorBlockNumber); - vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); - string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); - - string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-dev-with-tdx.json"); - vm.writeJson(json, outPath); - console.log("Deployment saved to:", outPath); } } From 86c7f0671be8f428177c81a829de3ef41f0ea009 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 17:37:31 -0400 Subject: [PATCH 056/146] clean up DeployDevWithTDX --- scripts/multiproof/DeployDevWithTDX.s.sol | 24 +++++------------------ 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 7d537485f..a9d4644cf 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -12,11 +12,9 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @notice Development deployment using the TDX signer-registration path. contract DeployDevWithTDX is DeployDevBase { - uint256 public constant INIT_BOND = 0.00001 ether; - - address public nitroEnclaveVerifierAddr; - address public tdxVerifierAddr; - address public tdxRegistrationManager; + address internal nitroEnclaveVerifierAddr; + address internal tdxVerifierAddr; + address internal tdxRegistrationManager; function run( address tdxVerifier, @@ -53,7 +51,7 @@ contract DeployDevWithTDX is DeployDevBase { } function _initBond() internal pure override returns (uint256) { - return INIT_BOND; + return 0.00001 ether; } function _outputSuffix() internal pure override returns (string memory) { @@ -90,20 +88,8 @@ contract DeployDevWithTDX is DeployDevBase { vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); } - function _logHeader() internal view override { + function _logHeader() internal pure override { console.log("=== Deploying Dev Infrastructure (WITH TDX) ==="); - console.log("Chain ID:", block.chainid); - console.log("Owner:", cfg.finalSystemOwner()); - console.log("TEE Proposer:", cfg.teeProposer()); - console.log("TEE Challenger:", cfg.teeChallenger()); - console.log("Game Type:", cfg.multiproofGameType()); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); - console.log("TDXVerifier:", tdxVerifierAddr); - console.log("TDX Registration Manager:", tdxRegistrationManager); - console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); - console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); - console.log(""); - console.log("NOTE: NitroEnclaveVerifier owner must be the broadcaster/finalSystemOwner."); } function _printSummary() internal view override { From d353c367e3724d8a05ec67c610860ffe48b707ce Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 17:43:53 -0400 Subject: [PATCH 057/146] clean up DeployDevWithTDX --- scripts/multiproof/DeployDevBase.s.sol | 28 ++++++--- scripts/multiproof/DeployDevWithNitro.s.sol | 5 -- scripts/multiproof/DeployDevWithTDX.s.sol | 56 +---------------- scripts/multiproof/README.md | 16 ++--- scripts/multiproof/justfile | 70 ++++----------------- 5 files changed, 35 insertions(+), 140 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 1cde3977f..2ba1afa58 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -95,9 +95,7 @@ abstract contract DeployDevBase is Script { Proxy teeProxy = new Proxy(msg.sender); teeProxy.upgradeToAndCall( teeRegistryImpl, - abi.encodeCall( - TEEProverRegistry.initialize, (owner, _teeRegistrationManager(owner), initialProposers, gameType) - ) + abi.encodeCall(TEEProverRegistry.initialize, (owner, _teeRegistrationManager(), initialProposers, gameType)) ); teeProxy.changeAdmin(address(0xdead)); teeProverRegistryProxy = address(teeProxy); @@ -156,19 +154,29 @@ abstract contract DeployDevBase is Script { console.log("Deployment saved to:", outPath); } - function _blockInterval() internal view virtual returns (uint256); - function _intermediateBlockInterval() internal view virtual returns (uint256); - function _initBond() internal pure virtual returns (uint256); + function _blockInterval() internal view virtual returns (uint256) { + return cfg.multiproofBlockInterval(); + } + + function _intermediateBlockInterval() internal view virtual returns (uint256) { + return cfg.multiproofIntermediateBlockInterval(); + } + + function _initBond() internal pure virtual returns (uint256) { + return 0.00001 ether; + } + function _outputSuffix() internal pure virtual returns (string memory); function _deployTEERegistryImpl() internal virtual returns (address); - function _logHeader() internal view virtual; - function _printSummary() internal view virtual; function _preflight() internal virtual { } function _serializeExtra(string memory key) internal virtual { } + function _logHeader() internal view virtual { } + function _printSummary() internal view virtual { } - function _teeRegistrationManager(address owner) internal view virtual returns (address) { - return owner; + function _teeRegistrationManager() internal view virtual returns (address) { + return cfg.finalSystemOwner(); } + function _afterTEERegistryDeploy() internal virtual { } } diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index ec40a6463..3ebb66623 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -17,7 +17,6 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; contract DeployDevWithNitro is DeployDevBase { uint256 public constant BLOCK_INTERVAL = 600; uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 30; - uint256 public constant INIT_BOND = 0.00001 ether; address public nitroEnclaveVerifierAddr; address public tdxVerifierAddr; @@ -30,10 +29,6 @@ contract DeployDevWithNitro is DeployDevBase { return INTERMEDIATE_BLOCK_INTERVAL; } - function _initBond() internal pure override returns (uint256) { - return INIT_BOND; - } - function _outputSuffix() internal pure override returns (string memory) { return "-dev-with-nitro.json"; } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index a9d4644cf..36850fadc 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { console2 as console } from "lib/forge-std/src/console2.sol"; - import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; @@ -16,17 +14,6 @@ contract DeployDevWithTDX is DeployDevBase { address internal tdxVerifierAddr; address internal tdxRegistrationManager; - function run( - address tdxVerifier, - address registrationManager, - bytes32 asrStartingOutputRoot, - uint256 asrStartingBlockNumber - ) - public - { - run(cfg.nitroEnclaveVerifier(), tdxVerifier, registrationManager, asrStartingOutputRoot, asrStartingBlockNumber); - } - function run( address nitroEnclaveVerifier, address tdxVerifier, @@ -42,18 +29,6 @@ contract DeployDevWithTDX is DeployDevBase { run(asrStartingOutputRoot, asrStartingBlockNumber); } - function _blockInterval() internal view override returns (uint256) { - return cfg.multiproofBlockInterval(); - } - - function _intermediateBlockInterval() internal view override returns (uint256) { - return cfg.multiproofIntermediateBlockInterval(); - } - - function _initBond() internal pure override returns (uint256) { - return 0.00001 ether; - } - function _outputSuffix() internal pure override returns (string memory) { return "-dev-with-tdx.json"; } @@ -74,7 +49,7 @@ contract DeployDevWithTDX is DeployDevBase { ); } - function _teeRegistrationManager(address) internal view override returns (address) { + function _teeRegistrationManager() internal view override returns (address) { return tdxRegistrationManager; } @@ -87,33 +62,4 @@ contract DeployDevWithTDX is DeployDevBase { vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); } - - function _logHeader() internal pure override { - console.log("=== Deploying Dev Infrastructure (WITH TDX) ==="); - } - - function _printSummary() internal view override { - console.log("\n========================================"); - console.log(" DEV DEPLOYMENT COMPLETE (TDX)"); - console.log("========================================"); - console.log("\nTDX Contracts:"); - console.log(" NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); - console.log(" TDXVerifier:", tdxVerifierAddr); - console.log(" TEEProverRegistry:", teeProverRegistryProxy); - console.log(" TDX Registration Manager:", tdxRegistrationManager); - console.log(" TEEVerifier:", teeVerifier); - console.log("\nInfrastructure:"); - console.log(" DisputeGameFactory:", disputeGameFactory); - console.log(" AnchorStateRegistry (mock):", address(mockAnchorRegistry)); - console.log(" ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); - console.log(" ASR Starting L2 Block:", startingAnchorBlockNumber); - console.log(" DelayedWETH (mock):", mockDelayedWETH); - console.log("\nGame:"); - console.log(" AggregateVerifier:", aggregateVerifier); - console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); - console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); - console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); - console.log("========================================"); - } } diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index f246e68cf..b122fe397 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -259,21 +259,14 @@ To override all Nitro and TDX inputs while still deploying both, pass the option ### Step 2: Deploy the TDX multiproof test stack -Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. +Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `NitroEnclaveVerifier` and `TDXVerifier` addresses from Step 1. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. -The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the resolved output root and L2 block through `run(address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, and `ASR_ANCHOR_BLOCK_LOOKBACK` to anchor a fixed number of L2 blocks behind head. For a fixed anchor, set both `ASR_ANCHOR_OUTPUT_ROOT` and `ASR_ANCHOR_BLOCK_NUMBER`. +The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the verifier addresses, resolved output root, and L2 block through `run(address,address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, and `ASR_ANCHOR_BLOCK_LOOKBACK` to anchor a fixed number of L2 blocks behind head. For a fixed anchor, set both `ASR_ANCHOR_OUTPUT_ROOT` and `ASR_ANCHOR_BLOCK_NUMBER`. ```bash -just --justfile scripts/multiproof/justfile deploy-tdx-stack $TDX_VERIFIER -``` - -If the Nitro verifier in `deploy-config/sepolia.json` is owned by another account, do not edit the shared Sepolia deploy -config. Pass both verifier addresses explicitly instead: - -```bash -just --justfile scripts/multiproof/justfile deploy-tdx-stack-with-verifiers $NITRO_VERIFIER $TDX_VERIFIER +just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER ``` To override the manager manually, use: @@ -286,7 +279,8 @@ ASR_ANCHOR_OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast to-hex $ASR_ANCH --rpc-url $L2_OUTPUT_ROOT_RPC_URL | jq -r '.outputRoot') forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ - --sig "run(address,address,bytes32,uint256)" \ + --sig "run(address,address,address,bytes32,uint256)" \ + $NITRO_VERIFIER \ $TDX_VERIFIER \ $TDX_REGISTRATION_MANAGER \ $ASR_ANCHOR_OUTPUT_ROOT \ diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 158879f5c..dd7e9025c 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -189,13 +189,19 @@ deploy-sepolia-tdx-dev tdx_manager=sepolia_tdx_registration_manager deploy_confi set -euo pipefail cd "{{repo_root}}" + nitro_verifier="$(jq -r '.nitroEnclaveVerifier // empty' "{{deploy_config}}")" tdx_verifier="$(jq -r '.tdxVerifier // empty' "{{deploy_config}}")" + if [[ -z "$nitro_verifier" || "$nitro_verifier" == "null" ]]; then + echo "Missing nitroEnclaveVerifier in {{deploy_config}}" >&2 + exit 1 + fi if [[ -z "$tdx_verifier" || "$tdx_verifier" == "null" ]]; then echo "Missing tdxVerifier in {{deploy_config}}" >&2 exit 1 fi just --justfile "{{justfile_path}}" deploy-tdx-stack \ + "$nitro_verifier" \ "$tdx_verifier" \ "{{tdx_manager}}" \ "{{deploy_config}}" \ @@ -256,63 +262,10 @@ run-sepolia-tdx-dev-offchain: # ASR_ANCHOR_OUTPUT_ROOT Optional explicit output root. Requires ASR_ANCHOR_BLOCK_NUMBER. # # Args: -# tdx_verifier TDXVerifier address from deploy-tdx-verifier -# tdx_manager Manager that can call registerTDXSigner() -deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: - #!/usr/bin/env bash - set -euo pipefail - cd "{{repo_root}}" - owner="$(cast wallet address --account "{{forge_account}}")" - echo "Using {{forge_account}} as deployer: $owner" - - if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 - exit 1 - fi - asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" - asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" - else - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" - l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" - if (( lookback > l2_head )); then - echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 - exit 1 - fi - asr_anchor_block=$((l2_head - lookback)) - asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" - fi - - if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then - echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 - exit 1 - fi - - echo "Using ASR anchor output root: $asr_anchor_output_root" - echo "Using ASR anchor L2 block: $asr_anchor_block" - - DEPLOY_CONFIG_PATH="{{deploy_config}}" \ - forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ - --sig "run(address,address,bytes32,uint256)" \ - "{{tdx_verifier}}" \ - "{{tdx_manager}}" \ - "$asr_anchor_output_root" \ - "$asr_anchor_block" \ - --rpc-url "{{l1_rpc_url}}" \ - --broadcast \ - --account "{{forge_account}}" \ - --sender "$owner" - -# Deploy the TDX multiproof dev/test stack with both verifier addresses supplied explicitly. -# -# Use this when you want to bind the deployed stack to explicit verifier addresses. -# -# Args: -# nitro_verifier NitroEnclaveVerifier address from deploy-nitro-verifier or deploy-tee-verifiers -# tdx_verifier TDXVerifier address from deploy-tdx-verifier or deploy-tee-verifiers +# nitro_verifier NitroEnclaveVerifier address from deploy-nitro-verifier +# tdx_verifier TDXVerifier address from deploy-tdx-verifier # tdx_manager Manager that can call registerTDXSigner() -deploy-tdx-stack-with-verifiers nitro_verifier tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: +deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" @@ -382,7 +335,7 @@ deploy-tdx-system nitro_root_cert nitro_verifier_id tdx_manager=sepolia_tdx_regi exit 1 fi - just --justfile "{{justfile_path}}" deploy-tdx-stack-with-verifiers \ + just --justfile "{{justfile_path}}" deploy-tdx-stack \ "$nitro_verifier" \ "$tdx_verifier" \ "{{tdx_manager}}" \ @@ -453,7 +406,6 @@ tdx-sepolia-help: @echo 'just deploy-tdx-system ' @echo 'just deploy-sepolia-tdx-dev' @echo 'just run-sepolia-tdx-dev-offchain' - @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' - @echo 'just deploy-tdx-stack-with-verifiers [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' + @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' @echo 'just tee-registered-signers' @echo 'just tdx-sepolia-config' From 2e5f38f32e1e678d0943acfa875a0c1584890c77 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 17:59:14 -0400 Subject: [PATCH 058/146] clean up DeployDevWithNitro --- scripts/multiproof/DeployDevWithNitro.s.sol | 56 --------------------- 1 file changed, 56 deletions(-) diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index 3ebb66623..f63b2976c 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { console2 as console } from "lib/forge-std/src/console2.sol"; - import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; @@ -15,20 +13,9 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; /// TEEProverRegistry, so signer registration requires a ZK proof of a valid AWS /// Nitro attestation (no addDevSigner bypass). contract DeployDevWithNitro is DeployDevBase { - uint256 public constant BLOCK_INTERVAL = 600; - uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 30; - address public nitroEnclaveVerifierAddr; address public tdxVerifierAddr; - function _blockInterval() internal pure override returns (uint256) { - return BLOCK_INTERVAL; - } - - function _intermediateBlockInterval() internal pure override returns (uint256) { - return INTERMEDIATE_BLOCK_INTERVAL; - } - function _outputSuffix() internal pure override returns (string memory) { return "-dev-with-nitro.json"; } @@ -57,47 +44,4 @@ contract DeployDevWithNitro is DeployDevBase { vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); } - - function _logHeader() internal view override { - console.log("=== Deploying Dev Infrastructure (WITH NITRO) ==="); - console.log("Chain ID:", block.chainid); - console.log("Owner:", cfg.finalSystemOwner()); - console.log("TEE Proposer:", cfg.teeProposer()); - console.log("TEE Challenger:", cfg.teeChallenger()); - console.log("Game Type:", cfg.multiproofGameType()); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); - console.log("TDXVerifier:", tdxVerifierAddr); - console.log(""); - console.log("NOTE: Using REAL TEEProverRegistry - ZK attestation proof REQUIRED."); - } - - function _printSummary() internal view override { - console.log("\n========================================"); - console.log(" DEV DEPLOYMENT COMPLETE (WITH NITRO)"); - console.log("========================================"); - console.log("\nTEE Contracts:"); - console.log(" NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); - console.log(" TDXVerifier:", tdxVerifierAddr); - console.log(" TEEProverRegistry:", teeProverRegistryProxy); - console.log(" TEEVerifier:", teeVerifier); - console.log("\nInfrastructure:"); - console.log(" DisputeGameFactory:", disputeGameFactory); - console.log(" AnchorStateRegistry (mock):", address(mockAnchorRegistry)); - console.log(" DelayedWETH (mock):", mockDelayedWETH); - console.log("\nGame:"); - console.log(" AggregateVerifier:", aggregateVerifier); - console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); - console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); - console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); - console.log("========================================"); - console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); - console.log("\n cast send", teeProverRegistryProxy); - console.log(' "registerSigner(bytes,bytes)" '); - console.log(" --private-key --rpc-url "); - console.log("\n cast send", teeProverRegistryProxy); - console.log(' "registerTDXSigner(bytes,bytes)" '); - console.log(" --private-key --rpc-url "); - console.log("\n========================================\n"); - } } From 9c6893796c954fb166f415ecf5a7d36e82a40e2b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:07:44 -0400 Subject: [PATCH 059/146] clean up DeployDevWithNitro --- scripts/multiproof/DeployDevWithNitro.s.sol | 47 -------------------- scripts/multiproof/DeployRiscZeroStack.s.sol | 11 ++--- scripts/multiproof/README.md | 8 +--- scripts/multiproof/SeedGames.s.sol | 2 +- 4 files changed, 5 insertions(+), 63 deletions(-) delete mode 100644 scripts/multiproof/DeployDevWithNitro.s.sol diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol deleted file mode 100644 index f63b2976c..000000000 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; - -import { DeployDevBase } from "./DeployDevBase.s.sol"; - -/// @title DeployDevWithNitro -/// @notice Development deployment WITH AWS Nitro attestation validation. Uses the real -/// TEEProverRegistry, so signer registration requires a ZK proof of a valid AWS -/// Nitro attestation (no addDevSigner bypass). -contract DeployDevWithNitro is DeployDevBase { - address public nitroEnclaveVerifierAddr; - address public tdxVerifierAddr; - - function _outputSuffix() internal pure override returns (string memory) { - return "-dev-with-nitro.json"; - } - - function _preflight() internal override { - nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); - tdxVerifierAddr = cfg.tdxVerifier(); - require( - nitroEnclaveVerifierAddr != address(0), - "nitroEnclaveVerifier must be set in config (deploy via DeployNitroVerifier.s.sol first)" - ); - require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); - } - - function _deployTEERegistryImpl() internal override returns (address) { - return address( - new TEEProverRegistry( - INitroEnclaveVerifier(nitroEnclaveVerifierAddr), - ITDXVerifier(tdxVerifierAddr), - IDisputeGameFactory(disputeGameFactory) - ) - ); - } - - function _serializeExtra(string memory key) internal override { - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); - vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); - } -} diff --git a/scripts/multiproof/DeployRiscZeroStack.s.sol b/scripts/multiproof/DeployRiscZeroStack.s.sol index 42fac5883..1bb071cf6 100644 --- a/scripts/multiproof/DeployRiscZeroStack.s.sol +++ b/scripts/multiproof/DeployRiscZeroStack.s.sol @@ -40,11 +40,8 @@ pragma solidity ^0.8.20; * - NitroEnclaveVerifier with route wired to the local SetVerifier * * POST-DEPLOY: - * After deploying TEEProverRegistry via DeployDevWithNitro.s.sol, update the - * proofSubmitter on NitroEnclaveVerifier to the TEEProverRegistry address: - * - * cast send "setProofSubmitter(address)" \ - * --rpc-url --private-key + * Deploy the multiproof stack with DeployDevWithTDX.s.sol; it updates + * proofSubmitter on NitroEnclaveVerifier to the TEEProverRegistry address. * * ───────────────────────────────────────────────────────────────────────────────── */ @@ -154,9 +151,7 @@ contract DeployRiscZeroStack is Script { console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); console.log("RISC Zero Router (external):", risc0VerifierRouter); console.log(""); - console.log(">>> Set nitroEnclaveVerifier in deploy config to:", nitroEnclaveVerifier); - console.log(">>> Then run DeployDevWithNitro.s.sol <<<"); - console.log(">>> Then call setProofSubmitter(TEEProverRegistry) on NitroEnclaveVerifier <<<"); + console.log(">>> Then deploy the multiproof stack with DeployDevWithTDX.s.sol <<<"); console.log("========================================"); string memory key = "deployment"; diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index b122fe397..76a7849d0 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -129,13 +129,7 @@ The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` --- -## Path 2: WithNitro (Dev — Real Attestation) - -> **TODO:** Add deployment and registration guide for `DeployDevWithNitro.s.sol`. - ---- - -## Path 3: TDX (Production-Path PoC) +## Path 2: TDX (Production-Path PoC) The TDX path follows the same split as Nitro: expensive attestation verification happens offchain in a ZK guest, and Solidity verifies the proof plus the onchain acceptance policy before registering the signer. diff --git a/scripts/multiproof/SeedGames.s.sol b/scripts/multiproof/SeedGames.s.sol index e04121dbd..91b734dff 100644 --- a/scripts/multiproof/SeedGames.s.sol +++ b/scripts/multiproof/SeedGames.s.sol @@ -26,7 +26,7 @@ import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; /// All transactions must confirm within the 256-block blockhash window of the /// L1 origin captured at simulation time. For large counts, use --slow. contract SeedGames is Script { - /// @notice Must match the AggregateVerifier deployment constants from DeployDevWithNitro/NoNitro. + /// @notice Must match the AggregateVerifier deployment constants from DeployDevWithTDX/NoNitro. uint256 public constant BLOCK_INTERVAL = 600; uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 30; uint256 public constant INTERMEDIATE_ROOTS_COUNT = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL; From 4f852fdcb3a172e94fadce1c52397fa7e383275c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:14:21 -0400 Subject: [PATCH 060/146] clean up DeployDevNoNitro --- scripts/multiproof/DeployDevNoNitro.s.sol | 73 ++--------------------- 1 file changed, 6 insertions(+), 67 deletions(-) diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 4602c4d4f..5a43a9498 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { console2 as console } from "lib/forge-std/src/console2.sol"; - import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; @@ -14,87 +12,28 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @notice Development deployment using DevTEEProverRegistry, which bypasses AWS Nitro attestation /// validation. See scripts/multiproof/README.md for usage. Not for production. contract DeployDevNoNitro is DeployDevBase { - uint256 public constant BLOCK_INTERVAL = 100; - uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 10; - uint256 public constant INIT_BOND = 0.001 ether; - - address public tdxVerifierAddr; - - function _blockInterval() internal pure override returns (uint256) { - return BLOCK_INTERVAL; - } - - function _intermediateBlockInterval() internal pure override returns (uint256) { - return INTERMEDIATE_BLOCK_INTERVAL; - } - function _initBond() internal pure override returns (uint256) { - return INIT_BOND; + return 0.001 ether; } function _outputSuffix() internal pure override returns (string memory) { return "-dev-no-nitro.json"; } - function _preflight() internal override { - tdxVerifierAddr = cfg.tdxVerifier(); - require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); + function _preflight() internal view override { + require(cfg.tdxVerifier() != address(0), "tdxVerifier must be set in config"); } function _deployTEERegistryImpl() internal override returns (address) { + address tdxVerifier = cfg.tdxVerifier(); return address( new DevTEEProverRegistry( - INitroEnclaveVerifier(address(0)), - ITDXVerifier(tdxVerifierAddr), - IDisputeGameFactory(disputeGameFactory) + INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifier), IDisputeGameFactory(disputeGameFactory) ) ); } function _serializeExtra(string memory key) internal override { - vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); - } - - function _logHeader() internal view override { - console.log("=== Deploying Dev Infrastructure (NO NITRO) ==="); - console.log("Chain ID:", block.chainid); - console.log("Owner:", cfg.finalSystemOwner()); - console.log("TEE Proposer:", cfg.teeProposer()); - console.log("TEE Challenger:", cfg.teeChallenger()); - console.log("Game Type:", cfg.multiproofGameType()); - console.log("TDXVerifier:", tdxVerifierAddr); - console.log(""); - console.log("NOTE: Using DevTEEProverRegistry - NO attestation required."); - } - - function _printSummary() internal view override { - console.log("\n========================================"); - console.log(" DEV DEPLOYMENT COMPLETE (NO NITRO)"); - console.log("========================================"); - console.log("\nTEE Contracts:"); - console.log(" DevTEEProverRegistry:", teeProverRegistryProxy); - console.log(" TDXVerifier:", tdxVerifierAddr); - console.log(" TEEVerifier:", teeVerifier); - console.log("\nInfrastructure:"); - console.log(" DisputeGameFactory:", disputeGameFactory); - console.log(" AnchorStateRegistry (mock):", address(mockAnchorRegistry)); - console.log(" DelayedWETH (mock):", mockDelayedWETH); - console.log("\nGame:"); - console.log(" AggregateVerifier:", aggregateVerifier); - console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); - console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); - console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); - console.log("========================================"); - console.log("\n>>> NEXT STEP - Register dev Nitro and TDX signers (NO ATTESTATION NEEDED) <<<"); - console.log("\ncast send", teeProverRegistryProxy); - console.log(' "addDevSigner(address,bytes32)" '); - console.log(" ", vm.toString(cfg.teeNitroImageHash())); - console.log(" --private-key --rpc-url "); - console.log("\ncast send", teeProverRegistryProxy); - console.log(' "addDevTDXSigner(address,bytes32)" '); - console.log(" ", vm.toString(cfg.teeTdxImageHash())); - console.log(" --private-key --rpc-url "); - console.log("\n========================================\n"); + vm.serializeAddress(key, "TDXVerifier", cfg.tdxVerifier()); } } From a1b30ccf8f682a925400602d2189fafa03db03d4 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:16:42 -0400 Subject: [PATCH 061/146] clean up DeployDevNoNitro --- scripts/multiproof/DeployDevNoNitro.s.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 5a43a9498..c86e7ecf7 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -25,10 +25,11 @@ contract DeployDevNoNitro is DeployDevBase { } function _deployTEERegistryImpl() internal override returns (address) { - address tdxVerifier = cfg.tdxVerifier(); return address( new DevTEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifier), IDisputeGameFactory(disputeGameFactory) + INitroEnclaveVerifier(address(0)), + ITDXVerifier(cfg.tdxVerifier()), + IDisputeGameFactory(disputeGameFactory) ) ); } From 589c15df2cf8e8c8d78f9ec233874664a7fb79c4 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:23:15 -0400 Subject: [PATCH 062/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 20 ++++--------------- .../multiproof/mocks/MinimalProxyAdmin.sol | 14 ------------- 2 files changed, 4 insertions(+), 30 deletions(-) delete mode 100644 scripts/multiproof/mocks/MinimalProxyAdmin.sol diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 2ba1afa58..6a8997969 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -9,6 +9,7 @@ import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { DisputeGameFactory } from "src/L1/proofs/DisputeGameFactory.sol"; import { GameType, Hash } from "src/libraries/bridge/Types.sol"; +import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; import { Config } from "scripts/libraries/Config.sol"; @@ -20,7 +21,6 @@ import { MockVerifier } from "test/mocks/MockVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; -import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; @@ -54,7 +54,6 @@ abstract contract DeployDevBase is Script { GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); _preflight(); - _logHeader(); vm.startBroadcast(); @@ -64,14 +63,13 @@ abstract contract DeployDevBase is Script { vm.stopBroadcast(); - _printSummary(); _writeOutput(); } function _deployInfrastructure(GameType gameType) internal { address owner = cfg.finalSystemOwner(); address factoryImpl = address(new DisputeGameFactory()); - MinimalProxyAdmin proxyAdmin = new MinimalProxyAdmin(owner); + ProxyAdmin proxyAdmin = new ProxyAdmin(owner); Proxy proxy = new Proxy(msg.sender); proxy.upgradeTo(factoryImpl); @@ -124,8 +122,8 @@ abstract contract DeployDevBase is Script { zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainId(), - _blockInterval(), - _intermediateBlockInterval() + cfg.multiproofBlockInterval(), + cfg.multiproofIntermediateBlockInterval() ) ); @@ -154,14 +152,6 @@ abstract contract DeployDevBase is Script { console.log("Deployment saved to:", outPath); } - function _blockInterval() internal view virtual returns (uint256) { - return cfg.multiproofBlockInterval(); - } - - function _intermediateBlockInterval() internal view virtual returns (uint256) { - return cfg.multiproofIntermediateBlockInterval(); - } - function _initBond() internal pure virtual returns (uint256) { return 0.00001 ether; } @@ -171,8 +161,6 @@ abstract contract DeployDevBase is Script { function _preflight() internal virtual { } function _serializeExtra(string memory key) internal virtual { } - function _logHeader() internal view virtual { } - function _printSummary() internal view virtual { } function _teeRegistrationManager() internal view virtual returns (address) { return cfg.finalSystemOwner(); diff --git a/scripts/multiproof/mocks/MinimalProxyAdmin.sol b/scripts/multiproof/mocks/MinimalProxyAdmin.sol deleted file mode 100644 index ccf9b20dd..000000000 --- a/scripts/multiproof/mocks/MinimalProxyAdmin.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -/// @title MinimalProxyAdmin -/// @notice Minimal contract to satisfy DisputeGameFactory's proxy admin check. -/// @dev ProxyAdminOwnedBase.proxyAdminOwner() reads `owner()` off the proxy admin, so this -/// exposes an `owner` getter set to the expected initializer caller. -contract MinimalProxyAdmin { - address public owner; - - constructor(address initialOwner) { - owner = initialOwner; - } -} From 25b789d3ed1e558a9099d45093a0afd25f25cb9b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:31:44 -0400 Subject: [PATCH 063/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 44 +++++++++++--------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 6a8997969..340196ed1 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -25,17 +25,17 @@ import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; abstract contract DeployDevBase is Script { - DeployConfig public constant cfg = + DeployConfig internal constant cfg = DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); - address public teeProverRegistryProxy; - address public teeVerifier; - address public disputeGameFactory; - IAnchorStateRegistry public mockAnchorRegistry; - address public mockDelayedWETH; - address public aggregateVerifier; - Hash public startingAnchorRoot; - uint256 public startingAnchorBlockNumber; + address internal teeProverRegistryProxy; + address internal teeVerifier; + address internal disputeGameFactory; + IAnchorStateRegistry internal mockAnchorRegistry; + address internal mockDelayedWETH; + address internal aggregateVerifier; + Hash internal startingAnchorRoot; + uint256 internal startingAnchorBlockNumber; function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); @@ -67,14 +67,10 @@ abstract contract DeployDevBase is Script { } function _deployInfrastructure(GameType gameType) internal { - address owner = cfg.finalSystemOwner(); - address factoryImpl = address(new DisputeGameFactory()); - ProxyAdmin proxyAdmin = new ProxyAdmin(owner); - Proxy proxy = new Proxy(msg.sender); - proxy.upgradeTo(factoryImpl); + proxy.upgradeTo(address(new DisputeGameFactory())); DisputeGameFactory(address(proxy)).initialize(msg.sender); - proxy.changeAdmin(address(proxyAdmin)); + proxy.changeAdmin(address(new ProxyAdmin(cfg.finalSystemOwner()))); disputeGameFactory = address(proxy); MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); @@ -83,17 +79,17 @@ abstract contract DeployDevBase is Script { } function _deployTEEContracts(GameType gameType) internal { - address owner = cfg.finalSystemOwner(); - address teeRegistryImpl = _deployTEERegistryImpl(); - address[] memory initialProposers = new address[](2); initialProposers[0] = cfg.teeProposer(); initialProposers[1] = cfg.teeChallenger(); Proxy teeProxy = new Proxy(msg.sender); teeProxy.upgradeToAndCall( - teeRegistryImpl, - abi.encodeCall(TEEProverRegistry.initialize, (owner, _teeRegistrationManager(), initialProposers, gameType)) + _deployTEERegistryImpl(), + abi.encodeCall( + TEEProverRegistry.initialize, + (cfg.finalSystemOwner(), _teeRegistrationManager(), initialProposers, gameType) + ) ); teeProxy.changeAdmin(address(0xdead)); teeProverRegistryProxy = address(teeProxy); @@ -145,10 +141,9 @@ abstract contract DeployDevBase is Script { vm.serializeBytes32(key, "ASRStartingOutputRoot", startingAnchorRoot.raw()); vm.serializeUint(key, "ASRStartingBlockNumber", startingAnchorBlockNumber); vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); - string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); string memory outPath = string.concat("deployments/", vm.toString(block.chainid), _outputSuffix()); - vm.writeJson(json, outPath); + vm.writeJson(vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier), outPath); console.log("Deployment saved to:", outPath); } @@ -158,9 +153,8 @@ abstract contract DeployDevBase is Script { function _outputSuffix() internal pure virtual returns (string memory); function _deployTEERegistryImpl() internal virtual returns (address); - - function _preflight() internal virtual { } - function _serializeExtra(string memory key) internal virtual { } + function _preflight() internal view virtual; + function _serializeExtra(string memory key) internal virtual; function _teeRegistrationManager() internal view virtual returns (address) { return cfg.finalSystemOwner(); From 836ef57fca890ce30f88920b209f00eae7b6d2fa Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:38:43 -0400 Subject: [PATCH 064/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 340196ed1..3ddab65b4 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -9,7 +9,6 @@ import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { DisputeGameFactory } from "src/L1/proofs/DisputeGameFactory.sol"; import { GameType, Hash } from "src/libraries/bridge/Types.sol"; -import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; import { Config } from "scripts/libraries/Config.sol"; @@ -70,7 +69,7 @@ abstract contract DeployDevBase is Script { Proxy proxy = new Proxy(msg.sender); proxy.upgradeTo(address(new DisputeGameFactory())); DisputeGameFactory(address(proxy)).initialize(msg.sender); - proxy.changeAdmin(address(new ProxyAdmin(cfg.finalSystemOwner()))); + proxy.changeAdmin(address(0xdead)); disputeGameFactory = address(proxy); MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); @@ -126,9 +125,7 @@ abstract contract DeployDevBase is Script { DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); factory.setInitBond(gameType, _initBond()); - if (factory.owner() != cfg.finalSystemOwner()) { - factory.transferOwnership(cfg.finalSystemOwner()); - } + factory.transferOwnership(cfg.finalSystemOwner()); } function _writeOutput() internal { From a89fbf4f5e1bcb766eda79410533c0cc4e79485f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:44:24 -0400 Subject: [PATCH 065/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 34 ++++++++++++-------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 3ddab65b4..9b65f0275 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -33,8 +33,6 @@ abstract contract DeployDevBase is Script { IAnchorStateRegistry internal mockAnchorRegistry; address internal mockDelayedWETH; address internal aggregateVerifier; - Hash internal startingAnchorRoot; - uint256 internal startingAnchorBlockNumber; function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); @@ -47,8 +45,7 @@ abstract contract DeployDevBase is Script { function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public { require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); - startingAnchorRoot = Hash.wrap(asrStartingOutputRoot); - startingAnchorBlockNumber = asrStartingBlockNumber; + Hash startingAnchorRoot = Hash.wrap(asrStartingOutputRoot); GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); @@ -56,16 +53,22 @@ abstract contract DeployDevBase is Script { vm.startBroadcast(); - _deployInfrastructure(gameType); + _deployInfrastructure(gameType, startingAnchorRoot, asrStartingBlockNumber); _deployTEEContracts(gameType); _deployAggregateVerifier(gameType); vm.stopBroadcast(); - _writeOutput(); + _writeOutput(startingAnchorRoot, asrStartingBlockNumber); } - function _deployInfrastructure(GameType gameType) internal { + function _deployInfrastructure( + GameType gameType, + Hash startingAnchorRoot, + uint256 startingAnchorBlockNumber + ) + internal + { Proxy proxy = new Proxy(msg.sender); proxy.upgradeTo(address(new DisputeGameFactory())); DisputeGameFactory(address(proxy)).initialize(msg.sender); @@ -99,22 +102,18 @@ abstract contract DeployDevBase is Script { } function _deployAggregateVerifier(GameType gameType) internal { - address zkVerifier = address(new MockVerifier(mockAnchorRegistry)); mockDelayedWETH = address(new MockDelayedWETH()); - AggregateVerifier.ZkHashes memory zkHashes = - AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }); - aggregateVerifier = address( new AggregateVerifier( gameType, mockAnchorRegistry, IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), - IVerifier(zkVerifier), + IVerifier(address(new MockVerifier(mockAnchorRegistry))), cfg.teeNitroImageHash(), cfg.teeTdxImageHash(), - zkHashes, + AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }), cfg.multiproofConfigHash(), cfg.l2ChainId(), cfg.multiproofBlockInterval(), @@ -122,13 +121,12 @@ abstract contract DeployDevBase is Script { ) ); - DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); - factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); - factory.setInitBond(gameType, _initBond()); - factory.transferOwnership(cfg.finalSystemOwner()); + DisputeGameFactory(disputeGameFactory).setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); + DisputeGameFactory(disputeGameFactory).setInitBond(gameType, _initBond()); + DisputeGameFactory(disputeGameFactory).transferOwnership(cfg.finalSystemOwner()); } - function _writeOutput() internal { + function _writeOutput(Hash startingAnchorRoot, uint256 startingAnchorBlockNumber) internal { string memory key = "deployment"; vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); vm.serializeAddress(key, "TEEVerifier", teeVerifier); From 454d011bfa28dc08fdec5c6c8289f006580f5df0 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:48:19 -0400 Subject: [PATCH 066/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 9b65f0275..88fdd3e26 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -121,7 +121,7 @@ abstract contract DeployDevBase is Script { ) ); - DisputeGameFactory(disputeGameFactory).setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); + DisputeGameFactory(disputeGameFactory).setImplementation(gameType, IDisputeGame(aggregateVerifier)); DisputeGameFactory(disputeGameFactory).setInitBond(gameType, _initBond()); DisputeGameFactory(disputeGameFactory).transferOwnership(cfg.finalSystemOwner()); } From e88b3605e34f251b92b2f646f4a97288812edb9e Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 18:55:43 -0400 Subject: [PATCH 067/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 74 +++++++---------------- scripts/multiproof/DeployDevNoNitro.s.sol | 2 +- scripts/multiproof/DeployDevWithTDX.s.sol | 4 +- 3 files changed, 26 insertions(+), 54 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 88fdd3e26..9d900808d 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -27,13 +27,6 @@ abstract contract DeployDevBase is Script { DeployConfig internal constant cfg = DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); - address internal teeProverRegistryProxy; - address internal teeVerifier; - address internal disputeGameFactory; - IAnchorStateRegistry internal mockAnchorRegistry; - address internal mockDelayedWETH; - address internal aggregateVerifier; - function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); cfg.read(Config.deployConfigPath()); @@ -45,66 +38,52 @@ abstract contract DeployDevBase is Script { function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public { require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); - Hash startingAnchorRoot = Hash.wrap(asrStartingOutputRoot); - GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); + string memory key = "deployment"; _preflight(); vm.startBroadcast(); - _deployInfrastructure(gameType, startingAnchorRoot, asrStartingBlockNumber); - _deployTEEContracts(gameType); - _deployAggregateVerifier(gameType); - - vm.stopBroadcast(); - - _writeOutput(startingAnchorRoot, asrStartingBlockNumber); - } - - function _deployInfrastructure( - GameType gameType, - Hash startingAnchorRoot, - uint256 startingAnchorBlockNumber - ) - internal - { Proxy proxy = new Proxy(msg.sender); proxy.upgradeTo(address(new DisputeGameFactory())); DisputeGameFactory(address(proxy)).initialize(msg.sender); proxy.changeAdmin(address(0xdead)); - disputeGameFactory = address(proxy); + address disputeGameFactory = address(proxy); + vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); - mockAnchorRegistry = IAnchorStateRegistry(address(asr)); - asr.initialize(disputeGameFactory, startingAnchorRoot, startingAnchorBlockNumber, gameType); - } + IAnchorStateRegistry mockAnchorRegistry = IAnchorStateRegistry(address(asr)); + asr.initialize(disputeGameFactory, Hash.wrap(asrStartingOutputRoot), asrStartingBlockNumber, gameType); + vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); + vm.serializeBytes32(key, "ASRStartingOutputRoot", asrStartingOutputRoot); + vm.serializeUint(key, "ASRStartingBlockNumber", asrStartingBlockNumber); - function _deployTEEContracts(GameType gameType) internal { address[] memory initialProposers = new address[](2); initialProposers[0] = cfg.teeProposer(); initialProposers[1] = cfg.teeChallenger(); Proxy teeProxy = new Proxy(msg.sender); teeProxy.upgradeToAndCall( - _deployTEERegistryImpl(), + _deployTEERegistryImpl(disputeGameFactory), abi.encodeCall( TEEProverRegistry.initialize, (cfg.finalSystemOwner(), _teeRegistrationManager(), initialProposers, gameType) ) ); teeProxy.changeAdmin(address(0xdead)); - teeProverRegistryProxy = address(teeProxy); + address teeProverRegistryProxy = address(teeProxy); + vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); - _afterTEERegistryDeploy(); + _afterTEERegistryDeploy(teeProverRegistryProxy); - teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); - } + address teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); + vm.serializeAddress(key, "TEEVerifier", teeVerifier); - function _deployAggregateVerifier(GameType gameType) internal { - mockDelayedWETH = address(new MockDelayedWETH()); + address mockDelayedWETH = address(new MockDelayedWETH()); + vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); - aggregateVerifier = address( + address aggregateVerifier = address( new AggregateVerifier( gameType, mockAnchorRegistry, @@ -124,21 +103,14 @@ abstract contract DeployDevBase is Script { DisputeGameFactory(disputeGameFactory).setImplementation(gameType, IDisputeGame(aggregateVerifier)); DisputeGameFactory(disputeGameFactory).setInitBond(gameType, _initBond()); DisputeGameFactory(disputeGameFactory).transferOwnership(cfg.finalSystemOwner()); - } - function _writeOutput(Hash startingAnchorRoot, uint256 startingAnchorBlockNumber) internal { - string memory key = "deployment"; - vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); - vm.serializeAddress(key, "TEEVerifier", teeVerifier); _serializeExtra(key); - vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); - vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); - vm.serializeBytes32(key, "ASRStartingOutputRoot", startingAnchorRoot.raw()); - vm.serializeUint(key, "ASRStartingBlockNumber", startingAnchorBlockNumber); - vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); + string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); + + vm.stopBroadcast(); string memory outPath = string.concat("deployments/", vm.toString(block.chainid), _outputSuffix()); - vm.writeJson(vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier), outPath); + vm.writeJson(json, outPath); console.log("Deployment saved to:", outPath); } @@ -147,7 +119,7 @@ abstract contract DeployDevBase is Script { } function _outputSuffix() internal pure virtual returns (string memory); - function _deployTEERegistryImpl() internal virtual returns (address); + function _deployTEERegistryImpl(address disputeGameFactory) internal virtual returns (address); function _preflight() internal view virtual; function _serializeExtra(string memory key) internal virtual; @@ -155,5 +127,5 @@ abstract contract DeployDevBase is Script { return cfg.finalSystemOwner(); } - function _afterTEERegistryDeploy() internal virtual { } + function _afterTEERegistryDeploy(address teeProverRegistryProxy) internal virtual { } } diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index c86e7ecf7..d312586cf 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -24,7 +24,7 @@ contract DeployDevNoNitro is DeployDevBase { require(cfg.tdxVerifier() != address(0), "tdxVerifier must be set in config"); } - function _deployTEERegistryImpl() internal override returns (address) { + function _deployTEERegistryImpl(address disputeGameFactory) internal override returns (address) { return address( new DevTEEProverRegistry( INitroEnclaveVerifier(address(0)), diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 36850fadc..1743792ad 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -39,7 +39,7 @@ contract DeployDevWithTDX is DeployDevBase { require(tdxRegistrationManager != address(0), "registrationManager must be non-zero"); } - function _deployTEERegistryImpl() internal override returns (address) { + function _deployTEERegistryImpl(address disputeGameFactory) internal override returns (address) { return address( new DevTEEProverRegistry( INitroEnclaveVerifier(nitroEnclaveVerifierAddr), @@ -53,7 +53,7 @@ contract DeployDevWithTDX is DeployDevBase { return tdxRegistrationManager; } - function _afterTEERegistryDeploy() internal override { + function _afterTEERegistryDeploy(address teeProverRegistryProxy) internal override { INitroEnclaveVerifier(nitroEnclaveVerifierAddr).setProofSubmitter(teeProverRegistryProxy); } From e79f88419d37a6b8fb3e3673bd12ed3f771f32cc Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:01:36 -0400 Subject: [PATCH 068/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 35 ++++++++++++++++------- scripts/multiproof/DeployDevNoNitro.s.sol | 15 ++-------- scripts/multiproof/DeployDevWithTDX.s.sol | 23 ++++----------- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 9d900808d..6cc8904eb 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -7,6 +7,9 @@ import { console2 as console } from "lib/forge-std/src/console2.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; +import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { DisputeGameFactory } from "src/L1/proofs/DisputeGameFactory.sol"; import { GameType, Hash } from "src/libraries/bridge/Types.sol"; @@ -16,6 +19,7 @@ import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; +import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { MockVerifier } from "test/mocks/MockVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; @@ -53,9 +57,8 @@ abstract contract DeployDevBase is Script { vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); - IAnchorStateRegistry mockAnchorRegistry = IAnchorStateRegistry(address(asr)); asr.initialize(disputeGameFactory, Hash.wrap(asrStartingOutputRoot), asrStartingBlockNumber, gameType); - vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); + vm.serializeAddress(key, "AnchorStateRegistry", address(asr)); vm.serializeBytes32(key, "ASRStartingOutputRoot", asrStartingOutputRoot); vm.serializeUint(key, "ASRStartingBlockNumber", asrStartingBlockNumber); @@ -63,9 +66,16 @@ abstract contract DeployDevBase is Script { initialProposers[0] = cfg.teeProposer(); initialProposers[1] = cfg.teeChallenger(); + address nitroVerifier = _nitroEnclaveVerifier(); Proxy teeProxy = new Proxy(msg.sender); teeProxy.upgradeToAndCall( - _deployTEERegistryImpl(disputeGameFactory), + address( + new DevTEEProverRegistry( + INitroEnclaveVerifier(nitroVerifier), + ITDXVerifier(_tdxVerifier()), + IDisputeGameFactory(disputeGameFactory) + ) + ), abi.encodeCall( TEEProverRegistry.initialize, (cfg.finalSystemOwner(), _teeRegistrationManager(), initialProposers, gameType) @@ -75,9 +85,12 @@ abstract contract DeployDevBase is Script { address teeProverRegistryProxy = address(teeProxy); vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); - _afterTEERegistryDeploy(teeProverRegistryProxy); + if (nitroVerifier != address(0)) { + INitroEnclaveVerifier(nitroVerifier).setProofSubmitter(teeProverRegistryProxy); + } - address teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); + address teeVerifier = + address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), IAnchorStateRegistry(address(asr)))); vm.serializeAddress(key, "TEEVerifier", teeVerifier); address mockDelayedWETH = address(new MockDelayedWETH()); @@ -86,10 +99,10 @@ abstract contract DeployDevBase is Script { address aggregateVerifier = address( new AggregateVerifier( gameType, - mockAnchorRegistry, + IAnchorStateRegistry(address(asr)), IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), - IVerifier(address(new MockVerifier(mockAnchorRegistry))), + IVerifier(address(new MockVerifier(IAnchorStateRegistry(address(asr))))), cfg.teeNitroImageHash(), cfg.teeTdxImageHash(), AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }), @@ -119,13 +132,15 @@ abstract contract DeployDevBase is Script { } function _outputSuffix() internal pure virtual returns (string memory); - function _deployTEERegistryImpl(address disputeGameFactory) internal virtual returns (address); + function _tdxVerifier() internal view virtual returns (address); function _preflight() internal view virtual; function _serializeExtra(string memory key) internal virtual; + function _nitroEnclaveVerifier() internal view virtual returns (address) { + return address(0); + } + function _teeRegistrationManager() internal view virtual returns (address) { return cfg.finalSystemOwner(); } - - function _afterTEERegistryDeploy(address teeProverRegistryProxy) internal virtual { } } diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index d312586cf..b7fb23d78 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -1,11 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; - import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @title DeployDevNoNitro @@ -24,14 +19,8 @@ contract DeployDevNoNitro is DeployDevBase { require(cfg.tdxVerifier() != address(0), "tdxVerifier must be set in config"); } - function _deployTEERegistryImpl(address disputeGameFactory) internal override returns (address) { - return address( - new DevTEEProverRegistry( - INitroEnclaveVerifier(address(0)), - ITDXVerifier(cfg.tdxVerifier()), - IDisputeGameFactory(disputeGameFactory) - ) - ); + function _tdxVerifier() internal view override returns (address) { + return cfg.tdxVerifier(); } function _serializeExtra(string memory key) internal override { diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 1743792ad..35c7d8777 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -1,11 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; - import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @notice Development deployment using the TDX signer-registration path. @@ -39,22 +34,16 @@ contract DeployDevWithTDX is DeployDevBase { require(tdxRegistrationManager != address(0), "registrationManager must be non-zero"); } - function _deployTEERegistryImpl(address disputeGameFactory) internal override returns (address) { - return address( - new DevTEEProverRegistry( - INitroEnclaveVerifier(nitroEnclaveVerifierAddr), - ITDXVerifier(tdxVerifierAddr), - IDisputeGameFactory(disputeGameFactory) - ) - ); + function _nitroEnclaveVerifier() internal view override returns (address) { + return nitroEnclaveVerifierAddr; } - function _teeRegistrationManager() internal view override returns (address) { - return tdxRegistrationManager; + function _tdxVerifier() internal view override returns (address) { + return tdxVerifierAddr; } - function _afterTEERegistryDeploy(address teeProverRegistryProxy) internal override { - INitroEnclaveVerifier(nitroEnclaveVerifierAddr).setProofSubmitter(teeProverRegistryProxy); + function _teeRegistrationManager() internal view override returns (address) { + return tdxRegistrationManager; } function _serializeExtra(string memory key) internal override { From b132ede34ae19f0eca55cce8f0398366247a6fe8 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:09:17 -0400 Subject: [PATCH 069/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 43 ++++++++++++----------- scripts/multiproof/DeployDevNoNitro.s.sol | 12 ------- scripts/multiproof/DeployDevWithTDX.s.sol | 40 +++++---------------- 3 files changed, 32 insertions(+), 63 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 6cc8904eb..d5fd211c2 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -36,16 +36,33 @@ abstract contract DeployDevBase is Script { cfg.read(Config.deployConfigPath()); } - function run() public { + function run() public virtual { run(cfg.multiproofGenesisOutputRoot(), cfg.multiproofGenesisBlockNumber()); } - function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public { + function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public virtual { + _run(asrStartingOutputRoot, asrStartingBlockNumber, address(0), cfg.tdxVerifier(), cfg.finalSystemOwner()); + } + + function _run( + bytes32 asrStartingOutputRoot, + uint256 asrStartingBlockNumber, + address nitroVerifier, + address tdxVerifier, + address registrationManager + ) + internal + { require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); + require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); + require(registrationManager != address(0), "registrationManager must be non-zero"); GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); string memory key = "deployment"; - - _preflight(); + if (nitroVerifier != address(0)) { + vm.serializeAddress(key, "NitroEnclaveVerifier", nitroVerifier); + vm.serializeAddress(key, "TDXRegistrationManager", registrationManager); + } + vm.serializeAddress(key, "TDXVerifier", tdxVerifier); vm.startBroadcast(); @@ -66,19 +83,17 @@ abstract contract DeployDevBase is Script { initialProposers[0] = cfg.teeProposer(); initialProposers[1] = cfg.teeChallenger(); - address nitroVerifier = _nitroEnclaveVerifier(); Proxy teeProxy = new Proxy(msg.sender); teeProxy.upgradeToAndCall( address( new DevTEEProverRegistry( INitroEnclaveVerifier(nitroVerifier), - ITDXVerifier(_tdxVerifier()), + ITDXVerifier(tdxVerifier), IDisputeGameFactory(disputeGameFactory) ) ), abi.encodeCall( - TEEProverRegistry.initialize, - (cfg.finalSystemOwner(), _teeRegistrationManager(), initialProposers, gameType) + TEEProverRegistry.initialize, (cfg.finalSystemOwner(), registrationManager, initialProposers, gameType) ) ); teeProxy.changeAdmin(address(0xdead)); @@ -117,7 +132,6 @@ abstract contract DeployDevBase is Script { DisputeGameFactory(disputeGameFactory).setInitBond(gameType, _initBond()); DisputeGameFactory(disputeGameFactory).transferOwnership(cfg.finalSystemOwner()); - _serializeExtra(key); string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); vm.stopBroadcast(); @@ -132,15 +146,4 @@ abstract contract DeployDevBase is Script { } function _outputSuffix() internal pure virtual returns (string memory); - function _tdxVerifier() internal view virtual returns (address); - function _preflight() internal view virtual; - function _serializeExtra(string memory key) internal virtual; - - function _nitroEnclaveVerifier() internal view virtual returns (address) { - return address(0); - } - - function _teeRegistrationManager() internal view virtual returns (address) { - return cfg.finalSystemOwner(); - } } diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index b7fb23d78..5c5a26a99 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -14,16 +14,4 @@ contract DeployDevNoNitro is DeployDevBase { function _outputSuffix() internal pure override returns (string memory) { return "-dev-no-nitro.json"; } - - function _preflight() internal view override { - require(cfg.tdxVerifier() != address(0), "tdxVerifier must be set in config"); - } - - function _tdxVerifier() internal view override returns (address) { - return cfg.tdxVerifier(); - } - - function _serializeExtra(string memory key) internal override { - vm.serializeAddress(key, "TDXVerifier", cfg.tdxVerifier()); - } } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 35c7d8777..24d2bf208 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -5,9 +5,13 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @notice Development deployment using the TDX signer-registration path. contract DeployDevWithTDX is DeployDevBase { - address internal nitroEnclaveVerifierAddr; - address internal tdxVerifierAddr; - address internal tdxRegistrationManager; + function run() public pure override { + revert("use parameterized run"); + } + + function run(bytes32, uint256) public pure override { + revert("use parameterized run"); + } function run( address nitroEnclaveVerifier, @@ -18,37 +22,11 @@ contract DeployDevWithTDX is DeployDevBase { ) public { - nitroEnclaveVerifierAddr = nitroEnclaveVerifier; - tdxVerifierAddr = tdxVerifier; - tdxRegistrationManager = registrationManager; - run(asrStartingOutputRoot, asrStartingBlockNumber); + require(nitroEnclaveVerifier != address(0), "nitroEnclaveVerifier must be non-zero"); + _run(asrStartingOutputRoot, asrStartingBlockNumber, nitroEnclaveVerifier, tdxVerifier, registrationManager); } function _outputSuffix() internal pure override returns (string memory) { return "-dev-with-tdx.json"; } - - function _preflight() internal view override { - require(tdxVerifierAddr != address(0), "tdxVerifier must be non-zero"); - require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be non-zero"); - require(tdxRegistrationManager != address(0), "registrationManager must be non-zero"); - } - - function _nitroEnclaveVerifier() internal view override returns (address) { - return nitroEnclaveVerifierAddr; - } - - function _tdxVerifier() internal view override returns (address) { - return tdxVerifierAddr; - } - - function _teeRegistrationManager() internal view override returns (address) { - return tdxRegistrationManager; - } - - function _serializeExtra(string memory key) internal override { - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); - vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); - vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); - } } From 075feaa52bdbc61830dfab8fd052a2ee28cbf28c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:13:51 -0400 Subject: [PATCH 070/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 8 -------- scripts/multiproof/DeployDevNoNitro.s.sol | 8 ++++++++ scripts/multiproof/DeployDevWithTDX.s.sol | 8 -------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index d5fd211c2..08e6c495c 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -36,14 +36,6 @@ abstract contract DeployDevBase is Script { cfg.read(Config.deployConfigPath()); } - function run() public virtual { - run(cfg.multiproofGenesisOutputRoot(), cfg.multiproofGenesisBlockNumber()); - } - - function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public virtual { - _run(asrStartingOutputRoot, asrStartingBlockNumber, address(0), cfg.tdxVerifier(), cfg.finalSystemOwner()); - } - function _run( bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber, diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 5c5a26a99..8246b5dc1 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -7,6 +7,14 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @notice Development deployment using DevTEEProverRegistry, which bypasses AWS Nitro attestation /// validation. See scripts/multiproof/README.md for usage. Not for production. contract DeployDevNoNitro is DeployDevBase { + function run() public { + run(cfg.multiproofGenesisOutputRoot(), cfg.multiproofGenesisBlockNumber()); + } + + function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public { + _run(asrStartingOutputRoot, asrStartingBlockNumber, address(0), cfg.tdxVerifier(), cfg.finalSystemOwner()); + } + function _initBond() internal pure override returns (uint256) { return 0.001 ether; } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 24d2bf208..04abeda78 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -5,14 +5,6 @@ import { DeployDevBase } from "./DeployDevBase.s.sol"; /// @notice Development deployment using the TDX signer-registration path. contract DeployDevWithTDX is DeployDevBase { - function run() public pure override { - revert("use parameterized run"); - } - - function run(bytes32, uint256) public pure override { - revert("use parameterized run"); - } - function run( address nitroEnclaveVerifier, address tdxVerifier, From 34397084df050c4008ad66ce2d3781829b98b31e Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:27:12 -0400 Subject: [PATCH 071/146] clean up DeployDevBase --- scripts/multiproof/DeployDevBase.s.sol | 34 ++++++++++------------- scripts/multiproof/DeployDevNoNitro.s.sol | 18 ++++++------ scripts/multiproof/DeployDevWithTDX.s.sol | 14 ++++++---- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/scripts/multiproof/DeployDevBase.s.sol b/scripts/multiproof/DeployDevBase.s.sol index 08e6c495c..01ac32d77 100644 --- a/scripts/multiproof/DeployDevBase.s.sol +++ b/scripts/multiproof/DeployDevBase.s.sol @@ -41,7 +41,9 @@ abstract contract DeployDevBase is Script { uint256 asrStartingBlockNumber, address nitroVerifier, address tdxVerifier, - address registrationManager + address registrationManager, + uint256 initBond, + string memory outputSuffix ) internal { @@ -60,13 +62,14 @@ abstract contract DeployDevBase is Script { Proxy proxy = new Proxy(msg.sender); proxy.upgradeTo(address(new DisputeGameFactory())); - DisputeGameFactory(address(proxy)).initialize(msg.sender); + DisputeGameFactory factory = DisputeGameFactory(address(proxy)); + factory.initialize(msg.sender); proxy.changeAdmin(address(0xdead)); - address disputeGameFactory = address(proxy); - vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); + factory.setInitBond(gameType, initBond); + vm.serializeAddress(key, "DisputeGameFactory", address(factory)); MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); - asr.initialize(disputeGameFactory, Hash.wrap(asrStartingOutputRoot), asrStartingBlockNumber, gameType); + asr.initialize(address(factory), Hash.wrap(asrStartingOutputRoot), asrStartingBlockNumber, gameType); vm.serializeAddress(key, "AnchorStateRegistry", address(asr)); vm.serializeBytes32(key, "ASRStartingOutputRoot", asrStartingOutputRoot); vm.serializeUint(key, "ASRStartingBlockNumber", asrStartingBlockNumber); @@ -81,7 +84,7 @@ abstract contract DeployDevBase is Script { new DevTEEProverRegistry( INitroEnclaveVerifier(nitroVerifier), ITDXVerifier(tdxVerifier), - IDisputeGameFactory(disputeGameFactory) + IDisputeGameFactory(address(factory)) ) ), abi.encodeCall( @@ -100,14 +103,14 @@ abstract contract DeployDevBase is Script { address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), IAnchorStateRegistry(address(asr)))); vm.serializeAddress(key, "TEEVerifier", teeVerifier); - address mockDelayedWETH = address(new MockDelayedWETH()); - vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); + MockDelayedWETH delayedWETH = new MockDelayedWETH(); + vm.serializeAddress(key, "DelayedWETH", address(delayedWETH)); address aggregateVerifier = address( new AggregateVerifier( gameType, IAnchorStateRegistry(address(asr)), - IDelayedWETH(payable(mockDelayedWETH)), + IDelayedWETH(payable(address(delayedWETH))), IVerifier(teeVerifier), IVerifier(address(new MockVerifier(IAnchorStateRegistry(address(asr))))), cfg.teeNitroImageHash(), @@ -120,22 +123,15 @@ abstract contract DeployDevBase is Script { ) ); - DisputeGameFactory(disputeGameFactory).setImplementation(gameType, IDisputeGame(aggregateVerifier)); - DisputeGameFactory(disputeGameFactory).setInitBond(gameType, _initBond()); - DisputeGameFactory(disputeGameFactory).transferOwnership(cfg.finalSystemOwner()); + factory.setImplementation(gameType, IDisputeGame(aggregateVerifier)); + factory.transferOwnership(cfg.finalSystemOwner()); string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); vm.stopBroadcast(); - string memory outPath = string.concat("deployments/", vm.toString(block.chainid), _outputSuffix()); + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), outputSuffix); vm.writeJson(json, outPath); console.log("Deployment saved to:", outPath); } - - function _initBond() internal pure virtual returns (uint256) { - return 0.00001 ether; - } - - function _outputSuffix() internal pure virtual returns (string memory); } diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 8246b5dc1..68eee6c38 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -12,14 +12,14 @@ contract DeployDevNoNitro is DeployDevBase { } function run(bytes32 asrStartingOutputRoot, uint256 asrStartingBlockNumber) public { - _run(asrStartingOutputRoot, asrStartingBlockNumber, address(0), cfg.tdxVerifier(), cfg.finalSystemOwner()); - } - - function _initBond() internal pure override returns (uint256) { - return 0.001 ether; - } - - function _outputSuffix() internal pure override returns (string memory) { - return "-dev-no-nitro.json"; + _run( + asrStartingOutputRoot, + asrStartingBlockNumber, + address(0), + cfg.tdxVerifier(), + cfg.finalSystemOwner(), + 0.001 ether, + "-dev-no-nitro.json" + ); } } diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol index 04abeda78..ccee75b0b 100644 --- a/scripts/multiproof/DeployDevWithTDX.s.sol +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -15,10 +15,14 @@ contract DeployDevWithTDX is DeployDevBase { public { require(nitroEnclaveVerifier != address(0), "nitroEnclaveVerifier must be non-zero"); - _run(asrStartingOutputRoot, asrStartingBlockNumber, nitroEnclaveVerifier, tdxVerifier, registrationManager); - } - - function _outputSuffix() internal pure override returns (string memory) { - return "-dev-with-tdx.json"; + _run( + asrStartingOutputRoot, + asrStartingBlockNumber, + nitroEnclaveVerifier, + tdxVerifier, + registrationManager, + 0.00001 ether, + "-dev-with-tdx.json" + ); } } From 6f7c50bdc57ce6cec408fc8160f568f857ae50bd Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:32:37 -0400 Subject: [PATCH 072/146] rm deploy cfg --- deploy-config/hoodi-zeronet-tdx-local.json | 48 ---------------------- 1 file changed, 48 deletions(-) delete mode 100644 deploy-config/hoodi-zeronet-tdx-local.json diff --git a/deploy-config/hoodi-zeronet-tdx-local.json b/deploy-config/hoodi-zeronet-tdx-local.json deleted file mode 100644 index edcb821c3..000000000 --- a/deploy-config/hoodi-zeronet-tdx-local.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", - "baseFeeVaultRecipient": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", - "baseFeeVaultWithdrawalNetwork": 0, - "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", - "disputeGameFinalityDelaySeconds": 302400, - "delayedWETHWithdrawalDelay": 302400, - "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", - "fundDevAccounts": true, - "gasPriceOracleBaseFeeScalar": 1368, - "gasPriceOracleBlobBaseFeeScalar": 810949, - "l1ChainId": 560048, - "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", - "l1FeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788", - "l1FeeVaultWithdrawalNetwork": 0, - "l2ChainId": 763360, - "l2GenesisBlockGasLimit": "0x17D7840", - "l2OutputOracleStartingBlockNumber": 1, - "l2OutputOracleStartingTimestamp": 1, - "multiproofBlockInterval": 100, - "multiproofConfigHash": "0xd14ddabfc0ad1dd737d6e5917cf271fd479bd539c9b3d85a602589c679a9983a", - "multiproofGameType": 621, - "multiproofGenesisBlockNumber": 0, - "multiproofIntermediateBlockInterval": 10, - "multiproofGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", - "nitroEnclaveVerifier": "0x0000000000000000000000000000000000000000", - "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", - "operatorFeeVaultRecipient": "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec", - "operatorFeeVaultWithdrawalNetwork": 0, - "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", - "proofMaturityDelaySeconds": 604800, - "proxyAdminOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", - "respectedGameType": 621, - "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", - "sequencerFeeVaultRecipient": "0xfabb0ac9d68b0b445fb7357272ff202c5651694a", - "sequencerFeeVaultWithdrawalNetwork": 0, - "sp1Verifier": "0x0000000000000000000000000000000000000000", - "superchainConfigGuardian": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", - "superchainConfigIncidentResponder": "0x0000000000000000000000000000000000000000", - "tdxVerifier": "0x0000000000000000000000000000000000000001", - "teeChallenger": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", - "teeImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", - "teeNitroImageHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "teeTdxImageHash": "0x278ba4ed4251d187fb7215be37d8ee2ccb36906c71611dd89b655a81ed2445dc", - "teeProposer": "0x0ce54eFf9256b1da3b67656278EbAD2A9dfCFd4F", - "zkAggregationHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "zkRangeHash": "0x0000000000000000000000000000000000000000000000000000000000000000" -} From 865ced032fded26c7de6ecfab4a9172199ac9ddb Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:33:53 -0400 Subject: [PATCH 073/146] fix sepolia final system owner --- deploy-config/sepolia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy-config/sepolia.json b/deploy-config/sepolia.json index 53560f23b..c2edba5bc 100644 --- a/deploy-config/sepolia.json +++ b/deploy-config/sepolia.json @@ -5,7 +5,7 @@ "batchSenderAddress": "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", "disputeGameFinalityDelaySeconds": 302400, "delayedWETHWithdrawalDelay": 604800, - "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", + "finalSystemOwner": "0x6e427c3212C0b63BE0C382F97715D49b011bFF33", "gasPriceOracleBaseFeeScalar": 1368, "gasPriceOracleBlobBaseFeeScalar": 810949, "l1ChainId": 11155111, From 497fc5729bc8e9d6905641b15df9dfaf1c5f49c3 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:41:38 -0400 Subject: [PATCH 074/146] clean up justfile --- scripts/multiproof/justfile | 167 +++++++++++++----------------------- 1 file changed, 58 insertions(+), 109 deletions(-) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index dd7e9025c..528afb3df 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -1,9 +1,7 @@ set dotenv-load repo_root := justfile_directory() + "/../.." -workspace_root := repo_root + "/.." justfile_path := justfile_directory() + "/justfile" -default_deploy_config := "deploy-config/sepolia.json" zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" hoodi_zeronet_tdx_local_deploy_config := "deploy-config/hoodi-zeronet-tdx-local.json" hoodi_zeronet_tdx_local_deployments := "deployments/560048-dev-no-nitro.json" @@ -30,6 +28,36 @@ zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000 default: @just --justfile "{{justfile_path}}" --list +_asr-anchor l2_rpc l2_output_root_rpc: + #!/usr/bin/env bash + set -euo pipefail + + if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 + exit 1 + fi + asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" + asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" + else + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + fi + + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi + + printf '%s %s\n' "$asr_anchor_block" "$asr_anchor_output_root" + # Deploy the TDXVerifier policy contract. # # Required env: @@ -146,29 +174,7 @@ deploy-hoodi-zeronet-tdx-local rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_ echo "Using {{forge_account}} as deployer: $deployer" echo "Using deploy config: {{deploy_config}}" - if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 - exit 1 - fi - asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" - asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" - else - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" - l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" - if (( lookback > l2_head )); then - echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 - exit 1 - fi - asr_anchor_block=$((l2_head - lookback)) - asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" - fi - - if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then - echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 - exit 1 - fi + read -r asr_anchor_block asr_anchor_output_root < <(just --justfile "{{justfile_path}}" _asr-anchor "{{l2_rpc}}" "{{l2_output_root_rpc}}") echo "Using ASR anchor output root: $asr_anchor_output_root" echo "Using ASR anchor L2 block: $asr_anchor_block" @@ -183,31 +189,6 @@ deploy-hoodi-zeronet-tdx-local rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_ --account "{{forge_account}}" \ --sender "$deployer" -# Deploy the TDX multiproof dev/test stack on Sepolia. -deploy-sepolia-tdx-dev tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: - #!/usr/bin/env bash - set -euo pipefail - cd "{{repo_root}}" - - nitro_verifier="$(jq -r '.nitroEnclaveVerifier // empty' "{{deploy_config}}")" - tdx_verifier="$(jq -r '.tdxVerifier // empty' "{{deploy_config}}")" - if [[ -z "$nitro_verifier" || "$nitro_verifier" == "null" ]]; then - echo "Missing nitroEnclaveVerifier in {{deploy_config}}" >&2 - exit 1 - fi - if [[ -z "$tdx_verifier" || "$tdx_verifier" == "null" ]]; then - echo "Missing tdxVerifier in {{deploy_config}}" >&2 - exit 1 - fi - - just --justfile "{{justfile_path}}" deploy-tdx-stack \ - "$nitro_verifier" \ - "$tdx_verifier" \ - "{{tdx_manager}}" \ - "{{deploy_config}}" \ - "{{l2_rpc}}" \ - "{{l2_output_root_rpc}}" - # Register a TDX signer in the Hoodi no-attestation local testing registry. register-dev-tdx-signer signer registry="" image_hash="" rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config deployments=hoodi_zeronet_tdx_local_deployments: #!/usr/bin/env bash @@ -247,10 +228,6 @@ register-dev-tdx-signer signer registry="" image_hash="" rpc=hoodi_l1_rpc_url de --account "{{forge_account}}" \ --from "$owner" -# Run the local offchain services needed to submit dual TEE proposals on Base Sepolia. -run-sepolia-tdx-dev-offchain: - cd "{{workspace_root}}/base" && just sepolia-tdx-dev-offchain - # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. # # Required env: @@ -262,50 +239,45 @@ run-sepolia-tdx-dev-offchain: # ASR_ANCHOR_OUTPUT_ROOT Optional explicit output root. Requires ASR_ANCHOR_BLOCK_NUMBER. # # Args: -# nitro_verifier NitroEnclaveVerifier address from deploy-nitro-verifier -# tdx_verifier TDXVerifier address from deploy-tdx-verifier +# nitro_verifier Optional NitroEnclaveVerifier address; defaults to deploy_config.nitroEnclaveVerifier +# tdx_verifier Optional TDXVerifier address; defaults to deploy_config.tdxVerifier # tdx_manager Manager that can call registerTDXSigner() -deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: +deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" owner="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as deployer: $owner" - if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 - exit 1 - fi - asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" - asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" - else - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" - l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" - if (( lookback > l2_head )); then - echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 - exit 1 - fi - asr_anchor_block=$((l2_head - lookback)) - asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + nitro_verifier="{{nitro_verifier}}" + tdx_verifier="{{tdx_verifier}}" + if [[ -z "$nitro_verifier" ]]; then + nitro_verifier="$(jq -r '.nitroEnclaveVerifier // empty' "{{deploy_config}}")" fi - - if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then - echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + if [[ -z "$tdx_verifier" ]]; then + tdx_verifier="$(jq -r '.tdxVerifier // empty' "{{deploy_config}}")" + fi + if [[ -z "$nitro_verifier" || "$nitro_verifier" == "null" ]]; then + echo "Missing nitroEnclaveVerifier in {{deploy_config}}" >&2 + exit 1 + fi + if [[ -z "$tdx_verifier" || "$tdx_verifier" == "null" ]]; then + echo "Missing tdxVerifier in {{deploy_config}}" >&2 exit 1 fi - echo "Using NitroEnclaveVerifier: {{nitro_verifier}}" - echo "Using TDXVerifier: {{tdx_verifier}}" + read -r asr_anchor_block asr_anchor_output_root < <(just --justfile "{{justfile_path}}" _asr-anchor "{{l2_rpc}}" "{{l2_output_root_rpc}}") + + echo "Using NitroEnclaveVerifier: $nitro_verifier" + echo "Using TDXVerifier: $tdx_verifier" echo "Using ASR anchor output root: $asr_anchor_output_root" echo "Using ASR anchor L2 block: $asr_anchor_block" DEPLOY_CONFIG_PATH="{{deploy_config}}" \ forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ --sig "run(address,address,address,bytes32,uint256)" \ - "{{nitro_verifier}}" \ - "{{tdx_verifier}}" \ + "$nitro_verifier" \ + "$tdx_verifier" \ "{{tdx_manager}}" \ "$asr_anchor_output_root" \ "$asr_anchor_block" \ @@ -366,25 +338,17 @@ tee-registered-signers registry="" rpc=l1_rpc_url: echo "TEEProverRegistry: $registry" echo "RPC: {{rpc}}" - signers=() - while IFS= read -r signer; do - signers+=("$signer") - done < <(cast call "$registry" 'getRegisteredSigners()(address[])' --rpc-url "{{rpc}}" --json | jq -r '.[0][]') - if (( ${#signers[@]} == 0 )); then + signers="$(cast call "$registry" 'getRegisteredSigners()(address[])' --rpc-url "{{rpc}}" --json | jq -r '.[0][]')" + if [[ -z "$signers" ]]; then echo "No registered TEE signers found." exit 0 fi printf "%-42s %-6s %-66s %-5s\n" "SIGNER" "TYPE" "IMAGE_HASH" "VALID" - for signer in "${signers[@]}"; do + while IFS= read -r signer; do tee_type_id="$(cast call "$registry" 'signerTEEType(address)(uint8)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" image_hash="$(cast call "$registry" 'signerImageHash(address)(bytes32)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" - valid_raw="$(cast call "$registry" 'isValidSigner(address)(bool)' "$signer" --rpc-url "{{rpc}}" --json 2>/dev/null || true)" - if [[ -n "$valid_raw" ]]; then - valid="$(echo "$valid_raw" | jq -r '.[0]')" - else - valid="unknown" - fi + valid="$(cast call "$registry" 'isValidSigner(address)(bool)' "$signer" --rpc-url "{{rpc}}" --json 2>/dev/null | jq -r '.[0]' || echo unknown)" case "$tee_type_id" in 1) tee_type="NITRO" ;; @@ -393,19 +357,4 @@ tee-registered-signers registry="" rpc=l1_rpc_url: esac printf "%-42s %-6s %-66s %-5s\n" "$signer" "$tee_type" "$image_hash" "$valid" - done - -# Print the two-step Sepolia command template. -tdx-sepolia-help: - @echo 'export L1_RPC_URL=' - @echo 'export L2_RPC_URL=' - @echo 'export L2_OUTPUT_ROOT_RPC_URL=' - @echo 'just deploy-nitro-verifier ' - @echo 'just deploy-tdx-verifier' - @echo 'just deploy-tee-verifiers ' - @echo 'just deploy-tdx-system ' - @echo 'just deploy-sepolia-tdx-dev' - @echo 'just run-sepolia-tdx-dev-offchain' - @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' - @echo 'just tee-registered-signers' - @echo 'just tdx-sepolia-config' + done <<< "$signers" From b452cbc174e01b47692346fe93be941dbdb28376 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 19:52:26 -0400 Subject: [PATCH 075/146] clean up justfile --- scripts/multiproof/README.md | 8 -- scripts/multiproof/justfile | 155 +---------------------------------- 2 files changed, 4 insertions(+), 159 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 76a7849d0..692e48503 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -196,14 +196,6 @@ just --justfile scripts/multiproof/justfile deploy-tee-verifiers $NITRO_ROOT_CER This saves output to `deployments/-tee-verifiers.json`. -To deploy a fresh Nitro verifier, a fresh TDX verifier, and then the TDX -multiproof stack against those exact verifier addresses without editing -`deploy-config/sepolia.json`, use: - -```bash -just --justfile scripts/multiproof/justfile deploy-tdx-system $NITRO_ROOT_CERT $NITRO_VERIFIER_ID -``` - To deploy Nitro separately: ```bash diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 528afb3df..f5c0a1fd5 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -3,16 +3,8 @@ set dotenv-load repo_root := justfile_directory() + "/../.." justfile_path := justfile_directory() + "/justfile" zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" -hoodi_zeronet_tdx_local_deploy_config := "deploy-config/hoodi-zeronet-tdx-local.json" -hoodi_zeronet_tdx_local_deployments := "deployments/560048-dev-no-nitro.json" -sepolia_tdx_stack_deployments := "deployments/11155111-dev-with-tdx.json" -sepolia_tee_verifiers_deployments := "deployments/11155111-tee-verifiers.json" forge_account := "testnet-admin" l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") -hoodi_l1_rpc_url := env_var_or_default("HOODI_L1_RPC_URL", "https://c3-chainproxy-eth-hoodi-full-dev.cbhq.net") -hoodi_l2_rpc_url := env_var_or_default("HOODI_L2_RPC_URL", "https://base-zeronet-reth-proofs-donotuse.cbhq.net:8545") -hoodi_rollup_rpc_url := env_var_or_default("HOODI_ROLLUP_RPC_URL", "https://base-zeronet-reth-rpc-donotuse.cbhq.net:7545") -hoodi_l2_output_root_rpc_url := env_var_or_default("HOODI_L2_OUTPUT_ROOT_RPC_URL", hoodi_rollup_rpc_url) l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-reth-proofs-k8s-donotuse.cbhq.net:8545") l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-reth-internal-rpc-donotuse.cbhq.net:7545") @@ -152,82 +144,6 @@ deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepoli '{NitroEnclaveVerifier: $nitro, TDXVerifier: $tdx}' > "$out_path" echo "Deployment saved to: $out_path" -# Print the Sepolia TDX constants used by these recipes. -tdx-sepolia-config: - @echo "L1_RPC_URL={{l1_rpc_url}}" - @jq -r '"TEE_PROVER_REGISTRY=" + (.TEEProverRegistry // "")' "{{repo_root}}/{{sepolia_tdx_stack_deployments}}" - @echo "NITRO_RISC0_VERIFIER_ROUTER={{sepolia_nitro_risc0_verifier_router}}" - @echo "TDX_RISC0_VERIFIER_ROUTER={{sepolia_tdx_risc0_verifier_router}}" - @echo "RISC0_SET_BUILDER_IMAGE_ID={{sepolia_risc0_set_builder_image_id}}" - @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" - @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" - @echo "TDX_REGISTRATION_MANAGER={{sepolia_tdx_registration_manager}}" - @echo "L2_RPC_URL={{l2_rpc_url}}" - @echo "L2_OUTPUT_ROOT_RPC_URL={{l2_output_root_rpc_url}}" - -# Deploy the no-attestation TDX local testing stack on Hoodi. -deploy-hoodi-zeronet-tdx-local rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config l2_rpc=hoodi_l2_rpc_url l2_output_root_rpc=hoodi_l2_output_root_rpc_url: - #!/usr/bin/env bash - set -euo pipefail - cd "{{repo_root}}" - deployer="$(cast wallet address --account "{{forge_account}}")" - echo "Using {{forge_account}} as deployer: $deployer" - echo "Using deploy config: {{deploy_config}}" - - read -r asr_anchor_block asr_anchor_output_root < <(just --justfile "{{justfile_path}}" _asr-anchor "{{l2_rpc}}" "{{l2_output_root_rpc}}") - - echo "Using ASR anchor output root: $asr_anchor_output_root" - echo "Using ASR anchor L2 block: $asr_anchor_block" - - DEPLOY_CONFIG_PATH="{{deploy_config}}" \ - forge script scripts/multiproof/DeployDevNoNitro.s.sol:DeployDevNoNitro \ - --sig "run(bytes32,uint256)" \ - "$asr_anchor_output_root" \ - "$asr_anchor_block" \ - --rpc-url "{{rpc}}" \ - --broadcast \ - --account "{{forge_account}}" \ - --sender "$deployer" - -# Register a TDX signer in the Hoodi no-attestation local testing registry. -register-dev-tdx-signer signer registry="" image_hash="" rpc=hoodi_l1_rpc_url deploy_config=hoodi_zeronet_tdx_local_deploy_config deployments=hoodi_zeronet_tdx_local_deployments: - #!/usr/bin/env bash - set -euo pipefail - cd "{{repo_root}}" - - registry="{{registry}}" - if [[ -z "$registry" ]]; then - registry="$(jq -r '.TEEProverRegistry // empty' "{{repo_root}}/{{deployments}}")" - fi - - image_hash="{{image_hash}}" - if [[ -z "$image_hash" ]]; then - image_hash="$(jq -r '.teeTdxImageHash // empty' "{{repo_root}}/{{deploy_config}}")" - fi - - if [[ -z "$registry" || "$registry" == "null" ]]; then - echo "Missing TEEProverRegistry address. Pass one explicitly or deploy {{deployments}}." >&2 - exit 1 - fi - if [[ -z "$image_hash" || "$image_hash" == "null" ]]; then - echo "Missing teeTdxImageHash. Pass one explicitly or update {{deploy_config}}." >&2 - exit 1 - fi - - owner="$(cast wallet address --account "{{forge_account}}")" - echo "Using {{forge_account}} as owner: $owner" - echo "TEEProverRegistry: $registry" - echo "TDX signer: {{signer}}" - echo "TDX image hash: $image_hash" - - cast send "$registry" \ - "addDevTDXSigner(address,bytes32)" \ - "{{signer}}" \ - "$image_hash" \ - --rpc-url "{{rpc}}" \ - --account "{{forge_account}}" \ - --from "$owner" - # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. # # Required env: @@ -286,75 +202,12 @@ deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_regis --account "{{forge_account}}" \ --sender "$owner" -# Deploy fresh Nitro and TDX verifier policy contracts, then deploy the TDX stack -# against those verifier addresses without editing shared Sepolia deploy config. -# -# Required args: -# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate -# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest -deploy-tdx-system nitro_root_cert nitro_verifier_id tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: - #!/usr/bin/env bash - set -euo pipefail - cd "{{repo_root}}" - - just --justfile "{{justfile_path}}" deploy-tee-verifiers "{{nitro_root_cert}}" "{{nitro_verifier_id}}" - - nitro_verifier="$(jq -r '.NitroEnclaveVerifier // empty' "{{sepolia_tee_verifiers_deployments}}")" - tdx_verifier="$(jq -r '.TDXVerifier // empty' "{{sepolia_tee_verifiers_deployments}}")" - - if [[ -z "$nitro_verifier" || -z "$tdx_verifier" ]]; then - echo "Missing verifier address in {{sepolia_tee_verifiers_deployments}}" >&2 - exit 1 - fi - +# Deploy the TDX multiproof dev/test stack on Sepolia. +deploy-sepolia-tdx-dev tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: just --justfile "{{justfile_path}}" deploy-tdx-stack \ - "$nitro_verifier" \ - "$tdx_verifier" \ + "" \ + "" \ "{{tdx_manager}}" \ "{{deploy_config}}" \ "{{l2_rpc}}" \ "{{l2_output_root_rpc}}" - -# Args: -# registry TEEProverRegistry address. Defaults to deployments/11155111-dev-with-tdx.json. -# rpc L1 Sepolia RPC URL. -# -# Query currently registered TEE signer addresses on Sepolia. -tee-registered-signers registry="" rpc=l1_rpc_url: - #!/usr/bin/env bash - set -euo pipefail - cd "{{repo_root}}" - - registry="{{registry}}" - if [[ -z "$registry" ]]; then - registry="$(jq -r '.TEEProverRegistry // empty' "{{sepolia_tdx_stack_deployments}}")" - fi - - if [[ -z "$registry" || "$registry" == "null" ]]; then - echo "Missing TEEProverRegistry address. Pass one explicitly or update {{sepolia_tdx_stack_deployments}}." >&2 - exit 1 - fi - - echo "TEEProverRegistry: $registry" - echo "RPC: {{rpc}}" - - signers="$(cast call "$registry" 'getRegisteredSigners()(address[])' --rpc-url "{{rpc}}" --json | jq -r '.[0][]')" - if [[ -z "$signers" ]]; then - echo "No registered TEE signers found." - exit 0 - fi - - printf "%-42s %-6s %-66s %-5s\n" "SIGNER" "TYPE" "IMAGE_HASH" "VALID" - while IFS= read -r signer; do - tee_type_id="$(cast call "$registry" 'signerTEEType(address)(uint8)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" - image_hash="$(cast call "$registry" 'signerImageHash(address)(bytes32)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" - valid="$(cast call "$registry" 'isValidSigner(address)(bool)' "$signer" --rpc-url "{{rpc}}" --json 2>/dev/null | jq -r '.[0]' || echo unknown)" - - case "$tee_type_id" in - 1) tee_type="NITRO" ;; - 2) tee_type="TDX" ;; - *) tee_type="NONE" ;; - esac - - printf "%-42s %-6s %-66s %-5s\n" "$signer" "$tee_type" "$image_hash" "$valid" - done <<< "$signers" From 78348d38877193870941211ee7a36f7d7ed6ee67 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:06:08 -0400 Subject: [PATCH 076/146] clean up justfile --- scripts/multiproof/justfile | 117 ++++++++---------------------------- 1 file changed, 25 insertions(+), 92 deletions(-) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index f5c0a1fd5..2fe4ab4bb 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -16,49 +16,10 @@ sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb24 sepolia_tdx_registration_manager := "0x44E999A5859c2D12378a349882fAe5805DCE71b9" zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000000" -# List available multiproof deployment recipes. default: @just --justfile "{{justfile_path}}" --list -_asr-anchor l2_rpc l2_output_root_rpc: - #!/usr/bin/env bash - set -euo pipefail - - if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 - exit 1 - fi - asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" - asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" - else - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" - l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" - if (( lookback > l2_head )); then - echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 - exit 1 - fi - asr_anchor_block=$((l2_head - lookback)) - asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" - fi - - if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then - echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 - exit 1 - fi - - printf '%s %s\n' "$asr_anchor_block" "$asr_anchor_output_root" - # Deploy the TDXVerifier policy contract. -# -# Required env: -# L1_RPC_URL -# -# Args: -# risc0_router RISC Zero verifier router -# tdx_verifier_id RISC Zero image ID for the TDX DCAP verifier guest -# intel_root_ca_hash Trusted Intel root CA hash consumed by the guest deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: #!/usr/bin/env bash set -euo pipefail @@ -76,15 +37,6 @@ deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_ --sender "$owner" # Deploy the NitroEnclaveVerifier policy contract and its local RISC Zero set verifier route. -# -# Required args: -# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate -# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest -# -# Optional args: -# risc0_router RISC Zero verifier router -# set_builder_image_id RISC Zero set builder image ID -# nitro_verifier_proof_id Verifier proof ID for batched Nitro proofs; defaults to zero deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nitro_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32: #!/usr/bin/env bash set -euo pipefail @@ -104,30 +56,12 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nit --account "{{forge_account}}" \ --sender "$owner" -# Deploy both TEE verifier policy contracts with the existing single-verifier scripts. -# -# Required args: -# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate -# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest -# -# Optional args default to the Sepolia TDX testing values in this justfile. -deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepolia_nitro_risc0_verifier_router tdx_risc0_router=sepolia_tdx_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32 tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: +# Deploy both TEE verifier policy contracts and write combined deployment output. +deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepolia_nitro_risc0_verifier_router tdx_risc0_router=sepolia_tdx_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32 tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: (deploy-nitro-verifier nitro_root_cert nitro_verifier_id nitro_risc0_router set_builder_image_id nitro_verifier_proof_id) (deploy-tdx-verifier tdx_risc0_router tdx_verifier_id intel_root_ca_hash) #!/usr/bin/env bash set -euo pipefail cd "{{repo_root}}" - just --justfile "{{justfile_path}}" deploy-nitro-verifier \ - "{{nitro_root_cert}}" \ - "{{nitro_verifier_id}}" \ - "{{nitro_risc0_router}}" \ - "{{set_builder_image_id}}" \ - "{{nitro_verifier_proof_id}}" - - just --justfile "{{justfile_path}}" deploy-tdx-verifier \ - "{{tdx_risc0_router}}" \ - "{{tdx_verifier_id}}" \ - "{{intel_root_ca_hash}}" - chain_id="$(cast chain-id --rpc-url "{{l1_rpc_url}}")" nitro_path="deployments/${chain_id}-nitro-verifier.json" tdx_path="deployments/${chain_id}-tdx-verifier.json" @@ -145,19 +79,6 @@ deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepoli echo "Deployment saved to: $out_path" # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. -# -# Required env: -# L1_RPC_URL -# L2_RPC_URL Optional when using this recipe; defaults to Base Sepolia L2. -# L2_OUTPUT_ROOT_RPC_URL Optional when using this recipe; defaults to Base Sepolia op-node/archive RPC. -# ASR_ANCHOR_BLOCK_LOOKBACK Optional L2 block lookback from head; defaults to 0. -# ASR_ANCHOR_BLOCK_NUMBER Optional explicit L2 block. Requires ASR_ANCHOR_OUTPUT_ROOT. -# ASR_ANCHOR_OUTPUT_ROOT Optional explicit output root. Requires ASR_ANCHOR_BLOCK_NUMBER. -# -# Args: -# nitro_verifier Optional NitroEnclaveVerifier address; defaults to deploy_config.nitroEnclaveVerifier -# tdx_verifier Optional TDXVerifier address; defaults to deploy_config.tdxVerifier -# tdx_manager Manager that can call registerTDXSigner() deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail @@ -182,7 +103,29 @@ deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_regis exit 1 fi - read -r asr_anchor_block asr_anchor_output_root < <(just --justfile "{{justfile_path}}" _asr-anchor "{{l2_rpc}}" "{{l2_output_root_rpc}}") + if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 + exit 1 + fi + asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" + asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" + else + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(printf '0x%x' "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + fi + + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi echo "Using NitroEnclaveVerifier: $nitro_verifier" echo "Using TDXVerifier: $tdx_verifier" @@ -201,13 +144,3 @@ deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_regis --broadcast \ --account "{{forge_account}}" \ --sender "$owner" - -# Deploy the TDX multiproof dev/test stack on Sepolia. -deploy-sepolia-tdx-dev tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: - just --justfile "{{justfile_path}}" deploy-tdx-stack \ - "" \ - "" \ - "{{tdx_manager}}" \ - "{{deploy_config}}" \ - "{{l2_rpc}}" \ - "{{l2_output_root_rpc}}" From 01d0b6d57c94bb98d4fc5483669ff55f3f86c2c3 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:14:09 -0400 Subject: [PATCH 077/146] clean up justfile --- scripts/multiproof/README.md | 3 ++- scripts/multiproof/justfile | 29 +++-------------------------- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 692e48503..89ea01d73 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -194,7 +194,8 @@ Deploy both TEE verifier policy contracts with one command: just --justfile scripts/multiproof/justfile deploy-tee-verifiers $NITRO_ROOT_CERT $NITRO_VERIFIER_ID ``` -This saves output to `deployments/-tee-verifiers.json`. +This saves the individual verifier outputs under `deployments/-nitro-verifier.json` +and `deployments/-tdx-verifier.json`. To deploy Nitro separately: diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 2fe4ab4bb..e832df00e 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -1,7 +1,6 @@ set dotenv-load +set working-directory := "../.." -repo_root := justfile_directory() + "/../.." -justfile_path := justfile_directory() + "/justfile" zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" forge_account := "testnet-admin" l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") @@ -17,13 +16,12 @@ sepolia_tdx_registration_manager := "0x44E999A5859c2D12378a349882fAe5805DCE71b9" zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000000" default: - @just --justfile "{{justfile_path}}" --list + @just --justfile "{{justfile()}}" --list # Deploy the TDXVerifier policy contract. deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: #!/usr/bin/env bash set -euo pipefail - cd "{{repo_root}}" owner="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as owner/deployer: $owner" forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ @@ -40,7 +38,6 @@ deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_ deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nitro_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32: #!/usr/bin/env bash set -euo pipefail - cd "{{repo_root}}" owner="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as owner/deployer: $owner" forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ @@ -56,33 +53,13 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nit --account "{{forge_account}}" \ --sender "$owner" -# Deploy both TEE verifier policy contracts and write combined deployment output. +# Deploy both TEE verifier policy contracts. deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepolia_nitro_risc0_verifier_router tdx_risc0_router=sepolia_tdx_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32 tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: (deploy-nitro-verifier nitro_root_cert nitro_verifier_id nitro_risc0_router set_builder_image_id nitro_verifier_proof_id) (deploy-tdx-verifier tdx_risc0_router tdx_verifier_id intel_root_ca_hash) - #!/usr/bin/env bash - set -euo pipefail - cd "{{repo_root}}" - - chain_id="$(cast chain-id --rpc-url "{{l1_rpc_url}}")" - nitro_path="deployments/${chain_id}-nitro-verifier.json" - tdx_path="deployments/${chain_id}-tdx-verifier.json" - out_path="deployments/${chain_id}-tee-verifiers.json" - nitro_verifier="$(jq -r '.NitroEnclaveVerifier // empty' "$nitro_path")" - tdx_verifier="$(jq -r '.TDXVerifier // empty' "$tdx_path")" - - if [[ -z "$nitro_verifier" || -z "$tdx_verifier" ]]; then - echo "Missing verifier address in $nitro_path or $tdx_path" >&2 - exit 1 - fi - - jq -n --arg nitro "$nitro_verifier" --arg tdx "$tdx_verifier" \ - '{NitroEnclaveVerifier: $nitro, TDXVerifier: $tdx}' > "$out_path" - echo "Deployment saved to: $out_path" # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail - cd "{{repo_root}}" owner="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as deployer: $owner" From e047e24a0289c4aadf71e2bc8c32bc98596fdbbd Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:18:27 -0400 Subject: [PATCH 078/146] clean up justfile --- scripts/multiproof/README.md | 17 +++-------------- scripts/multiproof/justfile | 32 +++++--------------------------- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 89ea01d73..5cc2ccbcf 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -188,16 +188,7 @@ TDX_REGISTRATION_MANAGER=0x44E999A5859c2D12378a349882fAe5805DCE71b9 constructor also requires a non-zero `tdxVerifier`; for config-driven deployments, set `tdxVerifier` in the deploy config to the deployed `TDXVerifier` address. -Deploy both TEE verifier policy contracts with one command: - -```bash -just --justfile scripts/multiproof/justfile deploy-tee-verifiers $NITRO_ROOT_CERT $NITRO_VERIFIER_ID -``` - -This saves the individual verifier outputs under `deployments/-nitro-verifier.json` -and `deployments/-tdx-verifier.json`. - -To deploy Nitro separately: +Deploy Nitro: ```bash just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID @@ -205,7 +196,7 @@ just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CE This saves output to `deployments/-nitro-verifier.json`. -To deploy TDX separately: +Deploy TDX: ```bash just --justfile scripts/multiproof/justfile deploy-tdx-verifier @@ -242,11 +233,9 @@ forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ --private-key $PRIVATE_KEY ``` -To override all Nitro and TDX inputs while still deploying both, pass the optional args to `deploy-tee-verifiers`. - ### Step 2: Deploy the TDX multiproof test stack -Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `NitroEnclaveVerifier` and `TDXVerifier` addresses from Step 1. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. +The recipe defaults `DEPLOY_CONFIG_PATH` to `deploy-config/zeronet-tdx.json` and requires the `NitroEnclaveVerifier` and `TDXVerifier` addresses from Step 1. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index e832df00e..4747d78d5 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -1,7 +1,6 @@ set dotenv-load set working-directory := "../.." -zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" forge_account := "testnet-admin" l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-reth-proofs-k8s-donotuse.cbhq.net:8545") @@ -12,7 +11,6 @@ sepolia_tdx_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 sepolia_risc0_set_builder_image_id := "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9" sepolia_tdx_verifier_id := "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009" -sepolia_tdx_registration_manager := "0x44E999A5859c2D12378a349882fAe5805DCE71b9" zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000000" default: @@ -53,33 +51,13 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nit --account "{{forge_account}}" \ --sender "$owner" -# Deploy both TEE verifier policy contracts. -deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepolia_nitro_risc0_verifier_router tdx_risc0_router=sepolia_tdx_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32 tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: (deploy-nitro-verifier nitro_root_cert nitro_verifier_id nitro_risc0_router set_builder_image_id nitro_verifier_proof_id) (deploy-tdx-verifier tdx_risc0_router tdx_verifier_id intel_root_ca_hash) - # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. -deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: +deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager="0x44E999A5859c2D12378a349882fAe5805DCE71b9" deploy_config="deploy-config/zeronet-tdx.json" l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: #!/usr/bin/env bash set -euo pipefail owner="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as deployer: $owner" - nitro_verifier="{{nitro_verifier}}" - tdx_verifier="{{tdx_verifier}}" - if [[ -z "$nitro_verifier" ]]; then - nitro_verifier="$(jq -r '.nitroEnclaveVerifier // empty' "{{deploy_config}}")" - fi - if [[ -z "$tdx_verifier" ]]; then - tdx_verifier="$(jq -r '.tdxVerifier // empty' "{{deploy_config}}")" - fi - if [[ -z "$nitro_verifier" || "$nitro_verifier" == "null" ]]; then - echo "Missing nitroEnclaveVerifier in {{deploy_config}}" >&2 - exit 1 - fi - if [[ -z "$tdx_verifier" || "$tdx_verifier" == "null" ]]; then - echo "Missing tdxVerifier in {{deploy_config}}" >&2 - exit 1 - fi - if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 @@ -104,16 +82,16 @@ deploy-tdx-stack nitro_verifier="" tdx_verifier="" tdx_manager=sepolia_tdx_regis exit 1 fi - echo "Using NitroEnclaveVerifier: $nitro_verifier" - echo "Using TDXVerifier: $tdx_verifier" + echo "Using NitroEnclaveVerifier: {{nitro_verifier}}" + echo "Using TDXVerifier: {{tdx_verifier}}" echo "Using ASR anchor output root: $asr_anchor_output_root" echo "Using ASR anchor L2 block: $asr_anchor_block" DEPLOY_CONFIG_PATH="{{deploy_config}}" \ forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ --sig "run(address,address,address,bytes32,uint256)" \ - "$nitro_verifier" \ - "$tdx_verifier" \ + "{{nitro_verifier}}" \ + "{{tdx_verifier}}" \ "{{tdx_manager}}" \ "$asr_anchor_output_root" \ "$asr_anchor_block" \ From 36d128bf7907ddeba5bdf66dec9eae7d2dd86304 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:21:35 -0400 Subject: [PATCH 079/146] clean up justfile --- scripts/multiproof/README.md | 2 +- scripts/multiproof/justfile | 29 ++++++++++------------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 5cc2ccbcf..0fe1eeaed 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -239,7 +239,7 @@ The recipe defaults `DEPLOY_CONFIG_PATH` to `deploy-config/zeronet-tdx.json` and The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. -The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the verifier addresses, resolved output root, and L2 block through `run(address,address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, and `ASR_ANCHOR_BLOCK_LOOKBACK` to anchor a fixed number of L2 blocks behind head. For a fixed anchor, set both `ASR_ANCHOR_OUTPUT_ROOT` and `ASR_ANCHOR_BLOCK_NUMBER`. +The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the verifier addresses, resolved output root, and L2 block through `run(address,address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, and `ASR_ANCHOR_BLOCK_LOOKBACK` to anchor a fixed number of L2 blocks behind head. ```bash just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 4747d78d5..5f0f8f258 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -52,32 +52,23 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nit --sender "$owner" # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. -deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager="0x44E999A5859c2D12378a349882fAe5805DCE71b9" deploy_config="deploy-config/zeronet-tdx.json" l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: +deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager="0x44E999A5859c2D12378a349882fAe5805DCE71b9" deploy_config="deploy-config/zeronet-tdx.json": #!/usr/bin/env bash set -euo pipefail owner="$(cast wallet address --account "{{forge_account}}")" echo "Using {{forge_account}} as deployer: $owner" - if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then - echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 - exit 1 - fi - asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" - asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" - else - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" - l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" - if (( lookback > l2_head )); then - echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 - exit 1 - fi - asr_anchor_block=$((l2_head - lookback)) - asr_anchor_block_hex="$(printf '0x%x' "$asr_anchor_block")" - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc_url}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(printf '0x%x' "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc_url}}" | jq -r '.outputRoot')" - if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "{{zero_bytes32}}" ]]; then echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 exit 1 fi From 212251f0fa7683305bfc24dcc0e12c00bfb83aa3 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:26:54 -0400 Subject: [PATCH 080/146] clean up justfile --- scripts/multiproof/justfile | 114 ++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 5f0f8f258..09c8e3508 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -2,6 +2,8 @@ set dotenv-load set working-directory := "../.." forge_account := "testnet-admin" +owner := `cast wallet address --account "{{forge_account}}"` +broadcast_args := "--rpc-url " + quote(l1_rpc_url) + " --broadcast --account " + quote(forge_account) + " --sender " + quote(owner) l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-reth-proofs-k8s-donotuse.cbhq.net:8545") l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-reth-internal-rpc-donotuse.cbhq.net:7545") @@ -14,79 +16,65 @@ sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb24 zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000000" default: - @just --justfile "{{justfile()}}" --list # Deploy the TDXVerifier policy contract. deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: - #!/usr/bin/env bash - set -euo pipefail - owner="$(cast wallet address --account "{{forge_account}}")" - echo "Using {{forge_account}} as owner/deployer: $owner" - forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ - --sig "run(address,bytes32,bytes32)" \ - "{{risc0_router}}" \ - "{{tdx_verifier_id}}" \ - "{{intel_root_ca_hash}}" \ - --rpc-url "{{l1_rpc_url}}" \ - --broadcast \ - --account "{{forge_account}}" \ - --sender "$owner" + #!/usr/bin/env bash + set -euo pipefail + echo "Using {{ forge_account }} as owner/deployer: {{ owner }}" + forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ + --sig "run(address,bytes32,bytes32)" \ + "{{ risc0_router }}" \ + "{{ tdx_verifier_id }}" \ + "{{ intel_root_ca_hash }}" \ + {{ broadcast_args }} # Deploy the NitroEnclaveVerifier policy contract and its local RISC Zero set verifier route. deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nitro_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32: - #!/usr/bin/env bash - set -euo pipefail - owner="$(cast wallet address --account "{{forge_account}}")" - echo "Using {{forge_account}} as owner/deployer: $owner" - forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ - --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ - "$owner" \ - "{{risc0_router}}" \ - "{{set_builder_image_id}}" \ - "{{nitro_root_cert}}" \ - "{{nitro_verifier_id}}" \ - "{{nitro_verifier_proof_id}}" \ - --rpc-url "{{l1_rpc_url}}" \ - --broadcast \ - --account "{{forge_account}}" \ - --sender "$owner" + #!/usr/bin/env bash + set -euo pipefail + echo "Using {{ forge_account }} as owner/deployer: {{ owner }}" + forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ + "{{ owner }}" \ + "{{ risc0_router }}" \ + "{{ set_builder_image_id }}" \ + "{{ nitro_root_cert }}" \ + "{{ nitro_verifier_id }}" \ + "{{ nitro_verifier_proof_id }}" \ + {{ broadcast_args }} # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager="0x44E999A5859c2D12378a349882fAe5805DCE71b9" deploy_config="deploy-config/zeronet-tdx.json": - #!/usr/bin/env bash - set -euo pipefail - owner="$(cast wallet address --account "{{forge_account}}")" - echo "Using {{forge_account}} as deployer: $owner" + #!/usr/bin/env bash + set -euo pipefail + echo "Using {{ forge_account }} as deployer: {{ owner }}" - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" - l2_head="$(cast block-number --rpc-url "{{l2_rpc_url}}")" - if (( lookback > l2_head )); then - echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 - exit 1 - fi - asr_anchor_block=$((l2_head - lookback)) - asr_anchor_block_hex="$(printf '0x%x' "$asr_anchor_block")" - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc_url}}" | jq -r '.outputRoot')" + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{ l2_rpc_url }}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$(printf '0x%x' "$asr_anchor_block")" --rpc-url "{{ l2_output_root_rpc_url }}" | jq -r '.outputRoot')" - if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "{{zero_bytes32}}" ]]; then - echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 - exit 1 - fi + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "{{ zero_bytes32 }}" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi - echo "Using NitroEnclaveVerifier: {{nitro_verifier}}" - echo "Using TDXVerifier: {{tdx_verifier}}" - echo "Using ASR anchor output root: $asr_anchor_output_root" - echo "Using ASR anchor L2 block: $asr_anchor_block" + echo "Using NitroEnclaveVerifier: {{ nitro_verifier }}" + echo "Using TDXVerifier: {{ tdx_verifier }}" + echo "Using ASR anchor output root: $asr_anchor_output_root" + echo "Using ASR anchor L2 block: $asr_anchor_block" - DEPLOY_CONFIG_PATH="{{deploy_config}}" \ - forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ - --sig "run(address,address,address,bytes32,uint256)" \ - "{{nitro_verifier}}" \ - "{{tdx_verifier}}" \ - "{{tdx_manager}}" \ - "$asr_anchor_output_root" \ - "$asr_anchor_block" \ - --rpc-url "{{l1_rpc_url}}" \ - --broadcast \ - --account "{{forge_account}}" \ - --sender "$owner" + DEPLOY_CONFIG_PATH="{{ deploy_config }}" \ + forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ + --sig "run(address,address,address,bytes32,uint256)" \ + "{{ nitro_verifier }}" \ + "{{ tdx_verifier }}" \ + "{{ tdx_manager }}" \ + "$asr_anchor_output_root" \ + "$asr_anchor_block" \ + {{ broadcast_args }} From 71fc84bbcb3ad762a578dad892b8fd833d3fe818 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:30:13 -0400 Subject: [PATCH 081/146] clean up justfile --- scripts/multiproof/justfile | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 09c8e3508..999695ee0 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -1,5 +1,6 @@ set dotenv-load set working-directory := "../.." +set shell := ["bash", "-euo", "pipefail", "-c"] forge_account := "testnet-admin" owner := `cast wallet address --account "{{forge_account}}"` @@ -18,36 +19,33 @@ zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000 default: # Deploy the TDXVerifier policy contract. -deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: - #!/usr/bin/env bash - set -euo pipefail +[script] +deploy-tdx-verifier: echo "Using {{ forge_account }} as owner/deployer: {{ owner }}" forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ --sig "run(address,bytes32,bytes32)" \ - "{{ risc0_router }}" \ - "{{ tdx_verifier_id }}" \ - "{{ intel_root_ca_hash }}" \ + "{{ sepolia_tdx_risc0_verifier_router }}" \ + "{{ sepolia_tdx_verifier_id }}" \ + "{{ sepolia_intel_root_ca_hash }}" \ {{ broadcast_args }} # Deploy the NitroEnclaveVerifier policy contract and its local RISC Zero set verifier route. -deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nitro_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32: - #!/usr/bin/env bash - set -euo pipefail +[script] +deploy-nitro-verifier nitro_root_cert nitro_verifier_id: echo "Using {{ forge_account }} as owner/deployer: {{ owner }}" forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ "{{ owner }}" \ - "{{ risc0_router }}" \ - "{{ set_builder_image_id }}" \ + "{{ sepolia_nitro_risc0_verifier_router }}" \ + "{{ sepolia_risc0_set_builder_image_id }}" \ "{{ nitro_root_cert }}" \ "{{ nitro_verifier_id }}" \ - "{{ nitro_verifier_proof_id }}" \ + "{{ zero_bytes32 }}" \ {{ broadcast_args }} # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. -deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager="0x44E999A5859c2D12378a349882fAe5805DCE71b9" deploy_config="deploy-config/zeronet-tdx.json": - #!/usr/bin/env bash - set -euo pipefail +[script] +deploy-tdx-stack nitro_verifier tdx_verifier: echo "Using {{ forge_account }} as deployer: {{ owner }}" lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" @@ -69,12 +67,12 @@ deploy-tdx-stack nitro_verifier tdx_verifier tdx_manager="0x44E999A5859c2D12378a echo "Using ASR anchor output root: $asr_anchor_output_root" echo "Using ASR anchor L2 block: $asr_anchor_block" - DEPLOY_CONFIG_PATH="{{ deploy_config }}" \ + DEPLOY_CONFIG_PATH="deploy-config/zeronet-tdx.json" \ forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ --sig "run(address,address,address,bytes32,uint256)" \ "{{ nitro_verifier }}" \ "{{ tdx_verifier }}" \ - "{{ tdx_manager }}" \ + "0x44E999A5859c2D12378a349882fAe5805DCE71b9" \ "$asr_anchor_output_root" \ "$asr_anchor_block" \ {{ broadcast_args }} From d4924e5c7e5a2f958fec93a386b4eabeb72d6e05 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:32:04 -0400 Subject: [PATCH 082/146] clean up justfile --- scripts/multiproof/justfile | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 999695ee0..8a83dfec6 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -19,9 +19,7 @@ zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000 default: # Deploy the TDXVerifier policy contract. -[script] deploy-tdx-verifier: - echo "Using {{ forge_account }} as owner/deployer: {{ owner }}" forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ --sig "run(address,bytes32,bytes32)" \ "{{ sepolia_tdx_risc0_verifier_router }}" \ @@ -30,9 +28,7 @@ deploy-tdx-verifier: {{ broadcast_args }} # Deploy the NitroEnclaveVerifier policy contract and its local RISC Zero set verifier route. -[script] deploy-nitro-verifier nitro_root_cert nitro_verifier_id: - echo "Using {{ forge_account }} as owner/deployer: {{ owner }}" forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ "{{ owner }}" \ @@ -46,8 +42,6 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id: # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. [script] deploy-tdx-stack nitro_verifier tdx_verifier: - echo "Using {{ forge_account }} as deployer: {{ owner }}" - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" l2_head="$(cast block-number --rpc-url "{{ l2_rpc_url }}")" if (( lookback > l2_head )); then @@ -55,9 +49,9 @@ deploy-tdx-stack nitro_verifier tdx_verifier: exit 1 fi asr_anchor_block=$((l2_head - lookback)) - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$(printf '0x%x' "$asr_anchor_block")" --rpc-url "{{ l2_output_root_rpc_url }}" | jq -r '.outputRoot')" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$(printf '0x%x' "$asr_anchor_block")" --rpc-url "{{ l2_output_root_rpc_url }}" | jq -er '.outputRoot')" - if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "{{ zero_bytes32 }}" ]]; then + if [[ "$asr_anchor_output_root" == "{{ zero_bytes32 }}" ]]; then echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 exit 1 fi From c0f5501d768b74b25a02f67e1e6271e768fb78b2 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:36:45 -0400 Subject: [PATCH 083/146] clean up justfile --- scripts/multiproof/README.md | 2 +- scripts/multiproof/justfile | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 0fe1eeaed..3e8d8d4e9 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -239,7 +239,7 @@ The recipe defaults `DEPLOY_CONFIG_PATH` to `deploy-config/zeronet-tdx.json` and The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. -The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the verifier addresses, resolved output root, and L2 block through `run(address,address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, and `ASR_ANCHOR_BLOCK_LOOKBACK` to anchor a fixed number of L2 blocks behind head. +The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the verifier addresses, resolved output root, and L2 block through `run(address,address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, or pass an explicit third argument to anchor a fixed L2 block. ```bash just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 8a83dfec6..c1394a368 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -14,7 +14,6 @@ sepolia_tdx_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 sepolia_risc0_set_builder_image_id := "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9" sepolia_tdx_verifier_id := "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009" -zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000000" default: @@ -36,22 +35,17 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id: "{{ sepolia_risc0_set_builder_image_id }}" \ "{{ nitro_root_cert }}" \ "{{ nitro_verifier_id }}" \ - "{{ zero_bytes32 }}" \ + "0x0000000000000000000000000000000000000000000000000000000000000000" \ {{ broadcast_args }} # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. [script] -deploy-tdx-stack nitro_verifier tdx_verifier: - lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" - l2_head="$(cast block-number --rpc-url "{{ l2_rpc_url }}")" - if (( lookback > l2_head )); then - echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 - exit 1 - fi - asr_anchor_block=$((l2_head - lookback)) - asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$(printf '0x%x' "$asr_anchor_block")" --rpc-url "{{ l2_output_root_rpc_url }}" | jq -er '.outputRoot')" +deploy-tdx-stack nitro_verifier tdx_verifier asr_anchor_block="": + asr_anchor_block="{{ asr_anchor_block }}" + asr_anchor_block="${asr_anchor_block:-$(cast block-number --rpc-url "{{ l2_rpc_url }}")}" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$(cast to-hex "$asr_anchor_block")" --rpc-url "{{ l2_output_root_rpc_url }}" | jq -er '.outputRoot')" - if [[ "$asr_anchor_output_root" == "{{ zero_bytes32 }}" ]]; then + if [[ "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 exit 1 fi From 56f30ff05dee3b89c9b756b39d95937142a80395 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:40:58 -0400 Subject: [PATCH 084/146] clean up justfile --- scripts/multiproof/justfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index c1394a368..ed6eee0e9 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -41,8 +41,7 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id: # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. [script] deploy-tdx-stack nitro_verifier tdx_verifier asr_anchor_block="": - asr_anchor_block="{{ asr_anchor_block }}" - asr_anchor_block="${asr_anchor_block:-$(cast block-number --rpc-url "{{ l2_rpc_url }}")}" + asr_anchor_block="$([[ -n "{{ asr_anchor_block }}" ]] && printf '%s' "{{ asr_anchor_block }}" || cast block-number --rpc-url "{{ l2_rpc_url }}")" asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$(cast to-hex "$asr_anchor_block")" --rpc-url "{{ l2_output_root_rpc_url }}" | jq -er '.outputRoot')" if [[ "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then From 1e0d95b437b2bd380d75d4fa0b0a1b3f4ff29a0f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:46:34 -0400 Subject: [PATCH 085/146] clean up readme --- scripts/multiproof/README.md | 191 +---------------------------------- 1 file changed, 4 insertions(+), 187 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 3e8d8d4e9..3db44bcae 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -86,16 +86,7 @@ Query the enclave for its signer public key: cast rpc enclave_signerPublicKey -r https://base-proofs-prover-nitro-dev.cbhq.net ``` -This returns a raw byte array representing an uncompressed secp256k1 public key (65 bytes, starting with `0x04`). To convert it to an Ethereum address, strip the `0x04` prefix byte, keccak256-hash the remaining 64 bytes, and take the last 20 bytes: - -```bash -# Example — replace the array with the actual bytes returned by enclave_signerPublicKey -% PUB_KEY_HEX=$(python3 -c 'data=[4,100,32,206,76,214,221,167,247,152,244,81,135,139,245,114,92,16,194,181,5,126,180,170,159,214,176,6,51,103,228,117,224,176,243,160,107,112,6,214,20,46,169,42,75,45,190,178,224,54,111,208,42,6,11,198,138,118,144,226,1,147,38,86,196]; print("0x" + bytes(data[1:]).hex())') -% HASH=$(cast keccak $PUB_KEY_HEX) -% RAW_ADDRESS="0x${HASH: -40}" -% cast to-check-sum-address $RAW_ADDRESS -0x0cbe4A965B41DA6B2D5AF4d53c0C16a37d6f9F7D -``` +This returns a raw byte array representing an uncompressed secp256k1 public key (65 bytes, starting with `0x04`). To convert it to an Ethereum address, strip the `0x04` prefix byte, keccak256-hash the remaining 64 bytes, and take the last 20 bytes. ### Step 5: Register the dev signers @@ -131,62 +122,13 @@ The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` ## Path 2: TDX (Production-Path PoC) -The TDX path follows the same split as Nitro: expensive attestation verification happens offchain in a ZK guest, -and Solidity verifies the proof plus the onchain acceptance policy before registering the signer. - -| Contract | Purpose | -| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `TDXVerifier` | Verifies a RISC Zero proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | -| `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, tracking which TEE type each signer came from for `TEEVerifier`. | - -The ZK verifier guest is expected to perform the full Intel DCAP verification path: - -```text -TD Quote signature -PCK certificate chain -TCB info signing chain and TCB status -QE identity signing chain -CRLs/revocation state -TDREPORT field extraction -``` - -The Solidity verifier then enforces local policy over the proven journal. The PoC maps TDX measurements into `TEE_TDX_IMAGE_HASH` as: - -```text -keccak256(MRTD || RTMR0 || RTMR1 || RTMR2 || RTMR3) -``` - -The attested public key must be supplied as an uncompressed 65-byte secp256k1 public key: - -```text -0x04 || x || y -``` - -The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 bytes. The last 32 bytes are returned by the verifier as app-specific binding data and emitted by the registry. - -`TEEVerifier` is still the proposal-proof verifier, but a TEE proposal proof now requires two signatures over the same journal: one from a Nitro-registered signer and one from a TDX-registered signer. The proof bytes are `proposer || signatureA || signatureB`; either signature order is accepted as long as both registered TEE types are present and both signers match their expected type-specific image hash. - -> **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining offchain piece is the actual RISC Zero TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. +TDX registration requires both Nitro and TDX signers. `TDXVerifier` verifies `TDXVerifierJournal`; `TEEProverRegistry` registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`. ### Step 1: Deploy verifier policy contracts `NitroEnclaveVerifier` and `TDXVerifier` are deployed separately from the main multiproof stack because they depend on verifier interfaces that require Solidity `^0.8.20`, while the rest of the multiproof deployment stack is pinned to Solidity `0.8.15`. -For Sepolia TDX testing, `scripts/multiproof/justfile` defaults to: - -```bash -NITRO_RISC0_VERIFIER_ROUTER=0xB121B667dd2cf27F95f9F5107137696F56f188f6 -TDX_RISC0_VERIFIER_ROUTER=0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 -RISC0_SET_BUILDER_IMAGE_ID=0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9 -TDX_VERIFIER_ID=0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b -INTEL_ROOT_CA_HASH=0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009 -TDX_IMAGE_HASH=0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66 -TDX_REGISTRATION_MANAGER=0x44E999A5859c2D12378a349882fAe5805DCE71b9 -``` - -`deploy-config/sepolia.json` uses the same TDX RISC Zero verifier router and `TDX_IMAGE_HASH`. The `TEEProverRegistry` -constructor also requires a non-zero `tdxVerifier`; for config-driven deployments, set `tdxVerifier` in the deploy -config to the deployed `TDXVerifier` address. +Sepolia defaults live in `scripts/multiproof/justfile`. Config-driven deployments get `tdxVerifier` and image hashes from the deploy config. Deploy Nitro: @@ -202,37 +144,8 @@ Deploy TDX: just --justfile scripts/multiproof/justfile deploy-tdx-verifier ``` -To override any TDX verifier input manually, pass all three verifier args: - -```bash -forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ - --sig "run(address,bytes32,bytes32)" \ - $TDX_RISC0_VERIFIER_ROUTER \ - $TDX_VERIFIER_ID \ - $INTEL_ROOT_CA_HASH \ - --rpc-url $L1_RPC_URL \ - --broadcast \ - --private-key $PRIVATE_KEY -``` - The script saves output to `deployments/-tdx-verifier.json`. -To override Nitro inputs manually: - -```bash -forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ - --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ - $OWNER \ - $NITRO_RISC0_VERIFIER_ROUTER \ - $RISC0_SET_BUILDER_IMAGE_ID \ - $NITRO_ROOT_CERT \ - $NITRO_VERIFIER_ID \ - $NITRO_VERIFIER_PROOF_ID \ - --rpc-url $L1_RPC_URL \ - --broadcast \ - --private-key $PRIVATE_KEY -``` - ### Step 2: Deploy the TDX multiproof test stack The recipe defaults `DEPLOY_CONFIG_PATH` to `deploy-config/zeronet-tdx.json` and requires the `NitroEnclaveVerifier` and `TDXVerifier` addresses from Step 1. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. @@ -245,27 +158,6 @@ The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking ` just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER ``` -To override the manager manually, use: - -```bash -export L2_RPC_URL= -export L2_OUTPUT_ROOT_RPC_URL= -ASR_ANCHOR_BLOCK_NUMBER=$(cast block-number --rpc-url $L2_RPC_URL) -ASR_ANCHOR_OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast to-hex $ASR_ANCHOR_BLOCK_NUMBER) \ - --rpc-url $L2_OUTPUT_ROOT_RPC_URL | jq -r '.outputRoot') - -forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ - --sig "run(address,address,address,bytes32,uint256)" \ - $NITRO_VERIFIER \ - $TDX_VERIFIER \ - $TDX_REGISTRATION_MANAGER \ - $ASR_ANCHOR_OUTPUT_ROOT \ - $ASR_ANCHOR_BLOCK_NUMBER \ - --rpc-url $L1_RPC_URL \ - --broadcast \ - --private-key $PRIVATE_KEY -``` - The script saves output to `deployments/-dev-with-tdx.json`. ### Step 3: Register Nitro and TDX signers @@ -296,79 +188,4 @@ cast send $TEE_PROVER_REGISTRY \ ## Pre-Seeding Games (Post-Deployment) -After deploying via either path, you can pre-seed the `DisputeGameFactory` with a chain of `AggregateVerifier` games. This is useful for testing forward traversal at proposer restart — the proposer can walk the linked list of games to find where to resume. - -Games are created using `ProofType.ZK` with the `MockVerifier` (deployed by both WithNitro and NoNitro), which auto-accepts any proof. The output roots themselves are real values fetched from an L2 archive node. - -### Step 1: Set the anchor state - -Pick an anchor block far enough behind the L2 tip to cover all the games you want to create. Each game covers `BLOCK_INTERVAL` (600) L2 blocks, so for 500 games you need 300,000 blocks of headroom. - -```bash -# Calculate an anchor block 300,000 blocks behind the L2 tip -ANCHOR_BLOCK=$(( $(cast block-number --rpc-url $L2_RPC_URL) - 300000 )) - -# Get the real output root at that block -OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(printf "0x%x" $ANCHOR_BLOCK) \ - --rpc-url $L2_RPC_URL | jq -r '.outputRoot') - -# Set it on the MockAnchorStateRegistry (no access control — any caller works) -cast send $ANCHOR_STATE_REGISTRY_ADDRESS \ - "setAnchorState(bytes32,uint256)" $OUTPUT_ROOT $ANCHOR_BLOCK \ - --rpc-url $L1_RPC_URL --private-key $PRIVATE_KEY -``` - -### Step 2: Generate real output roots - -Fetch the real L2 output roots for every intermediate block across all games. This queries `optimism_outputAtBlock` on the L2 archive node (10,000 queries for 500 games, parallelized). - -```bash -./scripts/multiproof/generate-roots.sh $ANCHOR_BLOCK $L2_RPC_URL 500 -``` - -Arguments: ` [game_count] [parallelism] [output_file]` - -Defaults: `game_count=500`, `parallelism=20`, `output_file=roots.json`. - -### Step 3: Move roots file for Foundry access - -Foundry's filesystem sandbox only allows reads from paths listed in `foundry.toml` `fs_permissions`. The `deployments/` directory already has read-write access, so move the file there: - -```bash -mv roots.json deployments/roots.json -``` - -### Step 4: Run the seeding script - -Create all games onchain. Each game is chained to the previous one (game 0's parent is the `AnchorStateRegistry`, game N's parent is game N-1). The account running this needs enough ETH for bonds and gas (500 games at 0.00001 ETH bond = 0.005 ETH + gas). - -```bash -ROOTS_FILE=./deployments/roots.json \ -FACTORY_ADDRESS=$FACTORY_ADDRESS \ -ANCHOR_STATE_REGISTRY_ADDRESS=$ANCHOR_STATE_REGISTRY_ADDRESS \ -forge script scripts/multiproof/SeedGames.s.sol \ - --rpc-url $L1_RPC_URL --broadcast --private-key $PRIVATE_KEY -``` - -> **Note:** Use `--private-key` instead of `--ledger` to avoid manually confirming 500 transactions on a hardware wallet. - -Optional env vars: - -| Variable | Default | Description | -| ------------ | ------------ | ----------------------------- | -| `GAME_COUNT` | 500 | Number of games to create | -| `ROOTS_FILE` | `roots.json` | Path to the output roots JSON | - -### Step 5: Verify onchain - -```bash -# Check total game count -cast call $FACTORY_ADDRESS "gameCount()(uint256)" --rpc-url $L1_RPC_URL - -# Check first game's parent is the AnchorStateRegistry -FIRST_GAME=$(cast call $FACTORY_ADDRESS \ - "gameAtIndex(uint256)(uint32,uint64,address)" 0 --rpc-url $L1_RPC_URL | tail -1) -cast call $FIRST_GAME "parentAddress()(address)" --rpc-url $L1_RPC_URL -``` - -Output metadata is saved to `deployments/-seeded-games.json`. +For forward-traversal testing, use `scripts/multiproof/generate-roots.sh` to write roots and `scripts/multiproof/SeedGames.s.sol` to create games onchain. Their usage and env docs live in those files. From a9fe2a6cace4da6956c39a6860168a6dba016644 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:54:59 -0400 Subject: [PATCH 086/146] clean up readme --- scripts/multiproof/README.md | 136 ++++++++++++----------------------- 1 file changed, 46 insertions(+), 90 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 3db44bcae..f0426c95a 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -2,17 +2,13 @@ This guide covers deploying the multiproof contracts and registering a prover on Sepolia. ---- - -## ⚠️ Dev/Test Scripts Only +## Dev/Test Scripts Only The scripts in this directory are **development and testing tools only**. They are not suitable for production deployments. Specifically, the NoNitro path (`DeployDevNoNitro.s.sol`): - Does **no AWS Nitro attestation checking**. Instead it uses a bypass function for quickly registering provers: [`MockDevTEEProverRegistry.addDevSigner()`](https://github.com/base/contracts/blob/main/test/mocks/MockDevTEEProverRegistry.sol#L22) - Uses a simplified mock `AnchorStateRegistry` (with some differences from the real one): [`MockAnchorStateRegistry`](https://github.com/base/contracts/blob/main/scripts/multiproof/mocks/MockAnchorStateRegistry.sol) ---- - ## Prerequisites Install dependencies if you haven't already (required after any `lib/` changes): @@ -21,15 +17,13 @@ Install dependencies if you haven't already (required after any `lib/` changes): just deps ``` ---- - -## Path 1: NoNitro (Dev — No Attestation) +## Path 1: NoNitro (Dev - No Attestation) Use this when you don't have access to an AWS Nitro enclave and want to quickly test the prover without attestation overhead. ### Step 1: Configure `deploy-config/sepolia.json` -Ensure `finalSystemOwner` is set to the address you will deploy from (i.e. the address on your Ledger at the HD path you intend to use). This address becomes the owner of all deployed contracts and must sign all subsequent admin calls. +Set `finalSystemOwner` to the deployer address, then set the multiproof and TEE fields for this environment. `finalSystemOwner` becomes the owner of all deployed contracts and must sign later admin calls. ```json { @@ -38,57 +32,38 @@ Ensure `finalSystemOwner` is set to the address you will deploy from (i.e. the a } ``` -Other relevant fields: - -| Field | Description | -| ------------------------------ | ------------------------------------------------------- | -| `teeProposer` | Address to be registered as the TEE proposer | -| `teeNitroImageHash` | PCR0 hash used when registering the Nitro dev signer | -| `teeTdxImageHash` | TDX image hash used when registering the TDX dev signer | -| `multiproofGameType` | Game type ID for the dispute game | -| `multiproofGenesisOutputRoot` | Initial anchor output root | -| `multiproofGenesisBlockNumber` | Initial anchor L2 block number | - -### Step 2: Deploy contracts - -```bash -DEPLOY_CONFIG_PATH=deploy-config/sepolia.json forge script scripts/multiproof/DeployDevNoNitro.s.sol --rpc-url https://sepolia.base.org --broadcast --ledger --hd-paths "m/44'/60'/1'/0/0" -``` - -On success, deployed addresses are printed to the console and saved to `deployments/-dev-no-nitro.json`. You will need the `AnchorStateRegistry` and `TEEProverRegistry` addresses for the steps below. - -### Step 3: Set the anchor state +### Step 2: Deploy contracts with a fresh anchor -The proving system needs a recent anchor state to catch up to chain tip. Set this immediately after deployment using a fresh block. +The proving system needs a recent anchor state to catch up to chain tip. Pass it to `DeployDevNoNitro.run(bytes32,uint256)` during deployment. ```bash -# 1. Get the latest L2 block number -BLOCK=$(cast block-number --rpc-url https://base-sepolia-reth-proofs-k8s-donotuse.cbhq.net:8545) - -# 2. Get the output root at that block -OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast 2h $BLOCK) --rpc-url https://base-sepolia-reth-internal-rpc-donotuse.cbhq.net:7545 | jq -r '.outputRoot') - -# 3. Set the anchor state on the deployed MockAnchorStateRegistry -# Replace 0x983b... with the AnchorStateRegistry address from your deployment output -cast send 0x983bD53AE522C74F1d505fb3A55d5d5B774573A7 \ - "setAnchorState(bytes32,uint256)" $OUTPUT_ROOT $BLOCK \ - --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ - --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" +BLOCK=$(cast block-number --rpc-url "$L2_RPC_URL") +OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock "$(cast to-hex "$BLOCK")" --rpc-url "$L2_OUTPUT_ROOT_RPC_URL" | jq -er '.outputRoot') + +DEPLOY_CONFIG_PATH=deploy-config/sepolia.json \ +forge script scripts/multiproof/DeployDevNoNitro.s.sol:DeployDevNoNitro \ + --sig "run(bytes32,uint256)" \ + "$OUTPUT_ROOT" \ + "$BLOCK" \ + --rpc-url "$L1_RPC_URL" \ + --broadcast \ + --ledger \ + --hd-paths "$LEDGER_PATH" ``` -> **Note:** `MockAnchorStateRegistry.setAnchorState()` has no access control — any address can call it. +On success, deployed addresses are printed to the console and saved to `deployments/-dev-no-nitro.json`. -### Step 4: Get the enclave signer public key +### Step 3: Get the enclave signer public key Query the enclave for its signer public key: ```bash -cast rpc enclave_signerPublicKey -r https://base-proofs-prover-nitro-dev.cbhq.net +cast rpc enclave_signerPublicKey --rpc-url "$ENCLAVE_RPC_URL" ``` This returns a raw byte array representing an uncompressed secp256k1 public key (65 bytes, starting with `0x04`). To convert it to an Ethereum address, strip the `0x04` prefix byte, keccak256-hash the remaining 64 bytes, and take the last 20 bytes. -### Step 5: Register the dev signers +### Step 4: Register the dev signers Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signer on the deployed `DevTEEProverRegistry`. @@ -97,29 +72,25 @@ Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signe > only tracks which signer addresses are valid. ```bash -# Replace: -# 0x587d... with the TEEProverRegistry address from your deployment output -# 0x080f... with the Nitro signer address derived in Step 4 -cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ +cast send "$TEE_PROVER_REGISTRY" \ "addDevSigner(address,bytes32)" \ - 0x080f42420846c613158D7b4334257C78bE5A9B90 \ - $TEE_NITRO_IMAGE_HASH \ - --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ - --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" + "$NITRO_SIGNER_ADDRESS" \ + "$TEE_NITRO_IMAGE_HASH" \ + --rpc-url "$L1_RPC_URL" \ + --ledger \ + --mnemonic-derivation-path "$LEDGER_PATH" -# Register a TDX dev signer for the TDX image hash. -cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ +cast send "$TEE_PROVER_REGISTRY" \ "addDevTDXSigner(address,bytes32)" \ - $TDX_SIGNER_ADDRESS \ - $TEE_TDX_IMAGE_HASH \ - --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ - --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" + "$TDX_SIGNER_ADDRESS" \ + "$TEE_TDX_IMAGE_HASH" \ + --rpc-url "$L1_RPC_URL" \ + --ledger \ + --mnemonic-derivation-path "$LEDGER_PATH" ``` The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` and must sign this call. ---- - ## Path 2: TDX (Production-Path PoC) TDX registration requires both Nitro and TDX signers. `TDXVerifier` verifies `TDXVerifierJournal`; `TEEProverRegistry` registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`. @@ -128,31 +99,18 @@ TDX registration requires both Nitro and TDX signers. `TDXVerifier` verifies `TD `NitroEnclaveVerifier` and `TDXVerifier` are deployed separately from the main multiproof stack because they depend on verifier interfaces that require Solidity `^0.8.20`, while the rest of the multiproof deployment stack is pinned to Solidity `0.8.15`. -Sepolia defaults live in `scripts/multiproof/justfile`. Config-driven deployments get `tdxVerifier` and image hashes from the deploy config. - -Deploy Nitro: +Sepolia defaults live in `scripts/multiproof/justfile`. ```bash just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID -``` - -This saves output to `deployments/-nitro-verifier.json`. - -Deploy TDX: - -```bash just --justfile scripts/multiproof/justfile deploy-tdx-verifier ``` -The script saves output to `deployments/-tdx-verifier.json`. +These save output to `deployments/-nitro-verifier.json` and `deployments/-tdx-verifier.json`. ### Step 2: Deploy the TDX multiproof test stack -The recipe defaults `DEPLOY_CONFIG_PATH` to `deploy-config/zeronet-tdx.json` and requires the `NitroEnclaveVerifier` and `TDXVerifier` addresses from Step 1. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. - -The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. - -The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the verifier addresses, resolved output root, and L2 block through `run(address,address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, or pass an explicit third argument to anchor a fixed L2 block. +The recipe defaults to `deploy-config/zeronet-tdx.json`, resolves a recent L2 output root, and deploys `DeployDevWithTDX`. Set `L2_OUTPUT_ROOT_RPC_URL` if the output-root RPC differs from `L2_RPC_URL`, or pass an anchor block as the third argument. ```bash just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER @@ -165,27 +123,25 @@ The script saves output to `deployments/-dev-with-tdx.json`. Register a Nitro signer with a ZK-proven Nitro attestation: ```bash -cast send $TEE_PROVER_REGISTRY \ +cast send "$TEE_PROVER_REGISTRY" \ "registerSigner(bytes,bytes)" \ - $NITRO_OUTPUT \ - $NITRO_PROOF_BYTES \ - --rpc-url $L1_RPC_URL \ - --private-key $PRIVATE_KEY + "$NITRO_OUTPUT" \ + "$NITRO_PROOF_BYTES" \ + --rpc-url "$L1_RPC_URL" \ + --private-key "$PRIVATE_KEY" ``` Once you have the ABI-encoded `TDXVerifierJournal` output and matching RISC Zero proof bytes from the TDX DCAP guest, register the signer through the TDX-aware registry: ```bash -cast send $TEE_PROVER_REGISTRY \ +cast send "$TEE_PROVER_REGISTRY" \ "registerTDXSigner(bytes,bytes)" \ - $TDX_OUTPUT \ - $PROOF_BYTES \ - --rpc-url $L1_RPC_URL \ - --private-key $PRIVATE_KEY + "$TDX_OUTPUT" \ + "$PROOF_BYTES" \ + --rpc-url "$L1_RPC_URL" \ + --private-key "$PRIVATE_KEY" ``` ---- - ## Pre-Seeding Games (Post-Deployment) For forward-traversal testing, use `scripts/multiproof/generate-roots.sh` to write roots and `scripts/multiproof/SeedGames.s.sol` to create games onchain. Their usage and env docs live in those files. From 6844092467d824d1c0e6f819c179a3f8de15f171 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 20:58:53 -0400 Subject: [PATCH 087/146] clean up readme --- scripts/multiproof/README.md | 71 +++++++++++------------------------- scripts/multiproof/justfile | 21 +++++++++++ 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index f0426c95a..930ede52e 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -6,16 +6,8 @@ This guide covers deploying the multiproof contracts and registering a prover on The scripts in this directory are **development and testing tools only**. They are not suitable for production deployments. Specifically, the NoNitro path (`DeployDevNoNitro.s.sol`): -- Does **no AWS Nitro attestation checking**. Instead it uses a bypass function for quickly registering provers: [`MockDevTEEProverRegistry.addDevSigner()`](https://github.com/base/contracts/blob/main/test/mocks/MockDevTEEProverRegistry.sol#L22) -- Uses a simplified mock `AnchorStateRegistry` (with some differences from the real one): [`MockAnchorStateRegistry`](https://github.com/base/contracts/blob/main/scripts/multiproof/mocks/MockAnchorStateRegistry.sol) - -## Prerequisites - -Install dependencies if you haven't already (required after any `lib/` changes): - -```bash -just deps -``` +- Does **no AWS Nitro attestation checking**. Instead it uses `MockDevTEEProverRegistry.addDevSigner()` in `test/mocks/MockDevTEEProverRegistry.sol`. +- Uses the simplified `MockAnchorStateRegistry` in `scripts/multiproof/mocks/MockAnchorStateRegistry.sol`. ## Path 1: NoNitro (Dev - No Attestation) @@ -34,21 +26,10 @@ Set `finalSystemOwner` to the deployer address, then set the multiproof and TEE ### Step 2: Deploy contracts with a fresh anchor -The proving system needs a recent anchor state to catch up to chain tip. Pass it to `DeployDevNoNitro.run(bytes32,uint256)` during deployment. +The recipe resolves a recent L2 output root and deploys `DeployDevNoNitro`. Pass an anchor block as the first argument if you need a specific one. ```bash -BLOCK=$(cast block-number --rpc-url "$L2_RPC_URL") -OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock "$(cast to-hex "$BLOCK")" --rpc-url "$L2_OUTPUT_ROOT_RPC_URL" | jq -er '.outputRoot') - -DEPLOY_CONFIG_PATH=deploy-config/sepolia.json \ -forge script scripts/multiproof/DeployDevNoNitro.s.sol:DeployDevNoNitro \ - --sig "run(bytes32,uint256)" \ - "$OUTPUT_ROOT" \ - "$BLOCK" \ - --rpc-url "$L1_RPC_URL" \ - --broadcast \ - --ledger \ - --hd-paths "$LEDGER_PATH" +just --justfile scripts/multiproof/justfile deploy-no-nitro-stack ``` On success, deployed addresses are printed to the console and saved to `deployments/-dev-no-nitro.json`. @@ -72,18 +53,13 @@ Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signe > only tracks which signer addresses are valid. ```bash -cast send "$TEE_PROVER_REGISTRY" \ - "addDevSigner(address,bytes32)" \ - "$NITRO_SIGNER_ADDRESS" \ - "$TEE_NITRO_IMAGE_HASH" \ - --rpc-url "$L1_RPC_URL" \ - --ledger \ - --mnemonic-derivation-path "$LEDGER_PATH" +METHOD=addDevSigner SIGNER="$NITRO_SIGNER_ADDRESS" IMAGE_HASH="$TEE_NITRO_IMAGE_HASH" +# METHOD=addDevTDXSigner SIGNER="$TDX_SIGNER_ADDRESS" IMAGE_HASH="$TEE_TDX_IMAGE_HASH" cast send "$TEE_PROVER_REGISTRY" \ - "addDevTDXSigner(address,bytes32)" \ - "$TDX_SIGNER_ADDRESS" \ - "$TEE_TDX_IMAGE_HASH" \ + "${METHOD}(address,bytes32)" \ + "$SIGNER" \ + "$IMAGE_HASH" \ --rpc-url "$L1_RPC_URL" \ --ledger \ --mnemonic-derivation-path "$LEDGER_PATH" @@ -97,9 +73,7 @@ TDX registration requires both Nitro and TDX signers. `TDXVerifier` verifies `TD ### Step 1: Deploy verifier policy contracts -`NitroEnclaveVerifier` and `TDXVerifier` are deployed separately from the main multiproof stack because they depend on verifier interfaces that require Solidity `^0.8.20`, while the rest of the multiproof deployment stack is pinned to Solidity `0.8.15`. - -Sepolia defaults live in `scripts/multiproof/justfile`. +Deploy the verifier policy contracts separately. Sepolia defaults live in `scripts/multiproof/justfile`. ```bash just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID @@ -120,24 +94,21 @@ The script saves output to `deployments/-dev-with-tdx.json`. ### Step 3: Register Nitro and TDX signers -Register a Nitro signer with a ZK-proven Nitro attestation: +Register each signer with its proof output: -```bash -cast send "$TEE_PROVER_REGISTRY" \ - "registerSigner(bytes,bytes)" \ - "$NITRO_OUTPUT" \ - "$NITRO_PROOF_BYTES" \ - --rpc-url "$L1_RPC_URL" \ - --private-key "$PRIVATE_KEY" -``` - -Once you have the ABI-encoded `TDXVerifierJournal` output and matching RISC Zero proof bytes from the TDX DCAP guest, register the signer through the TDX-aware registry: +| Signer | Method | Output | Proof | +| --- | --- | --- | --- | +| Nitro | `registerSigner` | `$NITRO_OUTPUT` | `$NITRO_PROOF_BYTES` | +| TDX | `registerTDXSigner` | `$TDX_OUTPUT` | `$TDX_PROOF_BYTES` | ```bash +METHOD=registerSigner OUTPUT="$NITRO_OUTPUT" PROOF="$NITRO_PROOF_BYTES" +# METHOD=registerTDXSigner OUTPUT="$TDX_OUTPUT" PROOF="$TDX_PROOF_BYTES" + cast send "$TEE_PROVER_REGISTRY" \ - "registerTDXSigner(bytes,bytes)" \ - "$TDX_OUTPUT" \ - "$PROOF_BYTES" \ + "${METHOD}(bytes,bytes)" \ + "$OUTPUT" \ + "$PROOF" \ --rpc-url "$L1_RPC_URL" \ --private-key "$PRIVATE_KEY" ``` diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index ed6eee0e9..723eacb12 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -38,6 +38,27 @@ deploy-nitro-verifier nitro_root_cert nitro_verifier_id: "0x0000000000000000000000000000000000000000000000000000000000000000" \ {{ broadcast_args }} +# Deploy the NoNitro multiproof dev/test stack using the Sepolia config. +[script] +deploy-no-nitro-stack asr_anchor_block="": + asr_anchor_block="$([[ -n "{{ asr_anchor_block }}" ]] && printf '%s' "{{ asr_anchor_block }}" || cast block-number --rpc-url "{{ l2_rpc_url }}")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$(cast to-hex "$asr_anchor_block")" --rpc-url "{{ l2_output_root_rpc_url }}" | jq -er '.outputRoot')" + + if [[ "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi + + echo "Using ASR anchor output root: $asr_anchor_output_root" + echo "Using ASR anchor L2 block: $asr_anchor_block" + + DEPLOY_CONFIG_PATH="deploy-config/sepolia.json" \ + forge script scripts/multiproof/DeployDevNoNitro.s.sol:DeployDevNoNitro \ + --sig "run(bytes32,uint256)" \ + "$asr_anchor_output_root" \ + "$asr_anchor_block" \ + {{ broadcast_args }} + # Deploy the TDX multiproof dev/test stack using the zeronet TDX config. [script] deploy-tdx-stack nitro_verifier tdx_verifier asr_anchor_block="": From d4f2e9246385a7ed7e912b5428f9b0b55411b42f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:01:50 -0400 Subject: [PATCH 088/146] clean up readme --- scripts/multiproof/README.md | 45 +++++++++++------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 930ede52e..f0340d3ed 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -2,6 +2,8 @@ This guide covers deploying the multiproof contracts and registering a prover on Sepolia. +For forward-traversal testing after deployment, use `scripts/multiproof/generate-roots.sh` and `scripts/multiproof/SeedGames.s.sol`; usage and env docs live in those files. + ## Dev/Test Scripts Only The scripts in this directory are **development and testing tools only**. They are not suitable for production deployments. Specifically, the NoNitro path (`DeployDevNoNitro.s.sol`): @@ -13,7 +15,7 @@ The scripts in this directory are **development and testing tools only**. They a Use this when you don't have access to an AWS Nitro enclave and want to quickly test the prover without attestation overhead. -### Step 1: Configure `deploy-config/sepolia.json` +### Configure `deploy-config/sepolia.json` Set `finalSystemOwner` to the deployer address, then set the multiproof and TEE fields for this environment. `finalSystemOwner` becomes the owner of all deployed contracts and must sign later admin calls. @@ -24,7 +26,7 @@ Set `finalSystemOwner` to the deployer address, then set the multiproof and TEE } ``` -### Step 2: Deploy contracts with a fresh anchor +### Deploy contracts with a fresh anchor The recipe resolves a recent L2 output root and deploys `DeployDevNoNitro`. Pass an anchor block as the first argument if you need a specific one. @@ -34,7 +36,7 @@ just --justfile scripts/multiproof/justfile deploy-no-nitro-stack On success, deployed addresses are printed to the console and saved to `deployments/-dev-no-nitro.json`. -### Step 3: Get the enclave signer public key +### Get the enclave signer public key Query the enclave for its signer public key: @@ -44,25 +46,17 @@ cast rpc enclave_signerPublicKey --rpc-url "$ENCLAVE_RPC_URL" This returns a raw byte array representing an uncompressed secp256k1 public key (65 bytes, starting with `0x04`). To convert it to an Ethereum address, strip the `0x04` prefix byte, keccak256-hash the remaining 64 bytes, and take the last 20 bytes. -### Step 4: Register the dev signers +### Register the dev signers -Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signer on the deployed `DevTEEProverRegistry`. +Call `addDevSigner(address,bytes32)` for Nitro or `addDevTDXSigner(address,bytes32)` for TDX on the deployed `DevTEEProverRegistry`. > **Note:** PCR0 / TDX image enforcement is handled by `AggregateVerifier` (which bakes > `teeNitroImageHash` and `teeTdxImageHash` into the journal the enclaves sign). The registry > only tracks which signer addresses are valid. ```bash -METHOD=addDevSigner SIGNER="$NITRO_SIGNER_ADDRESS" IMAGE_HASH="$TEE_NITRO_IMAGE_HASH" -# METHOD=addDevTDXSigner SIGNER="$TDX_SIGNER_ADDRESS" IMAGE_HASH="$TEE_TDX_IMAGE_HASH" - -cast send "$TEE_PROVER_REGISTRY" \ - "${METHOD}(address,bytes32)" \ - "$SIGNER" \ - "$IMAGE_HASH" \ - --rpc-url "$L1_RPC_URL" \ - --ledger \ - --mnemonic-derivation-path "$LEDGER_PATH" +cast send "$TEE_PROVER_REGISTRY" "$METHOD(address,bytes32)" "$SIGNER" "$IMAGE_HASH" \ + --rpc-url "$L1_RPC_URL" --ledger --mnemonic-derivation-path "$LEDGER_PATH" ``` The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` and must sign this call. @@ -71,7 +65,7 @@ The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` TDX registration requires both Nitro and TDX signers. `TDXVerifier` verifies `TDXVerifierJournal`; `TEEProverRegistry` registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`. -### Step 1: Deploy verifier policy contracts +### Deploy verifier policy contracts Deploy the verifier policy contracts separately. Sepolia defaults live in `scripts/multiproof/justfile`. @@ -82,7 +76,7 @@ just --justfile scripts/multiproof/justfile deploy-tdx-verifier These save output to `deployments/-nitro-verifier.json` and `deployments/-tdx-verifier.json`. -### Step 2: Deploy the TDX multiproof test stack +### Deploy the TDX multiproof test stack The recipe defaults to `deploy-config/zeronet-tdx.json`, resolves a recent L2 output root, and deploys `DeployDevWithTDX`. Set `L2_OUTPUT_ROOT_RPC_URL` if the output-root RPC differs from `L2_RPC_URL`, or pass an anchor block as the third argument. @@ -92,7 +86,7 @@ just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TD The script saves output to `deployments/-dev-with-tdx.json`. -### Step 3: Register Nitro and TDX signers +### Register Nitro and TDX signers Register each signer with its proof output: @@ -102,17 +96,6 @@ Register each signer with its proof output: | TDX | `registerTDXSigner` | `$TDX_OUTPUT` | `$TDX_PROOF_BYTES` | ```bash -METHOD=registerSigner OUTPUT="$NITRO_OUTPUT" PROOF="$NITRO_PROOF_BYTES" -# METHOD=registerTDXSigner OUTPUT="$TDX_OUTPUT" PROOF="$TDX_PROOF_BYTES" - -cast send "$TEE_PROVER_REGISTRY" \ - "${METHOD}(bytes,bytes)" \ - "$OUTPUT" \ - "$PROOF" \ - --rpc-url "$L1_RPC_URL" \ - --private-key "$PRIVATE_KEY" +cast send "$TEE_PROVER_REGISTRY" "$METHOD(bytes,bytes)" "$OUTPUT" "$PROOF" \ + --rpc-url "$L1_RPC_URL" --private-key "$PRIVATE_KEY" ``` - -## Pre-Seeding Games (Post-Deployment) - -For forward-traversal testing, use `scripts/multiproof/generate-roots.sh` to write roots and `scripts/multiproof/SeedGames.s.sol` to create games onchain. Their usage and env docs live in those files. From 9601da80590d5c58f9ddcf5317ed56108e048d0d Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:04:18 -0400 Subject: [PATCH 089/146] clean up readme --- scripts/multiproof/README.md | 39 ++++++++++++------------------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index f0340d3ed..a4f6d73a1 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -1,8 +1,7 @@ # Multiproof Deployment Guide This guide covers deploying the multiproof contracts and registering a prover on Sepolia. - -For forward-traversal testing after deployment, use `scripts/multiproof/generate-roots.sh` and `scripts/multiproof/SeedGames.s.sol`; usage and env docs live in those files. +Deploy recipes print addresses and write `deployments/-*.json`. ## Dev/Test Scripts Only @@ -17,14 +16,7 @@ Use this when you don't have access to an AWS Nitro enclave and want to quickly ### Configure `deploy-config/sepolia.json` -Set `finalSystemOwner` to the deployer address, then set the multiproof and TEE fields for this environment. `finalSystemOwner` becomes the owner of all deployed contracts and must sign later admin calls. - -```json -{ - "finalSystemOwner": "0xYOUR_DEPLOYER_ADDRESS", - ... -} -``` +Set `finalSystemOwner` in `deploy-config/sepolia.json` to the deployer address, then set the multiproof and TEE fields for this environment. `finalSystemOwner` becomes the owner of all deployed contracts and must sign later admin calls. ### Deploy contracts with a fresh anchor @@ -34,8 +26,6 @@ The recipe resolves a recent L2 output root and deploys `DeployDevNoNitro`. Pass just --justfile scripts/multiproof/justfile deploy-no-nitro-stack ``` -On success, deployed addresses are printed to the console and saved to `deployments/-dev-no-nitro.json`. - ### Get the enclave signer public key Query the enclave for its signer public key: @@ -48,18 +38,21 @@ This returns a raw byte array representing an uncompressed secp256k1 public key ### Register the dev signers -Call `addDevSigner(address,bytes32)` for Nitro or `addDevTDXSigner(address,bytes32)` for TDX on the deployed `DevTEEProverRegistry`. +Register each signer on the deployed `DevTEEProverRegistry`. > **Note:** PCR0 / TDX image enforcement is handled by `AggregateVerifier` (which bakes > `teeNitroImageHash` and `teeTdxImageHash` into the journal the enclaves sign). The registry > only tracks which signer addresses are valid. ```bash -cast send "$TEE_PROVER_REGISTRY" "$METHOD(address,bytes32)" "$SIGNER" "$IMAGE_HASH" \ +cast send "$TEE_PROVER_REGISTRY" "addDevSigner(address,bytes32)" "$NITRO_SIGNER" "$NITRO_IMAGE_HASH" \ + --rpc-url "$L1_RPC_URL" --ledger --mnemonic-derivation-path "$LEDGER_PATH" + +cast send "$TEE_PROVER_REGISTRY" "addDevTDXSigner(address,bytes32)" "$TDX_SIGNER" "$TDX_IMAGE_HASH" \ --rpc-url "$L1_RPC_URL" --ledger --mnemonic-derivation-path "$LEDGER_PATH" ``` -The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` and must sign this call. +The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` and must sign these calls. ## Path 2: TDX (Production-Path PoC) @@ -74,8 +67,6 @@ just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CE just --justfile scripts/multiproof/justfile deploy-tdx-verifier ``` -These save output to `deployments/-nitro-verifier.json` and `deployments/-tdx-verifier.json`. - ### Deploy the TDX multiproof test stack The recipe defaults to `deploy-config/zeronet-tdx.json`, resolves a recent L2 output root, and deploys `DeployDevWithTDX`. Set `L2_OUTPUT_ROOT_RPC_URL` if the output-root RPC differs from `L2_RPC_URL`, or pass an anchor block as the third argument. @@ -84,18 +75,14 @@ The recipe defaults to `deploy-config/zeronet-tdx.json`, resolves a recent L2 ou just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER ``` -The script saves output to `deployments/-dev-with-tdx.json`. - ### Register Nitro and TDX signers -Register each signer with its proof output: - -| Signer | Method | Output | Proof | -| --- | --- | --- | --- | -| Nitro | `registerSigner` | `$NITRO_OUTPUT` | `$NITRO_PROOF_BYTES` | -| TDX | `registerTDXSigner` | `$TDX_OUTPUT` | `$TDX_PROOF_BYTES` | +Register each signer with its proof output. ```bash -cast send "$TEE_PROVER_REGISTRY" "$METHOD(bytes,bytes)" "$OUTPUT" "$PROOF" \ +cast send "$TEE_PROVER_REGISTRY" "registerSigner(bytes,bytes)" "$NITRO_OUTPUT" "$NITRO_PROOF_BYTES" \ + --rpc-url "$L1_RPC_URL" --private-key "$PRIVATE_KEY" + +cast send "$TEE_PROVER_REGISTRY" "registerTDXSigner(bytes,bytes)" "$TDX_OUTPUT" "$TDX_PROOF_BYTES" \ --rpc-url "$L1_RPC_URL" --private-key "$PRIVATE_KEY" ``` From 53c9d3ee37fab852f16ec374ba369f0106a6b06a Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:06:59 -0400 Subject: [PATCH 090/146] clean up readme --- scripts/multiproof/README.md | 64 +++++++----------------------------- 1 file changed, 12 insertions(+), 52 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index a4f6d73a1..9a7d90238 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -5,44 +5,25 @@ Deploy recipes print addresses and write `deployments/-*.json`. ## Dev/Test Scripts Only -The scripts in this directory are **development and testing tools only**. They are not suitable for production deployments. Specifically, the NoNitro path (`DeployDevNoNitro.s.sol`): +The scripts in this directory are **development and testing tools only**. They are not suitable for production deployments. They use the simplified `MockAnchorStateRegistry`; the NoNitro path also bypasses AWS Nitro attestation checking through `MockDevTEEProverRegistry.addDevSigner()`. -- Does **no AWS Nitro attestation checking**. Instead it uses `MockDevTEEProverRegistry.addDevSigner()` in `test/mocks/MockDevTEEProverRegistry.sol`. -- Uses the simplified `MockAnchorStateRegistry` in `scripts/multiproof/mocks/MockAnchorStateRegistry.sol`. +## Run a Deploy Recipe -## Path 1: NoNitro (Dev - No Attestation) +See `scripts/multiproof/justfile` for defaults, config paths, and optional anchor-block arguments. -Use this when you don't have access to an AWS Nitro enclave and want to quickly test the prover without attestation overhead. +| Path | Recipe | +| --- | --- | +| NoNitro dev, no attestation | `just --justfile scripts/multiproof/justfile deploy-no-nitro-stack` | +| TDX dev/test verifier policies | `just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID`
`just --justfile scripts/multiproof/justfile deploy-tdx-verifier` | +| TDX dev/test stack | `just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER` | -### Configure `deploy-config/sepolia.json` +## Register Signers -Set `finalSystemOwner` in `deploy-config/sepolia.json` to the deployer address, then set the multiproof and TEE fields for this environment. `finalSystemOwner` becomes the owner of all deployed contracts and must sign later admin calls. - -### Deploy contracts with a fresh anchor - -The recipe resolves a recent L2 output root and deploys `DeployDevNoNitro`. Pass an anchor block as the first argument if you need a specific one. - -```bash -just --justfile scripts/multiproof/justfile deploy-no-nitro-stack -``` - -### Get the enclave signer public key - -Query the enclave for its signer public key: - -```bash -cast rpc enclave_signerPublicKey --rpc-url "$ENCLAVE_RPC_URL" -``` - -This returns a raw byte array representing an uncompressed secp256k1 public key (65 bytes, starting with `0x04`). To convert it to an Ethereum address, strip the `0x04` prefix byte, keccak256-hash the remaining 64 bytes, and take the last 20 bytes. - -### Register the dev signers +### NoNitro Dev Signers Register each signer on the deployed `DevTEEProverRegistry`. -> **Note:** PCR0 / TDX image enforcement is handled by `AggregateVerifier` (which bakes -> `teeNitroImageHash` and `teeTdxImageHash` into the journal the enclaves sign). The registry -> only tracks which signer addresses are valid. +The registry stores signer image hashes; `AggregateVerifier` enforces the expected Nitro and TDX image hashes. ```bash cast send "$TEE_PROVER_REGISTRY" "addDevSigner(address,bytes32)" "$NITRO_SIGNER" "$NITRO_IMAGE_HASH" \ @@ -54,28 +35,7 @@ cast send "$TEE_PROVER_REGISTRY" "addDevTDXSigner(address,bytes32)" "$TDX_SIGNER The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` and must sign these calls. -## Path 2: TDX (Production-Path PoC) - -TDX registration requires both Nitro and TDX signers. `TDXVerifier` verifies `TDXVerifierJournal`; `TEEProverRegistry` registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`. - -### Deploy verifier policy contracts - -Deploy the verifier policy contracts separately. Sepolia defaults live in `scripts/multiproof/justfile`. - -```bash -just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID -just --justfile scripts/multiproof/justfile deploy-tdx-verifier -``` - -### Deploy the TDX multiproof test stack - -The recipe defaults to `deploy-config/zeronet-tdx.json`, resolves a recent L2 output root, and deploys `DeployDevWithTDX`. Set `L2_OUTPUT_ROOT_RPC_URL` if the output-root RPC differs from `L2_RPC_URL`, or pass an anchor block as the third argument. - -```bash -just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER -``` - -### Register Nitro and TDX signers +### TDX Dev/Test Signers Register each signer with its proof output. From bf7dea530701f9b36f7ac2d053caa51373dfba59 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:08:18 -0400 Subject: [PATCH 091/146] fmt readme --- scripts/multiproof/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 9a7d90238..f497d9123 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -11,11 +11,11 @@ The scripts in this directory are **development and testing tools only**. They a See `scripts/multiproof/justfile` for defaults, config paths, and optional anchor-block arguments. -| Path | Recipe | -| --- | --- | -| NoNitro dev, no attestation | `just --justfile scripts/multiproof/justfile deploy-no-nitro-stack` | +| Path | Recipe | +| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| NoNitro dev, no attestation | `just --justfile scripts/multiproof/justfile deploy-no-nitro-stack` | | TDX dev/test verifier policies | `just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID`
`just --justfile scripts/multiproof/justfile deploy-tdx-verifier` | -| TDX dev/test stack | `just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER` | +| TDX dev/test stack | `just --justfile scripts/multiproof/justfile deploy-tdx-stack $NITRO_VERIFIER $TDX_VERIFIER` | ## Register Signers From 181422a62407d9d5443622b162ed1f4e6af64b81 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:16:46 -0400 Subject: [PATCH 092/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 96 +++++++------------------- 1 file changed, 24 insertions(+), 72 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 4eafac2f7..948397123 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -12,13 +12,9 @@ import { Claim, Timestamp } from "src/libraries/bridge/LibUDT.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; -import { LibClone } from "lib/solady/src/utils/LibClone.sol"; - import { BaseTest } from "./BaseTest.t.sol"; contract AggregateVerifierTest is BaseTest { - using LibClone for address; - AggregateVerifier private aggregateVerifierImpl; function setUp() public override { @@ -82,19 +78,24 @@ contract AggregateVerifierTest is BaseTest { } function testInitializeWithZKProof() public { - _createAndAssertInitializedGame("zk-proof", AggregateVerifier.ProofType.ZK, ZK_PROVER, address(0), ZK_PROVER); + Claim rootClaim = _advanceL2BlockAndClaim(); + bytes memory proof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); + + AggregateVerifier game = _createAggregateVerifierGame( + ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof + ); + + _assertInitializedGame(game, rootClaim, ZK_PROVER, address(0), ZK_PROVER); } function testInitializeFailsIfInvalidCallDataSize() public { Claim rootClaim = _advanceL2BlockAndClaim(); vm.deal(TEE_PROVER, INIT_BOND); - bytes memory extraData = ""; - bytes memory initData = ""; vm.prank(TEE_PROVER); vm.expectRevert(BadExtraData.selector); - factory.createWithInitData{ value: INIT_BOND }(GameTypes.AGGREGATE_VERIFIER, rootClaim, extraData, initData); + factory.createWithInitData{ value: INIT_BOND }(GameTypes.AGGREGATE_VERIFIER, rootClaim, hex"", hex""); } function testUpdatingAnchorStateRegistryWithTEEProof() public { @@ -202,13 +203,10 @@ contract AggregateVerifierTest is BaseTest { /// @notice Reverts when the parent is not factory-registered: `_isValidGame` requires /// `AnchorStateRegistry.isGameRegistered`, which checks `DisputeGameFactory.games(...) == parent`. - /// @dev Parent is a real `AggregateVerifier` clone initialized like a factory game, but deployed without - /// `_finalizeGameCreation`, so the factory UUID mapping has no entry. + /// @dev Parent is a real factory game, then the factory lookup is mocked to emulate a missing UUID entry. function testInitializeFailsIfParentGameNotFactoryRegistered() public { - currentL2BlockNumber += BLOCK_INTERVAL; - - Claim parentRootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "parent"))); - AggregateVerifier unregisteredParent = _deployAggregateVerifierCloneWithoutFactoryRegistration( + Claim parentRootClaim = _advanceL2BlockAndClaim(); + AggregateVerifier unregisteredParent = _createAggregateVerifierGame( TEE_PROVER, parentRootClaim, currentL2BlockNumber, @@ -216,6 +214,15 @@ contract AggregateVerifierTest is BaseTest { _generateProof("parent-tee", AggregateVerifier.ProofType.TEE) ); + vm.mockCall( + address(factory), + abi.encodeCall( + IDisputeGameFactory.games, + (GameTypes.AGGREGATE_VERIFIER, parentRootClaim, unregisteredParent.extraData()) + ), + abi.encode(IDisputeGame(address(0)), Timestamp.wrap(0)) + ); + currentL2BlockNumber += BLOCK_INTERVAL; Claim childRootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "child"))); @@ -234,8 +241,7 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = block.number + 1; bytes32 l1OriginHash = bytes32(uint256(1)); - bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); + bytes memory proofBytes = _teeProof(l1OriginHash, l1OriginNumber, rootClaim); _expectCreateGameRevertsForTeeProof( rootClaim, @@ -252,8 +258,7 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = 1; bytes32 l1OriginHash = bytes32(uint256(1)); - bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(130)); + bytes memory proofBytes = _teeProof(l1OriginHash, l1OriginNumber, rootClaim); _expectCreateGameRevertsForTeeProof( rootClaim, @@ -330,25 +335,6 @@ contract AggregateVerifierTest is BaseTest { return Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); } - function _createAndAssertInitializedGame( - bytes memory proofSalt, - AggregateVerifier.ProofType proofType, - address prover, - address expectedTeeProver, - address expectedZkProver - ) - private - { - Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory proof = _generateProof(proofSalt, proofType); - - AggregateVerifier game = _createAggregateVerifierGame( - prover, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof - ); - - _assertInitializedGame(game, rootClaim, prover, expectedTeeProver, expectedZkProver); - } - function _assertInitializedGame( AggregateVerifier game, Claim rootClaim, @@ -433,30 +419,6 @@ contract AggregateVerifierTest is BaseTest { return abi.encodePacked(intermediateRoots); } - function _deployAggregateVerifierCloneWithoutFactoryRegistration( - address creator, - Claim rootClaim, - uint256 l2BlockNumber, - address parentAddress, - bytes memory proof - ) - private - returns (AggregateVerifier) - { - bytes memory extraData = _aggregateVerifierExtraData(rootClaim, l2BlockNumber, parentAddress); - Hash uuid = factory.getGameUUID(GameTypes.AGGREGATE_VERIFIER, rootClaim, extraData); - address clone = address(aggregateVerifierImpl) - .cloneDeterministic( - abi.encodePacked(creator, rootClaim, blockhash(block.number - 1), extraData), Hash.unwrap(uuid) - ); - - vm.deal(creator, INIT_BOND); - vm.prank(creator); - AggregateVerifier(payable(clone)).initializeWithInitData{ value: INIT_BOND }(proof); - - return AggregateVerifier(payable(clone)); - } - function _expectDeployWithInvalidBlockIntervalsReverts( uint256 blockInterval, uint256 intermediateBlockInterval @@ -468,17 +430,7 @@ contract AggregateVerifierTest is BaseTest { AggregateVerifier.InvalidBlockInterval.selector, blockInterval, intermediateBlockInterval ) ); - _deployAggregateVerifierWithIntervals(blockInterval, intermediateBlockInterval); - } - - function _deployAggregateVerifierWithIntervals( - uint256 blockInterval, - uint256 intermediateBlockInterval - ) - private - returns (AggregateVerifier) - { - return new AggregateVerifier( + new AggregateVerifier( GameTypes.AGGREGATE_VERIFIER, IAnchorStateRegistry(address(anchorStateRegistry)), IDelayedWETH(payable(address(delayedWETH))), From 73d96062a64feff9bf9703cc4feba74d76843746 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:25:20 -0400 Subject: [PATCH 093/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 130 ++++++++----------------- test/L1/proofs/BaseTest.t.sol | 9 ++ test/L1/proofs/Challenge.t.sol | 14 +-- test/L1/proofs/Nullify.t.sol | 16 +-- 4 files changed, 56 insertions(+), 113 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 948397123..56c9ff3fb 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -15,16 +15,10 @@ import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; import { BaseTest } from "./BaseTest.t.sol"; contract AggregateVerifierTest is BaseTest { - AggregateVerifier private aggregateVerifierImpl; - - function setUp() public override { - super.setUp(); - aggregateVerifierImpl = AggregateVerifier(address(factory.gameImpls(GameTypes.AGGREGATE_VERIFIER))); - } + address private constant EIP2935_CONTRACT = 0x0000F90827F1C53a10cb7A02335B175320002935; function testInitializeWithTEEProof() public { - currentL2BlockNumber += BLOCK_INTERVAL; - Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); + Claim rootClaim = _advanceL2BlockAndClaim(); bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); bytes memory intermediateRoots = @@ -85,7 +79,24 @@ contract AggregateVerifierTest is BaseTest { ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof ); - _assertInitializedGame(game, rootClaim, ZK_PROVER, address(0), ZK_PROVER); + assertTrue(game.wasRespectedGameTypeWhenCreated()); + assertEq(game.teeProver(), address(0)); + assertEq(game.zkProver(), ZK_PROVER); + assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); + assertEq(game.l2SequenceNumber(), currentL2BlockNumber); + assertEq(game.rootClaim().raw(), rootClaim.raw()); + assertEq(game.parentAddress(), address(anchorStateRegistry)); + assertEq(game.gameType().raw(), GameTypes.AGGREGATE_VERIFIER.raw()); + assertEq(game.gameCreator(), ZK_PROVER); + bytes memory intermediateOutputRoots = game.intermediateOutputRoots(); + assertEq( + game.extraData(), + abi.encodePacked(currentL2BlockNumber, address(anchorStateRegistry), intermediateOutputRoots) + ); + assertEq(game.bondRecipient(), ZK_PROVER); + assertTrue(anchorStateRegistry.isGameProper(IDisputeGame(address(game)))); + assertEq(delayedWETH.balanceOf(address(game)), INIT_BOND); + assertEq(game.proofCount(), 1); } function testInitializeFailsIfInvalidCallDataSize() public { @@ -109,11 +120,11 @@ contract AggregateVerifierTest is BaseTest { vm.expectRevert(GameNotResolved.selector); game.claimCredit(); - vm.warp(block.timestamp + aggregateVerifierImpl.SLOW_FINALIZATION_DELAY()); + vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); game.resolve(); - _assertStatus(game, GameStatus.DEFENDER_WINS); + assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); - _claimCreditAfterDelay(game); + _claimCreditAfterDelay(game, game.gameCreator()); vm.warp(block.timestamp + 1); game.closeGame(); @@ -128,11 +139,11 @@ contract AggregateVerifierTest is BaseTest { ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof ); - vm.warp(block.timestamp + aggregateVerifierImpl.SLOW_FINALIZATION_DELAY()); + vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); game.resolve(); - _assertStatus(game, GameStatus.DEFENDER_WINS); + assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); - _claimCreditAfterDelay(game); + _claimCreditAfterDelay(game, game.gameCreator()); vm.warp(block.timestamp + 1); game.closeGame(); @@ -151,26 +162,26 @@ contract AggregateVerifierTest is BaseTest { _provideProof(game, ZK_PROVER, zkProof); assertEq(game.proofCount(), 2); - vm.warp(block.timestamp + aggregateVerifierImpl.FAST_FINALIZATION_DELAY()); + vm.warp(block.timestamp + game.FAST_FINALIZATION_DELAY()); game.resolve(); - _assertStatus(game, GameStatus.DEFENDER_WINS); + assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); vm.warp(block.timestamp + 1); game.closeGame(); _assertAnchorRoot(rootClaim); - _claimCreditAfterDelay(game); + _claimCreditAfterDelay(game, game.gameCreator()); } function testProofCannotIncreaseExpectedResolution() public { Claim rootClaim = _advanceL2BlockAndClaim(); bytes memory teeProof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); - uint256 slowDelay = aggregateVerifierImpl.SLOW_FINALIZATION_DELAY(); AggregateVerifier game = _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), teeProof ); + uint256 slowDelay = game.SLOW_FINALIZATION_DELAY(); Timestamp originalExpectedResolution = game.expectedResolution(); assertEq(originalExpectedResolution.raw(), block.timestamp + slowDelay); @@ -184,7 +195,7 @@ contract AggregateVerifierTest is BaseTest { vm.warp(block.timestamp + 1); game.resolve(); - _assertStatus(game, GameStatus.DEFENDER_WINS); + assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); } function testCannotCreateSameProposal() public { @@ -243,11 +254,12 @@ contract AggregateVerifierTest is BaseTest { bytes memory proofBytes = _teeProof(l1OriginHash, l1OriginNumber, rootClaim); - _expectCreateGameRevertsForTeeProof( - rootClaim, - proofBytes, + vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) ); + _createAggregateVerifierGame( + TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes + ); } function testVerifyFailsWithL1OriginTooOld() public { @@ -260,10 +272,9 @@ contract AggregateVerifierTest is BaseTest { bytes memory proofBytes = _teeProof(l1OriginHash, l1OriginNumber, rootClaim); - _expectCreateGameRevertsForTeeProof( - rootClaim, - proofBytes, - abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number) + vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); + _createAggregateVerifierGame( + TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes ); } @@ -275,10 +286,9 @@ contract AggregateVerifierTest is BaseTest { bytes memory proofBytes = _teeProof(wrongHash, l1OriginNumber, rootClaim); bytes32 actualHash = blockhash(l1OriginNumber); - _expectCreateGameRevertsForTeeProof( - rootClaim, - proofBytes, - abi.encodeWithSelector(AggregateVerifier.L1OriginHashMismatch.selector, wrongHash, actualHash) + vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginHashMismatch.selector, wrongHash, actualHash)); + _createAggregateVerifierGame( + TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes ); } @@ -310,7 +320,7 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = block.number - 260; bytes32 expectedHash = keccak256(abi.encodePacked("mock-blockhash", l1OriginNumber)); - vm.mockCall(aggregateVerifierImpl.EIP2935_CONTRACT(), abi.encode(l1OriginNumber), abi.encode(expectedHash)); + vm.mockCall(EIP2935_CONTRACT, abi.encode(l1OriginNumber), abi.encode(expectedHash)); bytes memory proofBytes = abi.encodePacked( uint8(AggregateVerifier.ProofType.TEE), @@ -335,68 +345,12 @@ contract AggregateVerifierTest is BaseTest { return Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); } - function _assertInitializedGame( - AggregateVerifier game, - Claim rootClaim, - address expectedCreator, - address expectedTeeProver, - address expectedZkProver - ) - private - view - { - assertTrue(game.wasRespectedGameTypeWhenCreated()); - assertEq(game.teeProver(), expectedTeeProver); - assertEq(game.zkProver(), expectedZkProver); - _assertStatus(game, GameStatus.IN_PROGRESS); - assertEq(game.l2SequenceNumber(), currentL2BlockNumber); - assertEq(game.rootClaim().raw(), rootClaim.raw()); - assertEq(game.parentAddress(), address(anchorStateRegistry)); - assertEq(game.gameType().raw(), GameTypes.AGGREGATE_VERIFIER.raw()); - assertEq(game.gameCreator(), expectedCreator); - bytes memory intermediateOutputRoots = game.intermediateOutputRoots(); - assertEq( - game.extraData(), - abi.encodePacked(currentL2BlockNumber, address(anchorStateRegistry), intermediateOutputRoots) - ); - assertEq(game.bondRecipient(), expectedCreator); - assertTrue(anchorStateRegistry.isGameProper(IDisputeGame(address(game)))); - assertEq(delayedWETH.balanceOf(address(game)), INIT_BOND); - assertEq(game.proofCount(), 1); - } - - function _assertStatus(AggregateVerifier game, GameStatus expectedStatus) private view { - assertEq(uint8(game.status()), uint8(expectedStatus)); - } - function _assertAnchorRoot(Claim rootClaim) private view { (Hash root, uint256 l2SequenceNumber) = anchorStateRegistry.getAnchorRoot(); assertEq(root.raw(), rootClaim.raw()); assertEq(l2SequenceNumber, currentL2BlockNumber); } - function _claimCreditAfterDelay(AggregateVerifier game) private { - uint256 balanceBefore = game.gameCreator().balance; - game.claimCredit(); - vm.warp(block.timestamp + DELAYED_WETH_DELAY); - game.claimCredit(); - assertEq(game.gameCreator().balance, balanceBefore + INIT_BOND); - assertEq(delayedWETH.balanceOf(address(game)), 0); - } - - function _expectCreateGameRevertsForTeeProof( - Claim rootClaim, - bytes memory proofBytes, - bytes memory revertData - ) - private - { - vm.expectRevert(revertData); - _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes - ); - } - function _teeProof( bytes32 l1OriginHash, uint256 l1OriginNumber, diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 80ccaf06b..ba80dd889 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -154,6 +154,15 @@ contract BaseTest is Test { game.verifyProposalProof(proofBytes); } + function _claimCreditAfterDelay(AggregateVerifier game, address recipient) internal { + uint256 balanceBefore = recipient.balance; + game.claimCredit(); + vm.warp(block.timestamp + DELAYED_WETH_DELAY); + game.claimCredit(); + assertEq(recipient.balance, balanceBefore + INIT_BOND); + assertEq(delayedWETH.balanceOf(address(game)), 0); + } + /// @dev Encodes proofType || l1OriginHash || l1OriginNumber || mock verifier payload. function _generateProof( bytes memory salt, diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index ebf66d9a5..ac51dbf6f 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -142,10 +142,9 @@ contract ChallengeTest is BaseTest { AggregateVerifier gameB = _createGame(TEE_PROVER, "tee-nullify-b", "tee-proof-b", AggregateVerifier.ProofType.TEE, address(gameA)); - Claim rootNullifyB = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee-nullify-b"))); + Claim rootNullifyB = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee-nullify-b-root"))); bytes memory teeNullifyB = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); - uint256 lastIdx = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; - gameB.nullify(teeNullifyB, lastIdx, rootNullifyB.raw()); + gameB.nullify(teeNullifyB, LAST_INTERMEDIATE_ROOT_INDEX, rootNullifyB.raw()); assertTrue(teeVerifier.nullified()); _resolveAndAssertStatus(gameA, GameStatus.IN_PROGRESS); @@ -215,13 +214,4 @@ contract ChallengeTest is BaseTest { assertEq(game.bondRecipient(), recipient); _claimCreditAfterDelay(game, recipient); } - - function _claimCreditAfterDelay(AggregateVerifier game, address recipient) private { - uint256 balanceBefore = recipient.balance; - game.claimCredit(); - vm.warp(block.timestamp + DELAYED_WETH_DELAY); - game.claimCredit(); - assertEq(recipient.balance, balanceBefore + INIT_BOND); - assertEq(delayedWETH.balanceOf(address(game)), 0); - } } diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index 51181da84..61d4a14f0 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -24,7 +24,7 @@ contract NullifyTest is BaseTest { _assertNullifiedToNoProofs(game, TEE_PROVER); vm.warp(block.timestamp + NO_PROOF_CREDIT_CLAIM_DELAY); - _claimCreditAfterDelay(game); + _claimCreditAfterDelay(game, game.gameCreator()); } function testNullifyWithZKProof() public { @@ -35,7 +35,7 @@ contract NullifyTest is BaseTest { _assertNullifiedToNoProofs(game, ZK_PROVER); vm.warp(block.timestamp + NO_PROOF_CREDIT_CLAIM_DELAY); - _claimCreditAfterDelay(game); + _claimCreditAfterDelay(game, game.gameCreator()); } function testNullifyWithTEEProofWhenTEEAndZKProofsAreProvided() public { @@ -94,7 +94,7 @@ contract NullifyTest is BaseTest { vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); game.resolve(); - _claimCreditAfterDelay(game); + _claimCreditAfterDelay(game, game.gameCreator()); } function testResolveEarlyReturnWhenSharedTeeVerifierNullifiedByAnotherGame() public { @@ -241,14 +241,4 @@ contract NullifyTest is BaseTest { vm.expectRevert(AggregateVerifier.GameNotOver.selector); gameA.resolve(); } - - function _claimCreditAfterDelay(AggregateVerifier game) private { - address recipient = game.gameCreator(); - uint256 balanceBefore = recipient.balance; - game.claimCredit(); - vm.warp(block.timestamp + DELAYED_WETH_DELAY); - game.claimCredit(); - assertEq(recipient.balance, balanceBefore + INIT_BOND); - assertEq(delayedWETH.balanceOf(address(game)), 0); - } } From 9a693dbcc4c1e517e3a4ef60ba286658247d839d Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:30:01 -0400 Subject: [PATCH 094/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 121 +++++++++---------------- test/L1/proofs/BaseTest.t.sol | 2 +- 2 files changed, 46 insertions(+), 77 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 56c9ff3fb..d178cb27b 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -21,8 +21,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = _advanceL2BlockAndClaim(); bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); - bytes memory intermediateRoots = - abi.encodePacked(_generateIntermediateRootsExceptLast(currentL2BlockNumber), rootClaim.raw()); + bytes memory intermediateRoots = _generateIntermediateRoots(currentL2BlockNumber, rootClaim); bytes32 l1OriginHash = blockhash(block.number - 1); bytes32 startingRoot = keccak256(abi.encode(uint256(0))); bytes32 saltHash = keccak256("tee-proof"); @@ -79,22 +78,9 @@ contract AggregateVerifierTest is BaseTest { ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof ); - assertTrue(game.wasRespectedGameTypeWhenCreated()); assertEq(game.teeProver(), address(0)); assertEq(game.zkProver(), ZK_PROVER); - assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); - assertEq(game.l2SequenceNumber(), currentL2BlockNumber); - assertEq(game.rootClaim().raw(), rootClaim.raw()); - assertEq(game.parentAddress(), address(anchorStateRegistry)); - assertEq(game.gameType().raw(), GameTypes.AGGREGATE_VERIFIER.raw()); - assertEq(game.gameCreator(), ZK_PROVER); - bytes memory intermediateOutputRoots = game.intermediateOutputRoots(); - assertEq( - game.extraData(), - abi.encodePacked(currentL2BlockNumber, address(anchorStateRegistry), intermediateOutputRoots) - ); assertEq(game.bondRecipient(), ZK_PROVER); - assertTrue(anchorStateRegistry.isGameProper(IDisputeGame(address(game)))); assertEq(delayedWETH.balanceOf(address(game)), INIT_BOND); assertEq(game.proofCount(), 1); } @@ -120,15 +106,7 @@ contract AggregateVerifierTest is BaseTest { vm.expectRevert(GameNotResolved.selector); game.claimCredit(); - vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); - game.resolve(); - assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); - - _claimCreditAfterDelay(game, game.gameCreator()); - - vm.warp(block.timestamp + 1); - game.closeGame(); - _assertAnchorRoot(rootClaim); + _resolveSlowAndClose(game, rootClaim); } function testUpdatingAnchorStateRegistryWithZKProof() public { @@ -139,15 +117,7 @@ contract AggregateVerifierTest is BaseTest { ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof ); - vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); - game.resolve(); - assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); - - _claimCreditAfterDelay(game, game.gameCreator()); - - vm.warp(block.timestamp + 1); - game.closeGame(); - _assertAnchorRoot(rootClaim); + _resolveSlowAndClose(game, rootClaim); } function testUpdatingAnchorStateRegistryWithBothProofs() public { @@ -252,13 +222,15 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = block.number + 1; bytes32 l1OriginHash = bytes32(uint256(1)); - bytes memory proofBytes = _teeProof(l1OriginHash, l1OriginNumber, rootClaim); - vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) ); _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _teeProof(l1OriginHash, l1OriginNumber) ); } @@ -270,11 +242,13 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = 1; bytes32 l1OriginHash = bytes32(uint256(1)); - bytes memory proofBytes = _teeProof(l1OriginHash, l1OriginNumber, rootClaim); - vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _teeProof(l1OriginHash, l1OriginNumber) ); } @@ -283,12 +257,14 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = block.number - 1; bytes32 wrongHash = bytes32(uint256(0xdeadbeef)); - bytes memory proofBytes = _teeProof(wrongHash, l1OriginNumber, rootClaim); - bytes32 actualHash = blockhash(l1OriginNumber); vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginHashMismatch.selector, wrongHash, actualHash)); _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _teeProof(wrongHash, l1OriginNumber) ); } @@ -300,15 +276,12 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = block.number - 50; bytes32 l1OriginHash = blockhash(l1OriginNumber); - bytes memory proofBytes = abi.encodePacked( - uint8(AggregateVerifier.ProofType.TEE), - l1OriginHash, - l1OriginNumber, - _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) - ); - _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _teeProof(l1OriginHash, l1OriginNumber) ); } @@ -322,15 +295,12 @@ contract AggregateVerifierTest is BaseTest { vm.mockCall(EIP2935_CONTRACT, abi.encode(l1OriginNumber), abi.encode(expectedHash)); - bytes memory proofBytes = abi.encodePacked( - uint8(AggregateVerifier.ProofType.TEE), - expectedHash, - l1OriginNumber, - _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) - ); - _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _teeProof(expectedHash, l1OriginNumber) ); } @@ -351,26 +321,25 @@ contract AggregateVerifierTest is BaseTest { assertEq(l2SequenceNumber, currentL2BlockNumber); } - function _teeProof( - bytes32 l1OriginHash, - uint256 l1OriginNumber, - Claim rootClaim - ) - private - pure - returns (bytes memory) - { - return abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); + function _resolveSlowAndClose(AggregateVerifier game, Claim rootClaim) private { + vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); + game.resolve(); + assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); + + _claimCreditAfterDelay(game, game.gameCreator()); + + vm.warp(block.timestamp + 1); + game.closeGame(); + _assertAnchorRoot(rootClaim); } - function _generateIntermediateRootsExceptLast(uint256 l2BlockNumber) private pure returns (bytes memory) { - uint256 rootsCount = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL; - bytes32[] memory intermediateRoots = new bytes32[](rootsCount - 1); - uint256 startingL2BlockNumber = l2BlockNumber - BLOCK_INTERVAL; - for (uint256 i = 1; i < rootsCount; i++) { - intermediateRoots[i - 1] = keccak256(abi.encode(startingL2BlockNumber + INTERMEDIATE_BLOCK_INTERVAL * i)); - } - return abi.encodePacked(intermediateRoots); + function _teeProof(bytes32 l1OriginHash, uint256 l1OriginNumber) private pure returns (bytes memory) { + return abi.encodePacked( + uint8(AggregateVerifier.ProofType.TEE), + l1OriginHash, + l1OriginNumber, + _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) + ); } function _expectDeployWithInvalidBlockIntervalsReverts( diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index ba80dd889..9c467b9ad 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -215,7 +215,7 @@ contract BaseTest is Test { return abi.encodePacked(l2BlockNumber, parentAddress, _generateIntermediateRoots(l2BlockNumber, rootClaim)); } - function _generateIntermediateRoots(uint256 l2BlockNumber, Claim rootClaim) private pure returns (bytes memory) { + function _generateIntermediateRoots(uint256 l2BlockNumber, Claim rootClaim) internal pure returns (bytes memory) { bytes32[] memory intermediateRoots = new bytes32[](INTERMEDIATE_ROOTS_COUNT); uint256 startingL2BlockNumber = l2BlockNumber - BLOCK_INTERVAL; for (uint256 i = 1; i < INTERMEDIATE_ROOTS_COUNT; i++) { From 413e291a302a278a11fd945634a54aa05ea190c5 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:33:07 -0400 Subject: [PATCH 095/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 87 ++++++++------------------ 1 file changed, 27 insertions(+), 60 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index d178cb27b..6d6eed94d 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -15,59 +15,19 @@ import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; import { BaseTest } from "./BaseTest.t.sol"; contract AggregateVerifierTest is BaseTest { - address private constant EIP2935_CONTRACT = 0x0000F90827F1C53a10cb7A02335B175320002935; - function testInitializeWithTEEProof() public { Claim rootClaim = _advanceL2BlockAndClaim(); bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); - bytes memory intermediateRoots = _generateIntermediateRoots(currentL2BlockNumber, rootClaim); - bytes32 l1OriginHash = blockhash(block.number - 1); - bytes32 startingRoot = keccak256(abi.encode(uint256(0))); - bytes32 saltHash = keccak256("tee-proof"); - bytes memory nitroSignature = abi.encodePacked(saltHash, bytes32(0), uint8(27)); - bytes memory tdxSignature = abi.encodePacked(saltHash, bytes32(uint256(1)), uint8(28)); - bytes32 nitroJournal = keccak256( - abi.encodePacked( - TEE_PROVER, - l1OriginHash, - startingRoot, - uint64(0), - rootClaim.raw(), - uint64(currentL2BlockNumber), - intermediateRoots, - CONFIG_HASH, - TEE_NITRO_IMAGE_HASH - ) - ); - bytes32 tdxJournal = keccak256( - abi.encodePacked( - TEE_PROVER, - l1OriginHash, - startingRoot, - uint64(0), - rootClaim.raw(), - uint64(currentL2BlockNumber), - intermediateRoots, - CONFIG_HASH, - TEE_TDX_IMAGE_HASH - ) - ); - - vm.expectCall( - address(teeVerifier), - abi.encodeCall( - IVerifier.verify, (abi.encodePacked(TEE_PROVER, nitroSignature), TEE_NITRO_IMAGE_HASH, nitroJournal) - ) - ); - vm.expectCall( - address(teeVerifier), - abi.encodeCall( - IVerifier.verify, (abi.encodePacked(TEE_PROVER, tdxSignature), TEE_TDX_IMAGE_HASH, tdxJournal) - ) + AggregateVerifier game = _createAggregateVerifierGame( + TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof ); - _createAggregateVerifierGame(TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof); + assertEq(game.teeProver(), TEE_PROVER); + assertEq(game.zkProver(), address(0)); + assertEq(game.bondRecipient(), TEE_PROVER); + assertEq(delayedWETH.balanceOf(address(game)), INIT_BOND); + assertEq(game.proofCount(), 1); } function testInitializeWithZKProof() public { @@ -96,12 +56,8 @@ contract AggregateVerifierTest is BaseTest { } function testUpdatingAnchorStateRegistryWithTEEProof() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); - - AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof - ); + (AggregateVerifier game, Claim rootClaim) = + _createGameForAnchorUpdate(TEE_PROVER, "tee-proof", AggregateVerifier.ProofType.TEE); vm.expectRevert(GameNotResolved.selector); game.claimCredit(); @@ -110,12 +66,8 @@ contract AggregateVerifierTest is BaseTest { } function testUpdatingAnchorStateRegistryWithZKProof() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory proof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); - - AggregateVerifier game = _createAggregateVerifierGame( - ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof - ); + (AggregateVerifier game, Claim rootClaim) = + _createGameForAnchorUpdate(ZK_PROVER, "zk-proof", AggregateVerifier.ProofType.ZK); _resolveSlowAndClose(game, rootClaim); } @@ -292,8 +244,9 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = block.number - 260; bytes32 expectedHash = keccak256(abi.encodePacked("mock-blockhash", l1OriginNumber)); + address eip2935 = AggregateVerifier(address(factory.gameImpls(GameTypes.AGGREGATE_VERIFIER))).EIP2935_CONTRACT(); - vm.mockCall(EIP2935_CONTRACT, abi.encode(l1OriginNumber), abi.encode(expectedHash)); + vm.mockCall(eip2935, abi.encode(l1OriginNumber), abi.encode(expectedHash)); _createAggregateVerifierGame( TEE_PROVER, @@ -321,6 +274,20 @@ contract AggregateVerifierTest is BaseTest { assertEq(l2SequenceNumber, currentL2BlockNumber); } + function _createGameForAnchorUpdate( + address prover, + bytes memory proofSalt, + AggregateVerifier.ProofType proofType + ) + private + returns (AggregateVerifier game, Claim rootClaim) + { + rootClaim = _advanceL2BlockAndClaim(); + game = _createAggregateVerifierGame( + prover, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), _generateProof(proofSalt, proofType) + ); + } + function _resolveSlowAndClose(AggregateVerifier game, Claim rootClaim) private { vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); game.resolve(); From 6a60a3111ea0c4d401bd48103861fcc4b78b7579 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:36:04 -0400 Subject: [PATCH 096/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 108 +++++++++++-------------- 1 file changed, 48 insertions(+), 60 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 6d6eed94d..d2da0bb3f 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -17,10 +17,13 @@ import { BaseTest } from "./BaseTest.t.sol"; contract AggregateVerifierTest is BaseTest { function testInitializeWithTEEProof() public { Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _generateProof("tee-proof", AggregateVerifier.ProofType.TEE) ); assertEq(game.teeProver(), TEE_PROVER); @@ -32,10 +35,13 @@ contract AggregateVerifierTest is BaseTest { function testInitializeWithZKProof() public { Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory proof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); AggregateVerifier game = _createAggregateVerifierGame( - ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof + ZK_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _generateProof("zk-proof", AggregateVerifier.ProofType.ZK) ); assertEq(game.teeProver(), address(0)); @@ -56,8 +62,14 @@ contract AggregateVerifierTest is BaseTest { } function testUpdatingAnchorStateRegistryWithTEEProof() public { - (AggregateVerifier game, Claim rootClaim) = - _createGameForAnchorUpdate(TEE_PROVER, "tee-proof", AggregateVerifier.ProofType.TEE); + Claim rootClaim = _advanceL2BlockAndClaim(); + AggregateVerifier game = _createAggregateVerifierGame( + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _generateProof("tee-proof", AggregateVerifier.ProofType.TEE) + ); vm.expectRevert(GameNotResolved.selector); game.claimCredit(); @@ -66,8 +78,14 @@ contract AggregateVerifierTest is BaseTest { } function testUpdatingAnchorStateRegistryWithZKProof() public { - (AggregateVerifier game, Claim rootClaim) = - _createGameForAnchorUpdate(ZK_PROVER, "zk-proof", AggregateVerifier.ProofType.ZK); + Claim rootClaim = _advanceL2BlockAndClaim(); + AggregateVerifier game = _createAggregateVerifierGame( + ZK_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _generateProof("zk-proof", AggregateVerifier.ProofType.ZK) + ); _resolveSlowAndClose(game, rootClaim); } @@ -134,9 +152,6 @@ contract AggregateVerifierTest is BaseTest { _createAggregateVerifierGame(ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), zkProof); } - /// @notice Reverts when the parent is not factory-registered: `_isValidGame` requires - /// `AnchorStateRegistry.isGameRegistered`, which checks `DisputeGameFactory.games(...) == parent`. - /// @dev Parent is a real factory game, then the factory lookup is mocked to emulate a missing UUID entry. function testInitializeFailsIfParentGameNotFactoryRegistered() public { Claim parentRootClaim = _advanceL2BlockAndClaim(); AggregateVerifier unregisteredParent = _createAggregateVerifierGame( @@ -177,13 +192,7 @@ contract AggregateVerifierTest is BaseTest { vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) ); - _createAggregateVerifierGame( - TEE_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _teeProof(l1OriginHash, l1OriginNumber) - ); + _createTEEGameWithOrigin(rootClaim, l1OriginHash, l1OriginNumber); } function testVerifyFailsWithL1OriginTooOld() public { @@ -195,13 +204,7 @@ contract AggregateVerifierTest is BaseTest { bytes32 l1OriginHash = bytes32(uint256(1)); vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); - _createAggregateVerifierGame( - TEE_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _teeProof(l1OriginHash, l1OriginNumber) - ); + _createTEEGameWithOrigin(rootClaim, l1OriginHash, l1OriginNumber); } function testVerifyFailsWithL1OriginHashMismatch() public { @@ -211,13 +214,7 @@ contract AggregateVerifierTest is BaseTest { bytes32 actualHash = blockhash(l1OriginNumber); vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginHashMismatch.selector, wrongHash, actualHash)); - _createAggregateVerifierGame( - TEE_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _teeProof(wrongHash, l1OriginNumber) - ); + _createTEEGameWithOrigin(rootClaim, wrongHash, l1OriginNumber); } function testVerifyWithBlockhashWindow() public { @@ -228,13 +225,7 @@ contract AggregateVerifierTest is BaseTest { uint256 l1OriginNumber = block.number - 50; bytes32 l1OriginHash = blockhash(l1OriginNumber); - _createAggregateVerifierGame( - TEE_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _teeProof(l1OriginHash, l1OriginNumber) - ); + _createTEEGameWithOrigin(rootClaim, l1OriginHash, l1OriginNumber); } function testVerifyWithEIP2935Window() public { @@ -248,13 +239,7 @@ contract AggregateVerifierTest is BaseTest { vm.mockCall(eip2935, abi.encode(l1OriginNumber), abi.encode(expectedHash)); - _createAggregateVerifierGame( - TEE_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _teeProof(expectedHash, l1OriginNumber) - ); + _createTEEGameWithOrigin(rootClaim, expectedHash, l1OriginNumber); } function testDeployWithInvalidBlockIntervals() public { @@ -274,20 +259,6 @@ contract AggregateVerifierTest is BaseTest { assertEq(l2SequenceNumber, currentL2BlockNumber); } - function _createGameForAnchorUpdate( - address prover, - bytes memory proofSalt, - AggregateVerifier.ProofType proofType - ) - private - returns (AggregateVerifier game, Claim rootClaim) - { - rootClaim = _advanceL2BlockAndClaim(); - game = _createAggregateVerifierGame( - prover, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), _generateProof(proofSalt, proofType) - ); - } - function _resolveSlowAndClose(AggregateVerifier game, Claim rootClaim) private { vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); game.resolve(); @@ -300,6 +271,23 @@ contract AggregateVerifierTest is BaseTest { _assertAnchorRoot(rootClaim); } + function _createTEEGameWithOrigin( + Claim rootClaim, + bytes32 l1OriginHash, + uint256 l1OriginNumber + ) + private + returns (AggregateVerifier) + { + return _createAggregateVerifierGame( + TEE_PROVER, + rootClaim, + currentL2BlockNumber, + address(anchorStateRegistry), + _teeProof(l1OriginHash, l1OriginNumber) + ); + } + function _teeProof(bytes32 l1OriginHash, uint256 l1OriginNumber) private pure returns (bytes memory) { return abi.encodePacked( uint8(AggregateVerifier.ProofType.TEE), From 954c589b43066e65840a3ef65533c27361623dc0 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:38:12 -0400 Subject: [PATCH 097/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index d2da0bb3f..2e9364b1f 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -284,16 +284,12 @@ contract AggregateVerifierTest is BaseTest { rootClaim, currentL2BlockNumber, address(anchorStateRegistry), - _teeProof(l1OriginHash, l1OriginNumber) - ); - } - - function _teeProof(bytes32 l1OriginHash, uint256 l1OriginNumber) private pure returns (bytes memory) { - return abi.encodePacked( - uint8(AggregateVerifier.ProofType.TEE), - l1OriginHash, - l1OriginNumber, - _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) + abi.encodePacked( + uint8(AggregateVerifier.ProofType.TEE), + l1OriginHash, + l1OriginNumber, + _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) + ) ); } From d209b25ade7c02dda91223621f1fd7175496311a Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:43:42 -0400 Subject: [PATCH 098/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 100 +++++++------------------ test/L1/proofs/BaseTest.t.sol | 22 ++++-- 2 files changed, 44 insertions(+), 78 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 2e9364b1f..3d2f126af 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -2,15 +2,11 @@ pragma solidity 0.8.15; import { BadExtraData, GameNotResolved } from "src/libraries/bridge/Errors.sol"; -import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; -import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; -import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { GameStatus, GameTypes, Hash } from "src/libraries/bridge/Types.sol"; import { Claim, Timestamp } from "src/libraries/bridge/LibUDT.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; -import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; import { BaseTest } from "./BaseTest.t.sol"; @@ -18,13 +14,7 @@ contract AggregateVerifierTest is BaseTest { function testInitializeWithTEEProof() public { Claim rootClaim = _advanceL2BlockAndClaim(); - AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _generateProof("tee-proof", AggregateVerifier.ProofType.TEE) - ); + AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); assertEq(game.teeProver(), TEE_PROVER); assertEq(game.zkProver(), address(0)); @@ -36,13 +26,7 @@ contract AggregateVerifierTest is BaseTest { function testInitializeWithZKProof() public { Claim rootClaim = _advanceL2BlockAndClaim(); - AggregateVerifier game = _createAggregateVerifierGame( - ZK_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _generateProof("zk-proof", AggregateVerifier.ProofType.ZK) - ); + AggregateVerifier game = _createGame(ZK_PROVER, rootClaim, "zk-proof", AggregateVerifier.ProofType.ZK); assertEq(game.teeProver(), address(0)); assertEq(game.zkProver(), ZK_PROVER); @@ -63,13 +47,7 @@ contract AggregateVerifierTest is BaseTest { function testUpdatingAnchorStateRegistryWithTEEProof() public { Claim rootClaim = _advanceL2BlockAndClaim(); - AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _generateProof("tee-proof", AggregateVerifier.ProofType.TEE) - ); + AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); vm.expectRevert(GameNotResolved.selector); game.claimCredit(); @@ -79,27 +57,17 @@ contract AggregateVerifierTest is BaseTest { function testUpdatingAnchorStateRegistryWithZKProof() public { Claim rootClaim = _advanceL2BlockAndClaim(); - AggregateVerifier game = _createAggregateVerifierGame( - ZK_PROVER, - rootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _generateProof("zk-proof", AggregateVerifier.ProofType.ZK) - ); + AggregateVerifier game = _createGame(ZK_PROVER, rootClaim, "zk-proof", AggregateVerifier.ProofType.ZK); _resolveSlowAndClose(game, rootClaim); } function testUpdatingAnchorStateRegistryWithBothProofs() public { Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory teeProof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); - bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); - AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), teeProof - ); + AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); - _provideProof(game, ZK_PROVER, zkProof); + _provideProof(game, ZK_PROVER, _generateProof("zk-proof", AggregateVerifier.ProofType.ZK)); assertEq(game.proofCount(), 2); vm.warp(block.timestamp + game.FAST_FINALIZATION_DELAY()); @@ -115,12 +83,8 @@ contract AggregateVerifierTest is BaseTest { function testProofCannotIncreaseExpectedResolution() public { Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory teeProof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); - bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); - AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), teeProof - ); + AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); uint256 slowDelay = game.SLOW_FINALIZATION_DELAY(); Timestamp originalExpectedResolution = game.expectedResolution(); @@ -130,7 +94,7 @@ contract AggregateVerifierTest is BaseTest { vm.expectRevert(AggregateVerifier.GameNotOver.selector); game.resolve(); - _provideProof(game, ZK_PROVER, zkProof); + _provideProof(game, ZK_PROVER, _generateProof("zk-proof", AggregateVerifier.ProofType.ZK)); assertEq(game.expectedResolution().raw(), originalExpectedResolution.raw()); vm.warp(block.timestamp + 1); @@ -140,27 +104,18 @@ contract AggregateVerifierTest is BaseTest { function testCannotCreateSameProposal() public { Claim rootClaim = _advanceL2BlockAndClaim(); - bytes memory teeProof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); - bytes memory zkProof = _generateProof("zk-proof", AggregateVerifier.ProofType.ZK); - AggregateVerifier game = _createAggregateVerifierGame( - TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), teeProof - ); + AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); Hash gameId = factory.getGameUUID(GameTypes.AGGREGATE_VERIFIER, rootClaim, game.extraData()); vm.expectRevert(abi.encodeWithSelector(IDisputeGameFactory.GameAlreadyExists.selector, gameId)); - _createAggregateVerifierGame(ZK_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), zkProof); + _createGame(ZK_PROVER, rootClaim, "zk-proof", AggregateVerifier.ProofType.ZK); } function testInitializeFailsIfParentGameNotFactoryRegistered() public { Claim parentRootClaim = _advanceL2BlockAndClaim(); - AggregateVerifier unregisteredParent = _createAggregateVerifierGame( - TEE_PROVER, - parentRootClaim, - currentL2BlockNumber, - address(anchorStateRegistry), - _generateProof("parent-tee", AggregateVerifier.ProofType.TEE) - ); + AggregateVerifier unregisteredParent = + _createGame(TEE_PROVER, parentRootClaim, "parent-tee", AggregateVerifier.ProofType.TEE); vm.mockCall( address(factory), @@ -168,7 +123,7 @@ contract AggregateVerifierTest is BaseTest { IDisputeGameFactory.games, (GameTypes.AGGREGATE_VERIFIER, parentRootClaim, unregisteredParent.extraData()) ), - abi.encode(IDisputeGame(address(0)), Timestamp.wrap(0)) + abi.encode(address(0), Timestamp.wrap(0)) ); currentL2BlockNumber += BLOCK_INTERVAL; @@ -271,6 +226,20 @@ contract AggregateVerifierTest is BaseTest { _assertAnchorRoot(rootClaim); } + function _createGame( + address creator, + Claim rootClaim, + bytes memory proofSalt, + AggregateVerifier.ProofType proofType + ) + private + returns (AggregateVerifier) + { + return _createAggregateVerifierGame( + creator, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), _generateProof(proofSalt, proofType) + ); + } + function _createTEEGameWithOrigin( Claim rootClaim, bytes32 l1OriginHash, @@ -304,19 +273,6 @@ contract AggregateVerifierTest is BaseTest { AggregateVerifier.InvalidBlockInterval.selector, blockInterval, intermediateBlockInterval ) ); - new AggregateVerifier( - GameTypes.AGGREGATE_VERIFIER, - IAnchorStateRegistry(address(anchorStateRegistry)), - IDelayedWETH(payable(address(delayedWETH))), - IVerifier(address(teeVerifier)), - IVerifier(address(zkVerifier)), - TEE_NITRO_IMAGE_HASH, - TEE_TDX_IMAGE_HASH, - AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), - CONFIG_HASH, - L2_CHAIN_ID, - blockInterval, - intermediateBlockInterval - ); + _newAggregateVerifier(blockInterval, intermediateBlockInterval); } } diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 9c467b9ad..51697c7dc 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -107,7 +107,20 @@ contract BaseTest is Test { } function _deployAndSetAggregateVerifier() internal { - AggregateVerifier aggregateVerifierImpl = new AggregateVerifier( + AggregateVerifier aggregateVerifierImpl = _newAggregateVerifier(BLOCK_INTERVAL, INTERMEDIATE_BLOCK_INTERVAL); + + factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(aggregateVerifierImpl))); + factory.setInitBond(GameTypes.AGGREGATE_VERIFIER, INIT_BOND); + } + + function _newAggregateVerifier( + uint256 blockInterval, + uint256 intermediateBlockInterval + ) + internal + returns (AggregateVerifier) + { + return new AggregateVerifier( GameTypes.AGGREGATE_VERIFIER, IAnchorStateRegistry(address(anchorStateRegistry)), IDelayedWETH(payable(address(delayedWETH))), @@ -118,12 +131,9 @@ contract BaseTest is Test { AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, - BLOCK_INTERVAL, - INTERMEDIATE_BLOCK_INTERVAL + blockInterval, + intermediateBlockInterval ); - - factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(aggregateVerifierImpl))); - factory.setInitBond(GameTypes.AGGREGATE_VERIFIER, INIT_BOND); } function _createAggregateVerifierGame( From b93ebe2b7de5014294951ce5d6ddd506270077d9 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:47:38 -0400 Subject: [PATCH 099/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 33 ++++++++++---------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index 3d2f126af..e8fd51af9 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -142,12 +142,11 @@ contract AggregateVerifierTest is BaseTest { function testVerifyFailsWithL1OriginInFuture() public { Claim rootClaim = _advanceL2BlockAndClaim(); uint256 l1OriginNumber = block.number + 1; - bytes32 l1OriginHash = bytes32(uint256(1)); vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) ); - _createTEEGameWithOrigin(rootClaim, l1OriginHash, l1OriginNumber); + _createTEEGameWithOrigin(rootClaim, bytes32(uint256(1)), l1OriginNumber); } function testVerifyFailsWithL1OriginTooOld() public { @@ -156,10 +155,9 @@ contract AggregateVerifierTest is BaseTest { vm.roll(block.number + 300); uint256 l1OriginNumber = 1; - bytes32 l1OriginHash = bytes32(uint256(1)); vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); - _createTEEGameWithOrigin(rootClaim, l1OriginHash, l1OriginNumber); + _createTEEGameWithOrigin(rootClaim, bytes32(uint256(1)), l1OriginNumber); } function testVerifyFailsWithL1OriginHashMismatch() public { @@ -198,9 +196,16 @@ contract AggregateVerifierTest is BaseTest { } function testDeployWithInvalidBlockIntervals() public { - _expectDeployWithInvalidBlockIntervalsReverts(0, INTERMEDIATE_BLOCK_INTERVAL); - _expectDeployWithInvalidBlockIntervalsReverts(BLOCK_INTERVAL, 0); - _expectDeployWithInvalidBlockIntervalsReverts(3, 2); + vm.expectRevert( + abi.encodeWithSelector(AggregateVerifier.InvalidBlockInterval.selector, 0, INTERMEDIATE_BLOCK_INTERVAL) + ); + _newAggregateVerifier(0, INTERMEDIATE_BLOCK_INTERVAL); + + vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.InvalidBlockInterval.selector, BLOCK_INTERVAL, 0)); + _newAggregateVerifier(BLOCK_INTERVAL, 0); + + vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.InvalidBlockInterval.selector, 3, 2)); + _newAggregateVerifier(3, 2); } function _advanceL2BlockAndClaim() private returns (Claim rootClaim) { @@ -261,18 +266,4 @@ contract AggregateVerifierTest is BaseTest { ) ); } - - function _expectDeployWithInvalidBlockIntervalsReverts( - uint256 blockInterval, - uint256 intermediateBlockInterval - ) - private - { - vm.expectRevert( - abi.encodeWithSelector( - AggregateVerifier.InvalidBlockInterval.selector, blockInterval, intermediateBlockInterval - ) - ); - _newAggregateVerifier(blockInterval, intermediateBlockInterval); - } } From 60ef5c03b06062b2993f9ea61430335fb8b8b912 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:51:22 -0400 Subject: [PATCH 100/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 72 ++++++++++---------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index e8fd51af9..dc8b60f09 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -12,9 +12,8 @@ import { BaseTest } from "./BaseTest.t.sol"; contract AggregateVerifierTest is BaseTest { function testInitializeWithTEEProof() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - - AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); + AggregateVerifier game = + _createGame(TEE_PROVER, _advanceL2BlockAndClaim(), "tee-proof", AggregateVerifier.ProofType.TEE); assertEq(game.teeProver(), TEE_PROVER); assertEq(game.zkProver(), address(0)); @@ -24,9 +23,8 @@ contract AggregateVerifierTest is BaseTest { } function testInitializeWithZKProof() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - - AggregateVerifier game = _createGame(ZK_PROVER, rootClaim, "zk-proof", AggregateVerifier.ProofType.ZK); + AggregateVerifier game = + _createGame(ZK_PROVER, _advanceL2BlockAndClaim(), "zk-proof", AggregateVerifier.ProofType.ZK); assertEq(game.teeProver(), address(0)); assertEq(game.zkProver(), ZK_PROVER); @@ -36,13 +34,11 @@ contract AggregateVerifierTest is BaseTest { } function testInitializeFailsIfInvalidCallDataSize() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - - vm.deal(TEE_PROVER, INIT_BOND); - - vm.prank(TEE_PROVER); + hoax(TEE_PROVER, INIT_BOND); vm.expectRevert(BadExtraData.selector); - factory.createWithInitData{ value: INIT_BOND }(GameTypes.AGGREGATE_VERIFIER, rootClaim, hex"", hex""); + factory.createWithInitData{ value: INIT_BOND }( + GameTypes.AGGREGATE_VERIFIER, _advanceL2BlockAndClaim(), hex"", hex"" + ); } function testUpdatingAnchorStateRegistryWithTEEProof() public { @@ -82,9 +78,8 @@ contract AggregateVerifierTest is BaseTest { } function testProofCannotIncreaseExpectedResolution() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - - AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); + AggregateVerifier game = + _createGame(TEE_PROVER, _advanceL2BlockAndClaim(), "tee-proof", AggregateVerifier.ProofType.TEE); uint256 slowDelay = game.SLOW_FINALIZATION_DELAY(); Timestamp originalExpectedResolution = game.expectedResolution(); @@ -127,12 +122,11 @@ contract AggregateVerifierTest is BaseTest { ); currentL2BlockNumber += BLOCK_INTERVAL; - Claim childRootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "child"))); vm.expectRevert(AggregateVerifier.InvalidParentGame.selector); _createAggregateVerifierGame( TEE_PROVER, - childRootClaim, + Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "child"))), currentL2BlockNumber, address(unregisteredParent), _generateProof("child-tee", AggregateVerifier.ProofType.TEE) @@ -140,59 +134,56 @@ contract AggregateVerifierTest is BaseTest { } function testVerifyFailsWithL1OriginInFuture() public { - Claim rootClaim = _advanceL2BlockAndClaim(); uint256 l1OriginNumber = block.number + 1; vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) ); - _createTEEGameWithOrigin(rootClaim, bytes32(uint256(1)), l1OriginNumber); + _createTEEGameWithOrigin(_advanceL2BlockAndClaim(), bytes32(uint256(1)), l1OriginNumber); } function testVerifyFailsWithL1OriginTooOld() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - vm.roll(block.number + 300); uint256 l1OriginNumber = 1; vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); - _createTEEGameWithOrigin(rootClaim, bytes32(uint256(1)), l1OriginNumber); + _createTEEGameWithOrigin(_advanceL2BlockAndClaim(), bytes32(uint256(1)), l1OriginNumber); } function testVerifyFailsWithL1OriginHashMismatch() public { - Claim rootClaim = _advanceL2BlockAndClaim(); uint256 l1OriginNumber = block.number - 1; bytes32 wrongHash = bytes32(uint256(0xdeadbeef)); - bytes32 actualHash = blockhash(l1OriginNumber); - vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginHashMismatch.selector, wrongHash, actualHash)); - _createTEEGameWithOrigin(rootClaim, wrongHash, l1OriginNumber); + vm.expectRevert( + abi.encodeWithSelector( + AggregateVerifier.L1OriginHashMismatch.selector, wrongHash, blockhash(l1OriginNumber) + ) + ); + _createTEEGameWithOrigin(_advanceL2BlockAndClaim(), wrongHash, l1OriginNumber); } function testVerifyWithBlockhashWindow() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - vm.roll(block.number + 100); uint256 l1OriginNumber = block.number - 50; - bytes32 l1OriginHash = blockhash(l1OriginNumber); - _createTEEGameWithOrigin(rootClaim, l1OriginHash, l1OriginNumber); + _createTEEGameWithOrigin(_advanceL2BlockAndClaim(), blockhash(l1OriginNumber), l1OriginNumber); } function testVerifyWithEIP2935Window() public { - Claim rootClaim = _advanceL2BlockAndClaim(); - vm.roll(block.number + 300); uint256 l1OriginNumber = block.number - 260; bytes32 expectedHash = keccak256(abi.encodePacked("mock-blockhash", l1OriginNumber)); - address eip2935 = AggregateVerifier(address(factory.gameImpls(GameTypes.AGGREGATE_VERIFIER))).EIP2935_CONTRACT(); - vm.mockCall(eip2935, abi.encode(l1OriginNumber), abi.encode(expectedHash)); + vm.mockCall( + AggregateVerifier(address(factory.gameImpls(GameTypes.AGGREGATE_VERIFIER))).EIP2935_CONTRACT(), + abi.encode(l1OriginNumber), + abi.encode(expectedHash) + ); - _createTEEGameWithOrigin(rootClaim, expectedHash, l1OriginNumber); + _createTEEGameWithOrigin(_advanceL2BlockAndClaim(), expectedHash, l1OriginNumber); } function testDeployWithInvalidBlockIntervals() public { @@ -245,15 +236,8 @@ contract AggregateVerifierTest is BaseTest { ); } - function _createTEEGameWithOrigin( - Claim rootClaim, - bytes32 l1OriginHash, - uint256 l1OriginNumber - ) - private - returns (AggregateVerifier) - { - return _createAggregateVerifierGame( + function _createTEEGameWithOrigin(Claim rootClaim, bytes32 l1OriginHash, uint256 l1OriginNumber) private { + _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, From cc154cbe53238924f9e204bc8da70e178b211a04 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:54:28 -0400 Subject: [PATCH 101/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index dc8b60f09..acc6dd6a0 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.15; import { BadExtraData, GameNotResolved } from "src/libraries/bridge/Errors.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { GameStatus, GameTypes, Hash } from "src/libraries/bridge/Types.sol"; -import { Claim, Timestamp } from "src/libraries/bridge/LibUDT.sol"; +import { GameStatus, GameTypes } from "src/libraries/bridge/Types.sol"; +import { Claim, Hash } from "src/libraries/bridge/LibUDT.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; @@ -82,15 +82,15 @@ contract AggregateVerifierTest is BaseTest { _createGame(TEE_PROVER, _advanceL2BlockAndClaim(), "tee-proof", AggregateVerifier.ProofType.TEE); uint256 slowDelay = game.SLOW_FINALIZATION_DELAY(); - Timestamp originalExpectedResolution = game.expectedResolution(); - assertEq(originalExpectedResolution.raw(), block.timestamp + slowDelay); + uint256 originalExpectedResolution = game.expectedResolution().raw(); + assertEq(originalExpectedResolution, block.timestamp + slowDelay); vm.warp(block.timestamp + slowDelay - 1); vm.expectRevert(AggregateVerifier.GameNotOver.selector); game.resolve(); _provideProof(game, ZK_PROVER, _generateProof("zk-proof", AggregateVerifier.ProofType.ZK)); - assertEq(game.expectedResolution().raw(), originalExpectedResolution.raw()); + assertEq(game.expectedResolution().raw(), originalExpectedResolution); vm.warp(block.timestamp + 1); game.resolve(); @@ -102,8 +102,12 @@ contract AggregateVerifierTest is BaseTest { AggregateVerifier game = _createGame(TEE_PROVER, rootClaim, "tee-proof", AggregateVerifier.ProofType.TEE); - Hash gameId = factory.getGameUUID(GameTypes.AGGREGATE_VERIFIER, rootClaim, game.extraData()); - vm.expectRevert(abi.encodeWithSelector(IDisputeGameFactory.GameAlreadyExists.selector, gameId)); + vm.expectRevert( + abi.encodeWithSelector( + IDisputeGameFactory.GameAlreadyExists.selector, + factory.getGameUUID(GameTypes.AGGREGATE_VERIFIER, rootClaim, game.extraData()) + ) + ); _createGame(ZK_PROVER, rootClaim, "zk-proof", AggregateVerifier.ProofType.ZK); } @@ -118,7 +122,7 @@ contract AggregateVerifierTest is BaseTest { IDisputeGameFactory.games, (GameTypes.AGGREGATE_VERIFIER, parentRootClaim, unregisteredParent.extraData()) ), - abi.encode(address(0), Timestamp.wrap(0)) + abi.encode(address(0), uint64(0)) ); currentL2BlockNumber += BLOCK_INTERVAL; From 0bb0cdc10972a8c9e792bfa534b440e82d0cb910 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Mon, 29 Jun 2026 21:57:17 -0400 Subject: [PATCH 102/146] clean up AggregateVerifier tests --- test/L1/proofs/AggregateVerifier.t.sol | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index acc6dd6a0..1065a59e5 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -67,8 +67,7 @@ contract AggregateVerifierTest is BaseTest { assertEq(game.proofCount(), 2); vm.warp(block.timestamp + game.FAST_FINALIZATION_DELAY()); - game.resolve(); - assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); + assertEq(uint8(game.resolve()), uint8(GameStatus.DEFENDER_WINS)); vm.warp(block.timestamp + 1); game.closeGame(); @@ -93,8 +92,7 @@ contract AggregateVerifierTest is BaseTest { assertEq(game.expectedResolution().raw(), originalExpectedResolution); vm.warp(block.timestamp + 1); - game.resolve(); - assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); + assertEq(uint8(game.resolve()), uint8(GameStatus.DEFENDER_WINS)); } function testCannotCreateSameProposal() public { @@ -216,8 +214,7 @@ contract AggregateVerifierTest is BaseTest { function _resolveSlowAndClose(AggregateVerifier game, Claim rootClaim) private { vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); - game.resolve(); - assertEq(uint8(game.status()), uint8(GameStatus.DEFENDER_WINS)); + assertEq(uint8(game.resolve()), uint8(GameStatus.DEFENDER_WINS)); _claimCreditAfterDelay(game, game.gameCreator()); From 7f9b38f4567be87f0f7c8daf7fa4cb10f491afe3 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:16:06 -0400 Subject: [PATCH 103/146] clean up BaseTest --- test/L1/proofs/BaseTest.t.sol | 65 +++++++++++------------------------ 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 51697c7dc..51fb7f4c0 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -32,19 +32,17 @@ contract BaseTest is Test { uint256 internal constant INIT_BOND = 1 ether; uint256 internal constant DELAYED_WETH_DELAY = 1 days; - // Finality delay handled by the AggregateVerifier - uint256 internal constant FINALITY_DELAY = 0 days; uint256 internal currentL2BlockNumber; address internal immutable TEE_PROVER = makeAddr("tee-prover"); address internal immutable ZK_PROVER = makeAddr("zk-prover"); - bytes32 public immutable TEE_NITRO_IMAGE_HASH = keccak256("tee-nitro-image"); - bytes32 public immutable TEE_TDX_IMAGE_HASH = keccak256("tee-tdx-image"); - bytes32 public immutable ZK_RANGE_HASH = keccak256("zk-range"); - bytes32 public immutable ZK_AGGREGATE_HASH = keccak256("zk-aggregate"); - bytes32 public immutable CONFIG_HASH = keccak256("config"); + bytes32 internal constant TEE_NITRO_IMAGE_HASH = keccak256("tee-nitro-image"); + bytes32 internal constant TEE_TDX_IMAGE_HASH = keccak256("tee-tdx-image"); + bytes32 internal constant ZK_RANGE_HASH = keccak256("zk-range"); + bytes32 internal constant ZK_AGGREGATE_HASH = keccak256("zk-aggregate"); + bytes32 internal constant CONFIG_HASH = keccak256("config"); ProxyAdmin internal proxyAdmin; ISystemConfig internal systemConfig; @@ -57,23 +55,11 @@ contract BaseTest is Test { MockVerifier internal zkVerifier; function setUp() public virtual { - _deployContractsAndProxies(); - _initializeProxies(); - - _deployAndSetAggregateVerifier(); - - anchorStateRegistry.setRespectedGameType(GameTypes.AGGREGATE_VERIFIER); - - // Games created at or before the registry's retirement timestamp are invalid. - vm.warp(block.timestamp + 1); - } - - function _deployContractsAndProxies() internal { systemConfig = ISystemConfig(makeAddr("system-config")); vm.mockCall(address(systemConfig), abi.encodeCall(ISystemConfig.guardian, ()), abi.encode(address(this))); vm.mockCall(address(systemConfig), abi.encodeCall(ISystemConfig.paused, ()), abi.encode(false)); - AnchorStateRegistry _anchorStateRegistry = new AnchorStateRegistry(FINALITY_DELAY); + AnchorStateRegistry _anchorStateRegistry = new AnchorStateRegistry(0); DelayedWETH _delayedWETH = new DelayedWETH(DELAYED_WETH_DELAY); DisputeGameFactory _factory = new DisputeGameFactory(); @@ -85,15 +71,7 @@ contract BaseTest is Test { teeVerifier = new MockVerifier(IAnchorStateRegistry(address(anchorStateRegistry))); zkVerifier = new MockVerifier(IAnchorStateRegistry(address(anchorStateRegistry))); - } - function _deployProxy(address implementation) private returns (address) { - Proxy proxy = new Proxy(address(proxyAdmin)); - proxyAdmin.upgrade(payable(address(proxy)), implementation); - return address(proxy); - } - - function _initializeProxies() internal { anchorStateRegistry.initialize( systemConfig, IDisputeGameFactory(address(factory)), @@ -104,13 +82,21 @@ contract BaseTest is Test { ); factory.initialize(address(this)); delayedWETH.initialize(systemConfig); - } - function _deployAndSetAggregateVerifier() internal { AggregateVerifier aggregateVerifierImpl = _newAggregateVerifier(BLOCK_INTERVAL, INTERMEDIATE_BLOCK_INTERVAL); - factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(aggregateVerifierImpl))); factory.setInitBond(GameTypes.AGGREGATE_VERIFIER, INIT_BOND); + + anchorStateRegistry.setRespectedGameType(GameTypes.AGGREGATE_VERIFIER); + + // Games created at or before the registry's retirement timestamp are invalid. + vm.warp(block.timestamp + 1); + } + + function _deployProxy(address implementation) private returns (address) { + Proxy proxy = new Proxy(address(proxyAdmin)); + proxyAdmin.upgrade(payable(address(proxy)), implementation); + return address(proxy); } function _newAggregateVerifier( @@ -144,9 +130,10 @@ contract BaseTest is Test { bytes memory proof ) internal - returns (AggregateVerifier game) + returns (AggregateVerifier) { - bytes memory extraData = _aggregateVerifierExtraData(rootClaim, l2BlockNumber, parentAddress); + bytes memory extraData = + abi.encodePacked(l2BlockNumber, parentAddress, _generateIntermediateRoots(l2BlockNumber, rootClaim)); vm.deal(creator, INIT_BOND); vm.prank(creator); @@ -213,18 +200,6 @@ contract BaseTest is Test { return abi.encodePacked(salt, bytes32(0), bytes32(0), uint8(27)); } - function _aggregateVerifierExtraData( - Claim rootClaim, - uint256 l2BlockNumber, - address parentAddress - ) - internal - pure - returns (bytes memory) - { - return abi.encodePacked(l2BlockNumber, parentAddress, _generateIntermediateRoots(l2BlockNumber, rootClaim)); - } - function _generateIntermediateRoots(uint256 l2BlockNumber, Claim rootClaim) internal pure returns (bytes memory) { bytes32[] memory intermediateRoots = new bytes32[](INTERMEDIATE_ROOTS_COUNT); uint256 startingL2BlockNumber = l2BlockNumber - BLOCK_INTERVAL; From 3e0657c39e8bf5b96f18f8db7d97926b55f6e9fe Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:23:21 -0400 Subject: [PATCH 104/146] clean up BaseTest --- test/L1/proofs/BaseTest.t.sol | 24 ++++++++++++------------ test/L1/proofs/Challenge.t.sol | 4 +--- test/L1/proofs/Nullify.t.sol | 6 ++---- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 51fb7f4c0..ce724be56 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -29,6 +29,7 @@ contract BaseTest is Test { uint256 internal constant BLOCK_INTERVAL = 100; uint256 internal constant INTERMEDIATE_BLOCK_INTERVAL = 10; uint256 private constant INTERMEDIATE_ROOTS_COUNT = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL; + uint256 internal constant LAST_INTERMEDIATE_ROOT_INDEX = INTERMEDIATE_ROOTS_COUNT - 1; uint256 internal constant INIT_BOND = 1 ether; uint256 internal constant DELAYED_WETH_DELAY = 1 days; @@ -59,15 +60,11 @@ contract BaseTest is Test { vm.mockCall(address(systemConfig), abi.encodeCall(ISystemConfig.guardian, ()), abi.encode(address(this))); vm.mockCall(address(systemConfig), abi.encodeCall(ISystemConfig.paused, ()), abi.encode(false)); - AnchorStateRegistry _anchorStateRegistry = new AnchorStateRegistry(0); - DelayedWETH _delayedWETH = new DelayedWETH(DELAYED_WETH_DELAY); - DisputeGameFactory _factory = new DisputeGameFactory(); - proxyAdmin = new ProxyAdmin(address(this)); - anchorStateRegistry = AnchorStateRegistry(_deployProxy(address(_anchorStateRegistry))); - factory = DisputeGameFactory(_deployProxy(address(_factory))); - delayedWETH = DelayedWETH(payable(_deployProxy(address(_delayedWETH)))); + anchorStateRegistry = AnchorStateRegistry(_deployProxy(address(new AnchorStateRegistry(0)))); + factory = DisputeGameFactory(_deployProxy(address(new DisputeGameFactory()))); + delayedWETH = DelayedWETH(payable(_deployProxy(address(new DelayedWETH(DELAYED_WETH_DELAY))))); teeVerifier = new MockVerifier(IAnchorStateRegistry(address(anchorStateRegistry))); zkVerifier = new MockVerifier(IAnchorStateRegistry(address(anchorStateRegistry))); @@ -83,8 +80,10 @@ contract BaseTest is Test { factory.initialize(address(this)); delayedWETH.initialize(systemConfig); - AggregateVerifier aggregateVerifierImpl = _newAggregateVerifier(BLOCK_INTERVAL, INTERMEDIATE_BLOCK_INTERVAL); - factory.setImplementation(GameTypes.AGGREGATE_VERIFIER, IDisputeGame(address(aggregateVerifierImpl))); + factory.setImplementation( + GameTypes.AGGREGATE_VERIFIER, + IDisputeGame(address(_newAggregateVerifier(BLOCK_INTERVAL, INTERMEDIATE_BLOCK_INTERVAL))) + ); factory.setInitBond(GameTypes.AGGREGATE_VERIFIER, INIT_BOND); anchorStateRegistry.setRespectedGameType(GameTypes.AGGREGATE_VERIFIER); @@ -170,8 +169,9 @@ contract BaseTest is Test { returns (bytes memory) { uint256 l1OriginNumber = block.number - 1; - bytes32 l1OriginHash = blockhash(l1OriginNumber); - return abi.encodePacked(uint8(proofType), l1OriginHash, l1OriginNumber, _generateProofBody(salt, proofType)); + return abi.encodePacked( + uint8(proofType), blockhash(l1OriginNumber), l1OriginNumber, _generateProofBody(salt, proofType) + ); } function _generateProposalProof( @@ -206,7 +206,7 @@ contract BaseTest is Test { for (uint256 i = 1; i < INTERMEDIATE_ROOTS_COUNT; i++) { intermediateRoots[i - 1] = keccak256(abi.encode(startingL2BlockNumber + INTERMEDIATE_BLOCK_INTERVAL * i)); } - intermediateRoots[INTERMEDIATE_ROOTS_COUNT - 1] = rootClaim.raw(); + intermediateRoots[LAST_INTERMEDIATE_ROOT_INDEX] = rootClaim.raw(); return abi.encodePacked(intermediateRoots); } diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index ac51dbf6f..5f406654b 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -12,8 +12,6 @@ import { Verifier } from "src/L1/proofs/Verifier.sol"; import { BaseTest } from "./BaseTest.t.sol"; contract ChallengeTest is BaseTest { - uint256 private constant LAST_INTERMEDIATE_ROOT_INDEX = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; - function testChallengeTEEProofWithZKProof() public { AggregateVerifier game = _createGame(TEE_PROVER, "tee", "tee-proof", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry)); @@ -87,7 +85,7 @@ contract ChallengeTest is BaseTest { Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); - game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); + game.nullify(teeProof2, LAST_INTERMEDIATE_ROOT_INDEX, rootClaim2.raw()); // challenge game: TEE proof was nullified, so MissingProof(TEE) is expected vm.expectRevert( diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index 61d4a14f0..9d10c0216 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -10,7 +10,6 @@ import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { BaseTest } from "./BaseTest.t.sol"; contract NullifyTest is BaseTest { - uint256 private constant LAST_INTERMEDIATE_ROOT_INDEX = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; uint256 private constant NO_PROOF_CREDIT_CLAIM_DELAY = 14 days; function testNullifyWithTEEProof() public { @@ -49,7 +48,7 @@ contract NullifyTest is BaseTest { Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); - game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); + game.nullify(teeProof2, LAST_INTERMEDIATE_ROOT_INDEX, rootClaim2.raw()); _assertStatus(game, GameStatus.IN_PROGRESS); assertEq(game.bondRecipient(), TEE_PROVER); @@ -119,8 +118,7 @@ contract NullifyTest is BaseTest { Claim rootClaimNullify = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "nullify-b"))); bytes memory teeProofNullify = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); - uint256 lastIntermediateIdx = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; - gameB.nullify(teeProofNullify, lastIntermediateIdx, rootClaimNullify.raw()); + gameB.nullify(teeProofNullify, LAST_INTERMEDIATE_ROOT_INDEX, rootClaimNullify.raw()); assertTrue(teeVerifier.nullified()); assertEq(gameA.proofCount(), 1); From c156a64507de1562145a349f75c12141b3b2d3ba Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:28:29 -0400 Subject: [PATCH 105/146] clean up BaseTest --- test/L1/proofs/BaseTest.t.sol | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index ce724be56..6abc6a28c 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -39,12 +39,6 @@ contract BaseTest is Test { address internal immutable TEE_PROVER = makeAddr("tee-prover"); address internal immutable ZK_PROVER = makeAddr("zk-prover"); - bytes32 internal constant TEE_NITRO_IMAGE_HASH = keccak256("tee-nitro-image"); - bytes32 internal constant TEE_TDX_IMAGE_HASH = keccak256("tee-tdx-image"); - bytes32 internal constant ZK_RANGE_HASH = keccak256("zk-range"); - bytes32 internal constant ZK_AGGREGATE_HASH = keccak256("zk-aggregate"); - bytes32 internal constant CONFIG_HASH = keccak256("config"); - ProxyAdmin internal proxyAdmin; ISystemConfig internal systemConfig; @@ -111,10 +105,10 @@ contract BaseTest is Test { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_NITRO_IMAGE_HASH, - TEE_TDX_IMAGE_HASH, - AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), - CONFIG_HASH, + keccak256("tee-nitro-image"), + keccak256("tee-tdx-image"), + AggregateVerifier.ZkHashes(keccak256("zk-range"), keccak256("zk-aggregate")), + keccak256("config"), L2_CHAIN_ID, blockInterval, intermediateBlockInterval From f406c252e787a7f38e6d272bab2fb24bf09464de Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:38:00 -0400 Subject: [PATCH 106/146] clean up Challenge --- test/L1/proofs/Challenge.t.sol | 35 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index 5f406654b..0113bc441 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -82,12 +82,8 @@ contract ChallengeTest is BaseTest { TEE_PROVER, "tee1", "tee-proof-1", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) ); - Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + _nullify(game, AggregateVerifier.ProofType.TEE, "tee2"); - game.nullify(teeProof2, LAST_INTERMEDIATE_ROOT_INDEX, rootClaim2.raw()); - - // challenge game: TEE proof was nullified, so MissingProof(TEE) is expected vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.MissingProof.selector, AggregateVerifier.ProofType.TEE) ); @@ -110,14 +106,15 @@ contract ChallengeTest is BaseTest { ); _challengeWithZk(gameA, "zk-challenge"); - _assertChallengeRecorded(gameA); + assertEq(gameA.proofCount(), 2); + assertGt(gameA.counteredByIntermediateRootIndexPlusOne(), 0); AggregateVerifier gameB = _createGame(ZK_PROVER, "zk-only-b", "zk-init-b", AggregateVerifier.ProofType.ZK, address(gameA)); _nullify(gameB, AggregateVerifier.ProofType.ZK, "zk-nullify-b"); assertTrue(zkVerifier.nullified()); - _resolveAndAssertStatus(gameA, GameStatus.IN_PROGRESS); + assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); assertEq(gameA.proofCount(), 1); assertEq(gameA.counteredByIntermediateRootIndexPlusOne(), 0); assertEq(address(gameA.zkProver()), address(0)); @@ -135,17 +132,16 @@ contract ChallengeTest is BaseTest { ); _challengeWithZk(gameA, "zk-challenge"); - _assertChallengeRecorded(gameA); + assertEq(gameA.proofCount(), 2); + assertGt(gameA.counteredByIntermediateRootIndexPlusOne(), 0); AggregateVerifier gameB = _createGame(TEE_PROVER, "tee-nullify-b", "tee-proof-b", AggregateVerifier.ProofType.TEE, address(gameA)); - Claim rootNullifyB = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee-nullify-b-root"))); - bytes memory teeNullifyB = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); - gameB.nullify(teeNullifyB, LAST_INTERMEDIATE_ROOT_INDEX, rootNullifyB.raw()); + _nullify(gameB, AggregateVerifier.ProofType.TEE, "tee-nullify-b-root"); assertTrue(teeVerifier.nullified()); - _resolveAndAssertStatus(gameA, GameStatus.IN_PROGRESS); + assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); assertEq(gameA.proofCount(), 1); assertGt(gameA.counteredByIntermediateRootIndexPlusOne(), 0); assertEq(address(gameA.teeProver()), address(0)); @@ -180,7 +176,9 @@ contract ChallengeTest is BaseTest { } function _nullify(AggregateVerifier game, AggregateVerifier.ProofType proofType, bytes memory claimSalt) private { - game.nullify(_proofOfType(proofType), LAST_INTERMEDIATE_ROOT_INDEX, _claim(claimSalt).raw()); + game.nullify( + _generateProposalProof(claimSalt, proofType), LAST_INTERMEDIATE_ROOT_INDEX, _claim(claimSalt).raw() + ); } function _proofOfType(AggregateVerifier.ProofType proofType) private pure returns (bytes memory) { @@ -191,15 +189,6 @@ contract ChallengeTest is BaseTest { return Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, salt))); } - function _assertChallengeRecorded(AggregateVerifier game) private view { - assertEq(game.proofCount(), 2); - assertGt(game.counteredByIntermediateRootIndexPlusOne(), 0); - } - - function _resolveAndAssertStatus(AggregateVerifier game, GameStatus expectedStatus) private { - assertEq(uint8(game.resolve()), uint8(expectedStatus)); - } - function _resolveAfterSlowDelayAndClaim( AggregateVerifier game, GameStatus expectedStatus, @@ -208,7 +197,7 @@ contract ChallengeTest is BaseTest { private { vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); - _resolveAndAssertStatus(game, expectedStatus); + assertEq(uint8(game.resolve()), uint8(expectedStatus)); assertEq(game.bondRecipient(), recipient); _claimCreditAfterDelay(game, recipient); } From 62367a24d40810468aa4e03651fab972177d5b35 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:43:51 -0400 Subject: [PATCH 107/146] clean up Challenge --- test/L1/proofs/Challenge.t.sol | 36 +++++++++++++--------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index 0113bc441..31405b3f1 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -20,7 +20,10 @@ contract ChallengeTest is BaseTest { assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); assertEq(game.proofCount(), 2); - _resolveAfterSlowDelayAndClaim(game, GameStatus.CHALLENGER_WINS, ZK_PROVER); + vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); + assertEq(uint8(game.resolve()), uint8(GameStatus.CHALLENGER_WINS)); + assertEq(game.bondRecipient(), ZK_PROVER); + _claimCreditAfterDelay(game, ZK_PROVER); } function testChallengeFailsIfNoTEEProof() public { @@ -94,7 +97,7 @@ contract ChallengeTest is BaseTest { AggregateVerifier game = _createGame(ZK_PROVER, "tee", "tee-proof", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry)); - _provideProof(game, ZK_PROVER, _proofOfType(AggregateVerifier.ProofType.ZK)); + _provideProof(game, ZK_PROVER, abi.encodePacked(uint8(AggregateVerifier.ProofType.ZK), bytes1(0))); _nullify(game, AggregateVerifier.ProofType.ZK, "zk2"); vm.expectRevert(Verifier.Nullified.selector); _challenge(game, AggregateVerifier.ProofType.ZK, _claim("zk3").raw()); @@ -119,7 +122,10 @@ contract ChallengeTest is BaseTest { assertEq(gameA.counteredByIntermediateRootIndexPlusOne(), 0); assertEq(address(gameA.zkProver()), address(0)); - _resolveAfterSlowDelayAndClaim(gameA, GameStatus.DEFENDER_WINS, TEE_PROVER); + vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); + assertEq(uint8(gameA.resolve()), uint8(GameStatus.DEFENDER_WINS)); + assertEq(gameA.bondRecipient(), TEE_PROVER); + _claimCreditAfterDelay(gameA, TEE_PROVER); } function testChallengeWinsWhenSharedTeeVerifierNullifiedByOtherGame() public { @@ -147,7 +153,10 @@ contract ChallengeTest is BaseTest { assertEq(address(gameA.teeProver()), address(0)); assertEq(gameA.zkProver(), ZK_PROVER); - _resolveAfterSlowDelayAndClaim(gameA, GameStatus.CHALLENGER_WINS, ZK_PROVER); + vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); + assertEq(uint8(gameA.resolve()), uint8(GameStatus.CHALLENGER_WINS)); + assertEq(gameA.bondRecipient(), ZK_PROVER); + _claimCreditAfterDelay(gameA, ZK_PROVER); } function _createGame( @@ -167,7 +176,7 @@ contract ChallengeTest is BaseTest { } function _challenge(AggregateVerifier game, AggregateVerifier.ProofType proofType, bytes32 claimRoot) private { - game.challenge(_proofOfType(proofType), LAST_INTERMEDIATE_ROOT_INDEX, claimRoot); + game.challenge(abi.encodePacked(uint8(proofType), bytes1(0)), LAST_INTERMEDIATE_ROOT_INDEX, claimRoot); } function _challengeWithZk(AggregateVerifier game, bytes memory claimSalt) private { @@ -181,24 +190,7 @@ contract ChallengeTest is BaseTest { ); } - function _proofOfType(AggregateVerifier.ProofType proofType) private pure returns (bytes memory) { - return abi.encodePacked(uint8(proofType), bytes1(0)); - } - function _claim(bytes memory salt) private view returns (Claim) { return Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, salt))); } - - function _resolveAfterSlowDelayAndClaim( - AggregateVerifier game, - GameStatus expectedStatus, - address recipient - ) - private - { - vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); - assertEq(uint8(game.resolve()), uint8(expectedStatus)); - assertEq(game.bondRecipient(), recipient); - _claimCreditAfterDelay(game, recipient); - } } From 7ecb0affc282c64663525e224054ee8c2f1958ff Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:50:33 -0400 Subject: [PATCH 108/146] clean up Challenge --- test/L1/proofs/BaseTest.t.sol | 20 ++++++++++++++++++++ test/L1/proofs/Challenge.t.sol | 25 ++----------------------- test/L1/proofs/Nullify.t.sol | 20 -------------------- 3 files changed, 22 insertions(+), 43 deletions(-) diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 6abc6a28c..0baa234b2 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -139,6 +139,26 @@ contract BaseTest is Test { ); } + function _createGame( + address prover, + bytes memory claimSalt, + bytes memory proofSalt, + AggregateVerifier.ProofType proofType, + address parent + ) + internal + returns (AggregateVerifier) + { + currentL2BlockNumber += BLOCK_INTERVAL; + return _createAggregateVerifierGame( + prover, _claim(claimSalt), currentL2BlockNumber, parent, _generateProof(proofSalt, proofType) + ); + } + + function _claim(bytes memory salt) internal view returns (Claim) { + return Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, salt))); + } + function _provideProof(AggregateVerifier game, address prover, bytes memory proofBytes) internal { vm.prank(prover); game.verifyProposalProof(proofBytes); diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index 31405b3f1..21bcd072e 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.15; import { ClaimAlreadyResolved } from "src/libraries/bridge/Errors.sol"; import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { GameStatus } from "src/libraries/bridge/Types.sol"; -import { Claim } from "src/libraries/bridge/LibUDT.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { Verifier } from "src/L1/proofs/Verifier.sol"; @@ -120,7 +119,7 @@ contract ChallengeTest is BaseTest { assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); assertEq(gameA.proofCount(), 1); assertEq(gameA.counteredByIntermediateRootIndexPlusOne(), 0); - assertEq(address(gameA.zkProver()), address(0)); + assertEq(gameA.zkProver(), address(0)); vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); assertEq(uint8(gameA.resolve()), uint8(GameStatus.DEFENDER_WINS)); @@ -150,7 +149,7 @@ contract ChallengeTest is BaseTest { assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); assertEq(gameA.proofCount(), 1); assertGt(gameA.counteredByIntermediateRootIndexPlusOne(), 0); - assertEq(address(gameA.teeProver()), address(0)); + assertEq(gameA.teeProver(), address(0)); assertEq(gameA.zkProver(), ZK_PROVER); vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); @@ -159,22 +158,6 @@ contract ChallengeTest is BaseTest { _claimCreditAfterDelay(gameA, ZK_PROVER); } - function _createGame( - address prover, - bytes memory claimSalt, - bytes memory proofSalt, - AggregateVerifier.ProofType proofType, - address parent - ) - private - returns (AggregateVerifier) - { - currentL2BlockNumber += BLOCK_INTERVAL; - Claim rootClaim = _claim(claimSalt); - bytes memory proof = _generateProof(proofSalt, proofType); - return _createAggregateVerifierGame(prover, rootClaim, currentL2BlockNumber, parent, proof); - } - function _challenge(AggregateVerifier game, AggregateVerifier.ProofType proofType, bytes32 claimRoot) private { game.challenge(abi.encodePacked(uint8(proofType), bytes1(0)), LAST_INTERMEDIATE_ROOT_INDEX, claimRoot); } @@ -189,8 +172,4 @@ contract ChallengeTest is BaseTest { _generateProposalProof(claimSalt, proofType), LAST_INTERMEDIATE_ROOT_INDEX, _claim(claimSalt).raw() ); } - - function _claim(bytes memory salt) private view returns (Claim) { - return Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, salt))); - } } diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index 9d10c0216..9c9c36fcc 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -166,26 +166,6 @@ contract NullifyTest is BaseTest { _assertStatus(gameA, GameStatus.DEFENDER_WINS); } - function _createGame( - address prover, - bytes memory claimSalt, - bytes memory proofSalt, - AggregateVerifier.ProofType proofType, - address parent - ) - private - returns (AggregateVerifier) - { - currentL2BlockNumber += BLOCK_INTERVAL; - return _createAggregateVerifierGame( - prover, _claim(claimSalt), currentL2BlockNumber, parent, _generateProof(proofSalt, proofType) - ); - } - - function _claim(bytes memory salt) private view returns (Claim) { - return Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, salt))); - } - function _nullify( AggregateVerifier game, bytes memory proofSalt, From a0a42c0c91c065b718ed45714d1abf045b6c2eaf Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:56:09 -0400 Subject: [PATCH 109/146] clean up Challenge --- test/L1/proofs/Challenge.t.sol | 83 ++++++++++++++++------------------ 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index 21bcd072e..12a5fa70a 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -12,17 +12,13 @@ import { BaseTest } from "./BaseTest.t.sol"; contract ChallengeTest is BaseTest { function testChallengeTEEProofWithZKProof() public { - AggregateVerifier game = - _createGame(TEE_PROVER, "tee", "tee-proof", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry)); + AggregateVerifier game = _rootTee(TEE_PROVER, "tee", "tee-proof"); _challengeWithZk(game, "zk"); assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); assertEq(game.proofCount(), 2); - vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); - assertEq(uint8(game.resolve()), uint8(GameStatus.CHALLENGER_WINS)); - assertEq(game.bondRecipient(), ZK_PROVER); - _claimCreditAfterDelay(game, ZK_PROVER); + _resolveSlow(game, GameStatus.CHALLENGER_WINS, ZK_PROVER); } function testChallengeFailsIfNoTEEProof() public { @@ -32,33 +28,28 @@ contract ChallengeTest is BaseTest { vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.MissingProof.selector, AggregateVerifier.ProofType.TEE) ); - _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); + _challengeWithEmptyZk(game); } function testChallengeFailsIfNotZKProof() public { - AggregateVerifier game = _createGame( - TEE_PROVER, "tee1", "tee-proof-1", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) - ); + AggregateVerifier game = _rootTee(TEE_PROVER, "tee1", "tee-proof-1"); vm.expectRevert(AggregateVerifier.InvalidProofType.selector); _challenge(game, AggregateVerifier.ProofType.TEE, bytes32(0)); } function testChallengeFailsIfGameAlreadyResolved() public { - AggregateVerifier game = - _createGame(TEE_PROVER, "tee", "tee-proof", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry)); + AggregateVerifier game = _rootTee(TEE_PROVER, "tee", "tee-proof"); vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY() + 1); game.resolve(); vm.expectRevert(ClaimAlreadyResolved.selector); - _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); + _challengeWithEmptyZk(game); } function testChallengeFailsIfParentGameStatusIsChallenged() public { - AggregateVerifier parentGame = _createGame( - TEE_PROVER, "tee", "parent-proof", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) - ); + AggregateVerifier parentGame = _rootTee(TEE_PROVER, "tee", "parent-proof"); AggregateVerifier childGame = _createGame(TEE_PROVER, "tee2", "child-proof", AggregateVerifier.ProofType.TEE, address(parentGame)); @@ -66,35 +57,31 @@ contract ChallengeTest is BaseTest { anchorStateRegistry.blacklistDisputeGame(IDisputeGame(address(parentGame))); vm.expectRevert(AggregateVerifier.InvalidParentGame.selector); - _challenge(childGame, AggregateVerifier.ProofType.ZK, bytes32(0)); + _challengeWithEmptyZk(childGame); } function testChallengeFailsIfGameItselfIsBlacklisted() public { - AggregateVerifier game = - _createGame(TEE_PROVER, "tee", "tee-proof", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry)); + AggregateVerifier game = _rootTee(TEE_PROVER, "tee", "tee-proof"); anchorStateRegistry.blacklistDisputeGame(IDisputeGame(address(game))); vm.expectRevert(AggregateVerifier.InvalidGame.selector); - _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); + _challengeWithEmptyZk(game); } function testChallengeFailsAfterTEENullification() public { - AggregateVerifier game = _createGame( - TEE_PROVER, "tee1", "tee-proof-1", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) - ); + AggregateVerifier game = _rootTee(TEE_PROVER, "tee1", "tee-proof-1"); _nullify(game, AggregateVerifier.ProofType.TEE, "tee2"); vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.MissingProof.selector, AggregateVerifier.ProofType.TEE) ); - _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); + _challengeWithEmptyZk(game); } function testChallengeFailsAfterZKNullification() public { - AggregateVerifier game = - _createGame(ZK_PROVER, "tee", "tee-proof", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry)); + AggregateVerifier game = _rootTee(ZK_PROVER, "tee", "tee-proof"); _provideProof(game, ZK_PROVER, abi.encodePacked(uint8(AggregateVerifier.ProofType.ZK), bytes1(0))); _nullify(game, AggregateVerifier.ProofType.ZK, "zk2"); @@ -103,9 +90,7 @@ contract ChallengeTest is BaseTest { } function testChallengeRemovedWhenZkVerifierNullifiedByOtherGame() public { - AggregateVerifier gameA = _createGame( - TEE_PROVER, "tee-challenge", "tee-ch-a", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) - ); + AggregateVerifier gameA = _rootTee(TEE_PROVER, "tee-challenge", "tee-ch-a"); _challengeWithZk(gameA, "zk-challenge"); assertEq(gameA.proofCount(), 2); @@ -121,20 +106,11 @@ contract ChallengeTest is BaseTest { assertEq(gameA.counteredByIntermediateRootIndexPlusOne(), 0); assertEq(gameA.zkProver(), address(0)); - vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); - assertEq(uint8(gameA.resolve()), uint8(GameStatus.DEFENDER_WINS)); - assertEq(gameA.bondRecipient(), TEE_PROVER); - _claimCreditAfterDelay(gameA, TEE_PROVER); + _resolveSlow(gameA, GameStatus.DEFENDER_WINS, TEE_PROVER); } function testChallengeWinsWhenSharedTeeVerifierNullifiedByOtherGame() public { - AggregateVerifier gameA = _createGame( - TEE_PROVER, - "tee-challenge-tee-null", - "tee-proof-a", - AggregateVerifier.ProofType.TEE, - address(anchorStateRegistry) - ); + AggregateVerifier gameA = _rootTee(TEE_PROVER, "tee-challenge-tee-null", "tee-proof-a"); _challengeWithZk(gameA, "zk-challenge"); assertEq(gameA.proofCount(), 2); @@ -152,16 +128,28 @@ contract ChallengeTest is BaseTest { assertEq(gameA.teeProver(), address(0)); assertEq(gameA.zkProver(), ZK_PROVER); - vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); - assertEq(uint8(gameA.resolve()), uint8(GameStatus.CHALLENGER_WINS)); - assertEq(gameA.bondRecipient(), ZK_PROVER); - _claimCreditAfterDelay(gameA, ZK_PROVER); + _resolveSlow(gameA, GameStatus.CHALLENGER_WINS, ZK_PROVER); + } + + function _rootTee( + address prover, + bytes memory claimSalt, + bytes memory proofSalt + ) + private + returns (AggregateVerifier) + { + return _createGame(prover, claimSalt, proofSalt, AggregateVerifier.ProofType.TEE, address(anchorStateRegistry)); } function _challenge(AggregateVerifier game, AggregateVerifier.ProofType proofType, bytes32 claimRoot) private { game.challenge(abi.encodePacked(uint8(proofType), bytes1(0)), LAST_INTERMEDIATE_ROOT_INDEX, claimRoot); } + function _challengeWithEmptyZk(AggregateVerifier game) private { + _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); + } + function _challengeWithZk(AggregateVerifier game, bytes memory claimSalt) private { vm.prank(ZK_PROVER); _challenge(game, AggregateVerifier.ProofType.ZK, _claim(claimSalt).raw()); @@ -172,4 +160,11 @@ contract ChallengeTest is BaseTest { _generateProposalProof(claimSalt, proofType), LAST_INTERMEDIATE_ROOT_INDEX, _claim(claimSalt).raw() ); } + + function _resolveSlow(AggregateVerifier game, GameStatus expectedStatus, address expectedBondRecipient) private { + vm.warp(block.timestamp + game.SLOW_FINALIZATION_DELAY()); + assertEq(uint8(game.resolve()), uint8(expectedStatus)); + assertEq(game.bondRecipient(), expectedBondRecipient); + _claimCreditAfterDelay(game, expectedBondRecipient); + } } From 5905ce0fbd114dcf373e4cde60ea9bd85508ca94 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 07:59:51 -0400 Subject: [PATCH 110/146] clean up Challenge --- test/L1/proofs/Challenge.t.sol | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index 12a5fa70a..1fcb36675 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -28,7 +28,7 @@ contract ChallengeTest is BaseTest { vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.MissingProof.selector, AggregateVerifier.ProofType.TEE) ); - _challengeWithEmptyZk(game); + _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); } function testChallengeFailsIfNotZKProof() public { @@ -45,7 +45,7 @@ contract ChallengeTest is BaseTest { game.resolve(); vm.expectRevert(ClaimAlreadyResolved.selector); - _challengeWithEmptyZk(game); + _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); } function testChallengeFailsIfParentGameStatusIsChallenged() public { @@ -57,7 +57,7 @@ contract ChallengeTest is BaseTest { anchorStateRegistry.blacklistDisputeGame(IDisputeGame(address(parentGame))); vm.expectRevert(AggregateVerifier.InvalidParentGame.selector); - _challengeWithEmptyZk(childGame); + _challenge(childGame, AggregateVerifier.ProofType.ZK, bytes32(0)); } function testChallengeFailsIfGameItselfIsBlacklisted() public { @@ -66,7 +66,7 @@ contract ChallengeTest is BaseTest { anchorStateRegistry.blacklistDisputeGame(IDisputeGame(address(game))); vm.expectRevert(AggregateVerifier.InvalidGame.selector); - _challengeWithEmptyZk(game); + _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); } function testChallengeFailsAfterTEENullification() public { @@ -77,7 +77,7 @@ contract ChallengeTest is BaseTest { vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.MissingProof.selector, AggregateVerifier.ProofType.TEE) ); - _challengeWithEmptyZk(game); + _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); } function testChallengeFailsAfterZKNullification() public { @@ -146,10 +146,6 @@ contract ChallengeTest is BaseTest { game.challenge(abi.encodePacked(uint8(proofType), bytes1(0)), LAST_INTERMEDIATE_ROOT_INDEX, claimRoot); } - function _challengeWithEmptyZk(AggregateVerifier game) private { - _challenge(game, AggregateVerifier.ProofType.ZK, bytes32(0)); - } - function _challengeWithZk(AggregateVerifier game, bytes memory claimSalt) private { vm.prank(ZK_PROVER); _challenge(game, AggregateVerifier.ProofType.ZK, _claim(claimSalt).raw()); From 18bda7366dd4d10d4c3dbf5fd531451dab4b0a54 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 08:08:21 -0400 Subject: [PATCH 111/146] clean up Nullify --- test/L1/proofs/Nullify.t.sol | 62 ++++++------------------------------ 1 file changed, 10 insertions(+), 52 deletions(-) diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index 9c9c36fcc..9af3a38c7 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.15; import { ClaimAlreadyResolved } from "src/libraries/bridge/Errors.sol"; import { GameStatus } from "src/libraries/bridge/Types.sol"; -import { Claim } from "src/libraries/bridge/LibUDT.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; @@ -17,9 +16,7 @@ contract NullifyTest is BaseTest { TEE_PROVER, "tee1", "tee-proof-1", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) ); - Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); - game.nullify(teeProof2, LAST_INTERMEDIATE_ROOT_INDEX, rootClaim2.raw()); + _nullify(game, "tee-proof-2", AggregateVerifier.ProofType.TEE, "tee2"); _assertNullifiedToNoProofs(game, TEE_PROVER); vm.warp(block.timestamp + NO_PROOF_CREDIT_CLAIM_DELAY); @@ -46,11 +43,9 @@ contract NullifyTest is BaseTest { assertEq(game.expectedResolution().raw(), block.timestamp + game.FAST_FINALIZATION_DELAY()); - Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); - game.nullify(teeProof2, LAST_INTERMEDIATE_ROOT_INDEX, rootClaim2.raw()); + _nullify(game, "tee-proof-2", AggregateVerifier.ProofType.TEE, "tee2"); - _assertStatus(game, GameStatus.IN_PROGRESS); + assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); assertEq(game.bondRecipient(), TEE_PROVER); assertEq(game.proofCount(), 1); assertEq(game.expectedResolution().raw(), block.timestamp + game.SLOW_FINALIZATION_DELAY()); @@ -97,38 +92,7 @@ contract NullifyTest is BaseTest { } function testResolveEarlyReturnWhenSharedTeeVerifierNullifiedByAnotherGame() public { - currentL2BlockNumber += BLOCK_INTERVAL; - - Claim rootClaimA = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "game-a"))); - bytes memory teeProofA = _generateProof("tee-proof-a", AggregateVerifier.ProofType.TEE); - AggregateVerifier gameA = _createAggregateVerifierGame( - TEE_PROVER, rootClaimA, currentL2BlockNumber, address(anchorStateRegistry), teeProofA - ); - - currentL2BlockNumber += BLOCK_INTERVAL; - - Claim rootClaimB = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "game-b"))); - bytes memory teeProofB = _generateProof("tee-proof-b", AggregateVerifier.ProofType.TEE); - AggregateVerifier gameB = - _createAggregateVerifierGame(TEE_PROVER, rootClaimB, currentL2BlockNumber, address(gameA), teeProofB); - - vm.warp(block.timestamp + 7 days); - assertTrue(gameA.gameOver()); - assertEq(gameA.proofCount(), 1); - - Claim rootClaimNullify = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "nullify-b"))); - bytes memory teeProofNullify = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); - gameB.nullify(teeProofNullify, LAST_INTERMEDIATE_ROOT_INDEX, rootClaimNullify.raw()); - - assertTrue(teeVerifier.nullified()); - assertEq(gameA.proofCount(), 1); - - assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); - assertEq(gameA.proofCount(), 0); - assertEq(gameA.expectedResolution().raw(), type(uint64).max); - - vm.expectRevert(AggregateVerifier.GameNotOver.selector); - gameA.resolve(); + _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(TEE_PROVER, AggregateVerifier.ProofType.TEE); } function testResolveEarlyReturnWhenSharedZkVerifierNullifiedByAnotherGame() public { @@ -163,7 +127,7 @@ contract NullifyTest is BaseTest { vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); assertEq(uint8(gameA.resolve()), uint8(GameStatus.DEFENDER_WINS)); - _assertStatus(gameA, GameStatus.DEFENDER_WINS); + assertEq(uint8(gameA.status()), uint8(GameStatus.DEFENDER_WINS)); } function _nullify( @@ -174,20 +138,18 @@ contract NullifyTest is BaseTest { ) private { - game.nullify(_generateProof(proofSalt, proofType), LAST_INTERMEDIATE_ROOT_INDEX, _claim(claimSalt).raw()); + game.nullify( + _generateProposalProof(proofSalt, proofType), LAST_INTERMEDIATE_ROOT_INDEX, _claim(claimSalt).raw() + ); } function _assertNullifiedToNoProofs(AggregateVerifier game, address expectedBondRecipient) private view { - _assertStatus(game, GameStatus.IN_PROGRESS); + assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); assertEq(game.bondRecipient(), expectedBondRecipient); assertEq(game.proofCount(), 0); assertEq(game.expectedResolution().raw(), type(uint64).max); } - function _assertStatus(AggregateVerifier game, GameStatus expectedStatus) private view { - assertEq(uint8(game.status()), uint8(expectedStatus)); - } - /// @notice When a shared verifier is nullified by another game, `resolve` persists the refutation and returns /// early `IN_PROGRESS` instead of reverting. function _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame( @@ -205,11 +167,7 @@ contract NullifyTest is BaseTest { _nullify(gameB, "nullify-proof", proofType, "nullify-claim"); - if (proofType == AggregateVerifier.ProofType.TEE) { - assertTrue(teeVerifier.nullified()); - } else { - assertTrue(zkVerifier.nullified()); - } + assertTrue(proofType == AggregateVerifier.ProofType.TEE ? teeVerifier.nullified() : zkVerifier.nullified()); assertEq(gameA.proofCount(), 1); assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); From ec789055f4300784417e60cd7f273ee718a8d7f4 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 08:13:45 -0400 Subject: [PATCH 112/146] clean up Nullify --- test/L1/proofs/Nullify.t.sol | 42 ++++++++++++++---------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index 9af3a38c7..99dfa51fb 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -9,29 +9,12 @@ import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { BaseTest } from "./BaseTest.t.sol"; contract NullifyTest is BaseTest { - uint256 private constant NO_PROOF_CREDIT_CLAIM_DELAY = 14 days; - function testNullifyWithTEEProof() public { - AggregateVerifier game = _createGame( - TEE_PROVER, "tee1", "tee-proof-1", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) - ); - - _nullify(game, "tee-proof-2", AggregateVerifier.ProofType.TEE, "tee2"); - _assertNullifiedToNoProofs(game, TEE_PROVER); - - vm.warp(block.timestamp + NO_PROOF_CREDIT_CLAIM_DELAY); - _claimCreditAfterDelay(game, game.gameCreator()); + _assertNullifyWithProof(TEE_PROVER, AggregateVerifier.ProofType.TEE); } function testNullifyWithZKProof() public { - AggregateVerifier game = - _createGame(ZK_PROVER, "zk1", "zk-proof-1", AggregateVerifier.ProofType.ZK, address(anchorStateRegistry)); - - _nullify(game, "zk-proof-2", AggregateVerifier.ProofType.ZK, "zk2"); - _assertNullifiedToNoProofs(game, ZK_PROVER); - - vm.warp(block.timestamp + NO_PROOF_CREDIT_CLAIM_DELAY); - _claimCreditAfterDelay(game, game.gameCreator()); + _assertNullifyWithProof(ZK_PROVER, AggregateVerifier.ProofType.ZK); } function testNullifyWithTEEProofWhenTEEAndZKProofsAreProvided() public { @@ -92,11 +75,11 @@ contract NullifyTest is BaseTest { } function testResolveEarlyReturnWhenSharedTeeVerifierNullifiedByAnotherGame() public { - _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(TEE_PROVER, AggregateVerifier.ProofType.TEE); + _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(AggregateVerifier.ProofType.TEE); } function testResolveEarlyReturnWhenSharedZkVerifierNullifiedByAnotherGame() public { - _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(ZK_PROVER, AggregateVerifier.ProofType.ZK); + _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(AggregateVerifier.ProofType.ZK); } /// @notice With TEE + ZK, the fast window is 1 day. Another game nullifies the shared ZK verifier; the first @@ -127,7 +110,16 @@ contract NullifyTest is BaseTest { vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); assertEq(uint8(gameA.resolve()), uint8(GameStatus.DEFENDER_WINS)); - assertEq(uint8(gameA.status()), uint8(GameStatus.DEFENDER_WINS)); + } + + function _assertNullifyWithProof(address prover, AggregateVerifier.ProofType proofType) private { + AggregateVerifier game = _createGame(prover, "claim", "proof-1", proofType, address(anchorStateRegistry)); + + _nullify(game, "proof-2", proofType, "nullify-claim"); + _assertNullifiedToNoProofs(game, prover); + + vm.warp(block.timestamp + 14 days); + _claimCreditAfterDelay(game, game.gameCreator()); } function _nullify( @@ -152,12 +144,10 @@ contract NullifyTest is BaseTest { /// @notice When a shared verifier is nullified by another game, `resolve` persists the refutation and returns /// early `IN_PROGRESS` instead of reverting. - function _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame( - address prover, - AggregateVerifier.ProofType proofType - ) + function _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(AggregateVerifier.ProofType proofType) private { + address prover = proofType == AggregateVerifier.ProofType.TEE ? TEE_PROVER : ZK_PROVER; AggregateVerifier gameA = _createGame(prover, "game-a", "proof-a", proofType, address(anchorStateRegistry)); AggregateVerifier gameB = _createGame(prover, "game-b", "proof-b", proofType, address(gameA)); From 192058b637dba476b84f9395e712dd7ffb9c1b2c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 08:18:19 -0400 Subject: [PATCH 113/146] clean up Nullify --- test/L1/proofs/Nullify.t.sol | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index 99dfa51fb..ba9456f85 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -92,11 +92,7 @@ contract NullifyTest is BaseTest { _provideProof(gameA, ZK_PROVER, _generateProof("zk-dual-a", AggregateVerifier.ProofType.ZK)); - assertEq(gameA.proofCount(), 2); - assertEq(gameA.expectedResolution().raw(), block.timestamp + gameA.FAST_FINALIZATION_DELAY()); - vm.warp(block.timestamp + gameA.FAST_FINALIZATION_DELAY()); - assertTrue(gameA.gameOver()); AggregateVerifier gameB = _createGame(ZK_PROVER, "dual-b", "zk-dual-b", AggregateVerifier.ProofType.ZK, address(gameA)); @@ -116,7 +112,10 @@ contract NullifyTest is BaseTest { AggregateVerifier game = _createGame(prover, "claim", "proof-1", proofType, address(anchorStateRegistry)); _nullify(game, "proof-2", proofType, "nullify-claim"); - _assertNullifiedToNoProofs(game, prover); + assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); + assertEq(game.bondRecipient(), prover); + assertEq(game.proofCount(), 0); + assertEq(game.expectedResolution().raw(), type(uint64).max); vm.warp(block.timestamp + 14 days); _claimCreditAfterDelay(game, game.gameCreator()); @@ -135,15 +134,6 @@ contract NullifyTest is BaseTest { ); } - function _assertNullifiedToNoProofs(AggregateVerifier game, address expectedBondRecipient) private view { - assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); - assertEq(game.bondRecipient(), expectedBondRecipient); - assertEq(game.proofCount(), 0); - assertEq(game.expectedResolution().raw(), type(uint64).max); - } - - /// @notice When a shared verifier is nullified by another game, `resolve` persists the refutation and returns - /// early `IN_PROGRESS` instead of reverting. function _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(AggregateVerifier.ProofType proofType) private { From 4fcd158944245d3f13b1b5861e23478837ae2428 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 08:23:21 -0400 Subject: [PATCH 114/146] clean up Nullify --- test/L1/proofs/Nullify.t.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index ba9456f85..be27f1a8a 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -82,9 +82,6 @@ contract NullifyTest is BaseTest { _assertResolveEarlyReturnWhenSharedVerifierNullifiedByAnotherGame(AggregateVerifier.ProofType.ZK); } - /// @notice With TEE + ZK, the fast window is 1 day. Another game nullifies the shared ZK verifier; the first - /// `resolve` persists the ZK refutation and returns `IN_PROGRESS`. After `SLOW_FINALIZATION_DELAY` - /// from that moment, a second `resolve` finalizes with only the TEE proof. function testTwoProofsResolveDelayedAfterExternalVerifierNullify() public { AggregateVerifier gameA = _createGame( TEE_PROVER, "dual-a", "tee-dual-a", AggregateVerifier.ProofType.TEE, address(anchorStateRegistry) From 1c7a9ce3d0ba1fa2d61d7ce69a21803962e8b1bb Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 08:30:21 -0400 Subject: [PATCH 115/146] clean up Nullify --- test/L1/proofs/Nullify.t.sol | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index be27f1a8a..141837b34 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -95,7 +95,6 @@ contract NullifyTest is BaseTest { _createGame(ZK_PROVER, "dual-b", "zk-dual-b", AggregateVerifier.ProofType.ZK, address(gameA)); _nullify(gameB, "zk-nullify-dual", AggregateVerifier.ProofType.ZK, "dual-nullify-b"); - assertTrue(zkVerifier.nullified()); assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); assertEq(gameA.proofCount(), 1); @@ -139,14 +138,9 @@ contract NullifyTest is BaseTest { AggregateVerifier gameB = _createGame(prover, "game-b", "proof-b", proofType, address(gameA)); vm.warp(block.timestamp + gameA.SLOW_FINALIZATION_DELAY()); - assertTrue(gameA.gameOver()); - assertEq(gameA.proofCount(), 1); _nullify(gameB, "nullify-proof", proofType, "nullify-claim"); - assertTrue(proofType == AggregateVerifier.ProofType.TEE ? teeVerifier.nullified() : zkVerifier.nullified()); - assertEq(gameA.proofCount(), 1); - assertEq(uint8(gameA.resolve()), uint8(GameStatus.IN_PROGRESS)); assertEq(gameA.proofCount(), 0); assertEq(gameA.expectedResolution().raw(), type(uint64).max); From 80d5a4d2c5c12af619cf6f9207ec79e1b2c8245a Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 08:49:08 -0400 Subject: [PATCH 116/146] clean up TDXVerifier --- test/L1/proofs/TDXVerifier.t.sol | 67 ++++++++++---------------------- 1 file changed, 21 insertions(+), 46 deletions(-) diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index 90aa5b1f4..f19696a20 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; import { Test } from "forge-std/Test.sol"; +import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; + import { TDXTcbStatus, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; @@ -13,7 +15,6 @@ contract TDXVerifierTest is Test { address internal mockRiscZeroVerifier; bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); - bytes32 internal constant WRONG_ROOT_CA_HASH = keccak256("wrong-root-ca"); bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); bytes32 internal constant PUBLIC_KEY_X = hex"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; @@ -32,13 +33,11 @@ contract TDXVerifierTest is Test { function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public { TDXVerifierJournal memory journal = _successJournal(); - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + (bytes memory output, bytes memory proofBytes) = _mockProof(journal); (address signer, bytes32 imageHash) = verifier.verify(output, proofBytes); - assertEq(signer, _signer()); + assertEq(signer, address(uint160(uint256(_publicKeyHash())))); assertEq(imageHash, IMAGE_HASH); } @@ -49,13 +48,11 @@ contract TDXVerifierTest is Test { function testVerifyRevertsWhenRootCaHashMismatches() public { TDXVerifierJournal memory journal = _successJournal(); - journal.rootCaHash = WRONG_ROOT_CA_HASH; - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + journal.rootCaHash = keccak256("wrong-root-ca"); + (bytes memory output, bytes memory proofBytes) = _mockProof(journal); vm.expectRevert( - abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, WRONG_ROOT_CA_HASH) + abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, journal.rootCaHash) ); verifier.verify(output, proofBytes); } @@ -63,9 +60,7 @@ contract TDXVerifierTest is Test { function testVerifyRevertsWhenTcbStatusIsNotAllowed() public { TDXVerifierJournal memory journal = _successJournal(); journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + (bytes memory output, bytes memory proofBytes) = _mockProof(journal); vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TcbStatusNotAllowed.selector, journal.tcbStatus)); verifier.verify(output, proofBytes); @@ -74,9 +69,7 @@ contract TDXVerifierTest is Test { function testVerifyRevertsWhenCollateralExpired() public { TDXVerifierJournal memory journal = _successJournal(); journal.collateralExpiration = uint64(block.timestamp); - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + (bytes memory output, bytes memory proofBytes) = _mockProof(journal); vm.expectRevert(abi.encodeWithSelector(TDXVerifier.CollateralExpired.selector, journal.collateralExpiration)); verifier.verify(output, proofBytes); @@ -85,9 +78,7 @@ contract TDXVerifierTest is Test { function testVerifyRevertsWhenTimestampTooOld() public { TDXVerifierJournal memory journal = _successJournal(); journal.timestamp = uint64(block.timestamp - MAX_TIME_DIFF) * 1000; - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + (bytes memory output, bytes memory proofBytes) = _mockProof(journal); vm.expectRevert( abi.encodeWithSelector( @@ -100,9 +91,7 @@ contract TDXVerifierTest is Test { function testVerifyRevertsWhenTimestampIsFromFuture() public { TDXVerifierJournal memory journal = _successJournal(); journal.timestamp = uint64(block.timestamp) * 1000; - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + (bytes memory output, bytes memory proofBytes) = _mockProof(journal); vm.expectRevert( abi.encodeWithSelector(TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp), block.timestamp) @@ -114,9 +103,7 @@ contract TDXVerifierTest is Test { TDXVerifierJournal memory journal = _successJournal(); bytes32 expected = journal.reportDataPrefix; journal.reportDataPrefix = keccak256("wrong-report-data"); - bytes memory output = abi.encode(journal); - bytes memory proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + (bytes memory output, bytes memory proofBytes) = _mockProof(journal); vm.expectRevert( abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, expected, journal.reportDataPrefix) @@ -125,8 +112,6 @@ contract TDXVerifierTest is Test { } function _successJournal() internal view returns (TDXVerifierJournal memory journal) { - bytes32 publicKeyHash = _publicKeyHash(); - journal = TDXVerifierJournal({ tcbStatus: TDXTcbStatus.UpToDate, timestamp: uint64(block.timestamp - 1) * 1000, @@ -135,7 +120,7 @@ contract TDXVerifierTest is Test { publicKeyX: PUBLIC_KEY_X, publicKeyY: PUBLIC_KEY_Y, imageHash: IMAGE_HASH, - reportDataPrefix: publicKeyHash + reportDataPrefix: _publicKeyHash() }); } @@ -143,28 +128,18 @@ contract TDXVerifierTest is Test { return keccak256(abi.encodePacked(PUBLIC_KEY_X, PUBLIC_KEY_Y)); } - function _signer() internal pure returns (address) { - return address(uint160(uint256(_publicKeyHash()))); + function _mockProof(TDXVerifierJournal memory journal) + internal + returns (bytes memory output, bytes memory proofBytes) + { + output = abi.encode(journal); + proofBytes = hex"1234"; + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); } function _mockRiscZeroVerify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { - _mockRiscZeroVerify(mockRiscZeroVerifier, programId, output, proofBytes); - } - - function _mockRiscZeroVerify( - address verifierAddress, - bytes32 programId, - bytes memory output, - bytes memory proofBytes - ) - internal - { vm.mockCall( - verifierAddress, - abi.encodeWithSelector( - bytes4(keccak256("verify(bytes,bytes32,bytes32)")), proofBytes, programId, sha256(output) - ), - "" + mockRiscZeroVerifier, abi.encodeCall(IRiscZeroVerifier.verify, (proofBytes, programId, sha256(output))), "" ); } } From f9c85331741e7037ec80656a30de7e9e8ad6a449 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 08:52:09 -0400 Subject: [PATCH 117/146] clean up TDXVerifier --- test/L1/proofs/TDXVerifier.t.sol | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index f19696a20..8901b1b3e 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -32,8 +32,7 @@ contract TDXVerifierTest is Test { } function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public { - TDXVerifierJournal memory journal = _successJournal(); - (bytes memory output, bytes memory proofBytes) = _mockProof(journal); + (bytes memory output, bytes memory proofBytes) = _mockProof(_successJournal()); (address signer, bytes32 imageHash) = verifier.verify(output, proofBytes); @@ -101,12 +100,11 @@ contract TDXVerifierTest is Test { function testVerifyRevertsWhenReportDataDoesNotBindPublicKey() public { TDXVerifierJournal memory journal = _successJournal(); - bytes32 expected = journal.reportDataPrefix; journal.reportDataPrefix = keccak256("wrong-report-data"); (bytes memory output, bytes memory proofBytes) = _mockProof(journal); vm.expectRevert( - abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, expected, journal.reportDataPrefix) + abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, _publicKeyHash(), journal.reportDataPrefix) ); verifier.verify(output, proofBytes); } @@ -134,12 +132,8 @@ contract TDXVerifierTest is Test { { output = abi.encode(journal); proofBytes = hex"1234"; - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - } - - function _mockRiscZeroVerify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { vm.mockCall( - mockRiscZeroVerifier, abi.encodeCall(IRiscZeroVerifier.verify, (proofBytes, programId, sha256(output))), "" + mockRiscZeroVerifier, abi.encodeCall(IRiscZeroVerifier.verify, (proofBytes, VERIFIER_ID, sha256(output))), "" ); } } From 07bb1d86f18019dc9f4a8ea3420f123c8876bae7 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 09:00:52 -0400 Subject: [PATCH 118/146] clean up TDXVerifier --- test/L1/proofs/TDXVerifier.t.sol | 62 ++++++++++++++------------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index 8901b1b3e..e2e634592 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -12,7 +12,7 @@ import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; contract TDXVerifierTest is Test { TDXVerifier internal verifier; - address internal mockRiscZeroVerifier; + address internal constant MOCK_RISC_ZERO_VERIFIER = address(0x1234); bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); @@ -26,15 +26,12 @@ contract TDXVerifierTest is Test { function setUp() public { vm.warp(NOW); - mockRiscZeroVerifier = makeAddr("mock-risc-zero"); - - verifier = new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, mockRiscZeroVerifier, VERIFIER_ID); + verifier = new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, MOCK_RISC_ZERO_VERIFIER, VERIFIER_ID); + vm.mockCall(MOCK_RISC_ZERO_VERIFIER, abi.encodeWithSelector(IRiscZeroVerifier.verify.selector), ""); } - function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public { - (bytes memory output, bytes memory proofBytes) = _mockProof(_successJournal()); - - (address signer, bytes32 imageHash) = verifier.verify(output, proofBytes); + function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public view { + (address signer, bytes32 imageHash) = _verify(_successJournal()); assertEq(signer, address(uint160(uint256(_publicKeyHash())))); assertEq(imageHash, IMAGE_HASH); @@ -48,65 +45,60 @@ contract TDXVerifierTest is Test { function testVerifyRevertsWhenRootCaHashMismatches() public { TDXVerifierJournal memory journal = _successJournal(); journal.rootCaHash = keccak256("wrong-root-ca"); - (bytes memory output, bytes memory proofBytes) = _mockProof(journal); - vm.expectRevert( - abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, journal.rootCaHash) + _expectVerifyRevert( + journal, abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, journal.rootCaHash) ); - verifier.verify(output, proofBytes); } function testVerifyRevertsWhenTcbStatusIsNotAllowed() public { TDXVerifierJournal memory journal = _successJournal(); journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; - (bytes memory output, bytes memory proofBytes) = _mockProof(journal); - vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TcbStatusNotAllowed.selector, journal.tcbStatus)); - verifier.verify(output, proofBytes); + _expectVerifyRevert( + journal, abi.encodeWithSelector(TDXVerifier.TcbStatusNotAllowed.selector, journal.tcbStatus) + ); } function testVerifyRevertsWhenCollateralExpired() public { TDXVerifierJournal memory journal = _successJournal(); journal.collateralExpiration = uint64(block.timestamp); - (bytes memory output, bytes memory proofBytes) = _mockProof(journal); - vm.expectRevert(abi.encodeWithSelector(TDXVerifier.CollateralExpired.selector, journal.collateralExpiration)); - verifier.verify(output, proofBytes); + _expectVerifyRevert( + journal, abi.encodeWithSelector(TDXVerifier.CollateralExpired.selector, journal.collateralExpiration) + ); } function testVerifyRevertsWhenTimestampTooOld() public { TDXVerifierJournal memory journal = _successJournal(); journal.timestamp = uint64(block.timestamp - MAX_TIME_DIFF) * 1000; - (bytes memory output, bytes memory proofBytes) = _mockProof(journal); - vm.expectRevert( + _expectVerifyRevert( + journal, abi.encodeWithSelector( TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp - MAX_TIME_DIFF), block.timestamp ) ); - verifier.verify(output, proofBytes); } function testVerifyRevertsWhenTimestampIsFromFuture() public { TDXVerifierJournal memory journal = _successJournal(); journal.timestamp = uint64(block.timestamp) * 1000; - (bytes memory output, bytes memory proofBytes) = _mockProof(journal); - vm.expectRevert( + _expectVerifyRevert( + journal, abi.encodeWithSelector(TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp), block.timestamp) ); - verifier.verify(output, proofBytes); } function testVerifyRevertsWhenReportDataDoesNotBindPublicKey() public { TDXVerifierJournal memory journal = _successJournal(); journal.reportDataPrefix = keccak256("wrong-report-data"); - (bytes memory output, bytes memory proofBytes) = _mockProof(journal); - vm.expectRevert( + _expectVerifyRevert( + journal, abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, _publicKeyHash(), journal.reportDataPrefix) ); - verifier.verify(output, proofBytes); } function _successJournal() internal view returns (TDXVerifierJournal memory journal) { @@ -126,14 +118,12 @@ contract TDXVerifierTest is Test { return keccak256(abi.encodePacked(PUBLIC_KEY_X, PUBLIC_KEY_Y)); } - function _mockProof(TDXVerifierJournal memory journal) - internal - returns (bytes memory output, bytes memory proofBytes) - { - output = abi.encode(journal); - proofBytes = hex"1234"; - vm.mockCall( - mockRiscZeroVerifier, abi.encodeCall(IRiscZeroVerifier.verify, (proofBytes, VERIFIER_ID, sha256(output))), "" - ); + function _verify(TDXVerifierJournal memory journal) internal view returns (address signer, bytes32 imageHash) { + return verifier.verify(abi.encode(journal), hex"1234"); + } + + function _expectVerifyRevert(TDXVerifierJournal memory journal, bytes memory expectedRevert) internal { + vm.expectRevert(expectedRevert); + verifier.verify(abi.encode(journal), hex"1234"); } } From 75f6090387cec4c63d09dbc7659907de02671d7d Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 09:05:00 -0400 Subject: [PATCH 119/146] clean up TDXVerifier --- test/L1/proofs/TDXVerifier.t.sol | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index e2e634592..8af66dfb9 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -15,23 +15,21 @@ contract TDXVerifierTest is Test { address internal constant MOCK_RISC_ZERO_VERIFIER = address(0x1234); bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); - bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); bytes32 internal constant PUBLIC_KEY_X = hex"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; bytes32 internal constant PUBLIC_KEY_Y = hex"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40"; uint64 internal constant MAX_TIME_DIFF = 3600; - uint256 internal constant NOW = 1_700_000_000; function setUp() public { - vm.warp(NOW); + vm.warp(1_700_000_000); - verifier = new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, MOCK_RISC_ZERO_VERIFIER, VERIFIER_ID); + verifier = new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, MOCK_RISC_ZERO_VERIFIER, keccak256("tdx-verifier-id")); vm.mockCall(MOCK_RISC_ZERO_VERIFIER, abi.encodeWithSelector(IRiscZeroVerifier.verify.selector), ""); } function testVerifySucceedsWithRiscZeroProofAndAllowedJournal() public view { - (address signer, bytes32 imageHash) = _verify(_successJournal()); + (address signer, bytes32 imageHash) = verifier.verify(abi.encode(_successJournal()), hex"1234"); assertEq(signer, address(uint160(uint256(_publicKeyHash())))); assertEq(imageHash, IMAGE_HASH); @@ -39,7 +37,7 @@ contract TDXVerifierTest is Test { function testConstructorRevertsIfZeroInput() public { vm.expectRevert(TDXVerifier.ZeroInput.selector); - new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, address(0), VERIFIER_ID); + new TDXVerifier(MAX_TIME_DIFF, ROOT_CA_HASH, address(0), keccak256("tdx-verifier-id")); } function testVerifyRevertsWhenRootCaHashMismatches() public { @@ -118,10 +116,6 @@ contract TDXVerifierTest is Test { return keccak256(abi.encodePacked(PUBLIC_KEY_X, PUBLIC_KEY_Y)); } - function _verify(TDXVerifierJournal memory journal) internal view returns (address signer, bytes32 imageHash) { - return verifier.verify(abi.encode(journal), hex"1234"); - } - function _expectVerifyRevert(TDXVerifierJournal memory journal, bytes memory expectedRevert) internal { vm.expectRevert(expectedRevert); verifier.verify(abi.encode(journal), hex"1234"); From 08308fea01c57804bc3d1e6264e08f951438a6ea Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 09:08:46 -0400 Subject: [PATCH 120/146] clean up TDXVerifier --- test/L1/proofs/TDXVerifier.t.sol | 37 +++++++++----------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol index 8af66dfb9..479a8ddbe 100644 --- a/test/L1/proofs/TDXVerifier.t.sol +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -16,8 +16,8 @@ contract TDXVerifierTest is Test { bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); - bytes32 internal constant PUBLIC_KEY_X = hex"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; - bytes32 internal constant PUBLIC_KEY_Y = hex"2122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40"; + bytes32 internal constant PUBLIC_KEY_X = bytes32(uint256(1)); + bytes32 internal constant PUBLIC_KEY_Y = bytes32(uint256(2)); uint64 internal constant MAX_TIME_DIFF = 3600; @@ -44,59 +44,42 @@ contract TDXVerifierTest is Test { TDXVerifierJournal memory journal = _successJournal(); journal.rootCaHash = keccak256("wrong-root-ca"); - _expectVerifyRevert( - journal, abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, journal.rootCaHash) - ); + _expectVerifyRevert(journal, TDXVerifier.RootCaHashMismatch.selector); } function testVerifyRevertsWhenTcbStatusIsNotAllowed() public { TDXVerifierJournal memory journal = _successJournal(); journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; - _expectVerifyRevert( - journal, abi.encodeWithSelector(TDXVerifier.TcbStatusNotAllowed.selector, journal.tcbStatus) - ); + _expectVerifyRevert(journal, TDXVerifier.TcbStatusNotAllowed.selector); } function testVerifyRevertsWhenCollateralExpired() public { TDXVerifierJournal memory journal = _successJournal(); journal.collateralExpiration = uint64(block.timestamp); - _expectVerifyRevert( - journal, abi.encodeWithSelector(TDXVerifier.CollateralExpired.selector, journal.collateralExpiration) - ); + _expectVerifyRevert(journal, TDXVerifier.CollateralExpired.selector); } function testVerifyRevertsWhenTimestampTooOld() public { TDXVerifierJournal memory journal = _successJournal(); journal.timestamp = uint64(block.timestamp - MAX_TIME_DIFF) * 1000; - _expectVerifyRevert( - journal, - abi.encodeWithSelector( - TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp - MAX_TIME_DIFF), block.timestamp - ) - ); + _expectVerifyRevert(journal, TDXVerifier.InvalidTimestamp.selector); } function testVerifyRevertsWhenTimestampIsFromFuture() public { TDXVerifierJournal memory journal = _successJournal(); journal.timestamp = uint64(block.timestamp) * 1000; - _expectVerifyRevert( - journal, - abi.encodeWithSelector(TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp), block.timestamp) - ); + _expectVerifyRevert(journal, TDXVerifier.InvalidTimestamp.selector); } function testVerifyRevertsWhenReportDataDoesNotBindPublicKey() public { TDXVerifierJournal memory journal = _successJournal(); journal.reportDataPrefix = keccak256("wrong-report-data"); - _expectVerifyRevert( - journal, - abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, _publicKeyHash(), journal.reportDataPrefix) - ); + _expectVerifyRevert(journal, TDXVerifier.ReportDataMismatch.selector); } function _successJournal() internal view returns (TDXVerifierJournal memory journal) { @@ -116,8 +99,8 @@ contract TDXVerifierTest is Test { return keccak256(abi.encodePacked(PUBLIC_KEY_X, PUBLIC_KEY_Y)); } - function _expectVerifyRevert(TDXVerifierJournal memory journal, bytes memory expectedRevert) internal { - vm.expectRevert(expectedRevert); + function _expectVerifyRevert(TDXVerifierJournal memory journal, bytes4 expectedRevert) internal { + vm.expectPartialRevert(expectedRevert); verifier.verify(abi.encode(journal), hex"1234"); } } From 219fc11ebf991a5f0aedb7479a41ae3ce9fb04fa Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 09:15:52 -0400 Subject: [PATCH 121/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 59 +++++++++----------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index 1770f5d1b..ff43d8a25 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -15,27 +15,12 @@ import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. -contract MockAggregateVerifierForRegistry { - bytes32 public TEE_NITRO_IMAGE_HASH; - bytes32 public TEE_TDX_IMAGE_HASH; - - constructor(bytes32 imageHash) { - TEE_NITRO_IMAGE_HASH = imageHash; - TEE_TDX_IMAGE_HASH = imageHash; - } -} - -contract MockDisputeGameFactoryForRegistry { - mapping(uint32 => address) internal _impls; - - function setImpl(GameType gameType, address impl) external { - _impls[GameType.unwrap(gameType)] = impl; - } +bytes32 constant REGISTRY_TEST_IMAGE_HASH = keccak256("test-image-hash"); - function gameImpls(GameType gameType) external view returns (IDisputeGame) { - return IDisputeGame(_impls[GameType.unwrap(gameType)]); - } +/// @notice Mock AggregateVerifier that returns TEE image hashes. +contract MockAggregateVerifierForRegistry { + bytes32 public constant TEE_NITRO_IMAGE_HASH = REGISTRY_TEST_IMAGE_HASH; + bytes32 public constant TEE_TDX_IMAGE_HASH = REGISTRY_TEST_IMAGE_HASH; } /// @dev Uses DevTEEProverRegistry because production signer registration requires a Nitro attestation proof. @@ -46,7 +31,7 @@ contract TEEProverRegistryTest is Test { address public manager; address public unauthorized; - bytes32 public constant TEST_IMAGE_HASH = keccak256("test-image-hash"); + bytes32 public constant TEST_IMAGE_HASH = REGISTRY_TEST_IMAGE_HASH; GameType public constant TEST_GAME_TYPE = GameType.wrap(621); string internal constant NOT_OWNER = "OwnableManaged: caller is not the owner"; string internal constant NOT_OWNER_OR_MANAGER = "OwnableManaged: caller is not the owner or the manager"; @@ -66,12 +51,17 @@ contract TEEProverRegistryTest is Test { } function _deployRegistry(address[] memory proposers, GameType gameType) internal returns (DevTEEProverRegistry) { - MockAggregateVerifierForRegistry verifier = new MockAggregateVerifierForRegistry(TEST_IMAGE_HASH); - MockDisputeGameFactoryForRegistry factory = new MockDisputeGameFactoryForRegistry(); - factory.setImpl(gameType, address(verifier)); + MockAggregateVerifierForRegistry verifier = new MockAggregateVerifierForRegistry(); + address factory = makeAddr("factory"); + vm.etch(factory, hex"00"); + vm.mockCall( + factory, + abi.encodeCall(IDisputeGameFactory.gameImpls, (gameType)), + abi.encode(IDisputeGame(address(verifier))) + ); DevTEEProverRegistry impl = new DevTEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(factory)) + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(factory) ); address proxyAdmin = makeAddr("proxy-admin"); @@ -122,22 +112,15 @@ contract TEEProverRegistryTest is Test { } function testInitializationWithProposers() public { - address proposer1 = makeAddr("proposer1"); - address proposer2 = makeAddr("proposer2"); - address proposer3 = makeAddr("proposer3"); address[] memory proposers = new address[](3); - proposers[0] = proposer1; - proposers[1] = proposer2; - proposers[2] = proposer3; + proposers[0] = makeAddr("proposer1"); + proposers[1] = makeAddr("proposer2"); + proposers[2] = makeAddr("proposer3"); DevTEEProverRegistry registry2 = _deployRegistry(proposers, TEST_GAME_TYPE); - assertTrue(registry2.isValidProposer(proposer1)); - assertTrue(registry2.isValidProposer(proposer2)); - assertTrue(registry2.isValidProposer(proposer3)); - } - - function testInitializationWithEmptyProposers() public view { - assertFalse(teeProverRegistry.isValidProposer(address(0))); + assertTrue(registry2.isValidProposer(proposers[0])); + assertTrue(registry2.isValidProposer(proposers[1])); + assertTrue(registry2.isValidProposer(proposers[2])); } function testDeregisterSignerAsOwner() public { From aab37ea19f544ece082cd9acac57861617befe6f Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 09:21:11 -0400 Subject: [PATCH 122/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 107 +------------------------ 1 file changed, 3 insertions(+), 104 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index ff43d8a25..bd3d9eae8 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -15,12 +15,12 @@ import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -bytes32 constant REGISTRY_TEST_IMAGE_HASH = keccak256("test-image-hash"); +bytes32 constant TEST_IMAGE_HASH = keccak256("test-image-hash"); /// @notice Mock AggregateVerifier that returns TEE image hashes. contract MockAggregateVerifierForRegistry { - bytes32 public constant TEE_NITRO_IMAGE_HASH = REGISTRY_TEST_IMAGE_HASH; - bytes32 public constant TEE_TDX_IMAGE_HASH = REGISTRY_TEST_IMAGE_HASH; + bytes32 public constant TEE_NITRO_IMAGE_HASH = TEST_IMAGE_HASH; + bytes32 public constant TEE_TDX_IMAGE_HASH = TEST_IMAGE_HASH; } /// @dev Uses DevTEEProverRegistry because production signer registration requires a Nitro attestation proof. @@ -31,7 +31,6 @@ contract TEEProverRegistryTest is Test { address public manager; address public unauthorized; - bytes32 public constant TEST_IMAGE_HASH = REGISTRY_TEST_IMAGE_HASH; GameType public constant TEST_GAME_TYPE = GameType.wrap(621); string internal constant NOT_OWNER = "OwnableManaged: caller is not the owner"; string internal constant NOT_OWNER_OR_MANAGER = "OwnableManaged: caller is not the owner or the manager"; @@ -218,83 +217,6 @@ contract TEEProverRegistryTest is Test { assertEq(teeProverRegistry.MAX_AGE(), 60 minutes); } - function testTransferOwnership() public { - address newOwner = makeAddr("newOwner"); - - vm.prank(owner); - teeProverRegistry.transferOwnership(newOwner); - - assertEq(teeProverRegistry.owner(), newOwner); - } - - function testTransferOwnershipFailsIfNotOwner() public { - address newOwner = makeAddr("newOwner"); - - _expectNotOwnerRevert(manager); - teeProverRegistry.transferOwnership(newOwner); - - _expectNotOwnerRevert(unauthorized); - teeProverRegistry.transferOwnership(newOwner); - } - - function testTransferOwnershipFailsForZeroAddress() public { - vm.prank(owner); - vm.expectRevert("OwnableManaged: new owner is the zero address"); - teeProverRegistry.transferOwnership(address(0)); - } - - function testTransferManagementAsOwner() public { - address newManager = makeAddr("newManager"); - - vm.prank(owner); - teeProverRegistry.transferManagement(newManager); - - assertEq(teeProverRegistry.manager(), newManager); - } - - function testTransferManagementAsManager() public { - address newManager = makeAddr("newManager"); - - vm.prank(manager); - teeProverRegistry.transferManagement(newManager); - - assertEq(teeProverRegistry.manager(), newManager); - } - - function testTransferManagementFailsIfUnauthorized() public { - address newManager = makeAddr("newManager"); - - _expectNotOwnerOrManagerRevert(unauthorized); - teeProverRegistry.transferManagement(newManager); - } - - function testTransferManagementFailsForZeroAddress() public { - vm.prank(owner); - vm.expectRevert("OwnableManaged: new manager is the zero address"); - teeProverRegistry.transferManagement(address(0)); - } - - function testRenounceOwnership() public { - vm.prank(owner); - teeProverRegistry.renounceOwnership(); - - assertEq(teeProverRegistry.owner(), address(0)); - } - - function testRenounceManagementAsOwner() public { - vm.prank(owner); - teeProverRegistry.renounceManagement(); - - assertEq(teeProverRegistry.manager(), address(0)); - } - - function testRenounceManagementAsManager() public { - vm.prank(manager); - teeProverRegistry.renounceManagement(); - - assertEq(teeProverRegistry.manager(), address(0)); - } - function testAddDevSigner() public { address signer = makeAddr("dev-signer"); @@ -326,18 +248,6 @@ contract TEEProverRegistryTest is Test { assertTrue(teeProverRegistry.isValidSigner(signer)); } - function testAddMultipleDevSigners() public { - address signer1 = makeAddr("dev-signer-1"); - address signer2 = makeAddr("dev-signer-2"); - address signer3 = makeAddr("dev-signer-3"); - - _addDevSigners(signer1, signer2, signer3); - - assertTrue(teeProverRegistry.isValidSigner(signer1)); - assertTrue(teeProverRegistry.isValidSigner(signer2)); - assertTrue(teeProverRegistry.isValidSigner(signer3)); - } - function testGetRegisteredSignersEmpty() public view { assertEq(teeProverRegistry.getRegisteredSigners().length, 0); } @@ -352,17 +262,6 @@ contract TEEProverRegistryTest is Test { assertEq(signers[0], signer); } - function testGetRegisteredSignersAfterDeregister() public { - address signer = makeAddr("signer"); - - _addDevSigner(signer); - - vm.prank(owner); - teeProverRegistry.deregisterSigner(signer); - - assertEq(teeProverRegistry.getRegisteredSigners().length, 0); - } - function testGetRegisteredSignersMultiple() public { address signer1 = makeAddr("signer-1"); address signer2 = makeAddr("signer-2"); From dc9fcb83d7d78d2d02816e588aa4e988a16aadbf Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 09:26:25 -0400 Subject: [PATCH 123/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 32 +++++++++----------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index bd3d9eae8..ab4afe2f3 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -10,8 +10,6 @@ import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { GameType } from "src/libraries/bridge/Types.sol"; -import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; - import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; @@ -29,7 +27,6 @@ contract TEEProverRegistryTest is Test { address public owner; address public manager; - address public unauthorized; GameType public constant TEST_GAME_TYPE = GameType.wrap(621); string internal constant NOT_OWNER = "OwnableManaged: caller is not the owner"; @@ -44,19 +41,16 @@ contract TEEProverRegistryTest is Test { function setUp() public { owner = makeAddr("owner"); manager = makeAddr("manager"); - unauthorized = makeAddr("unauthorized"); - teeProverRegistry = _deployRegistry(new address[](0), TEST_GAME_TYPE); + teeProverRegistry = _deployRegistry(new address[](0)); } - function _deployRegistry(address[] memory proposers, GameType gameType) internal returns (DevTEEProverRegistry) { + function _deployRegistry(address[] memory proposers) internal returns (DevTEEProverRegistry) { MockAggregateVerifierForRegistry verifier = new MockAggregateVerifierForRegistry(); address factory = makeAddr("factory"); vm.etch(factory, hex"00"); vm.mockCall( - factory, - abi.encodeCall(IDisputeGameFactory.gameImpls, (gameType)), - abi.encode(IDisputeGame(address(verifier))) + factory, abi.encodeCall(IDisputeGameFactory.gameImpls, (TEST_GAME_TYPE)), abi.encode(address(verifier)) ); DevTEEProverRegistry impl = new DevTEEProverRegistry( @@ -67,7 +61,7 @@ contract TEEProverRegistryTest is Test { Proxy proxy = new Proxy(proxyAdmin); vm.prank(proxyAdmin); proxy.upgradeToAndCall( - address(impl), abi.encodeCall(TEEProverRegistry.initialize, (owner, manager, proposers, gameType)) + address(impl), abi.encodeCall(TEEProverRegistry.initialize, (owner, manager, proposers, TEST_GAME_TYPE)) ); return DevTEEProverRegistry(address(proxy)); @@ -116,7 +110,7 @@ contract TEEProverRegistryTest is Test { proposers[1] = makeAddr("proposer2"); proposers[2] = makeAddr("proposer3"); - DevTEEProverRegistry registry2 = _deployRegistry(proposers, TEST_GAME_TYPE); + DevTEEProverRegistry registry2 = _deployRegistry(proposers); assertTrue(registry2.isValidProposer(proposers[0])); assertTrue(registry2.isValidProposer(proposers[1])); assertTrue(registry2.isValidProposer(proposers[2])); @@ -150,7 +144,7 @@ contract TEEProverRegistryTest is Test { function testDeregisterSignerFailsIfUnauthorized() public { address signer = makeAddr("signer"); - _expectNotOwnerOrManagerRevert(unauthorized); + _expectNotOwnerOrManagerRevert(makeAddr("unauthorized")); teeProverRegistry.deregisterSigner(signer); } @@ -172,7 +166,7 @@ contract TEEProverRegistryTest is Test { _expectNotOwnerRevert(manager); teeProverRegistry.setProposer(newProposer, true); - _expectNotOwnerRevert(unauthorized); + _expectNotOwnerRevert(makeAddr("unauthorized")); teeProverRegistry.setProposer(newProposer, true); } @@ -187,7 +181,7 @@ contract TEEProverRegistryTest is Test { _addDevSigner(signer); assertTrue(teeProverRegistry.isValidSigner(signer)); - assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.NITRO)); + assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.NITRO); } function testAddDevTDXSigner() public { @@ -197,7 +191,7 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); assertTrue(teeProverRegistry.isValidSigner(signer)); - assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); + assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.TDX); } function testRegisteringSameSignerWithDifferentTEETypeOverwritesTEEType() public { @@ -210,11 +204,7 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); assertTrue(teeProverRegistry.isRegisteredSigner(signer)); - assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); - } - - function testMaxAgeConstant() public view { - assertEq(teeProverRegistry.MAX_AGE(), 60 minutes); + assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.TDX); } function testAddDevSigner() public { @@ -235,7 +225,7 @@ contract TEEProverRegistryTest is Test { _expectNotOwnerRevert(manager); teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); - _expectNotOwnerRevert(unauthorized); + _expectNotOwnerRevert(makeAddr("unauthorized")); teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } From 0688716354b7392b7711c9365229c3fd8e05fc21 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:02:27 -0400 Subject: [PATCH 124/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 41 +++++++------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index ab4afe2f3..6abf2d6c9 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -30,7 +30,6 @@ contract TEEProverRegistryTest is Test { GameType public constant TEST_GAME_TYPE = GameType.wrap(621); string internal constant NOT_OWNER = "OwnableManaged: caller is not the owner"; - string internal constant NOT_OWNER_OR_MANAGER = "OwnableManaged: caller is not the owner or the manager"; // Events must be redeclared here because Solidity 0.8.15 doesn't support // referencing events from other contracts via qualified names (requires 0.8.21+) @@ -72,24 +71,11 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } - function _addDevSigners(address signer1, address signer2, address signer3) internal { - vm.startPrank(owner); - teeProverRegistry.addDevSigner(signer1, TEST_IMAGE_HASH); - teeProverRegistry.addDevSigner(signer2, TEST_IMAGE_HASH); - teeProverRegistry.addDevSigner(signer3, TEST_IMAGE_HASH); - vm.stopPrank(); - } - function _expectNotOwnerRevert(address caller) internal { vm.prank(caller); vm.expectRevert(bytes(NOT_OWNER)); } - function _expectNotOwnerOrManagerRevert(address caller) internal { - vm.prank(caller); - vm.expectRevert(bytes(NOT_OWNER_OR_MANAGER)); - } - function _assertContains(address[] memory values, address expected) internal { for (uint256 i = 0; i < values.length; i++) { if (values[i] == expected) return; @@ -144,7 +130,8 @@ contract TEEProverRegistryTest is Test { function testDeregisterSignerFailsIfUnauthorized() public { address signer = makeAddr("signer"); - _expectNotOwnerOrManagerRevert(makeAddr("unauthorized")); + vm.prank(makeAddr("unauthorized")); + vm.expectRevert(bytes("OwnableManaged: caller is not the owner or the manager")); teeProverRegistry.deregisterSigner(signer); } @@ -197,8 +184,7 @@ contract TEEProverRegistryTest is Test { function testRegisteringSameSignerWithDifferentTEETypeOverwritesTEEType() public { address signer = makeAddr("dev-signer"); - vm.prank(owner); - teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); + _addDevSigner(signer); vm.prank(owner); teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); @@ -213,8 +199,7 @@ contract TEEProverRegistryTest is Test { vm.expectEmit(true, false, false, false, address(teeProverRegistry)); emit SignerRegistered(signer); - vm.prank(owner); - teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); + _addDevSigner(signer); assertTrue(teeProverRegistry.isValidSigner(signer)); } @@ -242,22 +227,14 @@ contract TEEProverRegistryTest is Test { assertEq(teeProverRegistry.getRegisteredSigners().length, 0); } - function testGetRegisteredSignersAfterRegister() public { - address signer = makeAddr("signer"); - - _addDevSigner(signer); - - address[] memory signers = teeProverRegistry.getRegisteredSigners(); - assertEq(signers.length, 1); - assertEq(signers[0], signer); - } - function testGetRegisteredSignersMultiple() public { address signer1 = makeAddr("signer-1"); address signer2 = makeAddr("signer-2"); address signer3 = makeAddr("signer-3"); - _addDevSigners(signer1, signer2, signer3); + _addDevSigner(signer1); + _addDevSigner(signer2); + _addDevSigner(signer3); address[] memory signers = teeProverRegistry.getRegisteredSigners(); assertEq(signers.length, 3); @@ -272,7 +249,9 @@ contract TEEProverRegistryTest is Test { address signer2 = makeAddr("signer-2"); address signer3 = makeAddr("signer-3"); - _addDevSigners(signer1, signer2, signer3); + _addDevSigner(signer1); + _addDevSigner(signer2); + _addDevSigner(signer3); vm.prank(manager); teeProverRegistry.deregisterSigner(signer2); From 4979bc64f59c4f789281009be0c008eeb2b07474 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:05:47 -0400 Subject: [PATCH 125/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index 6abf2d6c9..ca64a16c6 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -29,7 +29,6 @@ contract TEEProverRegistryTest is Test { address public manager; GameType public constant TEST_GAME_TYPE = GameType.wrap(621); - string internal constant NOT_OWNER = "OwnableManaged: caller is not the owner"; // Events must be redeclared here because Solidity 0.8.15 doesn't support // referencing events from other contracts via qualified names (requires 0.8.21+) @@ -73,7 +72,7 @@ contract TEEProverRegistryTest is Test { function _expectNotOwnerRevert(address caller) internal { vm.prank(caller); - vm.expectRevert(bytes(NOT_OWNER)); + vm.expectRevert(bytes("OwnableManaged: caller is not the owner")); } function _assertContains(address[] memory values, address expected) internal { @@ -128,11 +127,9 @@ contract TEEProverRegistryTest is Test { } function testDeregisterSignerFailsIfUnauthorized() public { - address signer = makeAddr("signer"); - vm.prank(makeAddr("unauthorized")); vm.expectRevert(bytes("OwnableManaged: caller is not the owner or the manager")); - teeProverRegistry.deregisterSigner(signer); + teeProverRegistry.deregisterSigner(makeAddr("signer")); } function testSetProposer() public { @@ -152,23 +149,10 @@ contract TEEProverRegistryTest is Test { _expectNotOwnerRevert(manager); teeProverRegistry.setProposer(newProposer, true); - - _expectNotOwnerRevert(makeAddr("unauthorized")); - teeProverRegistry.setProposer(newProposer, true); } function testIsValidSignerReturnsFalseForUnregistered() public { - address unregistered = makeAddr("unregistered"); - assertFalse(teeProverRegistry.isValidSigner(unregistered)); - } - - function testIsValidSignerReturnsTrueForRegistered() public { - address signer = makeAddr("signer"); - - _addDevSigner(signer); - - assertTrue(teeProverRegistry.isValidSigner(signer)); - assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.NITRO); + assertFalse(teeProverRegistry.isValidSigner(makeAddr("unregistered"))); } function testAddDevTDXSigner() public { @@ -202,6 +186,7 @@ contract TEEProverRegistryTest is Test { _addDevSigner(signer); assertTrue(teeProverRegistry.isValidSigner(signer)); + assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.NITRO); } function testAddDevSignerFailsIfNotOwner() public { @@ -209,9 +194,6 @@ contract TEEProverRegistryTest is Test { _expectNotOwnerRevert(manager); teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); - - _expectNotOwnerRevert(makeAddr("unauthorized")); - teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } function testAddDevSignerIdempotent() public { From 02cc747d2d6a2327a5d15e2de9f9ff038cea9ea7 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:10:53 -0400 Subject: [PATCH 126/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index ca64a16c6..ad104f16c 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -70,8 +70,8 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } - function _expectNotOwnerRevert(address caller) internal { - vm.prank(caller); + function _expectNotOwnerRevert() internal { + vm.prank(manager); vm.expectRevert(bytes("OwnableManaged: caller is not the owner")); } @@ -106,7 +106,7 @@ contract TEEProverRegistryTest is Test { _addDevSigner(signer); - vm.expectEmit(true, false, false, false, address(teeProverRegistry)); + vm.expectEmit(address(teeProverRegistry)); emit SignerDeregistered(signer); vm.prank(owner); @@ -135,7 +135,7 @@ contract TEEProverRegistryTest is Test { function testSetProposer() public { address newProposer = makeAddr("proposer"); - vm.expectEmit(true, false, false, false, address(teeProverRegistry)); + vm.expectEmit(address(teeProverRegistry)); emit ProposerSet(newProposer, true); vm.prank(owner); @@ -145,10 +145,8 @@ contract TEEProverRegistryTest is Test { } function testSetProposerFailsIfNotOwner() public { - address newProposer = makeAddr("proposer"); - - _expectNotOwnerRevert(manager); - teeProverRegistry.setProposer(newProposer, true); + _expectNotOwnerRevert(); + teeProverRegistry.setProposer(makeAddr("proposer"), true); } function testIsValidSignerReturnsFalseForUnregistered() public { @@ -180,7 +178,7 @@ contract TEEProverRegistryTest is Test { function testAddDevSigner() public { address signer = makeAddr("dev-signer"); - vm.expectEmit(true, false, false, false, address(teeProverRegistry)); + vm.expectEmit(address(teeProverRegistry)); emit SignerRegistered(signer); _addDevSigner(signer); @@ -192,7 +190,7 @@ contract TEEProverRegistryTest is Test { function testAddDevSignerFailsIfNotOwner() public { address signer = makeAddr("dev-signer"); - _expectNotOwnerRevert(manager); + _expectNotOwnerRevert(); teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } From 0792f1342d9dfabb3b7af98d91efdc2828c3aa39 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:16:38 -0400 Subject: [PATCH 127/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 38 ++++---------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index ad104f16c..351765d01 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -30,12 +30,6 @@ contract TEEProverRegistryTest is Test { GameType public constant TEST_GAME_TYPE = GameType.wrap(621); - // Events must be redeclared here because Solidity 0.8.15 doesn't support - // referencing events from other contracts via qualified names (requires 0.8.21+) - event SignerRegistered(address indexed signer); - event SignerDeregistered(address indexed signer); - event ProposerSet(address indexed proposer, bool isValid); - function setUp() public { owner = makeAddr("owner"); manager = makeAddr("manager"); @@ -70,11 +64,6 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } - function _expectNotOwnerRevert() internal { - vm.prank(manager); - vm.expectRevert(bytes("OwnableManaged: caller is not the owner")); - } - function _assertContains(address[] memory values, address expected) internal { for (uint256 i = 0; i < values.length; i++) { if (values[i] == expected) return; @@ -90,15 +79,11 @@ contract TEEProverRegistryTest is Test { } function testInitializationWithProposers() public { - address[] memory proposers = new address[](3); - proposers[0] = makeAddr("proposer1"); - proposers[1] = makeAddr("proposer2"); - proposers[2] = makeAddr("proposer3"); + address[] memory proposers = new address[](1); + proposers[0] = makeAddr("proposer"); DevTEEProverRegistry registry2 = _deployRegistry(proposers); assertTrue(registry2.isValidProposer(proposers[0])); - assertTrue(registry2.isValidProposer(proposers[1])); - assertTrue(registry2.isValidProposer(proposers[2])); } function testDeregisterSignerAsOwner() public { @@ -106,9 +91,6 @@ contract TEEProverRegistryTest is Test { _addDevSigner(signer); - vm.expectEmit(address(teeProverRegistry)); - emit SignerDeregistered(signer); - vm.prank(owner); teeProverRegistry.deregisterSigner(signer); @@ -135,9 +117,6 @@ contract TEEProverRegistryTest is Test { function testSetProposer() public { address newProposer = makeAddr("proposer"); - vm.expectEmit(address(teeProverRegistry)); - emit ProposerSet(newProposer, true); - vm.prank(owner); teeProverRegistry.setProposer(newProposer, true); @@ -145,14 +124,11 @@ contract TEEProverRegistryTest is Test { } function testSetProposerFailsIfNotOwner() public { - _expectNotOwnerRevert(); + vm.prank(manager); + vm.expectRevert(bytes("OwnableManaged: caller is not the owner")); teeProverRegistry.setProposer(makeAddr("proposer"), true); } - function testIsValidSignerReturnsFalseForUnregistered() public { - assertFalse(teeProverRegistry.isValidSigner(makeAddr("unregistered"))); - } - function testAddDevTDXSigner() public { address signer = makeAddr("dev-tdx-signer"); @@ -178,9 +154,6 @@ contract TEEProverRegistryTest is Test { function testAddDevSigner() public { address signer = makeAddr("dev-signer"); - vm.expectEmit(address(teeProverRegistry)); - emit SignerRegistered(signer); - _addDevSigner(signer); assertTrue(teeProverRegistry.isValidSigner(signer)); @@ -190,7 +163,8 @@ contract TEEProverRegistryTest is Test { function testAddDevSignerFailsIfNotOwner() public { address signer = makeAddr("dev-signer"); - _expectNotOwnerRevert(); + vm.prank(manager); + vm.expectRevert(bytes("OwnableManaged: caller is not the owner")); teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } From 45b34ae5982a0d7fb0dd4e835b95b5d879c58b93 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:21:09 -0400 Subject: [PATCH 128/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 44 +++----------------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index 351765d01..c4ff16021 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -23,12 +23,12 @@ contract MockAggregateVerifierForRegistry { /// @dev Uses DevTEEProverRegistry because production signer registration requires a Nitro attestation proof. contract TEEProverRegistryTest is Test { - DevTEEProverRegistry public teeProverRegistry; + DevTEEProverRegistry internal teeProverRegistry; - address public owner; - address public manager; + address internal owner; + address internal manager; - GameType public constant TEST_GAME_TYPE = GameType.wrap(621); + GameType internal constant TEST_GAME_TYPE = GameType.wrap(621); function setUp() public { owner = makeAddr("owner"); @@ -129,16 +129,6 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.setProposer(makeAddr("proposer"), true); } - function testAddDevTDXSigner() public { - address signer = makeAddr("dev-tdx-signer"); - - vm.prank(owner); - teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); - - assertTrue(teeProverRegistry.isValidSigner(signer)); - assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.TDX); - } - function testRegisteringSameSignerWithDifferentTEETypeOverwritesTEEType() public { address signer = makeAddr("dev-signer"); @@ -168,36 +158,10 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } - function testAddDevSignerIdempotent() public { - address signer = makeAddr("dev-signer"); - - _addDevSigner(signer); - _addDevSigner(signer); - - assertTrue(teeProverRegistry.isValidSigner(signer)); - } - function testGetRegisteredSignersEmpty() public view { assertEq(teeProverRegistry.getRegisteredSigners().length, 0); } - function testGetRegisteredSignersMultiple() public { - address signer1 = makeAddr("signer-1"); - address signer2 = makeAddr("signer-2"); - address signer3 = makeAddr("signer-3"); - - _addDevSigner(signer1); - _addDevSigner(signer2); - _addDevSigner(signer3); - - address[] memory signers = teeProverRegistry.getRegisteredSigners(); - assertEq(signers.length, 3); - - _assertContains(signers, signer1); - _assertContains(signers, signer2); - _assertContains(signers, signer3); - } - function testGetRegisteredSignersConsistencyAfterMixedOperations() public { address signer1 = makeAddr("signer-1"); address signer2 = makeAddr("signer-2"); From 8eeab13e8db1c98683ee38f3611aa772b47d62da Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:26:15 -0400 Subject: [PATCH 129/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 35 ++++++++------------------ 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index c4ff16021..6c9cc9cde 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -15,35 +15,28 @@ import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; bytes32 constant TEST_IMAGE_HASH = keccak256("test-image-hash"); -/// @notice Mock AggregateVerifier that returns TEE image hashes. -contract MockAggregateVerifierForRegistry { - bytes32 public constant TEE_NITRO_IMAGE_HASH = TEST_IMAGE_HASH; - bytes32 public constant TEE_TDX_IMAGE_HASH = TEST_IMAGE_HASH; -} - /// @dev Uses DevTEEProverRegistry because production signer registration requires a Nitro attestation proof. contract TEEProverRegistryTest is Test { DevTEEProverRegistry internal teeProverRegistry; - address internal owner; - address internal manager; + address internal immutable owner = makeAddr("owner"); + address internal immutable manager = makeAddr("manager"); GameType internal constant TEST_GAME_TYPE = GameType.wrap(621); function setUp() public { - owner = makeAddr("owner"); - manager = makeAddr("manager"); - teeProverRegistry = _deployRegistry(new address[](0)); } function _deployRegistry(address[] memory proposers) internal returns (DevTEEProverRegistry) { - MockAggregateVerifierForRegistry verifier = new MockAggregateVerifierForRegistry(); + address verifier = makeAddr("verifier"); + vm.etch(verifier, hex"00"); + vm.mockCall(verifier, abi.encodeWithSignature("TEE_NITRO_IMAGE_HASH()"), abi.encode(TEST_IMAGE_HASH)); + vm.mockCall(verifier, abi.encodeWithSignature("TEE_TDX_IMAGE_HASH()"), abi.encode(TEST_IMAGE_HASH)); + address factory = makeAddr("factory"); vm.etch(factory, hex"00"); - vm.mockCall( - factory, abi.encodeCall(IDisputeGameFactory.gameImpls, (TEST_GAME_TYPE)), abi.encode(address(verifier)) - ); + vm.mockCall(factory, abi.encodeCall(IDisputeGameFactory.gameImpls, (TEST_GAME_TYPE)), abi.encode(verifier)); DevTEEProverRegistry impl = new DevTEEProverRegistry( INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(factory) @@ -64,14 +57,6 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); } - function _assertContains(address[] memory values, address expected) internal { - for (uint256 i = 0; i < values.length; i++) { - if (values[i] == expected) return; - } - - fail(); - } - function testInitialization() public view { assertEq(teeProverRegistry.owner(), owner); assertEq(teeProverRegistry.manager(), manager); @@ -176,8 +161,8 @@ contract TEEProverRegistryTest is Test { address[] memory signers = teeProverRegistry.getRegisteredSigners(); assertEq(signers.length, 2); - _assertContains(signers, signer1); - _assertContains(signers, signer3); + assertTrue(signers[0] == signer1 || signers[1] == signer1); + assertTrue(signers[0] == signer3 || signers[1] == signer3); assertFalse(teeProverRegistry.isValidSigner(signer2)); } From 6bfebc15154e541288c87c7937c07097e946789d Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:28:33 -0400 Subject: [PATCH 130/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index 6c9cc9cde..c59355261 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -136,15 +136,9 @@ contract TEEProverRegistryTest is Test { } function testAddDevSignerFailsIfNotOwner() public { - address signer = makeAddr("dev-signer"); - vm.prank(manager); vm.expectRevert(bytes("OwnableManaged: caller is not the owner")); - teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); - } - - function testGetRegisteredSignersEmpty() public view { - assertEq(teeProverRegistry.getRegisteredSigners().length, 0); + teeProverRegistry.addDevSigner(makeAddr("dev-signer"), TEST_IMAGE_HASH); } function testGetRegisteredSignersConsistencyAfterMixedOperations() public { @@ -165,18 +159,4 @@ contract TEEProverRegistryTest is Test { assertTrue(signers[0] == signer3 || signers[1] == signer3); assertFalse(teeProverRegistry.isValidSigner(signer2)); } - - function testGetRegisteredSignersDeregisterIdempotent() public { - address signer = makeAddr("signer"); - - _addDevSigner(signer); - - vm.prank(owner); - teeProverRegistry.deregisterSigner(signer); - - vm.prank(owner); - teeProverRegistry.deregisterSigner(signer); - - assertEq(teeProverRegistry.getRegisteredSigners().length, 0); - } } From 9df4040cdeef5ba024ff950b991fbf18aca66c3b Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:32:27 -0400 Subject: [PATCH 131/146] clean up TEEProverRegistry tests --- test/mocks/MockDevTEEProverRegistry.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/test/mocks/MockDevTEEProverRegistry.sol b/test/mocks/MockDevTEEProverRegistry.sol index 3f877815c..6a38d36e2 100644 --- a/test/mocks/MockDevTEEProverRegistry.sol +++ b/test/mocks/MockDevTEEProverRegistry.sol @@ -6,7 +6,6 @@ import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.so import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -import { EnumerableSetLib } from "src/vendor/EnumerableSetLib.sol"; /// @title DevTEEProverRegistry /// @notice Test/development registry that can register signers without Nitro attestation verification. From d799f6188316a053491ec2187622b1c0a4687d61 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:41:15 -0400 Subject: [PATCH 132/146] clean up TEEProverRegistryTDX tests --- test/L1/proofs/TEEProverRegistryTDX.t.sol | 69 +++++++---------------- 1 file changed, 20 insertions(+), 49 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 13e4466ce..4ee5d4ce4 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; -import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; @@ -11,57 +10,32 @@ import { GameType } from "src/libraries/bridge/Types.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. -contract MockAggregateVerifierForTDXRegistry { - bytes32 public TEE_NITRO_IMAGE_HASH; - bytes32 public TEE_TDX_IMAGE_HASH; - - constructor(bytes32 imageHash) { - TEE_NITRO_IMAGE_HASH = imageHash; - TEE_TDX_IMAGE_HASH = imageHash; - } -} - -/// @notice Mock DisputeGameFactory that returns a fixed game implementation. -contract MockDisputeGameFactoryForTDXRegistry { - mapping(uint32 => address) internal _impls; - - function setImpl(uint32 gameType, address impl) external { - _impls[gameType] = impl; - } - - function gameImpls(GameType gameType) external view returns (IDisputeGame) { - return IDisputeGame(_impls[GameType.unwrap(gameType)]); - } -} - -contract MockTDXVerifierForRegistry is ITDXVerifier { - address internal _signer; - bytes32 internal _imageHash; - - function setResult(address signer, bytes32 imageHash) external { - _signer = signer; - _imageHash = imageHash; - } - - function verify(bytes calldata, bytes calldata) external view returns (address, bytes32) { - return (_signer, _imageHash); - } -} - contract TEEProverRegistryTDXTest is Test { bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); + GameType internal constant TEST_GAME_TYPE = GameType.wrap(0); function testRegisterTDXSignerStoresImageHash() public { - address signer = address(0x1234); - MockTDXVerifierForRegistry verifier = new MockTDXVerifierForRegistry(); - verifier.setResult(signer, IMAGE_HASH); + address signer = makeAddr("signer"); + address tdxVerifier = makeAddr("tdx-verifier"); + address aggregateVerifier = makeAddr("aggregate-verifier"); + address factory = makeAddr("factory"); + + vm.etch(tdxVerifier, hex"00"); + vm.mockCall( + tdxVerifier, abi.encodeCall(ITDXVerifier.verify, (bytes(""), bytes(""))), abi.encode(signer, IMAGE_HASH) + ); - MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); - factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); + vm.etch(aggregateVerifier, hex"00"); + vm.mockCall(aggregateVerifier, abi.encodeWithSignature("TEE_NITRO_IMAGE_HASH()"), abi.encode(IMAGE_HASH)); + vm.mockCall(aggregateVerifier, abi.encodeWithSignature("TEE_TDX_IMAGE_HASH()"), abi.encode(IMAGE_HASH)); + + vm.etch(factory, hex"00"); + vm.mockCall( + factory, abi.encodeCall(IDisputeGameFactory.gameImpls, (TEST_GAME_TYPE)), abi.encode(aggregateVerifier) + ); TEEProverRegistry registry = new TEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(address(verifier)), IDisputeGameFactory(address(factory)) + INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifier), IDisputeGameFactory(factory) ); vm.prank(address(0xdEaD)); @@ -74,12 +48,9 @@ contract TEEProverRegistryTDXTest is Test { } function testConstructorRevertsIfTDXVerifierNotSet() public { - MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); - factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); - vm.expectRevert(TEEProverRegistry.TDXVerifierNotSet.selector); new TEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(address(0)), IDisputeGameFactory(address(factory)) + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(0)), IDisputeGameFactory(makeAddr("factory")) ); } } From 692ab6091835bf343be07a1a053a083532ae89a8 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:44:59 -0400 Subject: [PATCH 133/146] clean up TEEProverRegistryTDX tests --- test/L1/proofs/TEEProverRegistryTDX.t.sol | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol index 4ee5d4ce4..846fb593f 100644 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -6,36 +6,23 @@ import { Test } from "forge-std/Test.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; -import { GameType } from "src/libraries/bridge/Types.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; contract TEEProverRegistryTDXTest is Test { bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); - GameType internal constant TEST_GAME_TYPE = GameType.wrap(0); function testRegisterTDXSignerStoresImageHash() public { address signer = makeAddr("signer"); address tdxVerifier = makeAddr("tdx-verifier"); - address aggregateVerifier = makeAddr("aggregate-verifier"); - address factory = makeAddr("factory"); vm.etch(tdxVerifier, hex"00"); vm.mockCall( tdxVerifier, abi.encodeCall(ITDXVerifier.verify, (bytes(""), bytes(""))), abi.encode(signer, IMAGE_HASH) ); - vm.etch(aggregateVerifier, hex"00"); - vm.mockCall(aggregateVerifier, abi.encodeWithSignature("TEE_NITRO_IMAGE_HASH()"), abi.encode(IMAGE_HASH)); - vm.mockCall(aggregateVerifier, abi.encodeWithSignature("TEE_TDX_IMAGE_HASH()"), abi.encode(IMAGE_HASH)); - - vm.etch(factory, hex"00"); - vm.mockCall( - factory, abi.encodeCall(IDisputeGameFactory.gameImpls, (TEST_GAME_TYPE)), abi.encode(aggregateVerifier) - ); - TEEProverRegistry registry = new TEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifier), IDisputeGameFactory(factory) + INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifier), IDisputeGameFactory(makeAddr("factory")) ); vm.prank(address(0xdEaD)); @@ -44,7 +31,6 @@ contract TEEProverRegistryTDXTest is Test { assertTrue(registry.isRegisteredSigner(signer)); assertEq(registry.signerImageHash(signer), IMAGE_HASH); assertEq(uint8(registry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); - assertTrue(registry.isValidSigner(signer)); } function testConstructorRevertsIfTDXVerifierNotSet() public { From b39f70ab11fca5d08214247b5a2eb3866dfde2c3 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:50:34 -0400 Subject: [PATCH 134/146] clean up TEEProverRegistryTDX tests --- test/L1/proofs/TEEProverRegistry.t.sol | 28 +++++++++++++++ test/L1/proofs/TEEProverRegistryTDX.t.sol | 42 ----------------------- 2 files changed, 28 insertions(+), 42 deletions(-) delete mode 100644 test/L1/proofs/TEEProverRegistryTDX.t.sol diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index c59355261..cc9f9b159 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -126,6 +126,34 @@ contract TEEProverRegistryTest is Test { assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.TDX); } + function testRegisterTDXSignerStoresImageHash() public { + address signer = makeAddr("tdx-signer"); + address tdxVerifier = makeAddr("tdx-verifier"); + + vm.etch(tdxVerifier, hex"00"); + vm.mockCall( + tdxVerifier, abi.encodeCall(ITDXVerifier.verify, (bytes(""), bytes(""))), abi.encode(signer, TEST_IMAGE_HASH) + ); + + TEEProverRegistry registry = new TEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifier), IDisputeGameFactory(makeAddr("factory")) + ); + + vm.prank(address(0xdEaD)); + registry.registerTDXSigner("", ""); + + assertTrue(registry.isRegisteredSigner(signer)); + assertEq(registry.signerImageHash(signer), TEST_IMAGE_HASH); + assertTrue(registry.signerTEEType(signer) == TEEProverRegistry.TEEType.TDX); + } + + function testConstructorRevertsIfTDXVerifierNotSet() public { + vm.expectRevert(TEEProverRegistry.TDXVerifierNotSet.selector); + new TEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(0)), IDisputeGameFactory(makeAddr("factory")) + ); + } + function testAddDevSigner() public { address signer = makeAddr("dev-signer"); diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol deleted file mode 100644 index 846fb593f..000000000 --- a/test/L1/proofs/TEEProverRegistryTDX.t.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.15; - -import { Test } from "forge-std/Test.sol"; - -import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; - -import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; - -contract TEEProverRegistryTDXTest is Test { - bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); - - function testRegisterTDXSignerStoresImageHash() public { - address signer = makeAddr("signer"); - address tdxVerifier = makeAddr("tdx-verifier"); - - vm.etch(tdxVerifier, hex"00"); - vm.mockCall( - tdxVerifier, abi.encodeCall(ITDXVerifier.verify, (bytes(""), bytes(""))), abi.encode(signer, IMAGE_HASH) - ); - - TEEProverRegistry registry = new TEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(tdxVerifier), IDisputeGameFactory(makeAddr("factory")) - ); - - vm.prank(address(0xdEaD)); - registry.registerTDXSigner("", ""); - - assertTrue(registry.isRegisteredSigner(signer)); - assertEq(registry.signerImageHash(signer), IMAGE_HASH); - assertEq(uint8(registry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); - } - - function testConstructorRevertsIfTDXVerifierNotSet() public { - vm.expectRevert(TEEProverRegistry.TDXVerifierNotSet.selector); - new TEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(address(0)), IDisputeGameFactory(makeAddr("factory")) - ); - } -} From de688402682a576e93a50f1ffd7d0ca84605bf9d Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:55:49 -0400 Subject: [PATCH 135/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 31 +++++++++----------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index cc9f9b159..94a8e1f3a 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -71,26 +71,20 @@ contract TEEProverRegistryTest is Test { assertTrue(registry2.isValidProposer(proposers[0])); } - function testDeregisterSignerAsOwner() public { - address signer = makeAddr("signer"); + function testDeregisterSignerAsOwnerOrManager() public { + address ownerSigner = makeAddr("owner-signer"); + address managerSigner = makeAddr("manager-signer"); - _addDevSigner(signer); + _addDevSigner(ownerSigner); + _addDevSigner(managerSigner); vm.prank(owner); - teeProverRegistry.deregisterSigner(signer); - - assertFalse(teeProverRegistry.isValidSigner(signer)); - } - - function testDeregisterSignerAsManager() public { - address signer = makeAddr("signer"); - - _addDevSigner(signer); - + teeProverRegistry.deregisterSigner(ownerSigner); vm.prank(manager); - teeProverRegistry.deregisterSigner(signer); + teeProverRegistry.deregisterSigner(managerSigner); - assertFalse(teeProverRegistry.isValidSigner(signer)); + assertFalse(teeProverRegistry.isValidSigner(ownerSigner)); + assertFalse(teeProverRegistry.isValidSigner(managerSigner)); } function testDeregisterSignerFailsIfUnauthorized() public { @@ -172,19 +166,16 @@ contract TEEProverRegistryTest is Test { function testGetRegisteredSignersConsistencyAfterMixedOperations() public { address signer1 = makeAddr("signer-1"); address signer2 = makeAddr("signer-2"); - address signer3 = makeAddr("signer-3"); _addDevSigner(signer1); _addDevSigner(signer2); - _addDevSigner(signer3); vm.prank(manager); teeProverRegistry.deregisterSigner(signer2); address[] memory signers = teeProverRegistry.getRegisteredSigners(); - assertEq(signers.length, 2); - assertTrue(signers[0] == signer1 || signers[1] == signer1); - assertTrue(signers[0] == signer3 || signers[1] == signer3); + assertEq(signers.length, 1); + assertEq(signers[0], signer1); assertFalse(teeProverRegistry.isValidSigner(signer2)); } } From 320412f1ce032146d0a8ffdc58fcb179888da61c Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 10:59:25 -0400 Subject: [PATCH 136/146] clean up TEEProverRegistry tests --- test/L1/proofs/TEEProverRegistry.t.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index 94a8e1f3a..eb7f210aa 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -30,12 +30,9 @@ contract TEEProverRegistryTest is Test { function _deployRegistry(address[] memory proposers) internal returns (DevTEEProverRegistry) { address verifier = makeAddr("verifier"); - vm.etch(verifier, hex"00"); vm.mockCall(verifier, abi.encodeWithSignature("TEE_NITRO_IMAGE_HASH()"), abi.encode(TEST_IMAGE_HASH)); - vm.mockCall(verifier, abi.encodeWithSignature("TEE_TDX_IMAGE_HASH()"), abi.encode(TEST_IMAGE_HASH)); address factory = makeAddr("factory"); - vm.etch(factory, hex"00"); vm.mockCall(factory, abi.encodeCall(IDisputeGameFactory.gameImpls, (TEST_GAME_TYPE)), abi.encode(verifier)); DevTEEProverRegistry impl = new DevTEEProverRegistry( @@ -116,7 +113,6 @@ contract TEEProverRegistryTest is Test { vm.prank(owner); teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); - assertTrue(teeProverRegistry.isRegisteredSigner(signer)); assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.TDX); } @@ -124,7 +120,6 @@ contract TEEProverRegistryTest is Test { address signer = makeAddr("tdx-signer"); address tdxVerifier = makeAddr("tdx-verifier"); - vm.etch(tdxVerifier, hex"00"); vm.mockCall( tdxVerifier, abi.encodeCall(ITDXVerifier.verify, (bytes(""), bytes(""))), abi.encode(signer, TEST_IMAGE_HASH) ); From 0a2f82fdb09dc2394002dd47a0d96d684be3a6bf Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:07:45 -0400 Subject: [PATCH 137/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 112 ++++++------------------------- 1 file changed, 21 insertions(+), 91 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index b8dc32abd..654a09124 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -3,117 +3,45 @@ pragma solidity 0.8.15; import { Test } from "lib/forge-std/src/Test.sol"; -import { Proxy } from "src/universal/Proxy.sol"; - -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; -import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { GameType } from "src/libraries/bridge/Types.sol"; -import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; -import { MockAnchorStateRegistry } from "scripts/multiproof/mocks/MockAnchorStateRegistry.sol"; -import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; -/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. -contract MockAggregateVerifierForVerifier { - bytes32 public TEE_NITRO_IMAGE_HASH; - bytes32 public TEE_TDX_IMAGE_HASH; - - constructor(bytes32 nitroImageHash, bytes32 tdxImageHash) { - TEE_NITRO_IMAGE_HASH = nitroImageHash; - TEE_TDX_IMAGE_HASH = tdxImageHash; - } -} - -contract MockDisputeGameFactoryForVerifier { - mapping(uint32 => address) internal _impls; - - function setImpl(uint32 gameType, address impl) external { - _impls[gameType] = impl; - } - - function gameImpls(GameType gameType) external view returns (IDisputeGame) { - return IDisputeGame(_impls[GameType.unwrap(gameType)]); - } -} - contract TEEVerifierTest is Test { TEEVerifier public verifier; - DevTEEProverRegistry public teeProverRegistry; - MockAnchorStateRegistry public anchorStateRegistry; + address internal teeProverRegistry; uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; uint256 internal constant TDX_SIGNER_PRIVATE_KEY = 0x2234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - address internal nitroSignerAddress; - address internal tdxSignerAddress; bytes32 internal constant NITRO_IMAGE_ID = keccak256("test-nitro-image-id"); bytes32 internal constant TDX_IMAGE_ID = keccak256("test-tdx-image-id"); - uint32 internal constant TEST_GAME_TYPE = 621; address internal immutable PROPOSER = makeAddr("proposer"); function setUp() public { - nitroSignerAddress = vm.addr(NITRO_SIGNER_PRIVATE_KEY); - tdxSignerAddress = vm.addr(TDX_SIGNER_PRIVATE_KEY); - - // Deploy mock factory and verifier - MockAggregateVerifierForVerifier mockVerifier = - new MockAggregateVerifierForVerifier(NITRO_IMAGE_ID, TDX_IMAGE_ID); - MockDisputeGameFactoryForVerifier mockFactory = new MockDisputeGameFactoryForVerifier(); - mockFactory.setImpl(TEST_GAME_TYPE, address(mockVerifier)); - - // Deploy implementation (NitroEnclaveVerifier not needed for dev signer tests) - DevTEEProverRegistry impl = new DevTEEProverRegistry( - INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(mockFactory)) - ); + teeProverRegistry = makeAddr("tee-prover-registry"); + vm.mockCall(teeProverRegistry, abi.encodeWithSignature("isValidProposer(address)", PROPOSER), abi.encode(true)); + _mockSigner(NITRO_SIGNER_PRIVATE_KEY, NITRO_IMAGE_ID, true); + _mockSigner(TDX_SIGNER_PRIVATE_KEY, TDX_IMAGE_ID, true); - address proxyAdmin = makeAddr("proxy-admin"); - Proxy proxy = new Proxy(proxyAdmin); - vm.prank(proxyAdmin); - proxy.upgradeToAndCall( - address(impl), - abi.encodeCall( - TEEProverRegistry.initialize, - (address(this), address(this), new address[](0), GameType.wrap(TEST_GAME_TYPE)) - ) - ); - - teeProverRegistry = DevTEEProverRegistry(address(proxy)); - - // Register one Nitro signer and one TDX signer with the correct image hash. - teeProverRegistry.addDevSigner(nitroSignerAddress, NITRO_IMAGE_ID); - teeProverRegistry.addDevTDXSigner(tdxSignerAddress, TDX_IMAGE_ID); - - // Set the proposer as valid - teeProverRegistry.setProposer(PROPOSER, true); - - anchorStateRegistry = new MockAnchorStateRegistry(); verifier = new TEEVerifier( - TEEProverRegistry(address(teeProverRegistry)), IAnchorStateRegistry(address(anchorStateRegistry)) + TEEProverRegistry(teeProverRegistry), IAnchorStateRegistry(makeAddr("anchor-state-registry")) ); } - function testMockAnchorGameStartsUnset() public view { - assertEq(address(anchorStateRegistry.anchorGame()), address(0)); - } - function testVerifyValidNitroSignature() public view { bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _proofBytes(journal, NITRO_SIGNER_PRIVATE_KEY); - bool result = verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); - assertTrue(result); + bytes memory proofBytes = _buildProof(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); + assertTrue(verifier.verify(proofBytes, NITRO_IMAGE_ID, journal)); } function testVerifyValidTDXSignature() public view { bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _proofBytes(journal, TDX_SIGNER_PRIVATE_KEY); - bool result = verifier.verify(proofBytes, TDX_IMAGE_ID, journal); - assertTrue(result); + bytes memory proofBytes = _buildProof(PROPOSER, _signature(TDX_SIGNER_PRIVATE_KEY, journal)); + assertTrue(verifier.verify(proofBytes, TDX_IMAGE_ID, journal)); } function testVerifyFailsWithInvalidSignature() public { @@ -131,6 +59,9 @@ contract TEEVerifierTest is Test { bytes32 journal = keccak256("test-journal"); bytes memory proofBytes = _buildProof(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); + vm.mockCall( + teeProverRegistry, abi.encodeWithSignature("isValidProposer(address)", address(0)), abi.encode(false) + ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } @@ -143,6 +74,7 @@ contract TEEVerifierTest is Test { bytes memory proofBytes = _buildProof(PROPOSER, _signature(unregisteredKey, journal)); + _mockSigner(unregisteredKey, bytes32(0), false); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } @@ -150,9 +82,8 @@ contract TEEVerifierTest is Test { function testVerifyFailsWithImageIdMismatch() public { bytes32 journal = keccak256("test-journal"); - // Different imageId should fail — Nitro signer was registered with NITRO_IMAGE_ID bytes32 wrongImageId = keccak256("different-image"); - bytes memory proofBytes = _proofBytes(journal, NITRO_SIGNER_PRIVATE_KEY); + bytes memory proofBytes = _buildProof(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); verifier.verify(proofBytes, wrongImageId, journal); } @@ -168,19 +99,18 @@ contract TEEVerifierTest is Test { function testVerifyFailsWithInvalidProofFormat() public { bytes32 journal = keccak256("test-journal"); - // Proof too short (less than proposer + two signatures). bytes memory shortProof = new bytes(50); vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); verifier.verify(shortProof, NITRO_IMAGE_ID, journal); } - function testConstants() public view { - assertEq(address(verifier.TEE_PROVER_REGISTRY()), address(teeProverRegistry)); - } - - function _proofBytes(bytes32 journal, uint256 signerPrivateKey) internal view returns (bytes memory) { - return _buildProof(PROPOSER, _signature(signerPrivateKey, journal)); + function _mockSigner(uint256 privateKey, bytes32 imageId, bool registered) internal { + address signer = vm.addr(privateKey); + vm.mockCall( + teeProverRegistry, abi.encodeWithSignature("isRegisteredSigner(address)", signer), abi.encode(registered) + ); + vm.mockCall(teeProverRegistry, abi.encodeWithSignature("signerImageHash(address)", signer), abi.encode(imageId)); } function _signature(uint256 privateKey, bytes32 journal) internal pure returns (bytes memory) { From 2d62135526754509c07234ae0951dad2ae73328e Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:12:31 -0400 Subject: [PATCH 138/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 71 ++++++++++---------------------- 1 file changed, 21 insertions(+), 50 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index 654a09124..963ef8db9 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.15; import { Test } from "lib/forge-std/src/Test.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; +import { ITEEProverRegistry } from "interfaces/L1/proofs/tee/ITEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; @@ -14,18 +15,15 @@ contract TEEVerifierTest is Test { uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - uint256 internal constant TDX_SIGNER_PRIVATE_KEY = - 0x2234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; bytes32 internal constant NITRO_IMAGE_ID = keccak256("test-nitro-image-id"); - bytes32 internal constant TDX_IMAGE_ID = keccak256("test-tdx-image-id"); + bytes32 internal constant JOURNAL = keccak256("test-journal"); address internal immutable PROPOSER = makeAddr("proposer"); function setUp() public { teeProverRegistry = makeAddr("tee-prover-registry"); - vm.mockCall(teeProverRegistry, abi.encodeWithSignature("isValidProposer(address)", PROPOSER), abi.encode(true)); + vm.mockCall(teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (PROPOSER)), abi.encode(true)); _mockSigner(NITRO_SIGNER_PRIVATE_KEY, NITRO_IMAGE_ID, true); - _mockSigner(TDX_SIGNER_PRIVATE_KEY, TDX_IMAGE_ID, true); verifier = new TEEVerifier( TEEProverRegistry(teeProverRegistry), IAnchorStateRegistry(makeAddr("anchor-state-registry")) @@ -33,92 +31,65 @@ contract TEEVerifierTest is Test { } function testVerifyValidNitroSignature() public view { - bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _buildProof(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); - assertTrue(verifier.verify(proofBytes, NITRO_IMAGE_ID, journal)); - } - - function testVerifyValidTDXSignature() public view { - bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _buildProof(PROPOSER, _signature(TDX_SIGNER_PRIVATE_KEY, journal)); - assertTrue(verifier.verify(proofBytes, TDX_IMAGE_ID, journal)); + bytes memory proofBytes = abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); + assertTrue(verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL)); } function testVerifyFailsWithInvalidSignature() public { - bytes32 journal = keccak256("test-journal"); - bytes memory invalidSignature = new bytes(65); - invalidSignature[64] = bytes1(uint8(27)); // Set v to 27 - - bytes memory proofBytes = _buildProof(PROPOSER, invalidSignature); + bytes memory invalidSignature = abi.encodePacked(bytes32(0), bytes32(0), uint8(27)); + bytes memory proofBytes = abi.encodePacked(PROPOSER, invalidSignature); vm.expectRevert(TEEVerifier.InvalidSignature.selector); - verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithInvalidProposer() public { - bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = _buildProof(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); + bytes memory proofBytes = abi.encodePacked(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); vm.mockCall( - teeProverRegistry, abi.encodeWithSignature("isValidProposer(address)", address(0)), abi.encode(false) + teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (address(0))), abi.encode(false) ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); - verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithUnregisteredSigner() public { uint256 unregisteredKey = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef; address unregisteredSigner = vm.addr(unregisteredKey); - bytes32 journal = keccak256("test-journal"); - - bytes memory proofBytes = _buildProof(PROPOSER, _signature(unregisteredKey, journal)); + bytes memory proofBytes = abi.encodePacked(PROPOSER, _signature(unregisteredKey, JOURNAL)); _mockSigner(unregisteredKey, bytes32(0), false); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); - verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithImageIdMismatch() public { - bytes32 journal = keccak256("test-journal"); - bytes32 wrongImageId = keccak256("different-image"); - bytes memory proofBytes = _buildProof(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); + bytes memory proofBytes = abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); - verifier.verify(proofBytes, wrongImageId, journal); + verifier.verify(proofBytes, wrongImageId, JOURNAL); } function testVerifyFailsWhenTEETypeIsIncludedInProof() public { - bytes32 journal = keccak256("test-journal"); - bytes memory proofBytes = abi.encodePacked(PROPOSER, uint8(3), _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); - - vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); - verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); - } - - function testVerifyFailsWithInvalidProofFormat() public { - bytes32 journal = keccak256("test-journal"); - - bytes memory shortProof = new bytes(50); + bytes memory proofBytes = abi.encodePacked(PROPOSER, uint8(3), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); - verifier.verify(shortProof, NITRO_IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); } function _mockSigner(uint256 privateKey, bytes32 imageId, bool registered) internal { address signer = vm.addr(privateKey); vm.mockCall( - teeProverRegistry, abi.encodeWithSignature("isRegisteredSigner(address)", signer), abi.encode(registered) + teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (signer)), abi.encode(registered) + ); + vm.mockCall( + teeProverRegistry, abi.encodeCall(ITEEProverRegistry.signerImageHash, (signer)), abi.encode(imageId) ); - vm.mockCall(teeProverRegistry, abi.encodeWithSignature("signerImageHash(address)", signer), abi.encode(imageId)); } function _signature(uint256 privateKey, bytes32 journal) internal pure returns (bytes memory) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, journal); return abi.encodePacked(r, s, v); } - - function _buildProof(address proposer, bytes memory signature) internal pure returns (bytes memory) { - return abi.encodePacked(proposer, signature); - } } From d7be5c7d969163ac21aedf2c1251bfb50616e959 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:15:21 -0400 Subject: [PATCH 139/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 35 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index 963ef8db9..10f797ed6 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -10,7 +10,7 @@ import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; contract TEEVerifierTest is Test { - TEEVerifier public verifier; + TEEVerifier internal verifier; address internal teeProverRegistry; uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = @@ -31,51 +31,50 @@ contract TEEVerifierTest is Test { } function testVerifyValidNitroSignature() public view { - bytes memory proofBytes = abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); - assertTrue(verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL)); + assertTrue( + verifier.verify( + abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), NITRO_IMAGE_ID, JOURNAL + ) + ); } function testVerifyFailsWithInvalidSignature() public { - bytes memory invalidSignature = abi.encodePacked(bytes32(0), bytes32(0), uint8(27)); - bytes memory proofBytes = abi.encodePacked(PROPOSER, invalidSignature); - vm.expectRevert(TEEVerifier.InvalidSignature.selector); - verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); + verifier.verify(abi.encodePacked(PROPOSER, bytes32(0), bytes32(0), uint8(27)), NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithInvalidProposer() public { - bytes memory proofBytes = abi.encodePacked(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); - vm.mockCall( teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (address(0))), abi.encode(false) ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); - verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); + verifier.verify( + abi.encodePacked(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), NITRO_IMAGE_ID, JOURNAL + ); } function testVerifyFailsWithUnregisteredSigner() public { uint256 unregisteredKey = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef; address unregisteredSigner = vm.addr(unregisteredKey); - bytes memory proofBytes = abi.encodePacked(PROPOSER, _signature(unregisteredKey, JOURNAL)); - _mockSigner(unregisteredKey, bytes32(0), false); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); - verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); + verifier.verify(abi.encodePacked(PROPOSER, _signature(unregisteredKey, JOURNAL)), NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithImageIdMismatch() public { bytes32 wrongImageId = keccak256("different-image"); - bytes memory proofBytes = abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); - verifier.verify(proofBytes, wrongImageId, JOURNAL); + verifier.verify( + abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), wrongImageId, JOURNAL + ); } function testVerifyFailsWhenTEETypeIsIncludedInProof() public { - bytes memory proofBytes = abi.encodePacked(PROPOSER, uint8(3), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)); - vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); - verifier.verify(proofBytes, NITRO_IMAGE_ID, JOURNAL); + verifier.verify( + abi.encodePacked(PROPOSER, uint8(3), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), NITRO_IMAGE_ID, JOURNAL + ); } function _mockSigner(uint256 privateKey, bytes32 imageId, bool registered) internal { From d8c8fd6f71c1efa02b134b787c554e93895f0a21 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:22:25 -0400 Subject: [PATCH 140/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index 10f797ed6..ffe5032a0 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -11,7 +11,7 @@ import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; contract TEEVerifierTest is Test { TEEVerifier internal verifier; - address internal teeProverRegistry; + address internal immutable teeProverRegistry = makeAddr("tee-prover-registry"); uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; @@ -21,7 +21,6 @@ contract TEEVerifierTest is Test { address internal immutable PROPOSER = makeAddr("proposer"); function setUp() public { - teeProverRegistry = makeAddr("tee-prover-registry"); vm.mockCall(teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (PROPOSER)), abi.encode(true)); _mockSigner(NITRO_SIGNER_PRIVATE_KEY, NITRO_IMAGE_ID, true); From 87cac3a4bce297235cf22433d86808898d59a439 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:33:59 -0400 Subject: [PATCH 141/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index ffe5032a0..f1c81c83d 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -13,8 +13,7 @@ contract TEEVerifierTest is Test { TEEVerifier internal verifier; address internal immutable teeProverRegistry = makeAddr("tee-prover-registry"); - uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = - 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = 1; bytes32 internal constant NITRO_IMAGE_ID = keccak256("test-nitro-image-id"); bytes32 internal constant JOURNAL = keccak256("test-journal"); @@ -24,15 +23,13 @@ contract TEEVerifierTest is Test { vm.mockCall(teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (PROPOSER)), abi.encode(true)); _mockSigner(NITRO_SIGNER_PRIVATE_KEY, NITRO_IMAGE_ID, true); - verifier = new TEEVerifier( - TEEProverRegistry(teeProverRegistry), IAnchorStateRegistry(makeAddr("anchor-state-registry")) - ); + verifier = new TEEVerifier(TEEProverRegistry(teeProverRegistry), IAnchorStateRegistry(address(0))); } function testVerifyValidNitroSignature() public view { assertTrue( verifier.verify( - abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), NITRO_IMAGE_ID, JOURNAL + abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY)), NITRO_IMAGE_ID, JOURNAL ) ); } @@ -47,33 +44,29 @@ contract TEEVerifierTest is Test { teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (address(0))), abi.encode(false) ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); - verifier.verify( - abi.encodePacked(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), NITRO_IMAGE_ID, JOURNAL - ); + verifier.verify(abi.encodePacked(address(0), new bytes(65)), NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithUnregisteredSigner() public { - uint256 unregisteredKey = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef; + uint256 unregisteredKey = 2; address unregisteredSigner = vm.addr(unregisteredKey); _mockSigner(unregisteredKey, bytes32(0), false); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); - verifier.verify(abi.encodePacked(PROPOSER, _signature(unregisteredKey, JOURNAL)), NITRO_IMAGE_ID, JOURNAL); + verifier.verify(abi.encodePacked(PROPOSER, _signature(unregisteredKey)), NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithImageIdMismatch() public { bytes32 wrongImageId = keccak256("different-image"); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); verifier.verify( - abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), wrongImageId, JOURNAL + abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY)), wrongImageId, JOURNAL ); } function testVerifyFailsWhenTEETypeIsIncludedInProof() public { vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); - verifier.verify( - abi.encodePacked(PROPOSER, uint8(3), _signature(NITRO_SIGNER_PRIVATE_KEY, JOURNAL)), NITRO_IMAGE_ID, JOURNAL - ); + verifier.verify(new bytes(86), NITRO_IMAGE_ID, JOURNAL); } function _mockSigner(uint256 privateKey, bytes32 imageId, bool registered) internal { @@ -86,8 +79,8 @@ contract TEEVerifierTest is Test { ); } - function _signature(uint256 privateKey, bytes32 journal) internal pure returns (bytes memory) { - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, journal); + function _signature(uint256 privateKey) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, JOURNAL); return abi.encodePacked(r, s, v); } } From 877698fa87aec5cad3a0b23f0d5cfed7e0597498 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:38:04 -0400 Subject: [PATCH 142/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 42 ++++++++++++++------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index f1c81c83d..d145f503a 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -21,22 +21,24 @@ contract TEEVerifierTest is Test { function setUp() public { vm.mockCall(teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (PROPOSER)), abi.encode(true)); - _mockSigner(NITRO_SIGNER_PRIVATE_KEY, NITRO_IMAGE_ID, true); + address signer = vm.addr(NITRO_SIGNER_PRIVATE_KEY); + vm.mockCall( + teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (signer)), abi.encode(true) + ); + vm.mockCall( + teeProverRegistry, abi.encodeCall(ITEEProverRegistry.signerImageHash, (signer)), abi.encode(NITRO_IMAGE_ID) + ); verifier = new TEEVerifier(TEEProverRegistry(teeProverRegistry), IAnchorStateRegistry(address(0))); } function testVerifyValidNitroSignature() public view { - assertTrue( - verifier.verify( - abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY)), NITRO_IMAGE_ID, JOURNAL - ) - ); + assertTrue(verifier.verify(_proof(NITRO_SIGNER_PRIVATE_KEY), NITRO_IMAGE_ID, JOURNAL)); } function testVerifyFailsWithInvalidSignature() public { vm.expectRevert(TEEVerifier.InvalidSignature.selector); - verifier.verify(abi.encodePacked(PROPOSER, bytes32(0), bytes32(0), uint8(27)), NITRO_IMAGE_ID, JOURNAL); + verifier.verify(abi.encodePacked(PROPOSER, new bytes(65)), NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithInvalidProposer() public { @@ -51,17 +53,19 @@ contract TEEVerifierTest is Test { uint256 unregisteredKey = 2; address unregisteredSigner = vm.addr(unregisteredKey); - _mockSigner(unregisteredKey, bytes32(0), false); + vm.mockCall( + teeProverRegistry, + abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (unregisteredSigner)), + abi.encode(false) + ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); - verifier.verify(abi.encodePacked(PROPOSER, _signature(unregisteredKey)), NITRO_IMAGE_ID, JOURNAL); + verifier.verify(_proof(unregisteredKey), NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithImageIdMismatch() public { bytes32 wrongImageId = keccak256("different-image"); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); - verifier.verify( - abi.encodePacked(PROPOSER, _signature(NITRO_SIGNER_PRIVATE_KEY)), wrongImageId, JOURNAL - ); + verifier.verify(_proof(NITRO_SIGNER_PRIVATE_KEY), wrongImageId, JOURNAL); } function testVerifyFailsWhenTEETypeIsIncludedInProof() public { @@ -69,18 +73,8 @@ contract TEEVerifierTest is Test { verifier.verify(new bytes(86), NITRO_IMAGE_ID, JOURNAL); } - function _mockSigner(uint256 privateKey, bytes32 imageId, bool registered) internal { - address signer = vm.addr(privateKey); - vm.mockCall( - teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (signer)), abi.encode(registered) - ); - vm.mockCall( - teeProverRegistry, abi.encodeCall(ITEEProverRegistry.signerImageHash, (signer)), abi.encode(imageId) - ); - } - - function _signature(uint256 privateKey) internal pure returns (bytes memory) { + function _proof(uint256 privateKey) internal view returns (bytes memory) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, JOURNAL); - return abi.encodePacked(r, s, v); + return abi.encodePacked(PROPOSER, r, s, v); } } From 2fdc7428b6cb31dc81486cf991fc22ca9ddecd89 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:42:51 -0400 Subject: [PATCH 143/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index d145f503a..e4387d298 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -13,27 +13,25 @@ contract TEEVerifierTest is Test { TEEVerifier internal verifier; address internal immutable teeProverRegistry = makeAddr("tee-prover-registry"); - uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = 1; - bytes32 internal constant NITRO_IMAGE_ID = keccak256("test-nitro-image-id"); bytes32 internal constant JOURNAL = keccak256("test-journal"); + address internal constant SIGNER = 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf; address internal immutable PROPOSER = makeAddr("proposer"); function setUp() public { vm.mockCall(teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (PROPOSER)), abi.encode(true)); - address signer = vm.addr(NITRO_SIGNER_PRIVATE_KEY); vm.mockCall( - teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (signer)), abi.encode(true) + teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (SIGNER)), abi.encode(true) ); vm.mockCall( - teeProverRegistry, abi.encodeCall(ITEEProverRegistry.signerImageHash, (signer)), abi.encode(NITRO_IMAGE_ID) + teeProverRegistry, abi.encodeCall(ITEEProverRegistry.signerImageHash, (SIGNER)), abi.encode(NITRO_IMAGE_ID) ); verifier = new TEEVerifier(TEEProverRegistry(teeProverRegistry), IAnchorStateRegistry(address(0))); } function testVerifyValidNitroSignature() public view { - assertTrue(verifier.verify(_proof(NITRO_SIGNER_PRIVATE_KEY), NITRO_IMAGE_ID, JOURNAL)); + assertTrue(verifier.verify(_proof(1), NITRO_IMAGE_ID, JOURNAL)); } function testVerifyFailsWithInvalidSignature() public { @@ -50,8 +48,7 @@ contract TEEVerifierTest is Test { } function testVerifyFailsWithUnregisteredSigner() public { - uint256 unregisteredKey = 2; - address unregisteredSigner = vm.addr(unregisteredKey); + address unregisteredSigner = vm.addr(2); vm.mockCall( teeProverRegistry, @@ -59,13 +56,13 @@ contract TEEVerifierTest is Test { abi.encode(false) ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); - verifier.verify(_proof(unregisteredKey), NITRO_IMAGE_ID, JOURNAL); + verifier.verify(_proof(2), NITRO_IMAGE_ID, JOURNAL); } function testVerifyFailsWithImageIdMismatch() public { bytes32 wrongImageId = keccak256("different-image"); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); - verifier.verify(_proof(NITRO_SIGNER_PRIVATE_KEY), wrongImageId, JOURNAL); + verifier.verify(_proof(1), wrongImageId, JOURNAL); } function testVerifyFailsWhenTEETypeIsIncludedInProof() public { From 4047b325bdf45a9939e2bd94a63ab6a4a9d48727 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:47:02 -0400 Subject: [PATCH 144/146] clean up TEEVerifier tests --- test/L1/proofs/TEEVerifier.t.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index e4387d298..f931df1cb 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -4,7 +4,6 @@ pragma solidity 0.8.15; import { Test } from "lib/forge-std/src/Test.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; -import { ITEEProverRegistry } from "interfaces/L1/proofs/tee/ITEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; @@ -15,16 +14,17 @@ contract TEEVerifierTest is Test { bytes32 internal constant NITRO_IMAGE_ID = keccak256("test-nitro-image-id"); bytes32 internal constant JOURNAL = keccak256("test-journal"); - address internal constant SIGNER = 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf; address internal immutable PROPOSER = makeAddr("proposer"); function setUp() public { - vm.mockCall(teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (PROPOSER)), abi.encode(true)); + vm.mockCall(teeProverRegistry, abi.encodeWithSignature("isValidProposer(address)", PROPOSER), abi.encode(true)); vm.mockCall( - teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (SIGNER)), abi.encode(true) + teeProverRegistry, abi.encodeWithSignature("isRegisteredSigner(address)", vm.addr(1)), abi.encode(true) ); vm.mockCall( - teeProverRegistry, abi.encodeCall(ITEEProverRegistry.signerImageHash, (SIGNER)), abi.encode(NITRO_IMAGE_ID) + teeProverRegistry, + abi.encodeWithSignature("signerImageHash(address)", vm.addr(1)), + abi.encode(NITRO_IMAGE_ID) ); verifier = new TEEVerifier(TEEProverRegistry(teeProverRegistry), IAnchorStateRegistry(address(0))); @@ -41,7 +41,7 @@ contract TEEVerifierTest is Test { function testVerifyFailsWithInvalidProposer() public { vm.mockCall( - teeProverRegistry, abi.encodeCall(ITEEProverRegistry.isValidProposer, (address(0))), abi.encode(false) + teeProverRegistry, abi.encodeWithSignature("isValidProposer(address)", address(0)), abi.encode(false) ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); verifier.verify(abi.encodePacked(address(0), new bytes(65)), NITRO_IMAGE_ID, JOURNAL); @@ -52,7 +52,7 @@ contract TEEVerifierTest is Test { vm.mockCall( teeProverRegistry, - abi.encodeCall(ITEEProverRegistry.isRegisteredSigner, (unregisteredSigner)), + abi.encodeWithSignature("isRegisteredSigner(address)", unregisteredSigner), abi.encode(false) ); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); From f89c19ccc0d8e10c1594fdecf0bfe364a8b430b3 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 11:52:23 -0400 Subject: [PATCH 145/146] clean up MockDevTEEProverRegistry --- test/L1/proofs/TEEProverRegistry.t.sol | 6 +++--- test/mocks/MockDevTEEProverRegistry.sol | 16 ++-------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index eb7f210aa..047c98cae 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -51,7 +51,7 @@ contract TEEProverRegistryTest is Test { function _addDevSigner(address signer) internal { vm.prank(owner); - teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); + teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH, TEEProverRegistry.TEEType.NITRO); } function testInitialization() public view { @@ -111,7 +111,7 @@ contract TEEProverRegistryTest is Test { _addDevSigner(signer); vm.prank(owner); - teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); + teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH, TEEProverRegistry.TEEType.TDX); assertTrue(teeProverRegistry.signerTEEType(signer) == TEEProverRegistry.TEEType.TDX); } @@ -155,7 +155,7 @@ contract TEEProverRegistryTest is Test { function testAddDevSignerFailsIfNotOwner() public { vm.prank(manager); vm.expectRevert(bytes("OwnableManaged: caller is not the owner")); - teeProverRegistry.addDevSigner(makeAddr("dev-signer"), TEST_IMAGE_HASH); + teeProverRegistry.addDevSigner(makeAddr("dev-signer"), TEST_IMAGE_HASH, TEEProverRegistry.TEEType.NITRO); } function testGetRegisteredSignersConsistencyAfterMixedOperations() public { diff --git a/test/mocks/MockDevTEEProverRegistry.sol b/test/mocks/MockDevTEEProverRegistry.sol index 6a38d36e2..a41b8fe93 100644 --- a/test/mocks/MockDevTEEProverRegistry.sol +++ b/test/mocks/MockDevTEEProverRegistry.sol @@ -19,19 +19,7 @@ contract DevTEEProverRegistry is TEEProverRegistry { TEEProverRegistry(nitroVerifier, tdxVerifier, factory) { } - /// @notice Registers a signer and image hash without attestation verification. - /// @dev Only callable by owner. For development/testing use only. - /// @param signer The address of the signer to register. - /// @param imageHash The TEE image hash to associate with this signer. - function addDevSigner(address signer, bytes32 imageHash) external onlyOwner { - _registerSigner(signer, imageHash, TEEType.NITRO); - } - - /// @notice Registers a TDX signer for testing (bypasses attestation verification). - /// @dev Only callable by owner. For development/testing use only. - /// @param signer The address of the signer to register. - /// @param imageHash The TEE image hash to associate with this signer. - function addDevTDXSigner(address signer, bytes32 imageHash) external onlyOwner { - _registerSigner(signer, imageHash, TEEType.TDX); + function addDevSigner(address signer, bytes32 imageHash, TEEType teeType) external onlyOwner { + _registerSigner(signer, imageHash, teeType); } } From d94418fdff9978af681b21712963f4b3887bfbf6 Mon Sep 17 00:00:00 2001 From: Jack Chuma Date: Tue, 30 Jun 2026 14:26:15 -0400 Subject: [PATCH 146/146] one more dev deploy --- deployments/11155111-dev-with-tdx.json | 18 +++++++++--------- scripts/multiproof/README.md | 4 ++-- scripts/multiproof/justfile | 13 ++++++++++++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/deployments/11155111-dev-with-tdx.json b/deployments/11155111-dev-with-tdx.json index 679f83234..b90978d0f 100644 --- a/deployments/11155111-dev-with-tdx.json +++ b/deployments/11155111-dev-with-tdx.json @@ -1,13 +1,13 @@ { - "ASRStartingBlockNumber": 43486445, - "ASRStartingOutputRoot": "0x41c199b47625de52f6dda0ca9e412a72ab331540f802aa7de6fabf7b6f33381e", - "AggregateVerifier": "0x59937F72aa2a018d1e14E7763266F87f7737dF1f", - "AnchorStateRegistry": "0x465bf36F5a3832b3bD077552e7Ef3C54b9885718", - "DelayedWETH": "0x3c094F0d46A37054aD1620Ff57080835224f46b9", - "DisputeGameFactory": "0xB22CD1c3c0532ce0D0275F05CEBdFf99DBB7A348", + "ASRStartingBlockNumber": 43536599, + "ASRStartingOutputRoot": "0x5bc62bbffa839dff9e9a13cb0ec240be19e76fc2c0e44082402697c728ba6150", + "AggregateVerifier": "0x1c03e8Ce7184d2d3A075f93A3637c6a3be866704", + "AnchorStateRegistry": "0x6C0dF1080cbd2856A4E2d0708bE23F3E9cf44A6A", + "DelayedWETH": "0x4bBCFa9bE58620B4fb1989A774917D57E2519985", + "DisputeGameFactory": "0xa92fe37ED3DFEE686c305Ab7cc2c445A1bCf300F", "NitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", "TDXRegistrationManager": "0x44E999A5859c2D12378a349882fAe5805DCE71b9", "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", - "TEEProverRegistry": "0x98e565A0e98fB33f7E605221dF647C9C43Ef98C8", - "TEEVerifier": "0xdd36bB9254c65fE8D616E41DECC8D0Bc1B79efcC" -} \ No newline at end of file + "TEEProverRegistry": "0x6123Ab4Ab4f860BA4810f7f7Cb0be4a111D8C490", + "TEEVerifier": "0x828e90BA966Abcd869e39D69EC4d89F521547E5A" +} diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index f497d9123..3b1de9679 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -26,10 +26,10 @@ Register each signer on the deployed `DevTEEProverRegistry`. The registry stores signer image hashes; `AggregateVerifier` enforces the expected Nitro and TDX image hashes. ```bash -cast send "$TEE_PROVER_REGISTRY" "addDevSigner(address,bytes32)" "$NITRO_SIGNER" "$NITRO_IMAGE_HASH" \ +cast send "$TEE_PROVER_REGISTRY" "addDevSigner(address,bytes32,uint8)" "$NITRO_SIGNER" "$NITRO_IMAGE_HASH" 1 \ --rpc-url "$L1_RPC_URL" --ledger --mnemonic-derivation-path "$LEDGER_PATH" -cast send "$TEE_PROVER_REGISTRY" "addDevTDXSigner(address,bytes32)" "$TDX_SIGNER" "$TDX_IMAGE_HASH" \ +cast send "$TEE_PROVER_REGISTRY" "addDevSigner(address,bytes32,uint8)" "$TDX_SIGNER" "$TDX_IMAGE_HASH" 2 \ --rpc-url "$L1_RPC_URL" --ledger --mnemonic-derivation-path "$LEDGER_PATH" ``` diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile index 723eacb12..9fc6c4ba1 100644 --- a/scripts/multiproof/justfile +++ b/scripts/multiproof/justfile @@ -3,7 +3,7 @@ set working-directory := "../.." set shell := ["bash", "-euo", "pipefail", "-c"] forge_account := "testnet-admin" -owner := `cast wallet address --account "{{forge_account}}"` +owner := `cast wallet address --account testnet-admin` broadcast_args := "--rpc-url " + quote(l1_rpc_url) + " --broadcast --account " + quote(forge_account) + " --sender " + quote(owner) l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-reth-proofs-k8s-donotuse.cbhq.net:8545") @@ -84,3 +84,14 @@ deploy-tdx-stack nitro_verifier tdx_verifier asr_anchor_block="": "$asr_anchor_output_root" \ "$asr_anchor_block" \ {{ broadcast_args }} + +# Deploy the TDX multiproof dev/test stack on Sepolia. +[script] +deploy-sepolia-tdx-dev asr_anchor_block="": + nitro_verifier="$(jq -er '.nitroEnclaveVerifier' deploy-config/zeronet-tdx.json)" + tdx_verifier="$(jq -er '.tdxVerifier' deploy-config/zeronet-tdx.json)" + + just --justfile scripts/multiproof/justfile deploy-tdx-stack \ + "$nitro_verifier" \ + "$tdx_verifier" \ + "{{ asr_anchor_block }}"