diff --git a/deploy-config/local.json b/deploy-config/local.json index a67283831..eec8c2b1f 100644 --- a/deploy-config/local.json +++ b/deploy-config/local.json @@ -23,7 +23,6 @@ "multiproofGenesisBlockNumber": 0, "multiproofIntermediateBlockInterval": 10, "multiproofGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", - "nitroEnclaveVerifier": "0x0000000000000000000000000000000000000000", "nitroValidator": "0x0000000000000000000000000000000000000000", "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", "operatorFeeVaultRecipient": "0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec", diff --git a/deploy-config/mainnet.json b/deploy-config/mainnet.json index 1a673ca4f..05202ae28 100644 --- a/deploy-config/mainnet.json +++ b/deploy-config/mainnet.json @@ -25,7 +25,6 @@ "multiproofGenesisBlockNumber": 0, "multiproofGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", "multiproofIntermediateBlockInterval": 30, - "nitroEnclaveVerifier": "0x0000000000000000000000000000000000000000", "nitroValidator": "0x0000000000000000000000000000000000000000", "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", "operatorFeeVaultRecipient": "0xa3d596EAfaB6B13Ab18D40FaE1A962700C84ADEa", diff --git a/deploy-config/sepolia.json b/deploy-config/sepolia.json index 49b9ef6e4..28396b3f7 100644 --- a/deploy-config/sepolia.json +++ b/deploy-config/sepolia.json @@ -25,7 +25,6 @@ "multiproofGenesisBlockNumber": 37223829, "multiproofGenesisOutputRoot": "0xbc273d5876d1858ecd5aaf4ce4eaf16c73f0187ca4271b774ed5da7d2254ba79", "multiproofIntermediateBlockInterval": 30, - "nitroEnclaveVerifier": "0x77461a6434fFE3435206B19658F33274f3104e07", "nitroValidator": "0x0000000000000000000000000000000000000000", "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", "operatorFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", diff --git a/foundry.toml b/foundry.toml index 46b285523..bcb96efd5 100644 --- a/foundry.toml +++ b/foundry.toml @@ -43,7 +43,6 @@ evm_version = 'cancun' remappings = [ '@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts', - 'openzeppelin/=lib/risc0-ethereum/lib/openzeppelin-contracts/', ] fs_permissions = [ diff --git a/interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol b/interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol deleted file mode 100644 index a17b715f9..000000000 --- a/interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol +++ /dev/null @@ -1,349 +0,0 @@ -//SPDX-License-Identifier: Apache2.0 -pragma solidity ^0.8.0; - -/// @dev Custom version of Automata's NitroEnclaveVerifier contract at -/// https://github.com/automata-network/aws-nitro-enclave-attestation/tree/26c90565cb009e6539643a0956f9502a12ade672 -/// -/// Differences from the upstream Automata contract: -/// - Removes verification-with-explicit-program-ID and Pico logic -/// - Errors and events moved to the implementation contract -/// - Adds new admin functions - -/// @dev Enumeration of supported zero-knowledge proof coprocessor types -/// Used to specify which proving system to use for attestation verification -enum ZkCoProcessorType { - Unknown, - // RISC Zero zkVM proving system - RiscZero, - // Succinct SP1 proving system - Succinct -} - -/// @dev Configuration parameters for a specific zero-knowledge coprocessor -/// Contains all necessary identifiers and addresses for ZK proof verification -struct ZkCoProcessorConfig { - // Latest program ID for single attestation verification - bytes32 verifierId; - // Latest program ID for batch/aggregated verification - bytes32 aggregatorId; - // Default ZK verifier contract address (can be overridden per route) - address zkVerifier; -} - -/// @dev Input structure for attestation report verification -/// Contains the raw attestation data and trusted certificate chain length -struct VerifierInput { - // Number of trusted certificates in the chain - uint8 trustedCertsPrefixLen; - // Raw AWS Nitro Enclave attestation report (COSE_Sign1 format) - bytes attestationReport; -} - -/// @dev Output structure containing verified attestation data and metadata -/// This represents the journal/output from zero-knowledge proof verification -struct VerifierJournal { - // Overall verification result status - VerificationResult result; - // Number of certificates that were trusted during verification - uint8 trustedCertsPrefixLen; - // Attestation timestamp (Unix timestamp in milliseconds) - uint64 timestamp; - // Array of certificate hashes in the chain (root to leaf) - bytes32[] certs; - // Certificate notAfter timestamps in seconds (one per cert, matching certs[] order) - uint64[] certExpiries; - // User-defined data embedded in the attestation - bytes userData; - // Cryptographic nonce used for replay protection - bytes nonce; - // Public key extracted from the attestation - bytes publicKey; - // Platform Configuration Registers (integrity measurements) - Pcr[] pcrs; - // AWS Nitro Enclave module identifier - string moduleId; -} - -/// @dev Public value (journal) structure for batch verification operations -/// Contains the aggregated results of multiple attestation verifications -struct BatchVerifierJournal { - // Verification key that was used for batch verification - bytes32 verifierVk; - // Array of verified attestation results - VerifierJournal[] outputs; -} - -/// @dev 48-byte data structure for storing PCR values -/// Split into two parts due to Solidity's 32-byte word limitation -struct Bytes48 { - bytes32 first; - bytes16 second; -} - -/// @dev Platform Configuration Register (PCR) entry -/// PCRs contain cryptographic measurements of the enclave's runtime state -struct Pcr { - // PCR index number (0-23 for AWS Nitro Enclaves) - uint64 index; - // 48-byte PCR measurement value (SHA-384 hash) - Bytes48 value; -} - -/// @dev Enumeration of possible attestation verification results -/// Indicates the outcome of the verification process -/// -/// Note: Unknown is intentionally placed at index 0 so that uninitialized enum -/// variables default to a failure state rather than Success (fail-closed). -enum VerificationResult { - // Default/uninitialized value — treated as a verification failure - Unknown, - // Attestation successfully verified - Success, - // Root certificate is not in the trusted set - RootCertNotTrusted, - // One or more intermediate certificates are not trusted - IntermediateCertsNotTrusted, - // Attestation timestamp is outside acceptable range - InvalidTimestamp -} - -/// @title INitroEnclaveVerifier -/// @dev Interface for AWS Nitro Enclave attestation verification using zero-knowledge proofs -/// -/// This interface defines the contract for verifying AWS Nitro Enclave attestation reports -/// onchain using zero-knowledge proof systems (RISC Zero or Succinct SP1). The verifier -/// validates the cryptographic integrity of attestation reports while maintaining privacy -/// and reducing gas costs through ZK proofs. -/// -/// Key features: -/// - Single and batch attestation verification -/// - Support for multiple ZK proving systems -/// - Route-based verifier configuration -/// - Certificate chain management and revocation -/// - Timestamp validation with configurable tolerance -/// - Platform Configuration Register (PCR) verification -interface INitroEnclaveVerifier { - // ============ Query Functions ============ - - /// @dev Returns the maximum allowed time difference for attestation timestamp validation - /// @return Maximum time difference in seconds between attestation time and current block time - function maxTimeDiff() external view returns (uint64); - - /// @dev Returns the hash of the trusted root certificate - /// @return Hash of the AWS Nitro Enclave root certificate - function rootCert() external view returns (bytes32); - - /// @dev Returns the address of the proof submitter - /// @return Address of the proof submitter - function proofSubmitter() external view returns (address); - - /// @dev Returns the address authorized to revoke intermediate certificates - /// @return Address of the revoker (address(0) if disabled) - function revoker() external view returns (address); - - /// @dev Returns whether the given intermediate certificate hash has been revoked. - /// @param _certHash Hash of the certificate - /// @return `true` if the certificate is currently marked as revoked - /// - /// The revocation sentinel is persistent across `_cacheNewCert` overwrites and - /// blocks both verification (via `_verifyJournal`) and the offchain - /// `checkTrustedIntermediateCerts` helper from re-trusting the hash. Re-trust - /// requires an explicit `unrevokeCert` call. - function revokedCerts(bytes32 _certHash) external view returns (bool); - - /// @dev Returns the cached `notAfter` timestamp (seconds) for an intermediate certificate. - /// @param _certHash Hash of the certificate - /// @return Cached expiry timestamp; `0` indicates the certificate is not currently - /// cached (either never seen, expired-and-evicted, or revoked). - function trustedIntermediateCerts(bytes32 _certHash) external view returns (uint64); - - /// @dev Retrieves the configuration for a specific coprocessor - /// @param _zkCoProcessor Type of ZK coprocessor (RiscZero or Succinct) - /// @return ZkCoProcessorConfig Configuration parameters including program IDs and verifier address - function getZkConfig(ZkCoProcessorType _zkCoProcessor) external view returns (ZkCoProcessorConfig memory); - - /// @dev Gets the verifier address for a specific route - /// @param _zkCoProcessor Type of ZK coprocessor - /// @param _selector Proof selector - /// @return Verifier address (route-specific or default fallback) - /// - /// Note: Reverts if the route is frozen - function getZkVerifier(ZkCoProcessorType _zkCoProcessor, bytes4 _selector) external view returns (address); - - /// @dev Returns the verifierProofId for a given ZkCoProcessorType - /// @param _zkCoProcessor Type of ZK coprocessor - /// @return The corresponding verifierProofId - function getVerifierProofId(ZkCoProcessorType _zkCoProcessor) external view returns (bytes32); - - /// @dev Checks how many certificates in each report are trusted - /// @param _report_certs Array of certificate chains, each containing certificate hashes - /// @return Array indicating the number of trusted certificates in each chain - /// - /// For each certificate chain: - /// - Validates that the first certificate matches the root certificate - /// - Counts consecutive trusted certificates starting from the root - /// - Returns the count of trusted certificates for each chain - function checkTrustedIntermediateCerts(bytes32[][] calldata _report_certs) external view returns (uint8[] memory); - - // ============ Admin Functions ============ - - /// @dev Sets the trusted root certificate hash - /// @param _rootCert Hash of the new root certificate - /// - /// Requirements: - /// - Only callable by contract owner - function setRootCert(bytes32 _rootCert) external; - - /// @dev Updates the maximum allowed time difference for attestation timestamp validation - /// @param _maxTimeDiff New maximum time difference in seconds - /// - /// Requirements: - /// - Only callable by contract owner - /// - Must be greater than zero - function setMaxTimeDiff(uint64 _maxTimeDiff) external; - - /// @dev Sets the proof submitter address - /// @param _proofSubmitter The address of the proof submitter - /// - /// Requirements: - /// - Only callable by contract owner - /// - Address must not be zero - function setProofSubmitter(address _proofSubmitter) external; - - /// @dev Updates the revoker address - /// @param _newRevoker New revoker address (can be address(0) to disable the revoker role) - /// - /// Requirements: - /// - Only callable by contract owner - function setRevoker(address _newRevoker) external; - - /// @dev Configures the zero-knowledge verification parameters for a specific coprocessor - /// @param _zkCoProcessor Type of ZK coprocessor (RiscZero or Succinct) - /// @param _config Configuration parameters including program IDs and verifier address - /// @param _verifierProofId The verifierProofId corresponding to the verifierId in config - /// - /// Requirements: - /// - Only callable by contract owner - /// - Must specify valid coprocessor type and configuration - function setZkConfiguration( - ZkCoProcessorType _zkCoProcessor, - ZkCoProcessorConfig memory _config, - bytes32 _verifierProofId - ) - external; - - /// @dev Revokes an intermediate certificate, whether or not it has been cached as trusted. - /// @param _certHash Hash of the certificate to revoke - /// - /// Requirements: - /// - Only callable by contract owner or revoker - /// - /// In addition to clearing any cached entry, this flips a persistent revocation - /// sentinel that survives later cache writes. Certificates never seen onchain can - /// be revoked preemptively. Subsequent verifications whose chain traverses the - /// revoked hash are rejected regardless of the journal-supplied - /// `trustedCertsPrefixLen`. Re-trust requires an explicit `unrevokeCert` call. - function revokeCert(bytes32 _certHash) external; - - /// @dev Explicitly re-trusts a previously revoked intermediate certificate. - /// @param _certHash Hash of the certificate to un-revoke - /// - /// Requirements: - /// - Only callable by contract owner - /// - Certificate must currently be marked as revoked - /// - /// Clears the persistent revocation sentinel. The cached expiry is not - /// restored here; the next successful verification whose chain traverses - /// `_certHash` will re-cache it via `_cacheNewCert` with the journal-supplied - /// `notAfter` timestamp. - function unrevokeCert(bytes32 _certHash) external; - - /// @dev Updates the verifier program ID, adding the new version to the supported set - /// @param _zkCoProcessor Type of ZK coprocessor - /// @param _newVerifierId New verifier program ID to set as latest - /// @param _newVerifierProofId New verifier proof ID (used in batch verification) - /// - /// Requirements: - /// - Only callable by contract owner - /// - New ID must be different from current latest - function updateVerifierId( - ZkCoProcessorType _zkCoProcessor, - bytes32 _newVerifierId, - bytes32 _newVerifierProofId - ) - external; - - /// @dev Updates the aggregator program ID, adding the new version to the supported set - /// @param _zkCoProcessor Type of ZK coprocessor - /// @param _newAggregatorId New aggregator program ID to set as latest - /// - /// Requirements: - /// - Only callable by contract owner - /// - New ID must be different from current latest - function updateAggregatorId(ZkCoProcessorType _zkCoProcessor, bytes32 _newAggregatorId) external; - - /// @dev Adds a route-specific verifier override - /// @param _zkCoProcessor Type of ZK coprocessor - /// @param _selector Proof selector (first 4 bytes of proof data) - /// @param _verifier Address of the verifier contract for this route - /// - /// Requirements: - /// - Only callable by contract owner - /// - Route must not be frozen - /// - Verifier address must not be zero - function addVerifyRoute(ZkCoProcessorType _zkCoProcessor, bytes4 _selector, address _verifier) external; - - /// @dev Permanently freezes a verification route - /// @param _zkCoProcessor Type of ZK coprocessor - /// @param _selector Proof selector to freeze - /// - /// Requirements: - /// - Only callable by contract owner - /// - Route must not already be frozen - /// - /// WARNING: This action is IRREVERSIBLE - function freezeVerifyRoute(ZkCoProcessorType _zkCoProcessor, bytes4 _selector) external; - - // ============ Verification Functions ============ - - /// @dev Verifies a single attestation report using zero-knowledge proof - /// @param output Encoded VerifierJournal containing the verification result - /// @param zkCoprocessor Type of ZK coprocessor used to generate the proof - /// @param proofBytes Zero-knowledge proof data for the attestation - /// @return VerifierJournal containing the verification result and extracted data - /// - /// This function: - /// 1. Verifies the ZK proof using the specified coprocessor - /// 2. Decodes the verification result - /// 3. Validates the certificate chain against trusted certificates - /// 4. Checks timestamp validity within the allowed time difference - /// 5. Caches newly discovered trusted certificates - /// 6. Returns the complete verification result - function verify( - bytes calldata output, - ZkCoProcessorType zkCoprocessor, - bytes calldata proofBytes - ) - external - returns (VerifierJournal memory); - - /// @dev Verifies multiple attestation reports in a single batch operation - /// @param output Encoded BatchVerifierJournal containing aggregated verification results - /// @param zkCoprocessor Type of ZK coprocessor used to generate the proof - /// @param proofBytes Zero-knowledge proof data for batch verification - /// @return Array of VerifierJournal results, one for each attestation in the batch - /// - /// This function: - /// 1. Verifies the ZK proof using the specified coprocessor - /// 2. Decodes the batch verification results - /// 3. Validates each attestation's certificate chain and timestamp - /// 4. Caches newly discovered trusted certificates - /// 5. Returns the verification results for all attestations - function batchVerify( - bytes calldata output, - ZkCoProcessorType zkCoprocessor, - bytes calldata proofBytes - ) - external - returns (VerifierJournal[] memory); -} diff --git a/justfile b/justfile index 8b82d8a42..006ee4831 100644 --- a/justfile +++ b/justfile @@ -15,8 +15,7 @@ deps: clean-lib github.com/OpenZeppelin/openzeppelin-contracts-upgradeable@0a2cb9a445c365870ed7a8ab461b12acf3e27d63 \ github.com/transmissions11/solmate@8f9b23f8838670afda0fd8983f2c41e8037ae6bc \ github.com/Vectorized/solady@502cc1ea718e6fa73b380635ee0868b0740595f0 \ - github.com/base/nitro-validator@0ea0d12366b4fa44f9e07e4755f2ad36561cb674 \ - github.com/risc0/risc0-ethereum@a78ac4a52fe9cfa14120c3b496430f0d42e1d8d3 + github.com/base/nitro-validator@0ea0d12366b4fa44f9e07e4755f2ad36561cb674 # Cleans dependency installs. clean-lib: diff --git a/scripts/deploy/DeployConfig.s.sol b/scripts/deploy/DeployConfig.s.sol index 3c8bf5a70..c767026d1 100644 --- a/scripts/deploy/DeployConfig.s.sol +++ b/scripts/deploy/DeployConfig.s.sol @@ -14,7 +14,6 @@ contract DeployConfig is Script { address public batchSenderAddress; address public finalSystemOwner; address public l1FeeVaultRecipient; - address public nitroEnclaveVerifier; address public nitroValidator; address public operatorFeeVaultRecipient; address public p2pSequencerAddress; @@ -71,7 +70,6 @@ contract DeployConfig is Script { batchSenderAddress = _json.readAddress("$.batchSenderAddress"); finalSystemOwner = _json.readAddress("$.finalSystemOwner"); l1FeeVaultRecipient = _json.readAddress("$.l1FeeVaultRecipient"); - nitroEnclaveVerifier = _json.readAddress("$.nitroEnclaveVerifier"); nitroValidator = _json.readAddress("$.nitroValidator"); operatorFeeVaultRecipient = _json.readAddress("$.operatorFeeVaultRecipient"); p2pSequencerAddress = _json.readAddress("$.p2pSequencerAddress"); diff --git a/scripts/deploy/SystemDeploy.s.sol b/scripts/deploy/SystemDeploy.s.sol index 8fa4d4791..31aa2f29e 100644 --- a/scripts/deploy/SystemDeploy.s.sol +++ b/scripts/deploy/SystemDeploy.s.sol @@ -27,7 +27,6 @@ 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 { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { INitroValidator } from "interfaces/L1/proofs/tee/INitroValidator.sol"; import { ITEEProverRegistry } from "interfaces/L1/proofs/tee/ITEEProverRegistry.sol"; import { IOptimismMintableERC20Factory } from "interfaces/universal/IOptimismMintableERC20Factory.sol"; @@ -78,7 +77,6 @@ contract SystemDeploy is Script { bytes32 zkAggregationHash; bytes32 multiproofConfigHash; uint256 multiproofGameType; - address nitroEnclaveVerifier; address nitroValidator; AggregateVerifier.ScheduleConfig scheduleConfig; uint256 multiproofBlockInterval; @@ -267,7 +265,6 @@ contract SystemDeploy is Script { zkAggregationHash: cfg.zkAggregationHash(), multiproofConfigHash: cfg.multiproofConfigHash(), multiproofGameType: cfg.multiproofGameType(), - nitroEnclaveVerifier: cfg.nitroEnclaveVerifier(), nitroValidator: cfg.nitroValidator(), scheduleConfig: _configuredScheduleConfig(), multiproofBlockInterval: cfg.multiproofBlockInterval(), @@ -565,7 +562,6 @@ contract SystemDeploy is Script { output_.teeProverRegistryProxy = ITEEProverRegistry(address(multiproof.teeProverRegistryProxy)); output_.teeVerifier = multiproof.teeVerifier; output_.zkVerifier = multiproof.zkVerifier; - output_.nitroEnclaveVerifier = INitroEnclaveVerifier(_implementationsInput.nitroEnclaveVerifier); output_.sp1Verifier = _implementationsInput.sp1Verifier; output_.nitroValidator = INitroValidator(_implementationsInput.nitroValidator); } @@ -1114,10 +1110,8 @@ contract SystemDeploy is Script { require(_input.multiproofGameType != 0, "SystemDeploy: multiproofGameType not set"); require(_input.scheduleConfig.blockTime != 0, "SystemDeploy: L2 block time not set"); require(_input.scheduleConfig.genesisTimestamp != 0, "SystemDeploy: L2 genesis timestamp not set"); - require(_input.nitroEnclaveVerifier != address(0), "SystemDeploy: nitroEnclaveVerifier not set"); require(_input.nitroValidator != address(0), "SystemDeploy: nitroValidator not set"); require(address(_input.sp1Verifier) != address(0), "SystemDeploy: sp1Verifier not set"); - DeployUtils.assertValidContractAddress(_input.nitroEnclaveVerifier); DeployUtils.assertValidContractAddress(_input.nitroValidator); DeployUtils.assertValidContractAddress(address(_input.sp1Verifier)); require(_input.multiproofBlockInterval != 0, "SystemDeploy: multiproof block interval not set"); @@ -1179,7 +1173,6 @@ contract SystemDeploy is Script { artifacts.save("OptimismPortal2Proxy", address(chain.optimismPortalProxy)); _saveIfSet("TEEProverRegistryProxy", address(chain.teeProverRegistryProxy)); _saveIfSet("TEEProverRegistry", address(chain.teeProverRegistryProxy)); - _saveIfSet("NitroEnclaveVerifier", address(chain.nitroEnclaveVerifier)); _saveIfSet("NitroValidator", address(chain.nitroValidator)); _saveIfSet("SP1Verifier", address(chain.sp1Verifier)); } diff --git a/scripts/libraries/Types.sol b/scripts/libraries/Types.sol index aea2f0374..afab34bfd 100644 --- a/scripts/libraries/Types.sol +++ b/scripts/libraries/Types.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { INitroValidator } from "interfaces/L1/proofs/tee/INitroValidator.sol"; import { ITEEProverRegistry } from "interfaces/L1/proofs/tee/ITEEProverRegistry.sol"; import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; @@ -61,7 +60,6 @@ library Types { ITEEProverRegistry teeProverRegistryProxy; IVerifier teeVerifier; IVerifier zkVerifier; - INitroEnclaveVerifier nitroEnclaveVerifier; ISP1Verifier sp1Verifier; INitroValidator nitroValidator; } diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index ed7a777a3..1ef4fe01f 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -21,7 +21,6 @@ contract DeployDevWithNitro is DeployDevBase { uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 30; uint256 public constant INIT_BOND = 0.00001 ether; - address public nitroEnclaveVerifierAddr; address public nitroValidatorAddr; function _blockInterval() internal pure override returns (uint256) { @@ -42,7 +41,6 @@ contract DeployDevWithNitro is DeployDevBase { function _preflight() internal override { super._preflight(); - nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); nitroValidatorAddr = cfg.nitroValidator(); require( nitroValidatorAddr != address(0), @@ -59,7 +57,6 @@ contract DeployDevWithNitro is DeployDevBase { } function _serializeExtra(string memory key) internal override { - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); vm.serializeAddress(key, "NitroValidator", nitroValidatorAddr); } @@ -71,7 +68,6 @@ contract DeployDevWithNitro is DeployDevBase { console.log("TEE Challenger:", cfg.teeChallenger()); console.log("Game Type:", cfg.multiproofGameType()); console.log("NitroValidator:", nitroValidatorAddr); - console.log("NitroEnclaveVerifier (rollback):", nitroEnclaveVerifierAddr); console.log(""); console.log("NOTE: Using REAL TEEProverRegistry - hinted attestation REQUIRED."); } @@ -82,7 +78,6 @@ contract DeployDevWithNitro is DeployDevBase { console.log("========================================"); console.log("\nTEE Contracts:"); console.log(" NitroValidator:", nitroValidatorAddr); - console.log(" NitroEnclaveVerifier (rollback):", nitroEnclaveVerifierAddr); console.log(" TEEProverRegistry:", teeProverRegistryProxy); console.log(" TEEVerifier:", teeVerifier); console.log("\nInfrastructure:"); diff --git a/scripts/multiproof/DeployRiscZeroStack.s.sol b/scripts/multiproof/DeployRiscZeroStack.s.sol deleted file mode 100644 index 46e26b528..000000000 --- a/scripts/multiproof/DeployRiscZeroStack.s.sol +++ /dev/null @@ -1,170 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -/** - * @title DeployRiscZeroStack - * @notice Deploys the legacy rollback RiscZeroSetVerifier and NitroEnclaveVerifier that work with - * an existing RISC Zero verifier router (e.g. the Boundless-deployed - * Router on Sepolia). - * - * This script is separated from the main deployment scripts because the - * RiscZeroSetVerifier and NitroEnclaveVerifier contracts (via ISP1Verifier) - * require Solidity ^0.8.20, while the main deployment scripts and their - * transitive dependencies are pinned to =0.8.15. - * - * The RISC Zero Groth16 verifier and Router are NOT deployed by this script. - * They are assumed to already exist on-chain (typically deployed by the - * Boundless marketplace). Pass the router address as a parameter. - * - * A local RiscZeroSetVerifier is deployed that delegates root seal verification - * to the existing Router. This is necessary because the Boundless-deployed - * SetVerifier may have an outdated inner Groth16 verifier. By routing through - * the Router, root seals are dispatched to the correct Groth16 verifier - * regardless of version. - * - * ───────────────────────────────────────────────────────────────────────────────── - * USAGE - * ───────────────────────────────────────────────────────────────────────────────── - * - * forge script scripts/multiproof/DeployRiscZeroStack.s.sol:DeployRiscZeroStack \ - * --sig "run(address,address,bytes32,bytes32,bytes32)" \ - * \ - * \ - * --broadcast --rpc-url --private-key - * - * NOTE: The deployer MUST be the same address as OWNER, since the script calls - * addVerifyRoute() on the NitroEnclaveVerifier (onlyOwner). - * - * Outputs: - * - RiscZeroSetVerifier (delegates to existing Router for root verification) - * - NitroEnclaveVerifier with route wired to the local SetVerifier - * - * ROLLBACK PREPARATION: - * The hinted TEEProverRegistry does not call this verifier. Before rolling back to the - * legacy Registry implementation, point proofSubmitter at the existing Registry proxy: - * - * cast send "setProofSubmitter(address)" \ - * --rpc-url --private-key - * - * ───────────────────────────────────────────────────────────────────────────────── - */ - -import { Script } from "lib/forge-std/src/Script.sol"; -import { console2 as console } from "lib/forge-std/src/console2.sol"; - -import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; -import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; - -import { - NitroEnclaveVerifier, - ZkCoProcessorType, - ZkCoProcessorConfig -} from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; - -/// @title DeployRiscZeroStack -/// @notice Deploys RiscZeroSetVerifier + NitroEnclaveVerifier using an existing Router. -contract DeployRiscZeroStack is Script { - /// @notice Maximum attestation age accepted by the NitroEnclaveVerifier (1 hour). - uint64 public constant NITRO_MAX_TIME_DIFF = 3600; - - address public setVerifier; - address public nitroEnclaveVerifier; - - /// @param owner Owner for the NitroEnclaveVerifier (must equal msg.sender). - /// @param risc0VerifierRouter Address of an existing RISC Zero verifier router - /// (e.g. Boundless-deployed Router). - /// @param setBuilderImageId RISC Zero set builder image ID (from Boundless deployment). - /// @param nitroRootCert SHA-256 hash of the AWS Nitro root certificate. - /// @param nitroVerifierId RISC Zero image ID of the attestation verifier guest. - function run( - address owner, - address risc0VerifierRouter, - bytes32 setBuilderImageId, - bytes32 nitroRootCert, - bytes32 nitroVerifierId - ) - public - { - 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); - string memory selectorStr = vm.toString(setVerifierSelector); - - console.log("=== Deploying RiscZeroSetVerifier + 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:", selectorStr); - console.log("Nitro Root Cert:", vm.toString(nitroRootCert)); - console.log("Nitro Verifier ID:", vm.toString(nitroVerifierId)); - console.log(""); - console.log("NOTE: This is the legacy rollback verifier stack."); - console.log(" Set proofSubmitter to the Registry proxy before a legacy rollback."); - console.log(""); - - vm.startBroadcast(); - - // Deploy a SetVerifier whose inner VERIFIER is the Router so root seals dispatch to the - // correct Groth16 verifier regardless of version, avoiding selector mismatches when the - // Boundless provers upgrade to newer Groth16 ControlIDs. - setVerifier = address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); - console.log("RiscZeroSetVerifier:", setVerifier); - - ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ - verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter - }); - - // Empty trusted certs/expiries: certs auto-cache on first valid proof. - bytes32[] memory trustedCerts = new bytes32[](0); - uint64[] memory trustedCertExpiries = new uint64[](0); - - // proofSubmitter = owner (placeholder, update via setProofSubmitter after TEEProverRegistry deploy). - // revoker = address(0) (set via setRevoker after deployment). - NitroEnclaveVerifier nev = new NitroEnclaveVerifier( - owner, - NITRO_MAX_TIME_DIFF, - trustedCerts, - trustedCertExpiries, - nitroRootCert, - owner, - address(0), - ZkCoProcessorType.RiscZero, - zkConfig, - bytes32(0) - ); - nitroEnclaveVerifier = address(nev); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); - - // Boundless set-inclusion proofs carry the SetVerifier selector as their first 4 bytes; - // route that selector to our local SetVerifier so root seals go through the Router. - nev.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, setVerifier); - console.log(" Route added: selector", selectorStr, "->", setVerifier); - - vm.stopBroadcast(); - - console.log(""); - console.log("========================================"); - console.log(" RISC ZERO STACK + NITRO DEPLOYED"); - console.log("========================================"); - console.log("RiscZeroSetVerifier:", setVerifier); - console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); - console.log("RISC Zero Router (external):", risc0VerifierRouter); - console.log(""); - console.log(">>> Retain NitroEnclaveVerifier for rollback:", nitroEnclaveVerifier); - console.log(">>> The active hinted Registry uses NitroValidator instead <<<"); - console.log("========================================"); - - string memory key = "deployment"; - vm.serializeAddress(key, "RiscZeroSetVerifier", setVerifier); - vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifier); - string memory json = vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); - - string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-risc0-stack.json"); - vm.writeJson(json, outPath); - console.log("Deployment saved to:", outPath); - } -} diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index dcae83a2e..ac95e2c2f 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -129,8 +129,7 @@ forge script scripts/multiproof/DeployNitroValidatorStack.s.sol:DeployNitroValid ``` Copy the `NitroValidator` address from `deployments/-nitro-validator.json` into the -deploy config's `nitroValidator` field. Keep `nitroEnclaveVerifier` configured separately as a -rollback dependency; the hinted Registry does not call it or update its `proofSubmitter`. +deploy config's `nitroValidator` field. Run `DeployDevWithNitro.s.sol`. Before registering a signer, pre-cache every non-root CA and the leaf certificate in the validator's `CertManager`. The dependency's call-plan tool can generate diff --git a/snapshots/abi/NitroEnclaveVerifier.json b/snapshots/abi/NitroEnclaveVerifier.json deleted file mode 100644 index 2d59ea314..000000000 --- a/snapshots/abi/NitroEnclaveVerifier.json +++ /dev/null @@ -1,1259 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "uint64", - "name": "initialMaxTimeDiff", - "type": "uint64" - }, - { - "internalType": "bytes32[]", - "name": "initializeTrustedCerts", - "type": "bytes32[]" - }, - { - "internalType": "uint64[]", - "name": "initializeTrustedCertExpiries", - "type": "uint64[]" - }, - { - "internalType": "bytes32", - "name": "initialRootCert", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "initialProofSubmitter", - "type": "address" - }, - { - "internalType": "address", - "name": "initialRevoker", - "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": "bytes32", - "name": "verifierProofId", - "type": "bytes32" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - }, - { - "internalType": "address", - "name": "verifier", - "type": "address" - } - ], - "name": "addVerifyRoute", - "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": "batchVerify", - "outputs": [ - { - "components": [ - { - "internalType": "enum VerificationResult", - "name": "result", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "trustedCertsPrefixLen", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "timestamp", - "type": "uint64" - }, - { - "internalType": "bytes32[]", - "name": "certs", - "type": "bytes32[]" - }, - { - "internalType": "uint64[]", - "name": "certExpiries", - "type": "uint64[]" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "nonce", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "uint64", - "name": "index", - "type": "uint64" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "first", - "type": "bytes32" - }, - { - "internalType": "bytes16", - "name": "second", - "type": "bytes16" - } - ], - "internalType": "struct Bytes48", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct Pcr[]", - "name": "pcrs", - "type": "tuple[]" - }, - { - "internalType": "string", - "name": "moduleId", - "type": "string" - } - ], - "internalType": "struct VerifierJournal[]", - "name": "results", - "type": "tuple[]" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "cancelOwnershipHandover", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32[][]", - "name": "reportCerts", - "type": "bytes32[][]" - } - ], - "name": "checkTrustedIntermediateCerts", - "outputs": [ - { - "internalType": "uint8[]", - "name": "", - "type": "uint8[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "pendingOwner", - "type": "address" - } - ], - "name": "completeOwnershipHandover", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - } - ], - "name": "freezeVerifyRoute", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - } - ], - "name": "getVerifierProofId", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "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": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - } - ], - "name": "getZkVerifier", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "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": [ - { - "internalType": "bytes32", - "name": "certHash", - "type": "bytes32" - } - ], - "name": "revokeCert", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "revokedCerts", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "revoker", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "rootCert", - "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": "submitter", - "type": "address" - } - ], - "name": "setProofSubmitter", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newRevoker", - "type": "address" - } - ], - "name": "setRevoker", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "newRootCert", - "type": "bytes32" - } - ], - "name": "setRootCert", - "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" - }, - { - "internalType": "bytes32", - "name": "verifierProofId", - "type": "bytes32" - } - ], - "name": "setZkConfiguration", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "trustedIntermediateCerts", - "outputs": [ - { - "internalType": "uint64", - "name": "", - "type": "uint64" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "certHash", - "type": "bytes32" - } - ], - "name": "unrevokeCert", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "newAggregatorId", - "type": "bytes32" - } - ], - "name": "updateAggregatorId", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "newVerifierId", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "newVerifierProofId", - "type": "bytes32" - } - ], - "name": "updateVerifierId", - "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": "verify", - "outputs": [ - { - "components": [ - { - "internalType": "enum VerificationResult", - "name": "result", - "type": "uint8" - }, - { - "internalType": "uint8", - "name": "trustedCertsPrefixLen", - "type": "uint8" - }, - { - "internalType": "uint64", - "name": "timestamp", - "type": "uint64" - }, - { - "internalType": "bytes32[]", - "name": "certs", - "type": "bytes32[]" - }, - { - "internalType": "uint64[]", - "name": "certExpiries", - "type": "uint64[]" - }, - { - "internalType": "bytes", - "name": "userData", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "nonce", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "publicKey", - "type": "bytes" - }, - { - "components": [ - { - "internalType": "uint64", - "name": "index", - "type": "uint64" - }, - { - "components": [ - { - "internalType": "bytes32", - "name": "first", - "type": "bytes32" - }, - { - "internalType": "bytes16", - "name": "second", - "type": "bytes16" - } - ], - "internalType": "struct Bytes48", - "name": "value", - "type": "tuple" - } - ], - "internalType": "struct Pcr[]", - "name": "pcrs", - "type": "tuple[]" - }, - { - "internalType": "string", - "name": "moduleId", - "type": "string" - } - ], - "internalType": "struct VerifierJournal", - "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": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newId", - "type": "bytes32" - } - ], - "name": "AggregatorIdUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "enum VerificationResult", - "name": "result", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "output", - "type": "bytes" - } - ], - "name": "AttestationSubmitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "verifierId", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "indexed": false, - "internalType": "bytes", - "name": "encodedBatch", - "type": "bytes" - } - ], - "name": "BatchAttestationSubmitted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "certHash", - "type": "bytes32" - } - ], - "name": "CertRevoked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "certHash", - "type": "bytes32" - } - ], - "name": "CertUnrevoked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "newMaxTimeDiff", - "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": false, - "internalType": "address", - "name": "newProofSubmitter", - "type": "address" - } - ], - "name": "ProofSubmitterChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "newRevoker", - "type": "address" - } - ], - "name": "RevokerUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "bytes32", - "name": "newRootCert", - "type": "bytes32" - } - ], - "name": "RootCertChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newId", - "type": "bytes32" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "newProofId", - "type": "bytes32" - } - ], - "name": "VerifierIdUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "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" - }, - { - "indexed": false, - "internalType": "bytes32", - "name": "verifierProofId", - "type": "bytes32" - } - ], - "name": "ZKConfigurationUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - }, - { - "indexed": false, - "internalType": "address", - "name": "verifier", - "type": "address" - } - ], - "name": "ZkRouteAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "indexed": true, - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - } - ], - "name": "ZkRouteWasFrozen", - "type": "event" - }, - { - "inputs": [], - "name": "AlreadyInitialized", - "type": "error" - }, - { - "inputs": [], - "name": "CallerNotOwnerOrRevoker", - "type": "error" - }, - { - "inputs": [], - "name": "CallerNotProofSubmitter", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "certsLen", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "expiriesLen", - "type": "uint256" - } - ], - "name": "CertExpiriesLengthMismatch", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "certHash", - "type": "bytes32" - } - ], - "name": "CertificateNotRevoked", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidVerifierAddress", - "type": "error" - }, - { - "inputs": [], - "name": "NewOwnerIsZeroAddress", - "type": "error" - }, - { - "inputs": [], - "name": "NoHandoverRequest", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "identifier", - "type": "bytes32" - } - ], - "name": "ProgramIdAlreadyLatest", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "expected", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "actual", - "type": "bytes32" - } - ], - "name": "RootCertMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "Unauthorized", - "type": "error" - }, - { - "inputs": [], - "name": "Unknown_Zk_Coprocessor", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "expected", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "actual", - "type": "bytes32" - } - ], - "name": "VerifierVkMismatch", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroMaxTimeDiff", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroProgramId", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroProofSubmitter", - "type": "error" - }, - { - "inputs": [], - "name": "ZeroVerifierAddress", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "enum ZkCoProcessorType", - "name": "zkCoProcessor", - "type": "uint8" - }, - { - "internalType": "bytes4", - "name": "selector", - "type": "bytes4" - } - ], - "name": "ZkRouteFrozen", - "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 ab95eaa7e..0e36d8cf0 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -43,10 +43,6 @@ "initCodeHash": "0x17bd83cfb6911f7f7f74880249e6bf4e2b2884aed611f6fec8b589d5083f22d1", "sourceCodeHash": "0x62ff0209cffa08e5aaaaac47e0f22d89ea1fa6a0ea8773a162ab7230e50806ee" }, - "src/L1/proofs/tee/NitroEnclaveVerifier.sol:NitroEnclaveVerifier": { - "initCodeHash": "0xf79cd59d23f6a5ad78960b664e20779592d73bfcf52527c3e03adda04edd9645", - "sourceCodeHash": "0x03c164216b27f82ee13064ace6079d5e24e187d888f41bd01c8daffa60c575d6" - }, "src/L1/proofs/tee/TEEProverRegistry.sol:TEEProverRegistry": { "initCodeHash": "0xef50e4cd4734c5a7137c9071334fc159a36869744ec2fa25d320c172999c4b3b", "sourceCodeHash": "0xd7aef97975588bb749c24b69f83055f329da06ce1b6b7634e51d0080557fb98e" diff --git a/snapshots/storageLayout/NitroEnclaveVerifier.json b/snapshots/storageLayout/NitroEnclaveVerifier.json deleted file mode 100644 index f273406b5..000000000 --- a/snapshots/storageLayout/NitroEnclaveVerifier.json +++ /dev/null @@ -1,65 +0,0 @@ -[ - { - "bytes": "20", - "label": "proofSubmitter", - "offset": 0, - "slot": "0", - "type": "address" - }, - { - "bytes": "20", - "label": "revoker", - "offset": 0, - "slot": "1", - "type": "address" - }, - { - "bytes": "32", - "label": "zkConfig", - "offset": 0, - "slot": "2", - "type": "mapping(enum ZkCoProcessorType => struct ZkCoProcessorConfig)" - }, - { - "bytes": "32", - "label": "trustedIntermediateCerts", - "offset": 0, - "slot": "3", - "type": "mapping(bytes32 => uint64)" - }, - { - "bytes": "8", - "label": "maxTimeDiff", - "offset": 0, - "slot": "4", - "type": "uint64" - }, - { - "bytes": "32", - "label": "rootCert", - "offset": 0, - "slot": "5", - "type": "bytes32" - }, - { - "bytes": "32", - "label": "_zkVerifierRoutes", - "offset": 0, - "slot": "6", - "type": "mapping(enum ZkCoProcessorType => mapping(bytes4 => address))" - }, - { - "bytes": "32", - "label": "_verifierProofIds", - "offset": 0, - "slot": "7", - "type": "mapping(enum ZkCoProcessorType => bytes32)" - }, - { - "bytes": "32", - "label": "revokedCerts", - "offset": 0, - "slot": "8", - "type": "mapping(bytes32 => bool)" - } -] \ No newline at end of file diff --git a/src/L1/proofs/tee/NitroEnclaveVerifier.sol b/src/L1/proofs/tee/NitroEnclaveVerifier.sol deleted file mode 100644 index 40cfc03a7..000000000 --- a/src/L1/proofs/tee/NitroEnclaveVerifier.sol +++ /dev/null @@ -1,756 +0,0 @@ -// SPDX-License-Identifier: Apache2.0 -pragma solidity ^0.8.0; - -import { Ownable } from "lib/solady/src/auth/Ownable.sol"; -import { - INitroEnclaveVerifier, - ZkCoProcessorType, - ZkCoProcessorConfig, - VerifierJournal, - BatchVerifierJournal, - VerificationResult -} from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; -import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; -import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; -import { ISemver } from "interfaces/universal/ISemver.sol"; - -/// @title NitroEnclaveVerifier -/// @dev Implementation contract for AWS Nitro Enclave attestation verification using zero-knowledge proofs -/// @dev Custom version of Automata's NitroEnclaveVerifier contract at -/// https://github.com/automata-network/aws-nitro-enclave-attestation/tree/26c90565cb009e6539643a0956f9502a12ade672 -/// -/// Differences from the upstream Automata contract: -/// - Verification of ZK proofs is restricted to an authorized proof submitter address -/// - All privileged actions emit events for monitoring -/// - Removes verification-with-explicit-program-ID and Pico logic -/// -/// This contract provides onchain verification of AWS Nitro Enclave attestation reports by validating -/// zero-knowledge proofs generated offchain. It supports both single and batch verification modes -/// and can work with multiple ZK proof systems (RISC Zero and Succinct SP1). -/// -/// Key features: -/// - Certificate chain management with automatic caching of newly discovered certificates -/// - Timestamp validation with configurable time tolerance -/// - Certificate revocation capabilities for compromised intermediate certificates -/// - Gas-efficient batch verification for multiple attestations -/// - Support for both RISC Zero and SP1 proving systems -/// -/// Security considerations: -/// - Only the contract owner can manage certificates and configurations -/// - Root certificate is immutable once set (requires owner to change) -/// - Intermediate certificates are automatically cached but can be revoked -/// - Timestamp validation prevents replay attacks within the configured time window -contract NitroEnclaveVerifier is Ownable, INitroEnclaveVerifier, ISemver { - /// @dev Sentinel address to indicate a route has been permanently frozen - address private constant FROZEN = address(0xdead); - - /// @dev Address that can submit proofs - address public proofSubmitter; - - /// @dev Address authorized to revoke intermediate certificates (in addition to owner) - address public revoker; - - /// @dev Configuration mapping for each supported ZK coprocessor type - mapping(ZkCoProcessorType => ZkCoProcessorConfig) public zkConfig; - - /// @dev Mapping of trusted intermediate certificate hashes to their notAfter timestamps in seconds (0 = not cached) - mapping(bytes32 => uint64) public trustedIntermediateCerts; - - /// @dev Maximum allowed time difference in seconds for attestation timestamp validation - uint64 public maxTimeDiff; - - /// @dev Hash of the trusted AWS Nitro Enclave root certificate - bytes32 public rootCert; - - /// @dev Route-specific verifier overrides (selector -> verifier address) - mapping(ZkCoProcessorType => mapping(bytes4 => address)) private _zkVerifierRoutes; - - /// @dev Mapping from ZkCoProcessorType to its corresponding verifierProofId representation - mapping(ZkCoProcessorType => bytes32) private _verifierProofIds; - - /// @dev Persistent revocation sentinel for intermediate certificates. - /// - /// `revokeCert` zeroes `trustedIntermediateCerts[certHash]`, but the suffix-cache - /// path in `_cacheNewCert` would otherwise restore that entry on the next - /// successful verification whose chain traverses the revoked hash. This - /// mapping survives `_cacheNewCert` overwrites and is consulted in - /// `_verifyJournal`, `_cacheNewCert`, and `checkTrustedIntermediateCerts`, - /// making `revokeCert` durable independently of the journal's `trustedCertsPrefixLen`. - /// - /// Re-trust requires an explicit `unrevokeCert` admin call; it is never - /// granted as a side effect of verification. - mapping(bytes32 => bool) public revokedCerts; - - // ============ Custom Errors ============ - - /// @dev Error thrown when an unsupported or unknown ZK coprocessor type is used - error Unknown_Zk_Coprocessor(); - - /// @dev Error thrown when a ZK route has been permanently frozen - error ZkRouteFrozen(ZkCoProcessorType zkCoProcessor, bytes4 selector); - - /// @dev Error thrown when no ZK verifier is configured for the coprocessor - error ZkVerifierNotConfigured(ZkCoProcessorType zkCoProcessor); - - /// @dev Thrown when a caller other than the authorized proof submitter calls verify or batchVerify - error CallerNotProofSubmitter(); - - /// @dev Thrown when a program ID argument is bytes32(0) - error ZeroProgramId(); - - /// @dev Thrown when attempting to set a program ID that is already the latest - error ProgramIdAlreadyLatest(ZkCoProcessorType zkCoProcessor, bytes32 identifier); - - /// @dev Thrown when a zero address is provided where a verifier address is required - error ZeroVerifierAddress(); - - /// @dev Thrown when a zero address is provided for the proof submitter - error ZeroProofSubmitter(); - - /// @dev Thrown when the batch journal's verifier VK does not match the expected verifier proof ID - error VerifierVkMismatch(bytes32 expected, bytes32 actual); - - /// @dev Thrown when the first certificate in a chain does not match the stored root certificate - error RootCertMismatch(bytes32 expected, bytes32 actual); - - /// @dev Error thrown when a zero maxTimeDiff is provided - error ZeroMaxTimeDiff(); - - /// @dev Thrown when a zero address is provided for the verifier - error InvalidVerifierAddress(); - - /// @dev Thrown when caller is neither the owner nor the revoker - error CallerNotOwnerOrRevoker(); - - /// @dev Thrown when `unrevokeCert` is called for a hash that is not currently revoked - error CertificateNotRevoked(bytes32 certHash); - - // ============ Events ============ - - /// @dev Emitted when a new verifier program ID is added/updated - event VerifierIdUpdated(ZkCoProcessorType indexed zkCoProcessor, bytes32 indexed newId, bytes32 newProofId); - - /// @dev Emitted when a new aggregator program ID is added/updated - event AggregatorIdUpdated(ZkCoProcessorType indexed zkCoProcessor, bytes32 indexed newId); - - /// @dev Emitted when a route-specific verifier is added - event ZkRouteAdded(ZkCoProcessorType indexed zkCoProcessor, bytes4 indexed selector, address verifier); - - /// @dev Emitted when a route is permanently frozen - event ZkRouteWasFrozen(ZkCoProcessorType indexed zkCoProcessor, bytes4 indexed selector); - - /// @dev Emitted when the proof of attestation has been successfully verified - event AttestationSubmitted(VerificationResult result, ZkCoProcessorType indexed zkCoProcessor, bytes output); - - /// @dev Emitted when a batched proof has been successfully verified; encodedBatched = abi.encode(VerifierJournal[]) - event BatchAttestationSubmitted(bytes32 verifierId, ZkCoProcessorType indexed zkCoProcessor, bytes encodedBatch); - - /// @dev Event emitted when the proof submitter address is changed - event ProofSubmitterChanged(address newProofSubmitter); - - /// @dev Event emitted when the root certificate is changed - event RootCertChanged(bytes32 newRootCert); - - /// @dev Event emitted when the ZK configuration is updated - event ZKConfigurationUpdated(ZkCoProcessorType zkCoProcessor, ZkCoProcessorConfig config, bytes32 verifierProofId); - - /// @dev Event emitted when a certificate is revoked - event CertRevoked(bytes32 certHash); - - /// @dev Event emitted when a previously revoked certificate is explicitly re-trusted - event CertUnrevoked(bytes32 certHash); - - /// @dev Event emitted when the maximum time difference is updated - event MaxTimeDiffUpdated(uint64 newMaxTimeDiff); - - /// @dev Event emitted when the revoker address is updated - event RevokerUpdated(address indexed newRevoker); - - /// @dev Thrown when initializeTrustedCerts and initializeTrustedCertExpiries have different lengths - error CertExpiriesLengthMismatch(uint256 certsLen, uint256 expiriesLen); - - /// @dev Restricts access to the owner or the revoker - modifier onlyOwnerOrRevoker() { - if (msg.sender != owner() && msg.sender != revoker) revert CallerNotOwnerOrRevoker(); - _; - } - - /// @dev Initializes the contract with owner, time tolerance and initial trusted certificates - /// @param owner Address to be set as the contract owner - /// @param initialMaxTimeDiff Maximum time difference in seconds for timestamp validation - /// @param initializeTrustedCerts Array of initial trusted intermediate certificate hashes - /// @param initializeTrustedCertExpiries Array of notAfter timestamps (seconds) for each initial cert - /// @param initialRootCert Hash of the AWS Nitro Enclave root certificate - /// @param initialProofSubmitter Address that is authorized to submit proofs - /// @param initialRevoker Address authorized to revoke intermediate certificates (can be address(0) to disable) - /// @param zkCoProcessor Type of ZK coprocessor to configure (RiscZero or Succinct) - /// @param config Configuration parameters for the ZK coprocessor - /// @param verifierProofId The verifierProofId corresponding to the verifierId in config - constructor( - address owner, - uint64 initialMaxTimeDiff, - bytes32[] memory initializeTrustedCerts, - uint64[] memory initializeTrustedCertExpiries, - bytes32 initialRootCert, - address initialProofSubmitter, - address initialRevoker, - ZkCoProcessorType zkCoProcessor, - ZkCoProcessorConfig memory config, - bytes32 verifierProofId - ) { - if (initialMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); - if (initializeTrustedCerts.length != initializeTrustedCertExpiries.length) { - revert CertExpiriesLengthMismatch(initializeTrustedCerts.length, initializeTrustedCertExpiries.length); - } - maxTimeDiff = initialMaxTimeDiff; - for (uint256 i = 0; i < initializeTrustedCerts.length; i++) { - trustedIntermediateCerts[initializeTrustedCerts[i]] = initializeTrustedCertExpiries[i]; - } - _initializeOwner(owner); - _setRootCert(initialRootCert); - _setProofSubmitter(initialProofSubmitter); - revoker = initialRevoker; - _setZkConfiguration(zkCoProcessor, config, verifierProofId); - } - - // ============ Query Functions ============ - - /// @dev Retrieves the configuration for a specific coprocessor - /// @param zkCoProcessor Type of ZK coprocessor (RiscZero or Succinct) - /// @return ZkCoProcessorConfig Configuration parameters including program IDs and verifier address - function getZkConfig(ZkCoProcessorType zkCoProcessor) external view returns (ZkCoProcessorConfig memory) { - return zkConfig[zkCoProcessor]; - } - - /// @dev Gets the verifier address for a specific route - /// @param zkCoProcessor Type of ZK coprocessor - /// @param selector Proof selector - /// @return Verifier address (route-specific or default fallback) - function getZkVerifier(ZkCoProcessorType zkCoProcessor, bytes4 selector) external view returns (address) { - address verifier = _zkVerifierRoutes[zkCoProcessor][selector]; - - if (verifier == FROZEN) { - revert ZkRouteFrozen(zkCoProcessor, selector); - } - - if (verifier == address(0)) { - return zkConfig[zkCoProcessor].zkVerifier; - } - - return verifier; - } - - /// @dev Returns the verifierProofId for a given ZkCoProcessorType - /// @param zkCoProcessor Type of ZK coprocessor - /// @return The corresponding verifierProofId - function getVerifierProofId(ZkCoProcessorType zkCoProcessor) external view returns (bytes32) { - return _verifierProofIds[zkCoProcessor]; - } - - /// @dev Checks the prefix length of trusted certificates in each provided certificate chain for reports - /// @param reportCerts Array of certificate chains, each containing certificate hashes - /// @return Array indicating the prefix length of trusted certificates in each chain - /// - /// For each certificate chain: - /// 1. Validates that the first certificate matches the stored root certificate - /// 2. Counts consecutive trusted certificates starting from the root - /// 3. Stops counting when an untrusted certificate is encountered - /// - /// This function is used to pre-validate certificate chains before generating proofs, - /// helping to optimize the proving process by determining trusted certificate lengths. - /// Usually called from offchain - function checkTrustedIntermediateCerts(bytes32[][] calldata reportCerts) public view returns (uint8[] memory) { - uint8[] memory results = new uint8[](reportCerts.length); - bytes32 rootCertHash = rootCert; - for (uint256 i = 0; i < reportCerts.length; i++) { - bytes32[] calldata certs = reportCerts[i]; - uint8 trustedCertPrefixLen = 1; - if (certs[0] != rootCertHash) { - revert RootCertMismatch(rootCertHash, certs[0]); - } - for (uint256 j = 1; j < certs.length; j++) { - // Stop counting at any revoked entry so offchain callers cannot derive a - // prefix-len that walks past a revoked cert and then claim it as the trusted boundary. - if (revokedCerts[certs[j]]) { - break; - } - uint64 expiry = trustedIntermediateCerts[certs[j]]; - if (block.timestamp > expiry) { - break; - } - trustedCertPrefixLen += 1; - } - results[i] = trustedCertPrefixLen; - } - return results; - } - - // ============ Admin Functions ============ - - /// @dev Sets the trusted root certificate hash - /// @param newRootCert Hash of the AWS Nitro Enclave root certificate - /// - /// Requirements: - /// - Only callable by contract owner - /// - /// The root certificate serves as the trust anchor for all certificate chain validations. - /// This should be set to the hash of AWS's root certificate for Nitro Enclaves. - function setRootCert(bytes32 newRootCert) external onlyOwner { - _setRootCert(newRootCert); - } - - /// @dev Updates the maximum allowed time difference for attestation timestamp validation - /// @param newMaxTimeDiff New maximum time difference in seconds - /// - /// Requirements: - /// - Only callable by contract owner - /// - Must be greater than zero - function setMaxTimeDiff(uint64 newMaxTimeDiff) external onlyOwner { - if (newMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); - maxTimeDiff = newMaxTimeDiff; - emit MaxTimeDiffUpdated(newMaxTimeDiff); - } - - /// @dev Configures zero-knowledge verification parameters for a specific coprocessor - /// @param zkCoProcessor Type of ZK coprocessor (RiscZero or Succinct) - /// @param config Configuration parameters including program IDs and verifier address - /// @param verifierProofId The verifierProofId corresponding to the verifierId in config - /// - /// Requirements: - /// - Only callable by contract owner - /// - /// This function sets up the necessary parameters for ZK proof verification: - /// - verifierId: Program ID for single attestation verification - /// - aggregatorId: Program ID for batch/aggregated verification - /// - zkVerifier: Address of the deployed ZK verifier contract - function setZkConfiguration( - ZkCoProcessorType zkCoProcessor, - ZkCoProcessorConfig memory config, - bytes32 verifierProofId - ) - external - onlyOwner - { - _setZkConfiguration(zkCoProcessor, config, verifierProofId); - } - - /// @dev Revokes an intermediate certificate, whether or not it has been cached as trusted. - /// @param certHash Hash of the certificate to revoke - /// - /// Requirements: - /// - Only callable by contract owner or revoker - /// - /// Certificates that have never been seen onchain can be revoked preemptively; the - /// persistent `revokedCerts` sentinel blocks them from being trusted on first - /// verification. This function allows the owner or revoker to revoke compromised - /// intermediate certificates without affecting the root certificate or other trusted - /// certificates. - /// - /// Durability: in addition to clearing `trustedIntermediateCerts[certHash]`, this - /// function flips the persistent `revokedCerts[certHash]` sentinel. The sentinel - /// survives subsequent `_cacheNewCert` overwrites and causes both `_verifyJournal` - /// and `checkTrustedIntermediateCerts` to reject any chain whose suffix traverses - /// the revoked hash, regardless of the journal's `trustedCertsPrefixLen`. Reproving - /// the same chain therefore cannot silently restore trust; re-trust requires an - /// explicit `unrevokeCert` call by the owner. - function revokeCert(bytes32 certHash) external onlyOwnerOrRevoker { - delete trustedIntermediateCerts[certHash]; - revokedCerts[certHash] = true; - emit CertRevoked(certHash); - } - - /// @dev Explicitly re-trusts a previously revoked intermediate certificate. - /// @param certHash Hash of the certificate to un-revoke - /// - /// Requirements: - /// - Only callable by contract owner - /// - Certificate must currently be marked as revoked - /// - /// Clearing the revocation sentinel does not by itself restore the cached - /// expiry; the next successful verification whose chain traverses `certHash` - /// will re-cache it via `_cacheNewCert`. This two-step design (admin clears - /// the sentinel, verification re-caches the expiry) keeps re-trust an - /// explicit, owner-only action while still letting the normal cache path - /// supply the up-to-date `notAfter` timestamp. - function unrevokeCert(bytes32 certHash) external onlyOwner { - if (!revokedCerts[certHash]) { - revert CertificateNotRevoked(certHash); - } - delete revokedCerts[certHash]; - emit CertUnrevoked(certHash); - } - - /// @dev Updates the verifier program ID, adding the new version to the supported set - /// @param zkCoProcessor Type of ZK coprocessor - /// @param newVerifierId New verifier program ID to set as latest - /// @param newVerifierProofId New verifier proof ID (stored in mapping, used in batch verification) - function updateVerifierId( - ZkCoProcessorType zkCoProcessor, - bytes32 newVerifierId, - bytes32 newVerifierProofId - ) - external - onlyOwner - { - if (newVerifierId == bytes32(0)) revert ZeroProgramId(); - if (zkConfig[zkCoProcessor].verifierId == newVerifierId) { - revert ProgramIdAlreadyLatest(zkCoProcessor, newVerifierId); - } - - zkConfig[zkCoProcessor].verifierId = newVerifierId; - _verifierProofIds[zkCoProcessor] = newVerifierProofId; - - emit VerifierIdUpdated(zkCoProcessor, newVerifierId, newVerifierProofId); - } - - /// @dev Updates the aggregator program ID, adding the new version to the supported set - /// @param zkCoProcessor Type of ZK coprocessor - /// @param newAggregatorId New aggregator program ID to set as latest - function updateAggregatorId(ZkCoProcessorType zkCoProcessor, bytes32 newAggregatorId) external onlyOwner { - if (newAggregatorId == bytes32(0)) revert ZeroProgramId(); - if (zkConfig[zkCoProcessor].aggregatorId == newAggregatorId) { - revert ProgramIdAlreadyLatest(zkCoProcessor, newAggregatorId); - } - - zkConfig[zkCoProcessor].aggregatorId = newAggregatorId; - - emit AggregatorIdUpdated(zkCoProcessor, newAggregatorId); - } - - /// @dev Adds a route-specific verifier override - /// @param zkCoProcessor Type of ZK coprocessor - /// @param selector Proof selector (first 4 bytes of proof data) - /// @param verifier Address of the verifier contract for this route - function addVerifyRoute(ZkCoProcessorType zkCoProcessor, bytes4 selector, address verifier) external onlyOwner { - if (verifier == address(0)) revert ZeroVerifierAddress(); - if (verifier == FROZEN) revert InvalidVerifierAddress(); - - if (_zkVerifierRoutes[zkCoProcessor][selector] == FROZEN) { - revert ZkRouteFrozen(zkCoProcessor, selector); - } - - _zkVerifierRoutes[zkCoProcessor][selector] = verifier; - emit ZkRouteAdded(zkCoProcessor, selector, verifier); - } - - /// @dev Permanently freezes a verification route - /// @param zkCoProcessor Type of ZK coprocessor - /// @param selector Proof selector to freeze - /// - /// WARNING: This action is IRREVERSIBLE - function freezeVerifyRoute(ZkCoProcessorType zkCoProcessor, bytes4 selector) external onlyOwner { - address currentVerifier = _zkVerifierRoutes[zkCoProcessor][selector]; - - if (currentVerifier == FROZEN) { - revert ZkRouteFrozen(zkCoProcessor, selector); - } - - _zkVerifierRoutes[zkCoProcessor][selector] = FROZEN; - emit ZkRouteWasFrozen(zkCoProcessor, selector); - } - - /// @dev Sets the proof submitter address - /// @param submitter The address of the proof submitter - /// - /// Requirements: - /// - Only callable by contract owner - /// - Address must not be zero - function setProofSubmitter(address submitter) external onlyOwner { - _setProofSubmitter(submitter); - } - - /// @dev Updates the revoker address - /// @param newRevoker New revoker address (can be address(0) to disable the revoker role) - /// - /// Requirements: - /// - Only callable by contract owner - function setRevoker(address newRevoker) external onlyOwner { - revoker = newRevoker; - emit RevokerUpdated(newRevoker); - } - - // ============ Verification Functions ============ - - /// @dev Verifies a single attestation report using zero-knowledge proof - /// @param output Encoded VerifierJournal containing the verification result - /// @param zkCoprocessor Type of ZK coprocessor used to generate the proof - /// @param proofBytes Zero-knowledge proof data for the attestation - /// @return journal VerifierJournal containing the verification result and extracted data - /// - /// This function performs end-to-end verification of a single attestation: - /// 1. Retrieves the single verification program ID from configuration - /// 2. Verifies the zero-knowledge proof using the specified coprocessor - /// 3. Decodes the verification journal from the output - /// 4. Validates the journal through comprehensive checks - /// 5. Returns the final verification result - /// - /// The returned journal contains all extracted attestation data including: - /// - Verification status and any error conditions - /// - Certificate chain information and trust levels - /// - User data, nonce, and public key from the attestation - /// - Platform Configuration Registers (PCRs) for integrity measurement - /// - Module ID and timestamp information - function verify( - bytes calldata output, - ZkCoProcessorType zkCoprocessor, - bytes calldata proofBytes - ) - external - returns (VerifierJournal memory journal) - { - if (msg.sender != proofSubmitter) revert CallerNotProofSubmitter(); - bytes32 programId = zkConfig[zkCoprocessor].verifierId; - _verifyZk(zkCoprocessor, programId, output, proofBytes); - journal = abi.decode(output, (VerifierJournal)); - journal = _verifyJournal(journal); - emit AttestationSubmitted(journal.result, zkCoprocessor, abi.encode(journal)); - } - - /// @dev Verifies multiple attestation reports in a single batch operation - /// @param output Encoded BatchVerifierJournal containing aggregated verification results - /// @param zkCoprocessor Type of ZK coprocessor used to generate the proof - /// @param proofBytes Zero-knowledge proof data for batch verification - /// @return results Array of VerifierJournal results, one for each attestation in the batch - /// - /// This function provides gas-efficient batch verification by: - /// 1. Using the aggregator program ID for ZK proof verification - /// 2. Validating the batch verifier key matches the expected value - /// 3. Processing each individual attestation through standard validation - /// 4. Returning comprehensive results for all attestations - /// - /// Batch verification is recommended when processing multiple attestations - /// as it significantly reduces gas costs compared to individual verifications. - function batchVerify( - bytes calldata output, - ZkCoProcessorType zkCoprocessor, - bytes calldata proofBytes - ) - external - returns (VerifierJournal[] memory results) - { - if (msg.sender != proofSubmitter) revert CallerNotProofSubmitter(); - bytes32 aggregatorId = zkConfig[zkCoprocessor].aggregatorId; - bytes32 verifierId = zkConfig[zkCoprocessor].verifierId; - bytes32 verifierProofId = _verifierProofIds[zkCoprocessor]; - - _verifyZk(zkCoprocessor, aggregatorId, output, proofBytes); - BatchVerifierJournal memory batchJournal = abi.decode(output, (BatchVerifierJournal)); - if (batchJournal.verifierVk != verifierProofId) { - revert VerifierVkMismatch(verifierProofId, batchJournal.verifierVk); - } - uint256 n = batchJournal.outputs.length; - results = new VerifierJournal[](n); - for (uint256 i = 0; i < n; i++) { - results[i] = _verifyJournal(batchJournal.outputs[i]); - } - emit BatchAttestationSubmitted(verifierId, zkCoprocessor, abi.encode(results)); - } - - // ============ Internal Functions ============ - - function _setRootCert(bytes32 newRootCert) internal { - rootCert = newRootCert; - emit RootCertChanged(newRootCert); - } - - function _setProofSubmitter(address submitter) internal { - if (submitter == address(0)) revert ZeroProofSubmitter(); - proofSubmitter = submitter; - emit ProofSubmitterChanged(submitter); - } - - function _setZkConfiguration( - ZkCoProcessorType zkCoProcessor, - ZkCoProcessorConfig memory config, - bytes32 verifierProofId - ) - internal - { - zkConfig[zkCoProcessor] = config; - - // Auto-add program IDs to the version sets and store verifierProofId mapping - if (config.verifierId != bytes32(0)) { - _verifierProofIds[zkCoProcessor] = verifierProofId; - } - emit ZKConfigurationUpdated(zkCoProcessor, config, verifierProofId); - } - - /// @dev Internal function to cache newly discovered trusted certificates - /// @param journal Verification journal containing certificate chain information - /// - /// This function automatically adds any certificates beyond the trusted length - /// to the trusted intermediate certificates set. This optimizes future verifications - /// by expanding the known trusted certificate set based on successful verifications. - /// - /// Revoked entries terminate caching: once `revokedCerts[certHash]` is set by - /// `revokeCert`, no successful verification will silently restore the cache, - /// regardless of the journal's `trustedCertsPrefixLen`. Because `certs[i+1]` is - /// signed by `certs[i]`, every descendant of a revoked cert inherits its trust - /// from a revoked parent and must not be cached either — so we `break` rather - /// than `continue` on the first revoked entry, matching `checkTrustedIntermediateCerts`. - /// - /// Note: in current control flow this guard is unreachable because `_verifyJournal` - /// Pass 2 already rejects any journal whose suffix contains a revoked digest before - /// `_cacheNewCert` is invoked. The check is retained as defense-in-depth against - /// future refactors. Re-trust requires an explicit `unrevokeCert`. - function _cacheNewCert(VerifierJournal memory journal) internal { - for (uint256 i = journal.trustedCertsPrefixLen; i < journal.certs.length; i++) { - bytes32 certHash = journal.certs[i]; - if (revokedCerts[certHash]) { - break; - } - trustedIntermediateCerts[certHash] = journal.certExpiries[i]; - } - } - - /// @dev Internal function to verify and validate a journal entry - /// @param journal Verification journal to validate - /// @return Updated journal with final verification result - /// - /// This function performs comprehensive validation: - /// 1. Checks if the initial ZK verification was successful - /// 2. Validates the root certificate matches the trusted root - /// 3. Ensures all trusted certificates in the prefix are still valid (not revoked, not expired) - /// 4. Ensures no certificate in the suffix has been revoked, regardless of `trustedCertsPrefixLen` - /// 5. Validates the attestation timestamp is within acceptable range - /// 6. Caches newly discovered certificates for future use - /// - /// The suffix-side revocation check (step 4) is the load-bearing fix for the - /// `revokeCert` durability gap exposed under the production - /// `trustedCertsPrefixLen = 1` configuration. Without it, Pass 1 only walks - /// the root and a journal whose chain traverses a revoked intermediate in - /// the suffix would succeed and then re-cache the revoked entry via - /// `_cacheNewCert`. Rejecting any suffix entry present in `revokedCerts` - /// makes revocation durable independently of the journal-supplied prefix - /// length. - /// - /// The timestamp validation converts milliseconds to seconds and checks: - /// - Attestation is not too old (timestamp + maxTimeDiff > block.timestamp) - /// - Attestation is not from the future (timestamp < block.timestamp) - /// Note that due to truncating timestamp from milliseconds, to seconds, - /// some valid attestations may be rejected. However, this ensures all invalid - /// timestamps are rejected. - function _verifyJournal(VerifierJournal memory journal) internal returns (VerifierJournal memory) { - if (journal.result != VerificationResult.Success) { - return journal; - } - if (journal.trustedCertsPrefixLen == 0) { - journal.result = VerificationResult.RootCertNotTrusted; - return journal; - } - // Pass 1: trusted prefix — root must match the onchain root, and every - // intermediate must still hold a non-expired cached entry. - for (uint256 i = 0; i < journal.trustedCertsPrefixLen; i++) { - bytes32 certHash = journal.certs[i]; - if (i == 0) { - if (certHash != rootCert) { - journal.result = VerificationResult.RootCertNotTrusted; - return journal; - } - continue; - } - // `revokeCert` zeroes `trustedIntermediateCerts[certHash]`, so the - // expiry check below already catches a revoked cert reached through - // the prefix path. The explicit `revokedCerts` guard is retained as - // defense-in-depth against future code paths that might re-cache - // before this loop runs. - if (revokedCerts[certHash]) { - journal.result = VerificationResult.IntermediateCertsNotTrusted; - return journal; - } - uint64 expiry = trustedIntermediateCerts[certHash]; - if (block.timestamp > expiry) { - journal.result = VerificationResult.IntermediateCertsNotTrusted; - return journal; - } - } - // Pass 2: suffix — journal-supplied expiries plus a hard reject on any - // cert that the operator has explicitly revoked. This is the path that - // closes the production `trustedCertsPrefixLen = 1` bypass: a revoked - // intermediate in the suffix can no longer pass verification and then - // be silently re-cached. - for (uint256 i = journal.trustedCertsPrefixLen; i < journal.certs.length; i++) { - if (revokedCerts[journal.certs[i]]) { - journal.result = VerificationResult.IntermediateCertsNotTrusted; - return journal; - } - uint64 expiry = journal.certExpiries[i]; - if (block.timestamp > expiry) { - journal.result = VerificationResult.InvalidTimestamp; - return journal; - } - } - uint64 timestamp = journal.timestamp / 1000; - if (timestamp + maxTimeDiff <= block.timestamp || timestamp >= block.timestamp) { - journal.result = VerificationResult.InvalidTimestamp; - return journal; - } - _cacheNewCert(journal); - return journal; - } - - /// @dev Internal function to verify zero-knowledge proofs using the appropriate coprocessor - /// @param zkCoprocessor Type of ZK coprocessor (RiscZero or Succinct) - /// @param programId Program identifier for the verification program - /// @param output Encoded output data to verify - /// @param proofBytes Zero-knowledge proof data - function _verifyZk( - ZkCoProcessorType zkCoprocessor, - bytes32 programId, - bytes calldata output, - bytes calldata proofBytes - ) - internal - view - { - // Resolve the verifier address (route-specific or default) - address verifier = _resolveZkVerifier(zkCoprocessor, proofBytes); - - if (zkCoprocessor == ZkCoProcessorType.RiscZero) { - IRiscZeroVerifier(verifier).verify(proofBytes, programId, sha256(output)); - } else if (zkCoprocessor == ZkCoProcessorType.Succinct) { - ISP1Verifier(verifier).verifyProof(programId, output, proofBytes); - } else { - revert Unknown_Zk_Coprocessor(); - } - } - - /// @dev Internal function to resolve the ZK verifier address based on route configuration - /// @param zkCoprocessor Type of ZK coprocessor - /// @param proofBytes Proof data (selector extracted from first 4 bytes) - /// @return Resolved verifier address - function _resolveZkVerifier( - ZkCoProcessorType zkCoprocessor, - bytes calldata proofBytes - ) - internal - view - returns (address) - { - bytes4 selector = bytes4(proofBytes[0:4]); - address verifier = _zkVerifierRoutes[zkCoprocessor][selector]; - - // Check if route is frozen - if (verifier == FROZEN) { - revert ZkRouteFrozen(zkCoprocessor, selector); - } - - // Fall back to default verifier if no route-specific one configured - if (verifier == address(0)) { - verifier = zkConfig[zkCoprocessor].zkVerifier; - } - - // Ensure verifier is configured - if (verifier == address(0)) { - revert ZkVerifierNotConfigured(zkCoprocessor); - } - - return verifier; - } - - /// @notice Semantic version. - /// @custom:semver 0.4.0 - function version() public pure virtual returns (string memory) { - return "0.4.0"; - } -} diff --git a/test/L1/proofs/NitroEnclaveVerifier.t.sol b/test/L1/proofs/NitroEnclaveVerifier.t.sol deleted file mode 100644 index f1bfe1d85..000000000 --- a/test/L1/proofs/NitroEnclaveVerifier.t.sol +++ /dev/null @@ -1,1138 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import { Test } from "lib/forge-std/src/Test.sol"; -import { Ownable } from "lib/solady/src/auth/Ownable.sol"; - -import { - ZkCoProcessorType, - ZkCoProcessorConfig, - VerifierJournal, - BatchVerifierJournal, - VerificationResult, - Pcr -} from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; - -import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; - -contract NitroEnclaveVerifierTest is Test { - NitroEnclaveVerifier internal verifier; - - address internal owner; - address internal submitter; - address internal revokerAddr; - address internal mockRiscZeroVerifier; - address internal mockSP1Verifier; - - bytes32 internal constant ROOT_CERT = keccak256("root-cert"); - bytes32 internal constant INTERMEDIATE_CERT_1 = keccak256("intermediate-cert-1"); - bytes32 internal constant INTERMEDIATE_CERT_2 = keccak256("intermediate-cert-2"); - bytes32 internal constant VERIFIER_ID = keccak256("verifier-id"); - bytes32 internal constant AGGREGATOR_ID = keccak256("aggregator-id"); - bytes32 internal constant VERIFIER_PROOF_ID = keccak256("verifier-proof-id"); - bytes4 internal constant DEFAULT_PROOF_SELECTOR = bytes4(0); - bytes4 internal constant NON_DEFAULT_PROOF_SELECTOR = 0x00000001; - bytes4 internal constant TEST_ROUTE_SELECTOR = bytes4(keccak256("test")); - bytes4 internal constant RISC_ZERO_VERIFY_SELECTOR = bytes4(keccak256("verify(bytes,bytes32,bytes32)")); - bytes4 internal constant SP1_VERIFY_PROOF_SELECTOR = bytes4(keccak256("verifyProof(bytes32,bytes,bytes)")); - address internal constant FROZEN_ROUTE_SENTINEL = address(0xdead); - - uint64 internal constant MAX_TIME_DIFF = 3600; // 1 hour - - // Realistic timestamp so timestamp validation tests work correctly - uint256 internal constant REALISTIC_TIMESTAMP = 1_700_000_000; - - // Expiry timestamps for test certs (well after REALISTIC_TIMESTAMP) - uint64 internal constant INTERMEDIATE_CERT_1_EXPIRY = 1_800_000_000; // ~2027 - uint64 internal constant INTERMEDIATE_CERT_2_EXPIRY = 1_750_000_000; // ~2025 - uint64 internal constant ROOT_CERT_EXPIRY = 1_900_000_000; - uint64 internal constant NEW_LEAF_CERT_EXPIRY = 1_700_100_000; // ~28 hours after REALISTIC_TIMESTAMP - - function setUp() public { - vm.warp(REALISTIC_TIMESTAMP); - - owner = address(this); - submitter = makeAddr("submitter"); - revokerAddr = makeAddr("revoker"); - mockRiscZeroVerifier = makeAddr("mock-riscZero-verifier"); - mockSP1Verifier = makeAddr("mock-sp1-verifier"); - - bytes32[] memory trustedCerts = new bytes32[](1); - trustedCerts[0] = INTERMEDIATE_CERT_1; - - uint64[] memory trustedCertExpiries = new uint64[](1); - trustedCertExpiries[0] = INTERMEDIATE_CERT_1_EXPIRY; - - ZkCoProcessorConfig memory zkCfg = _zkConfig(mockSP1Verifier); - - verifier = new NitroEnclaveVerifier( - owner, - MAX_TIME_DIFF, - trustedCerts, - trustedCertExpiries, - ROOT_CERT, - submitter, - revokerAddr, - ZkCoProcessorType.Succinct, - zkCfg, - VERIFIER_PROOF_ID - ); - } - - // ============ Constructor Tests ============ - - function testConstructorSetsOwner() public view { - assertEq(verifier.owner(), owner); - } - - function testConstructorSetsMaxTimeDiff() public view { - assertEq(verifier.maxTimeDiff(), MAX_TIME_DIFF); - } - - function testConstructorSetsTrustedCerts() public view { - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), INTERMEDIATE_CERT_1_EXPIRY); - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_2), 0); - } - - function testConstructorRevertsIfZeroMaxTimeDiff() public { - bytes32[] memory certs = new bytes32[](0); - uint64[] memory expiries = new uint64[](0); - ZkCoProcessorConfig memory zkCfg = - ZkCoProcessorConfig({ verifierId: bytes32(0), aggregatorId: bytes32(0), zkVerifier: address(0) }); - vm.expectRevert(NitroEnclaveVerifier.ZeroMaxTimeDiff.selector); - new NitroEnclaveVerifier( - owner, 0, certs, expiries, bytes32(0), submitter, address(0), ZkCoProcessorType.Succinct, zkCfg, bytes32(0) - ); - } - - function testConstructorRevertsIfCertExpiriesLengthMismatch() public { - bytes32[] memory certs = new bytes32[](1); - certs[0] = INTERMEDIATE_CERT_1; - uint64[] memory expiries = new uint64[](0); - ZkCoProcessorConfig memory zkCfg = - ZkCoProcessorConfig({ verifierId: bytes32(0), aggregatorId: bytes32(0), zkVerifier: address(0) }); - vm.expectRevert(abi.encodeWithSelector(NitroEnclaveVerifier.CertExpiriesLengthMismatch.selector, 1, 0)); - new NitroEnclaveVerifier( - owner, - MAX_TIME_DIFF, - certs, - expiries, - bytes32(0), - submitter, - address(0), - ZkCoProcessorType.Succinct, - zkCfg, - bytes32(0) - ); - } - - // ============ setRootCert Tests ============ - - function testSetRootCert() public { - bytes32 newRoot = keccak256("new-root"); - verifier.setRootCert(newRoot); - assertEq(verifier.rootCert(), newRoot); - } - - function testSetRootCertRevertsIfNotOwner() public { - _expectNotOwnerRevert(submitter); - verifier.setRootCert(keccak256("bad")); - } - - // ============ setMaxTimeDiff Tests ============ - - function testSetMaxTimeDiff() public { - uint64 newTimeDiff = 7200; - verifier.setMaxTimeDiff(newTimeDiff); - assertEq(verifier.maxTimeDiff(), newTimeDiff); - } - - function testSetMaxTimeDiffRevertsIfZero() public { - vm.expectRevert(NitroEnclaveVerifier.ZeroMaxTimeDiff.selector); - verifier.setMaxTimeDiff(0); - } - - function testSetMaxTimeDiffRevertsIfNotOwner() public { - _expectNotOwnerRevert(submitter); - verifier.setMaxTimeDiff(7200); - } - - // ============ setProofSubmitter Tests ============ - - function testSetProofSubmitter() public { - address newSubmitter = makeAddr("new-submitter"); - verifier.setProofSubmitter(newSubmitter); - assertEq(verifier.proofSubmitter(), newSubmitter); - } - - function testSetProofSubmitterRevertsIfZeroAddress() public { - vm.expectRevert(NitroEnclaveVerifier.ZeroProofSubmitter.selector); - verifier.setProofSubmitter(address(0)); - } - - function testSetProofSubmitterRevertsIfNotOwner() public { - _expectNotOwnerRevert(submitter); - verifier.setProofSubmitter(makeAddr("anyone")); - } - - // ============ setZkConfiguration Tests ============ - - function testSetZkConfiguration() public { - ZkCoProcessorConfig memory config = _zkConfig(mockRiscZeroVerifier); - - verifier.setZkConfiguration(ZkCoProcessorType.RiscZero, config, VERIFIER_PROOF_ID); - - ZkCoProcessorConfig memory stored = verifier.getZkConfig(ZkCoProcessorType.RiscZero); - assertEq(stored.verifierId, VERIFIER_ID); - assertEq(stored.aggregatorId, AGGREGATOR_ID); - assertEq(stored.zkVerifier, mockRiscZeroVerifier); - - assertEq(verifier.getVerifierProofId(ZkCoProcessorType.RiscZero), VERIFIER_PROOF_ID); - } - - function testSetZkConfigurationRevertsIfNotOwner() public { - ZkCoProcessorConfig memory config = _zkConfig(mockRiscZeroVerifier); - - _expectNotOwnerRevert(submitter); - verifier.setZkConfiguration(ZkCoProcessorType.RiscZero, config, VERIFIER_PROOF_ID); - } - - // ============ revokeCert Tests ============ - - function testRevokeCert() public { - assertGt(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), 0); - verifier.revokeCert(INTERMEDIATE_CERT_1); - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), 0); - } - - function testRevokeCertRevertsIfNotOwnerOrRevoker() public { - vm.prank(submitter); - vm.expectRevert(NitroEnclaveVerifier.CallerNotOwnerOrRevoker.selector); - verifier.revokeCert(INTERMEDIATE_CERT_1); - } - - function testRevokeCertSetsDurableSentinel() public { - assertFalse(verifier.revokedCerts(INTERMEDIATE_CERT_1)); - verifier.revokeCert(INTERMEDIATE_CERT_1); - assertTrue(verifier.revokedCerts(INTERMEDIATE_CERT_1)); - } - - function testRevokeCertPreemptiveUnknownCert() public { - bytes32 unknown = keccak256("unknown-cert"); - assertEq(verifier.trustedIntermediateCerts(unknown), 0); - assertFalse(verifier.revokedCerts(unknown)); - - vm.expectEmit(false, false, false, true); - emit NitroEnclaveVerifier.CertRevoked(unknown); - verifier.revokeCert(unknown); - - assertTrue(verifier.revokedCerts(unknown)); - assertEq(verifier.trustedIntermediateCerts(unknown), 0); - } - - function testVerifyRejectsPreemptivelyRevokedCertInSuffix() public { - _setUpRiscZeroConfig(); - bytes32 unknown = keccak256("unknown-compromised-intermediate"); - verifier.revokeCert(unknown); - - VerifierJournal memory journal = _createSuccessJournal(); - bytes32[] memory certs = new bytes32[](3); - certs[0] = ROOT_CERT; - certs[1] = unknown; // preemptively revoked, lives in the suffix - certs[2] = keccak256("attacker-leaf"); - journal.certs = certs; - - uint64[] memory expiries = new uint64[](3); - expiries[0] = ROOT_CERT_EXPIRY; - expiries[1] = NEW_LEAF_CERT_EXPIRY; - expiries[2] = NEW_LEAF_CERT_EXPIRY; - journal.certExpiries = expiries; - journal.trustedCertsPrefixLen = 1; - - bytes memory output = abi.encode(journal); - bytes memory proofBytes = abi.encodePacked(bytes4(0), bytes32(0)); - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - - vm.prank(submitter); - VerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - - assertEq(uint8(result.result), uint8(VerificationResult.IntermediateCertsNotTrusted)); - assertEq(verifier.trustedIntermediateCerts(unknown), 0); - assertTrue(verifier.revokedCerts(unknown)); - } - - function testCheckTrustedIntermediateCertsBreaksAtPreemptivelyRevokedEntry() public { - bytes32 unknown = keccak256("unknown-compromised-intermediate"); - verifier.revokeCert(unknown); - - bytes32[][] memory reportCerts = new bytes32[][](1); - reportCerts[0] = new bytes32[](3); - reportCerts[0][0] = ROOT_CERT; - reportCerts[0][1] = unknown; - reportCerts[0][2] = keccak256("leaf"); - - uint8[] memory prefixLens = verifier.checkTrustedIntermediateCerts(reportCerts); - assertEq(prefixLens[0], 1); - } - - // ============ unrevokeCert Tests ============ - - function testUnrevokeCertClearsSentinel() public { - verifier.revokeCert(INTERMEDIATE_CERT_1); - assertTrue(verifier.revokedCerts(INTERMEDIATE_CERT_1)); - - vm.expectEmit(false, false, false, true); - emit NitroEnclaveVerifier.CertUnrevoked(INTERMEDIATE_CERT_1); - verifier.unrevokeCert(INTERMEDIATE_CERT_1); - - assertFalse(verifier.revokedCerts(INTERMEDIATE_CERT_1)); - // Cached expiry is intentionally not restored: the next successful - // verification re-caches it via _cacheNewCert with the journal-supplied - // notAfter timestamp. - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), 0); - } - - function testUnrevokeCertRevertsIfNotRevoked() public { - vm.expectRevert( - abi.encodeWithSelector(NitroEnclaveVerifier.CertificateNotRevoked.selector, INTERMEDIATE_CERT_1) - ); - verifier.unrevokeCert(INTERMEDIATE_CERT_1); - } - - function testUnrevokeCertRevertsIfNotOwner() public { - verifier.revokeCert(INTERMEDIATE_CERT_1); - vm.prank(revokerAddr); - vm.expectRevert(); - verifier.unrevokeCert(INTERMEDIATE_CERT_1); - } - - function testUnrevokeCertThenReproveRestoresCache() public { - _setUpRiscZeroConfig(); - verifier.revokeCert(INTERMEDIATE_CERT_1); - verifier.unrevokeCert(INTERMEDIATE_CERT_1); - - // Submit a verification whose chain re-introduces INTERMEDIATE_CERT_1 - // in the suffix; _cacheNewCert should now restore the cached expiry - // because the sentinel is clear. - VerifierJournal memory journal = _createSuccessJournal(); - bytes32[] memory certs = new bytes32[](3); - certs[0] = ROOT_CERT; - certs[1] = INTERMEDIATE_CERT_1; // in the suffix (prefixLen = 1) - certs[2] = keccak256("leaf"); - journal.certs = certs; - - uint64[] memory expiries = new uint64[](3); - expiries[0] = INTERMEDIATE_CERT_1_EXPIRY + 100_000_000; - expiries[1] = INTERMEDIATE_CERT_1_EXPIRY; - expiries[2] = NEW_LEAF_CERT_EXPIRY; - journal.certExpiries = expiries; - journal.trustedCertsPrefixLen = 1; - - bytes memory output = abi.encode(journal); - bytes memory proofBytes = abi.encodePacked(bytes4(0), bytes32(0)); - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - - vm.prank(submitter); - VerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - - assertEq(uint8(result.result), uint8(VerificationResult.Success)); - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), INTERMEDIATE_CERT_1_EXPIRY); - } - - // ============ Durable Revocation: production prefixLen = 1 bypass ============ - - /// Reproduces the Immunefi #75608 attack shape: with the production - /// `trustedCertsPrefixLen = 1`, a chain whose revoked intermediate sits in - /// the suffix would previously pass `_verifyJournal` and be silently - /// re-cached by `_cacheNewCert`. The suffix-side `revokedCerts` guard now - /// rejects the verification and leaves the cache zeroed. - function testVerifyRejectsRevokedCertInSuffixUnderProductionPrefixLen() public { - _setUpRiscZeroConfig(); - verifier.revokeCert(INTERMEDIATE_CERT_1); - - VerifierJournal memory journal = _createSuccessJournal(); - bytes32[] memory certs = new bytes32[](3); - certs[0] = ROOT_CERT; - certs[1] = INTERMEDIATE_CERT_1; // revoked, lives in the suffix - certs[2] = keccak256("attacker-leaf"); - journal.certs = certs; - - uint64[] memory expiries = new uint64[](3); - expiries[0] = INTERMEDIATE_CERT_1_EXPIRY + 100_000_000; - expiries[1] = INTERMEDIATE_CERT_1_EXPIRY; - expiries[2] = NEW_LEAF_CERT_EXPIRY; - journal.certExpiries = expiries; - journal.trustedCertsPrefixLen = 1; // production default — only root in prefix - - bytes memory output = abi.encode(journal); - bytes memory proofBytes = abi.encodePacked(bytes4(0), bytes32(0)); - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - - vm.prank(submitter); - VerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - - assertEq(uint8(result.result), uint8(VerificationResult.IntermediateCertsNotTrusted)); - // Cache must remain zeroed — _cacheNewCert never ran. - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), 0); - // And the durable sentinel must still be set. - assertTrue(verifier.revokedCerts(INTERMEDIATE_CERT_1)); - } - - /// Direct exercise of `_cacheNewCert`'s revocation skip: a verification - /// where the suffix contains both a revoked entry and an unrelated new cert - /// should leave the revoked cache zeroed but still cache the new cert. - /// Drives this through verify() because _cacheNewCert is internal. - function testCacheNewCertSkipsRevokedEntries() public { - _setUpRiscZeroConfig(); - bytes32 freshCert = keccak256("fresh-intermediate"); - - // Seed: cache INTERMEDIATE_CERT_2 by running a successful verification - // whose chain passes through it. - _seedIntermediateCert2(); - - // Revoke INTERMEDIATE_CERT_2, then submit a journal whose suffix re-presents - // it alongside `freshCert`. The verification must reject (because the suffix - // contains a revoked entry), and neither cache rewrite must happen. - verifier.revokeCert(INTERMEDIATE_CERT_2); - - VerifierJournal memory result = _verifySuffixWithRevokedAndFresh(freshCert); - - assertEq(uint8(result.result), uint8(VerificationResult.IntermediateCertsNotTrusted)); - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_2), 0); - assertEq(verifier.trustedIntermediateCerts(freshCert), 0); - } - - function _seedIntermediateCert2() private { - VerifierJournal memory j = _createSuccessJournal(); - bytes32[] memory c = new bytes32[](3); - c[0] = ROOT_CERT; - c[1] = INTERMEDIATE_CERT_1; - c[2] = INTERMEDIATE_CERT_2; - j.certs = c; - uint64[] memory e = new uint64[](3); - e[0] = INTERMEDIATE_CERT_1_EXPIRY + 100_000_000; - e[1] = INTERMEDIATE_CERT_1_EXPIRY; - e[2] = INTERMEDIATE_CERT_2_EXPIRY; - j.certExpiries = e; - j.trustedCertsPrefixLen = 2; - - bytes memory output = abi.encode(j); - bytes memory proofBytes = abi.encodePacked(bytes4(0), bytes32(0)); - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - vm.prank(submitter); - verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_2), INTERMEDIATE_CERT_2_EXPIRY); - } - - function _verifySuffixWithRevokedAndFresh(bytes32 freshCert) private returns (VerifierJournal memory) { - VerifierJournal memory j = _createSuccessJournal(); - bytes32[] memory c = new bytes32[](4); - c[0] = ROOT_CERT; - c[1] = INTERMEDIATE_CERT_1; - c[2] = INTERMEDIATE_CERT_2; // revoked - c[3] = freshCert; - j.certs = c; - uint64[] memory e = new uint64[](4); - e[0] = INTERMEDIATE_CERT_1_EXPIRY + 100_000_000; - e[1] = INTERMEDIATE_CERT_1_EXPIRY; - e[2] = INTERMEDIATE_CERT_2_EXPIRY; - e[3] = uint64(REALISTIC_TIMESTAMP + 86_400); - j.certExpiries = e; - j.trustedCertsPrefixLen = 2; - - bytes memory output = abi.encode(j); - bytes memory proofBytes = abi.encodePacked(bytes4(0), bytes32(0)); - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - vm.prank(submitter); - return verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - } - - function testCheckTrustedIntermediateCertsBreaksAtRevokedEntry() public { - // INTERMEDIATE_CERT_1 is initially trusted; revoke it and confirm the - // off-chain helper no longer counts it. - verifier.revokeCert(INTERMEDIATE_CERT_1); - - bytes32[][] memory reportCerts = new bytes32[][](1); - reportCerts[0] = new bytes32[](2); - reportCerts[0][0] = ROOT_CERT; - reportCerts[0][1] = INTERMEDIATE_CERT_1; // revoked - - uint8[] memory results = verifier.checkTrustedIntermediateCerts(reportCerts); - assertEq(results[0], 1); - } - - // ============ Revoker Role Tests ============ - - function testConstructorSetsRevoker() public view { - assertEq(verifier.revoker(), revokerAddr); - } - - function testConstructorAcceptsZeroRevoker() public { - bytes32[] memory certs = new bytes32[](0); - uint64[] memory expiries = new uint64[](0); - ZkCoProcessorConfig memory zkCfg = _zkConfig(mockSP1Verifier); - NitroEnclaveVerifier v = new NitroEnclaveVerifier( - owner, - MAX_TIME_DIFF, - certs, - expiries, - ROOT_CERT, - submitter, - address(0), - ZkCoProcessorType.Succinct, - zkCfg, - VERIFIER_PROOF_ID - ); - assertEq(v.revoker(), address(0)); - } - - function testRevokerCanRevokeCert() public { - assertGt(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), 0); - vm.prank(revokerAddr); - verifier.revokeCert(INTERMEDIATE_CERT_1); - assertEq(verifier.trustedIntermediateCerts(INTERMEDIATE_CERT_1), 0); - } - - function testSetRevoker() public { - address newRevoker = makeAddr("new-revoker"); - verifier.setRevoker(newRevoker); - assertEq(verifier.revoker(), newRevoker); - } - - function testSetRevokerToZeroDisablesRole() public { - verifier.setRevoker(address(0)); - assertEq(verifier.revoker(), address(0)); - - vm.prank(revokerAddr); - vm.expectRevert(NitroEnclaveVerifier.CallerNotOwnerOrRevoker.selector); - verifier.revokeCert(INTERMEDIATE_CERT_1); - } - - function testSetRevokerEmitsEvent() public { - address newRevoker = makeAddr("new-revoker"); - vm.expectEmit(true, false, false, false); - emit NitroEnclaveVerifier.RevokerUpdated(newRevoker); - verifier.setRevoker(newRevoker); - } - - function testSetRevokerRevertsIfNotOwner() public { - _expectNotOwnerRevert(submitter); - verifier.setRevoker(makeAddr("anyone")); - } - - function testSetRevokerRevertsIfCalledByRevoker() public { - _expectNotOwnerRevert(revokerAddr); - verifier.setRevoker(makeAddr("anyone")); - } - - // ============ updateVerifierId Tests ============ - - function testUpdateVerifierId() public { - _setUpRiscZeroConfig(); - - bytes32 newVerifierId = keccak256("new-verifier-id"); - bytes32 newVerifierProofId = keccak256("new-verifier-proof-id"); - verifier.updateVerifierId(ZkCoProcessorType.RiscZero, newVerifierId, newVerifierProofId); - - ZkCoProcessorConfig memory config = verifier.getZkConfig(ZkCoProcessorType.RiscZero); - assertEq(config.verifierId, newVerifierId); - assertEq(verifier.getVerifierProofId(ZkCoProcessorType.RiscZero), newVerifierProofId); - } - - function testUpdateVerifierIdRevertsIfZero() public { - _setUpRiscZeroConfig(); - vm.expectRevert(NitroEnclaveVerifier.ZeroProgramId.selector); - verifier.updateVerifierId(ZkCoProcessorType.RiscZero, bytes32(0), bytes32(0)); - } - - function testUpdateVerifierIdRevertsIfSame() public { - _setUpRiscZeroConfig(); - vm.expectRevert( - abi.encodeWithSelector( - NitroEnclaveVerifier.ProgramIdAlreadyLatest.selector, ZkCoProcessorType.RiscZero, VERIFIER_ID - ) - ); - verifier.updateVerifierId(ZkCoProcessorType.RiscZero, VERIFIER_ID, VERIFIER_PROOF_ID); - } - - function testUpdateVerifierIdRevertsIfNotOwner() public { - _setUpRiscZeroConfig(); - _expectNotOwnerRevert(submitter); - verifier.updateVerifierId(ZkCoProcessorType.RiscZero, keccak256("new"), keccak256("proof")); - } - - // ============ updateAggregatorId Tests ============ - - function testUpdateAggregatorId() public { - _setUpRiscZeroConfig(); - - bytes32 newAggregatorId = keccak256("new-aggregator-id"); - verifier.updateAggregatorId(ZkCoProcessorType.RiscZero, newAggregatorId); - - ZkCoProcessorConfig memory config = verifier.getZkConfig(ZkCoProcessorType.RiscZero); - assertEq(config.aggregatorId, newAggregatorId); - } - - function testUpdateAggregatorIdRevertsIfZero() public { - _setUpRiscZeroConfig(); - vm.expectRevert(NitroEnclaveVerifier.ZeroProgramId.selector); - verifier.updateAggregatorId(ZkCoProcessorType.RiscZero, bytes32(0)); - } - - function testUpdateAggregatorIdRevertsIfSame() public { - _setUpRiscZeroConfig(); - vm.expectRevert( - abi.encodeWithSelector( - NitroEnclaveVerifier.ProgramIdAlreadyLatest.selector, ZkCoProcessorType.RiscZero, AGGREGATOR_ID - ) - ); - verifier.updateAggregatorId(ZkCoProcessorType.RiscZero, AGGREGATOR_ID); - } - - function testUpdateAggregatorIdRevertsIfNotOwner() public { - _setUpRiscZeroConfig(); - _expectNotOwnerRevert(submitter); - verifier.updateAggregatorId(ZkCoProcessorType.RiscZero, keccak256("new")); - } - - // ============ addVerifyRoute / freezeVerifyRoute Tests ============ - - function testAddVerifyRoute() public { - address routeVerifier = makeAddr("route-verifier"); - - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR, routeVerifier); - assertEq(verifier.getZkVerifier(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR), routeVerifier); - } - - function testAddVerifyRouteRevertsIfZeroAddress() public { - vm.expectRevert(NitroEnclaveVerifier.ZeroVerifierAddress.selector); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, NON_DEFAULT_PROOF_SELECTOR, address(0)); - } - - function testAddVerifyRouteRevertsIfFrozenSentinel() public { - vm.expectRevert(NitroEnclaveVerifier.InvalidVerifierAddress.selector); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, NON_DEFAULT_PROOF_SELECTOR, FROZEN_ROUTE_SENTINEL); - } - - function testAddVerifyRouteRevertsIfNotOwner() public { - _expectNotOwnerRevert(submitter); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR, makeAddr("v")); - } - - function testFreezeVerifyRoute() public { - address routeVerifier = makeAddr("route-verifier"); - - _addAndFreezeVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR, routeVerifier); - - _expectZkRouteFrozenRevert(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR); - verifier.getZkVerifier(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR); - } - - function testAddVerifyRouteRevertsIfFrozen() public { - address routeVerifier = makeAddr("route-verifier"); - - _addAndFreezeVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR, routeVerifier); - - _expectZkRouteFrozenRevert(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR, routeVerifier); - } - - function testFreezeVerifyRouteRevertsIfAlreadyFrozen() public { - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR, makeAddr("v")); - verifier.freezeVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR); - - _expectZkRouteFrozenRevert(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR); - verifier.freezeVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR); - } - - function testFreezeVerifyRouteRevertsIfNotOwner() public { - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR, makeAddr("v")); - - _expectNotOwnerRevert(submitter); - verifier.freezeVerifyRoute(ZkCoProcessorType.RiscZero, TEST_ROUTE_SELECTOR); - } - - // ============ getZkVerifier Tests ============ - - function testGetZkVerifierFallsBackToDefault() public { - _setUpRiscZeroConfig(); - - bytes4 unknownSelector = bytes4(0xdeadbeef); - assertEq(verifier.getZkVerifier(ZkCoProcessorType.RiscZero, unknownSelector), mockRiscZeroVerifier); - } - - function testGetZkVerifierReturnsRouteSpecific() public { - _setUpRiscZeroConfig(); - - bytes4 selector = bytes4(keccak256("special")); - address routeVerifier = makeAddr("route-verifier"); - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, selector, routeVerifier); - - assertEq(verifier.getZkVerifier(ZkCoProcessorType.RiscZero, selector), routeVerifier); - } - - // ============ checkTrustedIntermediateCerts Tests ============ - - function testCheckTrustedIntermediateCerts() public view { - bytes32[][] memory reportCerts = new bytes32[][](1); - reportCerts[0] = new bytes32[](3); - reportCerts[0][0] = ROOT_CERT; - reportCerts[0][1] = INTERMEDIATE_CERT_1; - reportCerts[0][2] = INTERMEDIATE_CERT_2; - - uint8[] memory results = verifier.checkTrustedIntermediateCerts(reportCerts); - assertEq(results[0], 2); // root + 1 intermediate trusted - } - - function testCheckTrustedIntermediateCertsRevertsIfWrongRoot() public { - bytes32 wrongRoot = keccak256("wrong-root"); - bytes32[][] memory reportCerts = new bytes32[][](1); - reportCerts[0] = new bytes32[](1); - reportCerts[0][0] = wrongRoot; - - vm.expectRevert(abi.encodeWithSelector(NitroEnclaveVerifier.RootCertMismatch.selector, ROOT_CERT, wrongRoot)); - verifier.checkTrustedIntermediateCerts(reportCerts); - } - - // ============ verify — access control ============ - - function testVerifyRevertsIfNotProofSubmitter() public { - vm.expectRevert(NitroEnclaveVerifier.CallerNotProofSubmitter.selector); - verifier.verify("", ZkCoProcessorType.RiscZero, ""); - } - - // ============ verify — ZkVerifierNotConfigured ============ - - function testVerifyRevertsIfZkVerifierNotConfigured() public { - ZkCoProcessorConfig memory config = _zkConfig(address(0)); - verifier.setZkConfiguration(ZkCoProcessorType.RiscZero, config, VERIFIER_PROOF_ID); - - bytes memory proofBytes = _proofBytes(); - - vm.prank(submitter); - vm.expectRevert( - abi.encodeWithSelector(NitroEnclaveVerifier.ZkVerifierNotConfigured.selector, ZkCoProcessorType.RiscZero) - ); - verifier.verify("", ZkCoProcessorType.RiscZero, proofBytes); - } - - // ============ verify — Unknown_Zk_Coprocessor ============ - - function testVerifyRevertsForUnknownCoprocessor() public { - ZkCoProcessorConfig memory config = _zkConfig(mockRiscZeroVerifier); - verifier.setZkConfiguration(ZkCoProcessorType.Unknown, config, VERIFIER_PROOF_ID); - - bytes memory proofBytes = _proofBytes(); - - vm.prank(submitter); - vm.expectRevert(NitroEnclaveVerifier.Unknown_Zk_Coprocessor.selector); - verifier.verify("", ZkCoProcessorType.Unknown, proofBytes); - } - - // ============ verify — ZkRouteFrozen during verify() ============ - - function testVerifyRevertsIfRouteFrozen() public { - _setUpRiscZeroConfig(); - - bytes4 selector = DEFAULT_PROOF_SELECTOR; - verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, selector, makeAddr("route-v")); - verifier.freezeVerifyRoute(ZkCoProcessorType.RiscZero, selector); - - bytes memory proofBytes = _proofBytes(); - - vm.prank(submitter); - _expectZkRouteFrozenRevert(ZkCoProcessorType.RiscZero, selector); - verifier.verify("", ZkCoProcessorType.RiscZero, proofBytes); - } - - // ============ verify — RiscZero happy path ============ - - function testVerifySuccessfulJournal() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory result = _verifyRiscZeroJournal(_createSuccessJournal()); - - _assertVerificationResult(result, VerificationResult.Success); - } - - function testVerifyJournalRootCertNotTrusted() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory journal = _createSuccessJournal(); - journal.certs[0] = keccak256("wrong-root"); - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.RootCertNotTrusted); - } - - function testVerifyJournalRootCertNotTrustedZeroPrefixLen() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory journal = _createSuccessJournal(); - journal.trustedCertsPrefixLen = 0; - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.RootCertNotTrusted); - } - - function testVerifyJournalIntermediateCertNotTrusted() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory journal = _createSuccessJournal(); - // Replace trusted intermediate with untrusted one, but keep trustedCertsPrefixLen = 2 - journal.certs[1] = keccak256("untrusted-intermediate"); - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.IntermediateCertsNotTrusted); - } - - function testVerifyJournalInvalidTimestampTooOld() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory journal = _createSuccessJournal(); - // Set timestamp far in the past — more than maxTimeDiff seconds ago (in ms) - journal.timestamp = uint64(block.timestamp - MAX_TIME_DIFF - 1) * 1000; - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.InvalidTimestamp); - } - - function testVerifyJournalInvalidTimestampFuture() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory journal = _createSuccessJournal(); - // Set timestamp in the future (converted to ms) - journal.timestamp = uint64(block.timestamp + 100) * 1000; - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.InvalidTimestamp); - } - - function testVerifyCachesNewCerts() public { - _setUpRiscZeroConfig(); - - bytes32 newCert = keccak256("new-leaf-cert"); - assertEq(verifier.trustedIntermediateCerts(newCert), 0); - - VerifierJournal memory journal = _createSuccessJournalWithLeaf(newCert, NEW_LEAF_CERT_EXPIRY); - _verifyRiscZeroJournal(journal); - - assertEq(verifier.trustedIntermediateCerts(newCert), NEW_LEAF_CERT_EXPIRY); - } - - function testVerifyJournalPassesThroughFailedResult() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory journal = _createSuccessJournal(); - journal.result = VerificationResult.IntermediateCertsNotTrusted; - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.IntermediateCertsNotTrusted); - } - - // ============ verify — Succinct SP1 happy path ============ - - function testVerifySuccessfulJournalSP1() public { - VerifierJournal memory result = _verifySP1Journal(_createSuccessJournal()); - - _assertVerificationResult(result, VerificationResult.Success); - } - - function testVerifyRevertsIfZkVerifierNotConfiguredSP1() public { - ZkCoProcessorConfig memory config = _zkConfig(address(0)); - verifier.setZkConfiguration(ZkCoProcessorType.Succinct, config, VERIFIER_PROOF_ID); - - bytes memory proofBytes = _proofBytes(); - - vm.prank(submitter); - vm.expectRevert( - abi.encodeWithSelector(NitroEnclaveVerifier.ZkVerifierNotConfigured.selector, ZkCoProcessorType.Succinct) - ); - verifier.verify("", ZkCoProcessorType.Succinct, proofBytes); - } - - // ============ batchVerify Tests ============ - - function testBatchVerifyRevertsIfNotProofSubmitter() public { - vm.expectRevert(NitroEnclaveVerifier.CallerNotProofSubmitter.selector); - verifier.batchVerify("", ZkCoProcessorType.RiscZero, ""); - } - - function testBatchVerifySuccess() public { - _setUpRiscZeroConfig(); - - VerifierJournal[] memory results = _batchVerifyRiscZero(_createBatchJournal(VERIFIER_PROOF_ID, 2)); - - assertEq(results.length, 2); - _assertVerificationResult(results[0], VerificationResult.Success); - _assertVerificationResult(results[1], VerificationResult.Success); - } - - function testBatchVerifyRevertsIfVerifierVkMismatch() public { - _setUpRiscZeroConfig(); - - bytes32 wrongVk = keccak256("wrong-vk"); - BatchVerifierJournal memory batchJournal = _createBatchJournal(wrongVk, 1); - (bytes memory output, bytes memory proofBytes) = _mockRiscZeroBatchVerify(batchJournal); - - vm.prank(submitter); - vm.expectRevert( - abi.encodeWithSelector(NitroEnclaveVerifier.VerifierVkMismatch.selector, VERIFIER_PROOF_ID, wrongVk) - ); - verifier.batchVerify(output, ZkCoProcessorType.RiscZero, proofBytes); - } - - function testBatchVerifySuccessSP1() public { - VerifierJournal[] memory results = _batchVerifySP1(_createBatchJournal(VERIFIER_PROOF_ID, 1)); - - assertEq(results.length, 1); - _assertVerificationResult(results[0], VerificationResult.Success); - } - - // ============ Revoked Cert Invalidates Journal ============ - - function testRevokedCertInvalidatesVerification() public { - _setUpRiscZeroConfig(); - - VerifierJournal memory journal = _createSuccessJournal(); - - verifier.revokeCert(INTERMEDIATE_CERT_1); - - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.IntermediateCertsNotTrusted); - } - - // ============ Expiry-Aware Caching Tests ============ - - function testExpiredCachedCertFailsVerification() public { - _setUpRiscZeroConfig(); - - vm.warp(INTERMEDIATE_CERT_1_EXPIRY + 1); - - VerifierJournal memory journal = _createSuccessJournal(); - journal.timestamp = uint64(block.timestamp - 1) * 1000; - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.IntermediateCertsNotTrusted); - } - - function testNonExpiredCachedCertPassesVerification() public { - _setUpRiscZeroConfig(); - - vm.warp(INTERMEDIATE_CERT_1_EXPIRY - 1); - - VerifierJournal memory journal = _createSuccessJournal(); - journal.timestamp = uint64(block.timestamp - 1) * 1000; - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.Success); - } - - // Untrusted chain certs (past trustedCertsPrefixLen): expired journal notAfter => InvalidTimestamp - function testVerifyJournalInvalidTimestampExpiredUntrustedCertInChain() public { - _setUpRiscZeroConfig(); - - bytes32 expiredLeaf = keccak256("expired-untrusted-leaf"); - - VerifierJournal memory journal = _createSuccessJournalWithLeaf(expiredLeaf, uint64(block.timestamp - 1)); - VerifierJournal memory result = _verifyRiscZeroJournal(journal); - - _assertVerificationResult(result, VerificationResult.InvalidTimestamp); - assertEq(verifier.trustedIntermediateCerts(expiredLeaf), 0); - } - - function testCheckTrustedIntermediateCertsStopsAtExpiredCert() public { - vm.warp(INTERMEDIATE_CERT_1_EXPIRY + 1); - - bytes32[][] memory reportCerts = new bytes32[][](1); - reportCerts[0] = new bytes32[](2); - reportCerts[0][0] = ROOT_CERT; - reportCerts[0][1] = INTERMEDIATE_CERT_1; - - uint8[] memory results = verifier.checkTrustedIntermediateCerts(reportCerts); - assertEq(results[0], 1); // only root counted, expired intermediate skipped - } - - function testCacheNewCertStoresCorrectExpiry() public { - _setUpRiscZeroConfig(); - - bytes32 newCert = keccak256("brand-new-cert"); - uint64 newCertExpiry = uint64(REALISTIC_TIMESTAMP + 86_400); // 1 day from now - - VerifierJournal memory journal = _createSuccessJournalWithLeaf(newCert, newCertExpiry); - _verifyRiscZeroJournal(journal); - - assertEq(verifier.trustedIntermediateCerts(newCert), newCertExpiry); - } - - // ============ Helpers ============ - - function _setUpRiscZeroConfig() internal { - verifier.setZkConfiguration(ZkCoProcessorType.RiscZero, _zkConfig(mockRiscZeroVerifier), VERIFIER_PROOF_ID); - } - - function _zkConfig(address zkVerifier) internal pure returns (ZkCoProcessorConfig memory) { - return ZkCoProcessorConfig({ verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: zkVerifier }); - } - - function _expectNotOwnerRevert(address caller) internal { - vm.prank(caller); - vm.expectRevert(Ownable.Unauthorized.selector); - } - - function _expectZkRouteFrozenRevert(ZkCoProcessorType zkType, bytes4 selector) internal { - vm.expectRevert(abi.encodeWithSelector(NitroEnclaveVerifier.ZkRouteFrozen.selector, zkType, selector)); - } - - function _addAndFreezeVerifyRoute(ZkCoProcessorType zkType, bytes4 selector, address routeVerifier) internal { - verifier.addVerifyRoute(zkType, selector, routeVerifier); - verifier.freezeVerifyRoute(zkType, selector); - } - - function _assertVerificationResult(VerifierJournal memory journal, VerificationResult expected) internal pure { - assertEq(uint8(journal.result), uint8(expected)); - } - - function _verifyRiscZeroJournal(VerifierJournal memory journal) internal returns (VerifierJournal memory) { - bytes memory output = abi.encode(journal); - bytes memory proofBytes = _proofBytes(); - - _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); - - vm.prank(submitter); - return verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); - } - - function _verifySP1Journal(VerifierJournal memory journal) internal returns (VerifierJournal memory) { - bytes memory output = abi.encode(journal); - bytes memory proofBytes = _proofBytes(); - - _mockSP1Verify(VERIFIER_ID, output, proofBytes); - - vm.prank(submitter); - return verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); - } - - function _createBatchJournal( - bytes32 verifierVk, - uint256 outputCount - ) - internal - view - returns (BatchVerifierJournal memory) - { - VerifierJournal[] memory outputs = new VerifierJournal[](outputCount); - VerifierJournal memory journal = _createSuccessJournal(); - for (uint256 i; i < outputCount; ++i) { - outputs[i] = journal; - } - - return BatchVerifierJournal({ verifierVk: verifierVk, outputs: outputs }); - } - - function _batchVerifyRiscZero(BatchVerifierJournal memory batchJournal) - internal - returns (VerifierJournal[] memory) - { - (bytes memory output, bytes memory proofBytes) = _mockRiscZeroBatchVerify(batchJournal); - - vm.prank(submitter); - return verifier.batchVerify(output, ZkCoProcessorType.RiscZero, proofBytes); - } - - function _batchVerifySP1(BatchVerifierJournal memory batchJournal) internal returns (VerifierJournal[] memory) { - bytes memory output = abi.encode(batchJournal); - bytes memory proofBytes = _proofBytes(); - - _mockSP1Verify(AGGREGATOR_ID, output, proofBytes); - - vm.prank(submitter); - return verifier.batchVerify(output, ZkCoProcessorType.Succinct, proofBytes); - } - - function _mockRiscZeroBatchVerify(BatchVerifierJournal memory batchJournal) - internal - returns (bytes memory output, bytes memory proofBytes) - { - output = abi.encode(batchJournal); - proofBytes = _proofBytes(); - - _mockRiscZeroVerify(AGGREGATOR_ID, output, proofBytes); - } - - function _proofBytes() internal pure returns (bytes memory) { - return abi.encodePacked(DEFAULT_PROOF_SELECTOR, bytes32(0)); - } - - function _createSuccessJournal() internal view returns (VerifierJournal memory) { - bytes32[] memory certs = new bytes32[](2); - certs[0] = ROOT_CERT; - certs[1] = INTERMEDIATE_CERT_1; - - uint64[] memory expiries = new uint64[](2); - expiries[0] = ROOT_CERT_EXPIRY; - expiries[1] = INTERMEDIATE_CERT_1_EXPIRY; - - return _successJournal(certs, expiries); - } - - function _createSuccessJournalWithLeaf( - bytes32 leafCert, - uint64 leafExpiry - ) - internal - view - returns (VerifierJournal memory) - { - bytes32[] memory certs = new bytes32[](3); - certs[0] = ROOT_CERT; - certs[1] = INTERMEDIATE_CERT_1; - certs[2] = leafCert; - - uint64[] memory expiries = new uint64[](3); - expiries[0] = ROOT_CERT_EXPIRY; - expiries[1] = INTERMEDIATE_CERT_1_EXPIRY; - expiries[2] = leafExpiry; - - return _successJournal(certs, expiries); - } - - function _successJournal( - bytes32[] memory certs, - uint64[] memory expiries - ) - internal - view - returns (VerifierJournal memory) - { - Pcr[] memory pcrs = new Pcr[](0); - - return VerifierJournal({ - result: VerificationResult.Success, - trustedCertsPrefixLen: 2, - timestamp: uint64(block.timestamp - 1) * 1000, - certs: certs, - certExpiries: expiries, - userData: "", - nonce: "", - publicKey: "", - pcrs: pcrs, - moduleId: "test-module" - }); - } - - function _mockRiscZeroVerify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { - vm.mockCall( - mockRiscZeroVerifier, - abi.encodeWithSelector(RISC_ZERO_VERIFY_SELECTOR, proofBytes, programId, sha256(output)), - "" - ); - } - - function _mockSP1Verify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { - vm.mockCall( - mockSP1Verifier, abi.encodeWithSelector(SP1_VERIFY_PROOF_SELECTOR, programId, output, proofBytes), "" - ); - } -} diff --git a/test/deploy/SystemDeploy.t.sol b/test/deploy/SystemDeploy.t.sol index 9c44b0dd2..b9cbcf4c7 100644 --- a/test/deploy/SystemDeploy.t.sol +++ b/test/deploy/SystemDeploy.t.sol @@ -22,14 +22,6 @@ import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { MockNitroValidator } from "test/mocks/MockNitroValidator.sol"; -contract MockNitroEnclaveVerifier { - address public proofSubmitter; - - function setProofSubmitter(address _proofSubmitter) external { - proofSubmitter = _proofSubmitter; - } -} - contract MockLegacyTEEProverRegistry is DevTEEProverRegistry { constructor( INitroValidator nitroValidator, @@ -59,7 +51,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { address internal unsafeBlockSigner = makeAddr("unsafeBlockSigner"); address internal proposer = makeAddr("proposer"); address internal challenger = makeAddr("challenger"); - MockNitroEnclaveVerifier internal nitroEnclaveVerifier; MockNitroValidator internal nitroValidator; MockSP1Verifier internal sp1Verifier; @@ -67,7 +58,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { function setUp() public { systemDeploy = new SystemDeploy(); - nitroEnclaveVerifier = new MockNitroEnclaveVerifier(); nitroValidator = new MockNitroValidator(); sp1Verifier = new MockSP1Verifier(); } @@ -349,7 +339,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { zkAggregationHash: bytes32(uint256(3)), multiproofConfigHash: bytes32(uint256(4)), multiproofGameType: 621, - nitroEnclaveVerifier: address(nitroEnclaveVerifier), nitroValidator: address(nitroValidator), scheduleConfig: AggregateVerifier.ScheduleConfig({ protocolVersions: IProtocolVersions(address(0)), @@ -399,11 +388,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { assertEq(impls.aggregateVerifierImpl, address(_output.opChain.aggregateVerifier), "aggregate verifier impl"); assertEq(impls.teeVerifierImpl, teeVerifierAddr, "tee verifier impl"); assertEq(impls.zkVerifierImpl, zkVerifierAddr, "zk verifier impl"); - assertEq( - address(_output.opChain.nitroEnclaveVerifier), - _input.implementationsInput.nitroEnclaveVerifier, - "nitro enclave verifier" - ); assertEq(address(_output.opChain.nitroValidator), _input.implementationsInput.nitroValidator, "nitro validator"); assertEq(address(_output.opChain.sp1Verifier), address(_input.implementationsInput.sp1Verifier), "sp1 verifier"); assertEq( @@ -417,11 +401,6 @@ contract SystemDeploy_Test is Test, SystemDeployAssertions { assertEq(teeProverRegistry.manager(), _input.opChainInput.roles.opChainProxyAdminOwner, "tee registry manager"); assertTrue(teeProverRegistry.isValidProposer(_input.implementationsInput.teeProposer), "tee proposer"); assertTrue(teeProverRegistry.isValidProposer(_input.implementationsInput.teeChallenger), "tee challenger"); - assertEq( - MockNitroEnclaveVerifier(_input.implementationsInput.nitroEnclaveVerifier).proofSubmitter(), - address(0), - "legacy nitro proof submitter" - ); assertEq( address(teeProverRegistry.NITRO_VALIDATOR()), _input.implementationsInput.nitroValidator, diff --git a/test/setup/Setup.sol b/test/setup/Setup.sol index 230ad88f7..ffe6d60f7 100644 --- a/test/setup/Setup.sol +++ b/test/setup/Setup.sol @@ -35,7 +35,6 @@ import { IOptimismMintableERC721Factory } from "interfaces/L2/IOptimismMintableE import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; -import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { INitroValidator } from "interfaces/L1/proofs/tee/INitroValidator.sol"; import { IL2CrossDomainMessenger } from "interfaces/L2/IL2CrossDomainMessenger.sol"; import { IL2StandardBridge } from "interfaces/L2/IL2StandardBridge.sol"; @@ -125,7 +124,6 @@ abstract contract Setup is FeatureFlags { IGasPriceOracle gasPriceOracle = IGasPriceOracle(Predeploys.GAS_PRICE_ORACLE); IL1Block l1Block = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES); IWETH98 weth = IWETH98(payable(Predeploys.WETH)); - INitroEnclaveVerifier nitroEnclaveVerifier; INitroValidator nitroValidator; TEEProverRegistry teeProverRegistry; @@ -243,7 +241,6 @@ abstract contract Setup is FeatureFlags { proxyAdminOwner = proxyAdmin.owner(); superchainProxyAdmin = IProxyAdmin(EIP1967Helper.getAdmin(address(superchainConfig))); superchainProxyAdminOwner = superchainProxyAdmin.owner(); - nitroEnclaveVerifier = INitroEnclaveVerifier(artifacts.getAddress("NitroEnclaveVerifier")); nitroValidator = INitroValidator(artifacts.getAddress("NitroValidator")); teeProverRegistry = TEEProverRegistry(artifacts.getAddress("TEEProverRegistry"));