diff --git a/deploy-config/local.json b/deploy-config/local.json index 0d4b0ed8f..501074350 100644 --- a/deploy-config/local.json +++ b/deploy-config/local.json @@ -67,6 +67,8 @@ "useRevenueShare": false, "chainFeesRecipient": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "teeImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "teeNitroImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", + "teeTdxImageHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "multiproofConfigHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "multiproofGameType": 621, "teeProposer": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", @@ -74,6 +76,7 @@ "zkRangeHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "zkAggregationHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "nitroEnclaveVerifier": "0x0000000000000000000000000000000000000000", + "tdxVerifier": "0x0000000000000000000000000000000000000001", "multiproofGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", "multiproofGenesisBlockNumber": 0, "multiproofBlockInterval": 100, diff --git a/deploy-config/sepolia.json b/deploy-config/sepolia.json index d2b6b853a..ceb331ae1 100644 --- a/deploy-config/sepolia.json +++ b/deploy-config/sepolia.json @@ -1,5 +1,5 @@ { - "finalSystemOwner": "0x6e427c3212C0b63BE0C382F97715D49b011bFF33", + "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", "superchainConfigGuardian": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", "l1StartingBlockTag": "0x48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b", "l1ChainID": 11155111, @@ -58,7 +58,8 @@ "useFaultProofs": true, "useRevenueShare": true, "chainFeesRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", - "teeImageHash": "0xd04c8147410930fade36986c9357fd9a7591231e38e6e87e51183bd5909f509c", + "teeNitroImageHash": "0x11fb64617dfa2875d31b0cfb656666fd8cee65eb134fefeca171b9b6b4444a64", + "teeTdxImageHash": "0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66", "multiproofConfigHash": "0x12e9c45f19f9817c6d4385fad29e7a70c355502cf0883e76a9a7e478a85d1360", "multiproofGameType": 621, "teeProposer": "0xdb84125f2f4229c81c579f41bc129c71b174eb58", @@ -66,11 +67,12 @@ "zkRangeHash": "0x51708a6b4a3b800a14607e902c1aad47719905c12698a3e01ca8b3321761bc52", "zkAggregationHash": "0x005aa369dd9445e172018a9eaa4a0f9767b2c2079ece90ca120422b3c4c65f11", "nitroEnclaveVerifier": "0x77461a6434fFE3435206B19658F33274f3104e07", + "tdxVerifier": "0x5431a6Fd5365e9629BD2B0969e54c4f09d6781bA", "multiproofGenesisOutputRoot": "0xbc273d5876d1858ecd5aaf4ce4eaf16c73f0187ca4271b774ed5da7d2254ba79", "multiproofGenesisBlockNumber": 37223829, "multiproofBlockInterval": 600, "multiproofIntermediateBlockInterval": 30, - "risc0VerifierRouter": "0xb121b667dd2cf27f95f9f5107137696f56f188f6", + "risc0VerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", "risc0SetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9", "sp1Verifier": "0x397A5f7f3dBd538f23DE225B51f532c34448dA9B" } diff --git a/deploy-config/zeronet-tdx.json b/deploy-config/zeronet-tdx.json new file mode 100644 index 000000000..3aad31a1e --- /dev/null +++ b/deploy-config/zeronet-tdx.json @@ -0,0 +1,78 @@ +{ + "finalSystemOwner": "0x8C1a617BdB47342F9C17Ac8750E0b070c372C721", + "superchainConfigGuardian": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l1StartingBlockTag": "0x48f520cf4ddaf34c8336e6e490632ea3cf1e5e93b0b2bc6e917557e31845371b", + "l1ChainID": 11155111, + "l2ChainID": 11155420, + "l2BlockTime": 2, + "l1BlockTime": 12, + "maxSequencerDrift": 600, + "sequencerWindowSize": 3600, + "channelTimeout": 300, + "p2pSequencerAddress": "0x57CACBB0d30b01eb2462e5dC940c161aff3230D3", + "batchInboxAddress": "0xff00000000000000000000000000000011155420", + "batchSenderAddress": "0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c", + "l2OutputOracleSubmissionInterval": 120, + "l2OutputOracleStartingBlockNumber": 0, + "l2OutputOracleStartingTimestamp": 1690493568, + "l2OutputOracleProposer": "0x49277EE36A024120Ee218127354c4a3591dc90A9", + "l2OutputOracleChallenger": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "finalizationPeriodSeconds": 12, + "proxyAdminOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "baseFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l1FeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "sequencerFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "operatorFeeVaultRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "operatorFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "baseFeeVaultWithdrawalNetwork": 0, + "l1FeeVaultWithdrawalNetwork": 0, + "sequencerFeeVaultWithdrawalNetwork": 0, + "operatorFeeVaultWithdrawalNetwork": 0, + "enableGovernance": true, + "governanceTokenSymbol": "OP", + "governanceTokenName": "Optimism", + "governanceTokenOwner": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "l2GenesisBlockGasLimit": "0x1c9c380", + "l2GenesisBlockBaseFeePerGas": "0x3b9aca00", + "eip1559Denominator": 50, + "eip1559Elasticity": 6, + "l2GenesisRegolithTimeOffset": "0x0", + "systemConfigStartBlock": 4071248, + "fundDevAccounts": false, + "faultGameAbsolutePrestate": "0x03b7eaa4e3cbce90381921a4b48008f4769871d64f93d113fcadca08ecee503b", + "faultGameMaxDepth": 73, + "faultGameClockExtension": 10800, + "faultGameMaxClockDuration": 302400, + "faultGameGenesisBlock": 9496192, + "faultGameGenesisOutputRoot": "0x63b1cda487c072b020a57c1203f7c2921754005cadbd54bed7f558111b8278d8", + "faultGameSplitDepth": 30, + "faultGameWithdrawalDelay": 604800, + "preimageOracleMinProposalSize": 126000, + "preimageOracleChallengePeriod": 86400, + "proofMaturityDelaySeconds": 604800, + "disputeGameFinalityDelaySeconds": 302400, + "respectedGameType": 0, + "useFaultProofs": true, + "useRevenueShare": true, + "chainFeesRecipient": "0xfd1D2e729aE8eEe2E146c033bf4400fE75284301", + "teeNitroImageHash": "0x213ad2e8f1594e1c386ef88ed9d2ec0df5a968f5228a5b17f4b0040599726515", + "teeTdxImageHash": "0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66", + "multiproofConfigHash": "0x12e9c45f19f9817c6d4385fad29e7a70c355502cf0883e76a9a7e478a85d1360", + "multiproofGameType": 621, + "teeProposer": "0xdb84125f2f4229c81c579f41bc129c71b174eb58", + "teeChallenger": "0xadc09b63a3ac57a2ce86d946617a18df9db029a1", + "zkRangeHash": "0x51708a6b4a3b800a14607e902c1aad47719905c12698a3e01ca8b3321761bc52", + "zkAggregationHash": "0x005aa369dd9445e172018a9eaa4a0f9767b2c2079ece90ca120422b3c4c65f11", + "nitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", + "tdxVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", + "multiproofGenesisOutputRoot": "0xbc273d5876d1858ecd5aaf4ce4eaf16c73f0187ca4271b774ed5da7d2254ba79", + "multiproofGenesisBlockNumber": 37223829, + "multiproofBlockInterval": 600, + "multiproofIntermediateBlockInterval": 30, + "risc0VerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "risc0SetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9", + "sp1Verifier": "0x397A5f7f3dBd538f23DE225B51f532c34448dA9B" +} diff --git a/deployments/11155111-dev-with-tdx.json b/deployments/11155111-dev-with-tdx.json new file mode 100644 index 000000000..17d4be0a7 --- /dev/null +++ b/deployments/11155111-dev-with-tdx.json @@ -0,0 +1,13 @@ +{ + "ASRStartingBlockNumber": 41281684, + "ASRStartingOutputRoot": "0xc9f0f3a76f7c15306635a9990a8d4a3c1e281ca9e1519824679a73ca781f3404", + "AggregateVerifier": "0xA1Fd6E923d8Efa8deebde9D2AE98Df08B9A31C7d", + "AnchorStateRegistry": "0xe3E9ec1a72D8AD322187B4a4B757B2e906f4edF8", + "DelayedWETH": "0x2CCe62a3496aC7bD1A706cf7E1a765fd24e59666", + "DisputeGameFactory": "0x4aae21Fc11669463d37d2B875B52c7D63F794a08", + "NitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", + "TDXRegistrationManager": "0x44E999A5859c2D12378a349882fAe5805DCE71b9", + "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", + "TEEProverRegistry": "0x45111D4FDF5EC6bD2bDBF9Aed4a5f01140B892D9", + "TEEVerifier": "0x3065B3175F0265212e041Ea119D0001cf751E5a7" +} \ No newline at end of file diff --git a/deployments/11155111-tdx-verifier.json b/deployments/11155111-tdx-verifier.json new file mode 100644 index 000000000..e35a8cffc --- /dev/null +++ b/deployments/11155111-tdx-verifier.json @@ -0,0 +1,7 @@ +{ + "IntelRootCaHash": "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009", + "MaxTimeDiff": 3600, + "RiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "TDXVerifier": "0x9c38b756B31529Cad02D54A4ba810F3ff1e00054", + "TDXVerifierId": "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" +} diff --git a/deployments/11155111-tee-verifiers.json b/deployments/11155111-tee-verifiers.json new file mode 100644 index 000000000..ebe25da12 --- /dev/null +++ b/deployments/11155111-tee-verifiers.json @@ -0,0 +1,17 @@ +{ + "IntelRootCaHash": "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009", + "NitroEnclaveVerifier": "0x2DC52760D13a3C2dF33fcc42913C4dddd8d976B9", + "NitroMaxTimeDiff": 3600, + "NitroRiscZeroVerifierRouter": "0xB121B667dd2cf27F95f9F5107137696F56f188f6", + "NitroRootCert": "0x641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68f79bb5b", + "NitroVerifierId": "0x15051db631d6ed382d957c795a558a0abdd00d0d22a1670455721bc2712d3d6e", + "NitroVerifierProofId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "RiscZeroSetBuilderImageId": "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9", + "RiscZeroSetVerifier": "0xef981185595Ed2a49dEC9B2D5073B88Ef9e00Fd4", + "RiscZeroSetVerifierSelector": "0x242f9d5b00000000000000000000000000000000000000000000000000000000", + "RiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "TDXMaxTimeDiff": 3600, + "TDXRiscZeroVerifierRouter": "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187", + "TDXVerifier": "0xB5F7f92dA7aBfDBDEB8e84EE78765fd0D3D3E092", + "TDXVerifierId": "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" +} \ No newline at end of file diff --git a/interfaces/L1/proofs/tee/ITDXVerifier.sol b/interfaces/L1/proofs/tee/ITDXVerifier.sol new file mode 100644 index 000000000..de7ae0f05 --- /dev/null +++ b/interfaces/L1/proofs/tee/ITDXVerifier.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ZkCoProcessorType, ZkCoProcessorConfig } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; + +/// @notice Statuses that a TDX quote/collateral verifier may emit. +/// @dev Unknown is index 0 so uninitialized values fail closed. +enum TDXVerificationResult { + Unknown, + Success, + InvalidQuote, + QuoteSignatureInvalid, + RootCaNotTrusted, + PckCertChainInvalid, + TcbInfoInvalid, + QeIdentityInvalid, + TcbStatusNotAllowed, + CollateralExpired, + InvalidTimestamp, + ReportDataMismatch +} + +/// @notice Intel TDX TCB status reduced to the statuses this contract's policy needs. +/// @dev Unknown is index 0 so uninitialized values fail closed. +enum TDXTcbStatus { + Unknown, + UpToDate, + SwHardeningNeeded, + ConfigurationNeeded, + ConfigurationAndSwHardeningNeeded, + OutOfDate, + OutOfDateConfigurationNeeded, + Revoked +} + +/// @notice Public journal emitted by the off-chain/ZK TDX DCAP verifier. +/// @param result Overall verification result after quote and collateral validation in the guest. +/// @param tcbStatus Intel TDX TCB status for the platform. +/// @param timestamp Quote timestamp in milliseconds since Unix epoch. +/// @param collateralExpiration Earliest expiration timestamp in seconds across accepted collateral. +/// @param rootCaHash Hash of the Intel root CA used to validate the PCK/collateral signing chains. +/// @param pckCertHash Hash of the PCK leaf certificate that signed the quote attestation key chain. +/// @param tcbInfoHash Hash of the TCB info collateral consumed by the guest. +/// @param qeIdentityHash Hash of the QE identity collateral consumed by the guest. +/// @param publicKey Uncompressed secp256k1 public key: 0x04 || x || y. +/// @param signer Ethereum address derived from publicKey. +/// @param imageHash Multiproof-compatible image hash derived from MRTD and RTMR0-3. +/// @param mrTdHash keccak256 hash of the 48-byte MRTD measurement. +/// @param reportDataPrefix First 32 bytes of TDREPORT.REPORTDATA. +/// @param reportDataSuffix Last 32 bytes of TDREPORT.REPORTDATA, available for app-specific binding. +struct TDXVerifierJournal { + TDXVerificationResult result; + TDXTcbStatus tcbStatus; + uint64 timestamp; + uint64 collateralExpiration; + bytes32 rootCaHash; + bytes32 pckCertHash; + bytes32 tcbInfoHash; + bytes32 qeIdentityHash; + bytes publicKey; + address signer; + bytes32 imageHash; + bytes32 mrTdHash; + bytes32 reportDataPrefix; + bytes32 reportDataSuffix; +} + +/// @title ITDXVerifier +/// @notice Interface for Intel TDX quote verification used by TDX-aware TEE prover registries. +interface ITDXVerifier { + /// @notice Verifies a ZK proof of Intel TDX DCAP quote verification and returns attested signer metadata. + /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. + /// @param zkCoprocessor ZK proving system used to generate the proof. + /// @param proofBytes ZK proof bytes. + /// @return journal Verified TDX attestation metadata. + function verify( + bytes calldata output, + ZkCoProcessorType zkCoprocessor, + bytes calldata proofBytes + ) + external + returns (TDXVerifierJournal memory journal); + + /// @notice Retrieves the configuration for a specific coprocessor. + function getZkConfig(ZkCoProcessorType zkCoprocessor) external view returns (ZkCoProcessorConfig memory); + + /// @notice Returns whether a TCB status is accepted by verifier policy. + function allowedTcbStatuses(TDXTcbStatus status) external view returns (bool); + + /// @notice Updates the address authorized to submit verified proofs. + function setProofSubmitter(address newProofSubmitter) external; +} diff --git a/scripts/deploy/Deploy.s.sol b/scripts/deploy/Deploy.s.sol index df774492d..a1d7fe9ba 100644 --- a/scripts/deploy/Deploy.s.sol +++ b/scripts/deploy/Deploy.s.sol @@ -235,10 +235,12 @@ contract Deploy is Deployer { faultGameV2SplitDepth: cfg.faultGameV2SplitDepth(), faultGameV2ClockExtension: cfg.faultGameV2ClockExtension(), faultGameV2MaxClockDuration: cfg.faultGameV2MaxClockDuration(), - teeImageHash: cfg.teeImageHash(), + teeNitroImageHash: cfg.teeNitroImageHash(), + teeTdxImageHash: cfg.teeTdxImageHash(), multiproofConfigHash: cfg.multiproofConfigHash(), multiproofGameType: cfg.multiproofGameType(), nitroEnclaveVerifier: cfg.nitroEnclaveVerifier(), + tdxVerifier: cfg.tdxVerifier(), l2ChainID: cfg.l2ChainID(), multiproofBlockInterval: cfg.multiproofBlockInterval(), multiproofIntermediateBlockInterval: cfg.multiproofIntermediateBlockInterval(), diff --git a/scripts/deploy/DeployConfig.s.sol b/scripts/deploy/DeployConfig.s.sol index 2f3270533..e036b643d 100644 --- a/scripts/deploy/DeployConfig.s.sol +++ b/scripts/deploy/DeployConfig.s.sol @@ -85,6 +85,8 @@ contract DeployConfig is Script { // Multiproof Configuration bytes32 public teeImageHash; + bytes32 public teeNitroImageHash; + bytes32 public teeTdxImageHash; bytes32 public multiproofConfigHash; uint256 public multiproofGameType; address public teeProposer; @@ -92,6 +94,7 @@ contract DeployConfig is Script { bytes32 public zkRangeHash; bytes32 public zkAggregationHash; address public nitroEnclaveVerifier; + address public tdxVerifier; bytes32 public multiproofGenesisOutputRoot; uint256 public multiproofGenesisBlockNumber; uint256 public multiproofBlockInterval; @@ -198,6 +201,8 @@ contract DeployConfig is Script { faultGameV2ClockExtension = _readOr(_json, "$.faultGameV2ClockExtension", 10800); faultGameV2MaxClockDuration = _readOr(_json, "$.faultGameV2MaxClockDuration", 302400); teeImageHash = bytes32(_readOr(_json, "$.teeImageHash", 0)); + teeNitroImageHash = bytes32(_readOr(_json, "$.teeNitroImageHash", uint256(teeImageHash))); + teeTdxImageHash = bytes32(_readOr(_json, "$.teeTdxImageHash", uint256(teeImageHash))); multiproofConfigHash = bytes32(_readOr(_json, "$.multiproofConfigHash", 0)); multiproofGameType = _readOr(_json, "$.multiproofGameType", 621); teeProposer = stdJson.readAddress(_json, "$.teeProposer"); @@ -205,6 +210,7 @@ contract DeployConfig is Script { zkRangeHash = stdJson.readBytes32(_json, "$.zkRangeHash"); zkAggregationHash = stdJson.readBytes32(_json, "$.zkAggregationHash"); nitroEnclaveVerifier = _readOr(_json, "$.nitroEnclaveVerifier", address(0)); + tdxVerifier = _readOr(_json, "$.tdxVerifier", address(0)); multiproofGenesisOutputRoot = bytes32(_readOr(_json, "$.multiproofGenesisOutputRoot", uint256(1))); multiproofGenesisBlockNumber = _readOr(_json, "$.multiproofGenesisBlockNumber", 0); multiproofBlockInterval = _readOr(_json, "$.multiproofBlockInterval", 100); diff --git a/scripts/deploy/DeployImplementations.s.sol b/scripts/deploy/DeployImplementations.s.sol index cf7a516ea..9ca6e9ac2 100644 --- a/scripts/deploy/DeployImplementations.s.sol +++ b/scripts/deploy/DeployImplementations.s.sol @@ -45,6 +45,7 @@ import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; import { GameType } from "src/libraries/bridge/Types.sol"; import { ZKVerifier } from "src/L1/proofs/zk/ZKVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; contract DeployImplementations is Script { struct Input { @@ -61,10 +62,12 @@ contract DeployImplementations is Script { uint256 faultGameV2ClockExtension; uint256 faultGameV2MaxClockDuration; // Multiproof parameters - bytes32 teeImageHash; + bytes32 teeNitroImageHash; + bytes32 teeTdxImageHash; bytes32 multiproofConfigHash; uint256 multiproofGameType; address nitroEnclaveVerifier; + address tdxVerifier; uint256 l2ChainID; uint256 multiproofBlockInterval; uint256 multiproofIntermediateBlockInterval; @@ -615,7 +618,9 @@ contract DeployImplementations is Script { address teeVerifierImpl; { TEEProverRegistry scgImpl = new TEEProverRegistry( - INitroEnclaveVerifier(_input.nitroEnclaveVerifier), IDisputeGameFactory(address(1)) + INitroEnclaveVerifier(_input.nitroEnclaveVerifier), + ITDXVerifier(_input.tdxVerifier), + IDisputeGameFactory(address(1)) ); vm.label(address(scgImpl), "TEEProverRegistryImpl"); _output.teeProverRegistryImpl = scgImpl; @@ -630,7 +635,7 @@ contract DeployImplementations is Script { _output.delayedWETHImpl, IVerifier(teeVerifierImpl), IVerifier(zkVerifier), - _input.teeImageHash, + AggregateVerifier.TeeHashes(_input.teeNitroImageHash, _input.teeTdxImageHash), AggregateVerifier.ZkHashes(bytes32(0), bytes32(0)), _input.multiproofConfigHash, _input.l2ChainID, @@ -680,6 +685,7 @@ contract DeployImplementations is Script { "DeployImplementations: disputeGameFinalityDelaySeconds not set" ); require(_input.mipsVersion != 0, "DeployImplementations: mipsVersion not set"); + require(_input.tdxVerifier != address(0), "DeployImplementations: tdxVerifier not set"); require( address(_input.superchainConfigProxy) != address(0), "DeployImplementations: superchainConfigProxy not set" ); diff --git a/scripts/multiproof/DeployDevNoNitro.s.sol b/scripts/multiproof/DeployDevNoNitro.s.sol index 6c925590f..6147061de 100644 --- a/scripts/multiproof/DeployDevNoNitro.s.sol +++ b/scripts/multiproof/DeployDevNoNitro.s.sol @@ -26,10 +26,14 @@ pragma solidity 0.8.15; * SIGNER REGISTRATION (SIMPLIFIED) * ───────────────────────────────────────────────────────────────────────────────── * - * After deployment, register a signer with a single call: + * After deployment, register one dev Nitro signer and one dev TDX signer: * * cast send $TEE_PROVER_REGISTRY \ - * "addDevSigner(address,bytes32)" $SIGNER_ADDRESS $TEE_IMAGE_HASH \ + * "addDevSigner(address,bytes32)" $NITRO_SIGNER_ADDRESS $TEE_NITRO_IMAGE_HASH \ + * --private-key $OWNER_KEY --rpc-url $RPC_URL + * + * cast send $TEE_PROVER_REGISTRY \ + * "addDevTDXSigner(address,bytes32)" $TDX_SIGNER_ADDRESS $TEE_TDX_IMAGE_HASH \ * --private-key $OWNER_KEY --rpc-url $RPC_URL * * No attestation, PCR0 registration, or certificate validation required. @@ -72,6 +76,7 @@ import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; import { MockVerifier } from "test/mocks/MockVerifier.sol"; import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; @@ -89,6 +94,7 @@ contract DeployDevNoNitro is Script { DeployConfig public constant cfg = DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); + address public tdxVerifierAddr; address public teeProverRegistryProxy; address public teeVerifier; address public disputeGameFactory; @@ -99,6 +105,7 @@ contract DeployDevNoNitro is Script { function setUp() public { DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); cfg.read(Config.deployConfigPath()); + tdxVerifierAddr = cfg.tdxVerifier(); } function run() public { @@ -110,8 +117,10 @@ contract DeployDevNoNitro is Script { console.log("TEE Proposer:", cfg.teeProposer()); console.log("TEE Challenger:", cfg.teeChallenger()); console.log("Game Type:", cfg.multiproofGameType()); + console.log("TDXVerifier:", tdxVerifierAddr); console.log(""); console.log("NOTE: Using DevTEEProverRegistry - NO attestation required."); + require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); vm.startBroadcast(); @@ -128,7 +137,11 @@ contract DeployDevNoNitro is Script { function _deployTEEContracts(GameType gameType) internal { address owner = cfg.finalSystemOwner(); address teeRegistryImpl = address( - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(disputeGameFactory)) + new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), + ITDXVerifier(tdxVerifierAddr), + IDisputeGameFactory(disputeGameFactory) + ) ); address[] memory initialProposers = new address[](2); initialProposers[0] = cfg.teeProposer(); @@ -177,7 +190,7 @@ contract DeployDevNoNitro is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - cfg.teeImageHash(), + AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainID(), @@ -197,6 +210,7 @@ contract DeployDevNoNitro is Script { console.log("========================================"); console.log("\nTEE Contracts:"); console.log(" DevTEEProverRegistry:", teeProverRegistryProxy); + console.log(" TDXVerifier:", tdxVerifierAddr); console.log(" TEEVerifier:", teeVerifier); console.log("\nInfrastructure:"); console.log(" DisputeGameFactory:", disputeGameFactory); @@ -205,13 +219,18 @@ contract DeployDevNoNitro is Script { console.log("\nGame:"); console.log(" AggregateVerifier:", aggregateVerifier); console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); + console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); + console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); - console.log("\n>>> NEXT STEP - Register dev signer (NO ATTESTATION NEEDED) <<<"); + console.log("\n>>> NEXT STEP - Register dev Nitro and TDX signers (NO ATTESTATION NEEDED) <<<"); + console.log("\ncast send", teeProverRegistryProxy); + console.log(' "addDevSigner(address,bytes32)" '); + console.log(" ", vm.toString(cfg.teeNitroImageHash())); + console.log(" --private-key --rpc-url "); console.log("\ncast send", teeProverRegistryProxy); - console.log(' "addDevSigner(address,bytes32)" '); - console.log(" ", vm.toString(cfg.teeImageHash())); + console.log(' "addDevTDXSigner(address,bytes32)" '); + console.log(" ", vm.toString(cfg.teeTdxImageHash())); console.log(" --private-key --rpc-url "); console.log("\n========================================\n"); } @@ -219,6 +238,7 @@ contract DeployDevNoNitro is Script { function _writeOutput() internal { string memory key = "deployment"; vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); vm.serializeAddress(key, "TEEVerifier", teeVerifier); vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); diff --git a/scripts/multiproof/DeployDevWithNitro.s.sol b/scripts/multiproof/DeployDevWithNitro.s.sol index fd1dfc721..492026b63 100644 --- a/scripts/multiproof/DeployDevWithNitro.s.sol +++ b/scripts/multiproof/DeployDevWithNitro.s.sol @@ -14,8 +14,8 @@ pragma solidity 0.8.15; * You cannot use addDevSigner() - you must go through the full registerSigner() flow. * * PREREQUISITES: - * 1. Deploy the RISC Zero verifier stack AND NitroEnclaveVerifier using - * DeployRiscZeroStack.s.sol (required because NitroEnclaveVerifier and its + * 1. Deploy the RISC Zero verifier route AND NitroEnclaveVerifier using + * DeployNitroVerifier.s.sol (required because NitroEnclaveVerifier and its * dependencies need Solidity ^0.8.20, while this script is pinned to =0.8.15). * 2. Set `nitroEnclaveVerifier` in the deploy config to the deployed address. * @@ -37,6 +37,7 @@ pragma solidity 0.8.15; */ import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { Proxy } from "src/universal/Proxy.sol"; import { Script } from "lib/forge-std/src/Script.sol"; import { console2 as console } from "lib/forge-std/src/console2.sol"; @@ -64,7 +65,7 @@ import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; /// @title DeployDevWithNitro /// @notice Development deployment WITH AWS Nitro attestation validation. /// @dev Uses real TEEProverRegistry which requires registerSigner() with valid attestation. -/// NitroEnclaveVerifier must be pre-deployed via DeployRiscZeroStack.s.sol. +/// NitroEnclaveVerifier must be pre-deployed via DeployNitroVerifier.s.sol. contract DeployDevWithNitro is Script { uint256 public constant BLOCK_INTERVAL = 600; uint256 public constant INTERMEDIATE_BLOCK_INTERVAL = 30; @@ -74,6 +75,7 @@ contract DeployDevWithNitro is Script { DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); address public nitroEnclaveVerifierAddr; + address public tdxVerifierAddr; address public teeProverRegistryProxy; address public teeVerifier; address public disputeGameFactory; @@ -89,12 +91,14 @@ contract DeployDevWithNitro is Script { function run() public { GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); - // NitroEnclaveVerifier must be pre-deployed (via DeployRiscZeroStack.s.sol) + // NitroEnclaveVerifier must be pre-deployed (via DeployNitroVerifier.s.sol) nitroEnclaveVerifierAddr = cfg.nitroEnclaveVerifier(); + tdxVerifierAddr = cfg.tdxVerifier(); require( nitroEnclaveVerifierAddr != address(0), - "nitroEnclaveVerifier must be set in config (deploy via DeployRiscZeroStack.s.sol first)" + "nitroEnclaveVerifier must be set in config (deploy via DeployNitroVerifier.s.sol first)" ); + require(tdxVerifierAddr != address(0), "tdxVerifier must be set in config"); console.log("=== Deploying Dev Infrastructure (WITH NITRO) ==="); console.log("Chain ID:", block.chainid); @@ -103,6 +107,7 @@ contract DeployDevWithNitro is Script { console.log("TEE Challenger:", cfg.teeChallenger()); console.log("Game Type:", cfg.multiproofGameType()); console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); + console.log("TDXVerifier:", tdxVerifierAddr); console.log(""); console.log("NOTE: Using REAL TEEProverRegistry - ZK attestation proof REQUIRED."); @@ -122,7 +127,9 @@ contract DeployDevWithNitro is Script { address owner = cfg.finalSystemOwner(); address teeRegistryImpl = address( new TEEProverRegistry( - INitroEnclaveVerifier(nitroEnclaveVerifierAddr), IDisputeGameFactory(disputeGameFactory) + INitroEnclaveVerifier(nitroEnclaveVerifierAddr), + ITDXVerifier(tdxVerifierAddr), + IDisputeGameFactory(disputeGameFactory) ) ); address[] memory initialProposers = new address[](2); @@ -172,7 +179,7 @@ contract DeployDevWithNitro is Script { IDelayedWETH(payable(mockDelayedWETH)), IVerifier(teeVerifier), IVerifier(zkVerifier), - cfg.teeImageHash(), + AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), zkHashes, cfg.multiproofConfigHash(), cfg.l2ChainID(), @@ -192,6 +199,7 @@ contract DeployDevWithNitro is Script { console.log("========================================"); console.log("\nTEE Contracts:"); console.log(" NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); + console.log(" TDXVerifier:", tdxVerifierAddr); console.log(" TEEProverRegistry:", teeProverRegistryProxy); console.log(" TEEVerifier:", teeVerifier); console.log("\nInfrastructure:"); @@ -201,12 +209,16 @@ contract DeployDevWithNitro is Script { console.log("\nGame:"); console.log(" AggregateVerifier:", aggregateVerifier); console.log(" Game Type:", cfg.multiproofGameType()); - console.log(" TEE Image Hash:", vm.toString(cfg.teeImageHash())); + console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); + console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); console.log("========================================"); - console.log("\n>>> NEXT STEP: Register signer with ZK attestation proof <<<"); + console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); console.log("\n cast send", teeProverRegistryProxy); - console.log(' "registerSigner(bytes,bytes)" '); + console.log(' "registerSigner(bytes,bytes)" '); + console.log(" --private-key --rpc-url "); + console.log("\n cast send", teeProverRegistryProxy); + console.log(' "registerTDXSigner(bytes,bytes)" '); console.log(" --private-key --rpc-url "); console.log("\n========================================\n"); } @@ -216,6 +228,7 @@ contract DeployDevWithNitro is Script { vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); vm.serializeAddress(key, "TEEVerifier", teeVerifier); vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); diff --git a/scripts/multiproof/DeployDevWithTDX.s.sol b/scripts/multiproof/DeployDevWithTDX.s.sol new file mode 100644 index 000000000..e600c33c6 --- /dev/null +++ b/scripts/multiproof/DeployDevWithTDX.s.sol @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +/** + * @title DeployDevWithTDX + * @notice Development deployment using the TDX signer-registration path. + * + * This deploys the same local multiproof testing infrastructure as the existing + * dev scripts, but configures TEEProverRegistry for TDX signer registration. Deploy + * TDXVerifier first with DeployTDXVerifier.s.sol and pass its address to run(). + * The NitroEnclaveVerifier can either come from the deploy config or be passed + * explicitly to the five-argument run() overload. + * The default run(address) entrypoint configures DEFAULT_TDX_REGISTRATION_MANAGER + * as the registry manager so it can submit TDX signer registrations. + */ + +import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { Proxy } from "src/universal/Proxy.sol"; +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; +import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; +import { IDelayedWETH } from "interfaces/L1/proofs/IDelayedWETH.sol"; +import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; +import { DisputeGameFactory } from "src/L1/proofs/DisputeGameFactory.sol"; +import { GameType, Hash } from "src/libraries/bridge/Types.sol"; + +import { DeployConfig } from "scripts/deploy/DeployConfig.s.sol"; +import { Config } from "scripts/libraries/Config.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +import { AggregateVerifier } from "src/L1/proofs/AggregateVerifier.sol"; +import { IVerifier } from "interfaces/L1/proofs/IVerifier.sol"; +import { MockVerifier } from "test/mocks/MockVerifier.sol"; +import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; +import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; + +import { MinimalProxyAdmin } from "./mocks/MinimalProxyAdmin.sol"; +import { MockAnchorStateRegistry } from "./mocks/MockAnchorStateRegistry.sol"; +import { MockDelayedWETH } from "./mocks/MockDelayedWETH.sol"; + +contract DeployDevWithTDX is Script { + uint256 public constant INIT_BOND = 0.00001 ether; + address public constant DEFAULT_TDX_REGISTRATION_MANAGER = 0x93900CB7eCdB5994352b19DfD8a900Cd4fa437B7; + + DeployConfig public constant cfg = + DeployConfig(address(uint160(uint256(keccak256(abi.encode("optimism.deployconfig")))))); + + address public nitroEnclaveVerifierAddr; + address public tdxVerifierAddr; + address public tdxRegistrationManager; + address public teeProverRegistryProxy; + address public teeVerifier; + address public disputeGameFactory; + IAnchorStateRegistry public mockAnchorRegistry; + address public mockDelayedWETH; + address public aggregateVerifier; + Hash public startingAnchorRoot; + uint256 public startingAnchorBlockNumber; + + function setUp() public { + DeployUtils.etchLabelAndAllowCheatcodes({ _etchTo: address(cfg), _cname: "DeployConfig" }); + cfg.read(Config.deployConfigPath()); + } + + function run(address tdxVerifier) public { + run(tdxVerifier, DEFAULT_TDX_REGISTRATION_MANAGER); + } + + function run(address tdxVerifier, address registrationManager) public { + run(tdxVerifier, registrationManager, cfg.multiproofGenesisOutputRoot(), cfg.multiproofGenesisBlockNumber()); + } + + function run( + address tdxVerifier, + address registrationManager, + bytes32 asrStartingOutputRoot, + uint256 asrStartingBlockNumber + ) + public + { + run(cfg.nitroEnclaveVerifier(), tdxVerifier, registrationManager, asrStartingOutputRoot, asrStartingBlockNumber); + } + + function run( + address nitroEnclaveVerifier, + address tdxVerifier, + address registrationManager, + bytes32 asrStartingOutputRoot, + uint256 asrStartingBlockNumber + ) + public + { + nitroEnclaveVerifierAddr = nitroEnclaveVerifier; + require(tdxVerifier != address(0), "tdxVerifier must be non-zero"); + require(nitroEnclaveVerifierAddr != address(0), "nitroEnclaveVerifier must be non-zero"); + require(registrationManager != address(0), "registrationManager must be non-zero"); + require(asrStartingOutputRoot != bytes32(0), "asrStartingOutputRoot must be non-zero"); + tdxVerifierAddr = tdxVerifier; + tdxRegistrationManager = registrationManager; + startingAnchorRoot = Hash.wrap(asrStartingOutputRoot); + startingAnchorBlockNumber = asrStartingBlockNumber; + + GameType gameType = GameType.wrap(uint32(cfg.multiproofGameType())); + + console.log("=== Deploying Dev Infrastructure (WITH TDX) ==="); + console.log("Chain ID:", block.chainid); + console.log("Owner:", cfg.finalSystemOwner()); + console.log("TEE Proposer:", cfg.teeProposer()); + console.log("TEE Challenger:", cfg.teeChallenger()); + console.log("Game Type:", cfg.multiproofGameType()); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); + console.log("TDXVerifier:", tdxVerifierAddr); + console.log("TDX Registration Manager:", tdxRegistrationManager); + console.log("ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); + console.log("ASR Starting L2 Block:", startingAnchorBlockNumber); + console.log(""); + console.log("NOTE: TDXVerifier and NitroEnclaveVerifier owners must be the broadcaster/finalSystemOwner."); + + vm.startBroadcast(); + + _deployInfrastructure(gameType); + _deployTDXContracts(gameType); + _deployAggregateVerifier(gameType); + + vm.stopBroadcast(); + + _printSummary(); + _writeOutput(); + } + + function _deployTDXContracts(GameType gameType) internal { + address owner = cfg.finalSystemOwner(); + address registryImpl = address( + new TEEProverRegistry( + INitroEnclaveVerifier(nitroEnclaveVerifierAddr), + ITDXVerifier(tdxVerifierAddr), + IDisputeGameFactory(disputeGameFactory) + ) + ); + + address[] memory initialProposers = new address[](2); + initialProposers[0] = cfg.teeProposer(); + initialProposers[1] = cfg.teeChallenger(); + + Proxy registryProxy = new Proxy(msg.sender); + registryProxy.upgradeToAndCall( + registryImpl, + abi.encodeCall(TEEProverRegistry.initialize, (owner, tdxRegistrationManager, initialProposers, gameType)) + ); + registryProxy.changeAdmin(address(0xdead)); + teeProverRegistryProxy = address(registryProxy); + + ITDXVerifier(tdxVerifierAddr).setProofSubmitter(teeProverRegistryProxy); + INitroEnclaveVerifier(nitroEnclaveVerifierAddr).setProofSubmitter(teeProverRegistryProxy); + + teeVerifier = address(new TEEVerifier(TEEProverRegistry(teeProverRegistryProxy), mockAnchorRegistry)); + } + + function _deployInfrastructure(GameType gameType) internal { + address factoryImpl = address(new DisputeGameFactory()); + MinimalProxyAdmin proxyAdmin = new MinimalProxyAdmin(cfg.finalSystemOwner()); + + Proxy proxy = new Proxy(msg.sender); + proxy.upgradeTo(factoryImpl); + proxy.changeAdmin(address(proxyAdmin)); + DisputeGameFactory(address(proxy)).initialize(cfg.finalSystemOwner()); + disputeGameFactory = address(proxy); + + MockAnchorStateRegistry asr = new MockAnchorStateRegistry(); + mockAnchorRegistry = IAnchorStateRegistry(address(asr)); + asr.initialize(disputeGameFactory, startingAnchorRoot, startingAnchorBlockNumber, gameType); + } + + function _deployAggregateVerifier(GameType gameType) internal { + address zkVerifier = address(new MockVerifier(mockAnchorRegistry)); + mockDelayedWETH = address(new MockDelayedWETH()); + + AggregateVerifier.ZkHashes memory zkHashes = + AggregateVerifier.ZkHashes({ rangeHash: cfg.zkRangeHash(), aggregateHash: cfg.zkAggregationHash() }); + + aggregateVerifier = address( + new AggregateVerifier( + gameType, + mockAnchorRegistry, + IDelayedWETH(payable(mockDelayedWETH)), + IVerifier(teeVerifier), + IVerifier(zkVerifier), + AggregateVerifier.TeeHashes(cfg.teeNitroImageHash(), cfg.teeTdxImageHash()), + zkHashes, + cfg.multiproofConfigHash(), + cfg.l2ChainID(), + cfg.multiproofBlockInterval(), + cfg.multiproofIntermediateBlockInterval() + ) + ); + + DisputeGameFactory factory = DisputeGameFactory(disputeGameFactory); + factory.setImplementation(gameType, IDisputeGame(aggregateVerifier), ""); + factory.setInitBond(gameType, INIT_BOND); + } + + function _printSummary() internal view { + console.log("\n========================================"); + console.log(" DEV DEPLOYMENT COMPLETE (TDX)"); + console.log("========================================"); + console.log("\nTDX Contracts:"); + console.log(" NitroEnclaveVerifier:", nitroEnclaveVerifierAddr); + console.log(" TDXVerifier:", tdxVerifierAddr); + console.log(" TEEProverRegistry:", teeProverRegistryProxy); + console.log(" TDX Registration Manager:", tdxRegistrationManager); + console.log(" TEEVerifier:", teeVerifier); + console.log("\nInfrastructure:"); + console.log(" DisputeGameFactory:", disputeGameFactory); + console.log(" AnchorStateRegistry (mock):", address(mockAnchorRegistry)); + console.log(" ASR Starting Output Root:", vm.toString(startingAnchorRoot.raw())); + console.log(" ASR Starting L2 Block:", startingAnchorBlockNumber); + console.log(" DelayedWETH (mock):", mockDelayedWETH); + console.log("\nGame:"); + console.log(" AggregateVerifier:", aggregateVerifier); + console.log(" Game Type:", cfg.multiproofGameType()); + console.log(" Nitro Image Hash:", vm.toString(cfg.teeNitroImageHash())); + console.log(" TDX Image Hash:", vm.toString(cfg.teeTdxImageHash())); + console.log(" Config Hash:", vm.toString(cfg.multiproofConfigHash())); + console.log("========================================"); + console.log("\n>>> NEXT STEP: Register one Nitro signer and one TDX signer <<<"); + console.log("\n cast send", teeProverRegistryProxy); + console.log(' "registerSigner(bytes,bytes)" '); + console.log(" --private-key --rpc-url "); + console.log("\n cast send", teeProverRegistryProxy); + console.log(' "registerTDXSigner(bytes,bytes)" '); + console.log(" --private-key --rpc-url "); + console.log("\n========================================\n"); + } + + function _writeOutput() internal { + string memory key = "deployment"; + vm.serializeAddress(key, "NitroEnclaveVerifier", nitroEnclaveVerifierAddr); + vm.serializeAddress(key, "TDXVerifier", tdxVerifierAddr); + vm.serializeAddress(key, "TDXRegistrationManager", tdxRegistrationManager); + vm.serializeAddress(key, "TEEProverRegistry", teeProverRegistryProxy); + vm.serializeAddress(key, "TEEVerifier", teeVerifier); + vm.serializeAddress(key, "DisputeGameFactory", disputeGameFactory); + vm.serializeAddress(key, "AnchorStateRegistry", address(mockAnchorRegistry)); + vm.serializeBytes32(key, "ASRStartingOutputRoot", startingAnchorRoot.raw()); + vm.serializeUint(key, "ASRStartingBlockNumber", startingAnchorBlockNumber); + vm.serializeAddress(key, "DelayedWETH", mockDelayedWETH); + string memory json = vm.serializeAddress(key, "AggregateVerifier", aggregateVerifier); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-dev-with-tdx.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/DeployNitroVerifier.s.sol b/scripts/multiproof/DeployNitroVerifier.s.sol new file mode 100644 index 000000000..f84289df6 --- /dev/null +++ b/scripts/multiproof/DeployNitroVerifier.s.sol @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title DeployNitroVerifier + * @notice Deploys the RISC Zero set verifier route and NitroEnclaveVerifier used by TEEProverRegistry. + * + * This script is separated from the main multiproof deployment scripts because + * NitroEnclaveVerifier imports verifier interfaces that require Solidity ^0.8.20, + * while the multiproof stack is pinned to Solidity 0.8.15. + * + * Usage: + * + * forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + * --sig "run(address,address,bytes32,bytes32,bytes32)" \ + * \ + * \ + * --rpc-url --broadcast --private-key + * + * If using batched Nitro proofs, use the overload that also supplies + * : + * + * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" + * + * The broadcaster must be the owner because this script calls addVerifyRoute() + * on the freshly deployed NitroEnclaveVerifier. + * + * After running DeployDevWithTDX.s.sol, the Nitro verifier's proofSubmitter is + * updated to the deployed TEEProverRegistry. + */ + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; + +import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; +import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; + +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; + +contract DeployNitroVerifier is Script { + /// @notice Maximum Nitro attestation age accepted by NitroEnclaveVerifier. + uint64 internal constant NITRO_MAX_TIME_DIFF = 3600; + + address public setVerifier; + address public nitroEnclaveVerifier; + + /// @param owner Owner for NitroEnclaveVerifier. Must be the broadcaster for route setup. + /// @param risc0VerifierRouter Existing RISC Zero verifier router. + /// @param setBuilderImageId RISC Zero set builder image ID. + /// @param nitroRootCert SHA-256 hash of the AWS Nitro root certificate. + /// @param nitroVerifierId RISC Zero image ID for the Nitro attestation verifier guest. + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId + ) + public + { + run(owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, bytes32(0)); + } + + /// @param nitroVerifierProofId Optional verifier proof ID used by Nitro batch verification. + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + public + { + bytes4 setVerifierSelector = + _validateInputs(owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId); + + console.log("=== Deploying NitroEnclaveVerifier ==="); + console.log("Owner:", owner); + console.log("RISC Zero Verifier Router:", risc0VerifierRouter); + console.log("Set Builder Image ID:", vm.toString(setBuilderImageId)); + console.log("Set Verifier Selector:", vm.toString(setVerifierSelector)); + console.log("Nitro Root Cert:", vm.toString(nitroRootCert)); + console.log("Nitro Verifier ID:", vm.toString(nitroVerifierId)); + console.log("Nitro Verifier Proof ID:", vm.toString(nitroVerifierProofId)); + console.log("Max Time Diff:", NITRO_MAX_TIME_DIFF); + console.log(""); + console.log("NOTE: proofSubmitter is set to owner as placeholder."); + console.log(" DeployDevWithTDX.s.sol updates it to TEEProverRegistry."); + console.log(""); + + vm.startBroadcast(); + + (setVerifier, nitroEnclaveVerifier) = _deployNitroVerifier( + owner, risc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, nitroVerifierProofId + ); + + vm.stopBroadcast(); + + console.log("RiscZeroSetVerifier:", setVerifier); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + console.log(""); + console.log(">>> Use this NitroEnclaveVerifier address in the deploy config <<<"); + + _writeOutput( + setVerifier, + nitroEnclaveVerifier, + risc0VerifierRouter, + setBuilderImageId, + setVerifierSelector, + nitroRootCert, + nitroVerifierId, + nitroVerifierProofId + ); + } + + function _deployNitroVerifier( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + internal + returns (address deployedSetVerifier, address deployedNitroVerifier) + { + deployedSetVerifier = + address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + NitroEnclaveVerifier verifier = new NitroEnclaveVerifier( + owner, + NITRO_MAX_TIME_DIFF, + new bytes32[](0), + new uint64[](0), + nitroRootCert, + owner, + address(0), + ZkCoProcessorType.RiscZero, + zkConfig, + nitroVerifierProofId + ); + deployedNitroVerifier = address(verifier); + + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, deployedSetVerifier); + } + + function _validateInputs( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId + ) + internal + pure + returns (bytes4 setVerifierSelector) + { + require(owner != address(0), "owner must be non-zero"); + require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); + require(setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); + require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); + require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); + + setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + } + + function _writeOutput( + address deployedSetVerifier, + address deployedNitroVerifier, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes4 setVerifierSelector, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + internal + { + string memory key = "deployment"; + vm.serializeAddress(key, "RiscZeroSetVerifier", deployedSetVerifier); + vm.serializeAddress(key, "NitroEnclaveVerifier", deployedNitroVerifier); + vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); + vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); + vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); + vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); + vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); + vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); + string memory json = vm.serializeUint(key, "MaxTimeDiff", NITRO_MAX_TIME_DIFF); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-nitro-verifier.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/DeployTDXVerifier.s.sol b/scripts/multiproof/DeployTDXVerifier.s.sol new file mode 100644 index 000000000..f083a527f --- /dev/null +++ b/scripts/multiproof/DeployTDXVerifier.s.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title DeployTDXVerifier + * @notice Deploys the Solidity TDX policy verifier used by TEEProverRegistry. + * + * This script is separated from DeployDevWithTDX.s.sol because TDXVerifier imports + * verifier interfaces that require Solidity ^0.8.20, while the multiproof stack is + * pinned to Solidity 0.8.15. + * + * Usage: + * + * forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ + * --sig "run(address,address,bytes32,bytes32)" \ + * \ + * --rpc-url --broadcast --private-key + * + * After running DeployDevWithTDX.s.sol, the TDX verifier's proofSubmitter is + * updated to the deployed TEEProverRegistry. + */ + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; + +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { TDXTcbStatus } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; + +contract DeployTDXVerifier is Script { + /// @notice Maximum TDX quote age accepted by TDXVerifier. + uint64 internal constant TDX_MAX_TIME_DIFF = 3600; + + /// @param owner Owner for TDXVerifier. + /// @param risc0VerifierRouter Existing RISC Zero verifier router. + /// @param tdxVerifierId RISC Zero image ID for the TDX DCAP verifier guest. + /// @param intelRootCaHash Hash of the trusted Intel root CA consumed by the guest. + function run(address owner, address risc0VerifierRouter, bytes32 tdxVerifierId, bytes32 intelRootCaHash) public { + require(owner != address(0), "owner must be non-zero"); + require(risc0VerifierRouter != address(0), "risc0VerifierRouter must be non-zero"); + require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); + require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); + + TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); + allowedStatuses[0] = TDXTcbStatus.UpToDate; + allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: tdxVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + console.log("=== Deploying TDXVerifier ==="); + console.log("Owner:", owner); + console.log("RISC Zero Verifier Router:", risc0VerifierRouter); + console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); + console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); + console.log("Max Time Diff:", TDX_MAX_TIME_DIFF); + console.log(""); + + vm.startBroadcast(); + + address tdxVerifier = address( + new TDXVerifier( + owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, ZkCoProcessorType.RiscZero, zkConfig, allowedStatuses + ) + ); + + vm.stopBroadcast(); + + console.log("TDXVerifier:", tdxVerifier); + console.log(""); + console.log(">>> Use this address as the DeployDevWithTDX.s.sol argument <<<"); + + _writeOutput(tdxVerifier, risc0VerifierRouter, tdxVerifierId, intelRootCaHash); + } + + function _writeOutput( + address tdxVerifier, + address risc0VerifierRouter, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + { + string memory key = "deployment"; + vm.serializeAddress(key, "TDXVerifier", tdxVerifier); + vm.serializeAddress(key, "RiscZeroVerifierRouter", risc0VerifierRouter); + vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); + vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); + string memory json = vm.serializeUint(key, "MaxTimeDiff", TDX_MAX_TIME_DIFF); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-tdx-verifier.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/DeployTEEVerifiers.s.sol b/scripts/multiproof/DeployTEEVerifiers.s.sol new file mode 100644 index 000000000..46613dfd2 --- /dev/null +++ b/scripts/multiproof/DeployTEEVerifiers.s.sol @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title DeployTEEVerifiers + * @notice Deploys NitroEnclaveVerifier and TDXVerifier in one broadcast. + * + * Usage: + * + * forge script scripts/multiproof/DeployTEEVerifiers.s.sol:DeployTEEVerifiers \ + * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32,bytes32)" \ + * \ + * \ + * \ + * --rpc-url --broadcast --private-key + * + * If using batched Nitro proofs, use the overload that also supplies + * : + * + * --sig "run(address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" + * + * If Nitro and TDX use different RISC Zero verifier routers, use: + * + * --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" + * + * The broadcaster must be the owner because this script calls addVerifyRoute() + * on the freshly deployed NitroEnclaveVerifier. Use the longest overload when + * Nitro and TDX proofs use different RISC Zero verifier router contracts. + */ + +import { Script } from "forge-std/Script.sol"; +import { console2 as console } from "forge-std/console2.sol"; + +import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; +import { RiscZeroSetVerifier, RiscZeroSetVerifierLib } from "lib/risc0-ethereum/contracts/src/RiscZeroSetVerifier.sol"; + +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { TDXTcbStatus } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { NitroEnclaveVerifier } from "src/L1/proofs/tee/NitroEnclaveVerifier.sol"; +import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; + +contract DeployTEEVerifiers is Script { + /// @notice Maximum Nitro attestation age accepted by NitroEnclaveVerifier. + uint64 internal constant NITRO_MAX_TIME_DIFF = 3600; + + /// @notice Maximum TDX quote age accepted by TDXVerifier. + uint64 internal constant TDX_MAX_TIME_DIFF = 3600; + + address public setVerifier; + address public nitroEnclaveVerifier; + address public tdxVerifier; + + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + run( + owner, + risc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + bytes32(0), + tdxVerifierId, + intelRootCaHash + ); + } + + function run( + address owner, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + run( + owner, + nitroRisc0VerifierRouter, + tdxRisc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + bytes32(0), + tdxVerifierId, + intelRootCaHash + ); + } + + function run( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + run( + owner, + risc0VerifierRouter, + risc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + nitroVerifierProofId, + tdxVerifierId, + intelRootCaHash + ); + } + + function run( + address owner, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + public + { + bytes4 setVerifierSelector = _validateInputs( + owner, + nitroRisc0VerifierRouter, + tdxRisc0VerifierRouter, + setBuilderImageId, + nitroRootCert, + nitroVerifierId, + tdxVerifierId, + intelRootCaHash + ); + + console.log("=== Deploying TEE Verifiers ==="); + console.log("Owner:", owner); + console.log("Nitro RISC Zero Verifier Router:", nitroRisc0VerifierRouter); + console.log("TDX RISC Zero Verifier Router:", tdxRisc0VerifierRouter); + console.log("Set Builder Image ID:", vm.toString(setBuilderImageId)); + console.log("Set Verifier Selector:", vm.toString(setVerifierSelector)); + console.log("Nitro Root Cert:", vm.toString(nitroRootCert)); + console.log("Nitro Verifier ID:", vm.toString(nitroVerifierId)); + console.log("Nitro Verifier Proof ID:", vm.toString(nitroVerifierProofId)); + console.log("TDX Verifier ID:", vm.toString(tdxVerifierId)); + console.log("Intel Root CA Hash:", vm.toString(intelRootCaHash)); + console.log(""); + console.log("NOTE: proofSubmitter is set to owner as placeholder on both verifiers."); + console.log(" DeployDevWithTDX.s.sol updates both to TEEProverRegistry."); + console.log(""); + + vm.startBroadcast(); + + (setVerifier, nitroEnclaveVerifier) = _deployNitroVerifier( + owner, nitroRisc0VerifierRouter, setBuilderImageId, nitroRootCert, nitroVerifierId, nitroVerifierProofId + ); + tdxVerifier = _deployTDXVerifier(owner, tdxRisc0VerifierRouter, tdxVerifierId, intelRootCaHash); + + vm.stopBroadcast(); + + console.log("RiscZeroSetVerifier:", setVerifier); + console.log("NitroEnclaveVerifier:", nitroEnclaveVerifier); + console.log("TDXVerifier:", tdxVerifier); + console.log(""); + console.log(">>> Use these verifier addresses for the TDX multiproof deployment <<<"); + + _writeOutput( + setVerifier, + nitroEnclaveVerifier, + tdxVerifier, + nitroRisc0VerifierRouter, + tdxRisc0VerifierRouter, + setBuilderImageId, + setVerifierSelector, + nitroRootCert, + nitroVerifierId, + nitroVerifierProofId, + tdxVerifierId, + intelRootCaHash + ); + } + + function _deployNitroVerifier( + address owner, + address risc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId + ) + internal + returns (address deployedSetVerifier, address deployedNitroVerifier) + { + deployedSetVerifier = + address(new RiscZeroSetVerifier(IRiscZeroVerifier(risc0VerifierRouter), setBuilderImageId, "")); + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: nitroVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + NitroEnclaveVerifier verifier = new NitroEnclaveVerifier( + owner, + NITRO_MAX_TIME_DIFF, + new bytes32[](0), + new uint64[](0), + nitroRootCert, + owner, + address(0), + ZkCoProcessorType.RiscZero, + zkConfig, + nitroVerifierProofId + ); + deployedNitroVerifier = address(verifier); + + bytes4 setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + verifier.addVerifyRoute(ZkCoProcessorType.RiscZero, setVerifierSelector, deployedSetVerifier); + } + + function _deployTDXVerifier( + address owner, + address risc0VerifierRouter, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + returns (address deployedTDXVerifier) + { + TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); + allowedStatuses[0] = TDXTcbStatus.UpToDate; + allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; + + ZkCoProcessorConfig memory zkConfig = ZkCoProcessorConfig({ + verifierId: tdxVerifierId, aggregatorId: bytes32(0), zkVerifier: risc0VerifierRouter + }); + + deployedTDXVerifier = address( + new TDXVerifier( + owner, TDX_MAX_TIME_DIFF, intelRootCaHash, owner, ZkCoProcessorType.RiscZero, zkConfig, allowedStatuses + ) + ); + } + + function _validateInputs( + address owner, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + pure + returns (bytes4 setVerifierSelector) + { + require(owner != address(0), "owner must be non-zero"); + require(nitroRisc0VerifierRouter != address(0), "nitroRisc0VerifierRouter must be non-zero"); + require(tdxRisc0VerifierRouter != address(0), "tdxRisc0VerifierRouter must be non-zero"); + require(setBuilderImageId != bytes32(0), "setBuilderImageId must be non-zero"); + require(nitroRootCert != bytes32(0), "nitroRootCert must be non-zero"); + require(nitroVerifierId != bytes32(0), "nitroVerifierId must be non-zero"); + require(tdxVerifierId != bytes32(0), "tdxVerifierId must be non-zero"); + require(intelRootCaHash != bytes32(0), "intelRootCaHash must be non-zero"); + + setVerifierSelector = RiscZeroSetVerifierLib.selector(setBuilderImageId); + } + + function _writeOutput( + address deployedSetVerifier, + address deployedNitroVerifier, + address deployedTDXVerifier, + address nitroRisc0VerifierRouter, + address tdxRisc0VerifierRouter, + bytes32 setBuilderImageId, + bytes4 setVerifierSelector, + bytes32 nitroRootCert, + bytes32 nitroVerifierId, + bytes32 nitroVerifierProofId, + bytes32 tdxVerifierId, + bytes32 intelRootCaHash + ) + internal + { + string memory key = "deployment"; + vm.serializeAddress(key, "RiscZeroSetVerifier", deployedSetVerifier); + vm.serializeAddress(key, "NitroEnclaveVerifier", deployedNitroVerifier); + vm.serializeAddress(key, "TDXVerifier", deployedTDXVerifier); + vm.serializeAddress(key, "RiscZeroVerifierRouter", tdxRisc0VerifierRouter); + vm.serializeAddress(key, "NitroRiscZeroVerifierRouter", nitroRisc0VerifierRouter); + vm.serializeAddress(key, "TDXRiscZeroVerifierRouter", tdxRisc0VerifierRouter); + vm.serializeBytes32(key, "RiscZeroSetBuilderImageId", setBuilderImageId); + vm.serializeString(key, "RiscZeroSetVerifierSelector", vm.toString(setVerifierSelector)); + vm.serializeBytes32(key, "NitroRootCert", nitroRootCert); + vm.serializeBytes32(key, "NitroVerifierId", nitroVerifierId); + vm.serializeBytes32(key, "NitroVerifierProofId", nitroVerifierProofId); + vm.serializeBytes32(key, "TDXVerifierId", tdxVerifierId); + vm.serializeBytes32(key, "IntelRootCaHash", intelRootCaHash); + vm.serializeUint(key, "NitroMaxTimeDiff", NITRO_MAX_TIME_DIFF); + string memory json = vm.serializeUint(key, "TDXMaxTimeDiff", TDX_MAX_TIME_DIFF); + + string memory outPath = string.concat("deployments/", vm.toString(block.chainid), "-tee-verifiers.json"); + vm.writeJson(json, outPath); + console.log("Deployment saved to:", outPath); + } +} diff --git a/scripts/multiproof/README.md b/scripts/multiproof/README.md index 166f86d16..a48f93383 100644 --- a/scripts/multiproof/README.md +++ b/scripts/multiproof/README.md @@ -43,7 +43,8 @@ Other relevant fields: | Field | Description | | ------------------------------ | --------------------------------------------------------------------------------- | | `teeProposer` | Address to be registered as the TEE proposer | -| `teeImageHash` | PCR0 hash used when registering the dev signer (use `bytes32(0x01...01)` for dev) | +| `teeNitroImageHash` | PCR0 hash used when registering the Nitro dev signer | +| `teeTdxImageHash` | TDX image hash used when registering the TDX dev signer | | `multiproofGameType` | Game type ID for the dispute game | | `multiproofGenesisOutputRoot` | Initial anchor output root | | `multiproofGenesisBlockNumber` | Initial anchor L2 block number | @@ -96,20 +97,30 @@ This returns a raw byte array representing an uncompressed secp256k1 public key 0x0cbe4A965B41DA6B2D5AF4d53c0C16a37d6f9F7D ``` -### Step 5: Register the dev signer +### Step 5: Register the dev signers -Call `addDevSigner` on the deployed `DevTEEProverRegistry` with the **signer address** derived in Step 4. +Call `addDevSigner` for the Nitro signer and `addDevTDXSigner` for the TDX signer on the deployed `DevTEEProverRegistry`. -> **Note:** PCR0 enforcement is handled by `AggregateVerifier` (which bakes `teeImageHash` into the -> journal the enclave signs). The registry only tracks which signer addresses are valid. +> **Note:** PCR0 / TDX image enforcement is handled by `AggregateVerifier` (which bakes +> `teeNitroImageHash` and `teeTdxImageHash` into the journal the enclaves sign). The registry +> only tracks which signer addresses are valid. ```bash # Replace: # 0x587d... with the TEEProverRegistry address from your deployment output -# 0x080f... with the signer address derived in Step 4 +# 0x080f... with the Nitro signer address derived in Step 4 cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ - "addDevSigner(address)" \ + "addDevSigner(address,bytes32)" \ 0x080f42420846c613158D7b4334257C78bE5A9B90 \ + $TEE_NITRO_IMAGE_HASH \ + --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ + --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" + +# Register a TDX dev signer for the TDX image hash. +cast send 0x587d410B205449fB889EC4a5b351D375C656d084 \ + "addDevTDXSigner(address,bytes32)" \ + $TDX_SIGNER_ADDRESS \ + $TEE_TDX_IMAGE_HASH \ --rpc-url https://c3-chainproxy-eth-sepolia-full-dev.cbhq.net \ --ledger --mnemonic-derivation-path "m/44'/60'/1'/0/0" ``` @@ -124,6 +135,195 @@ The deployer address (`finalSystemOwner`) is the owner of `DevTEEProverRegistry` --- +## Path 3: TDX (Production-Path PoC) + +The TDX path follows the same split as Nitro: expensive attestation verification happens off-chain in a ZK guest, +and Solidity verifies the proof plus the on-chain acceptance policy before registering the signer. + +| Contract | Purpose | +| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TDXVerifier` | Verifies a RISC Zero or SP1 proof whose public values are an ABI-encoded `TDXVerifierJournal`, then checks trusted Intel root, TCB status policy, collateral expiry, quote freshness, signer derivation, and `REPORTDATA` public-key binding. | +| `TEEProverRegistry` | Registers Nitro signers through `registerSigner(bytes,bytes)` and TDX signers through `registerTDXSigner(bytes,bytes)`, tracking which TEE type each signer came from for `TEEVerifier`. | + +The ZK verifier guest is expected to perform the full Intel DCAP verification path: + +```text +TD Quote signature +PCK certificate chain +TCB info signing chain and TCB status +QE identity signing chain +CRLs/revocation state +TDREPORT field extraction +``` + +The Solidity verifier then enforces local policy over the proven journal. The PoC maps TDX measurements into `TEE_TDX_IMAGE_HASH` as: + +```text +keccak256(MRTD || RTMR0 || RTMR1 || RTMR2 || RTMR3) +``` + +The attested public key must be supplied as an uncompressed 65-byte secp256k1 public key: + +```text +0x04 || x || y +``` + +The quote's TDREPORT `REPORTDATA` must put `keccak256(x || y)` in the first 32 bytes. The last 32 bytes are returned by the verifier as app-specific binding data and emitted by the registry. + +`TEEVerifier` is still the proposal-proof verifier, but a TEE proposal proof now requires two signatures over the same journal: one from a Nitro-registered signer and one from a TDX-registered signer. The proof bytes are `proposer || signatureA || signatureB`; either signature order is accepted as long as both registered TEE types are present and both signers match their expected type-specific image hash. + +> **PoC boundary:** this repo now contains the production-shaped Solidity path and policy checks. The remaining off-chain piece is the actual RISC Zero/SP1 TDX DCAP guest that emits `TDXVerifierJournal` after verifying Intel collateral. + +### Step 1: Deploy verifier policy contracts + +`NitroEnclaveVerifier` and `TDXVerifier` are deployed separately from the main multiproof stack because they depend on verifier interfaces that require Solidity `^0.8.20`, while the rest of the multiproof deployment stack is pinned to Solidity `0.8.15`. + +For Sepolia TDX testing, `scripts/multiproof/justfile` defaults to: + +```bash +NITRO_RISC0_VERIFIER_ROUTER=0xB121B667dd2cf27F95f9F5107137696F56f188f6 +TDX_RISC0_VERIFIER_ROUTER=0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187 +RISC0_SET_BUILDER_IMAGE_ID=0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9 +TDX_VERIFIER_ID=0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b +INTEL_ROOT_CA_HASH=0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009 +TDX_IMAGE_HASH=0x4cb35ee476a8098c4e567098714c65f5afe25236fc460b38487a356e14e7db66 +TDX_REGISTRATION_MANAGER=0x44E999A5859c2D12378a349882fAe5805DCE71b9 +``` + +`deploy-config/sepolia.json` uses the same TDX RISC Zero verifier router and `TDX_IMAGE_HASH`. The `TEEProverRegistry` +constructor also requires a non-zero `tdxVerifier`; for config-driven deployments, set `tdxVerifier` in the deploy +config to the deployed `TDXVerifier` address. + +Deploy both TEE verifier policy contracts with one command: + +```bash +just --justfile scripts/multiproof/justfile deploy-tee-verifiers $NITRO_ROOT_CERT $NITRO_VERIFIER_ID +``` + +This saves output to `deployments/-tee-verifiers.json`. + +To deploy a fresh Nitro verifier, a fresh TDX verifier, and then the TDX +multiproof stack against those exact verifier addresses without editing +`deploy-config/sepolia.json`, use: + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-system $NITRO_ROOT_CERT $NITRO_VERIFIER_ID +``` + +To deploy Nitro separately: + +```bash +just --justfile scripts/multiproof/justfile deploy-nitro-verifier $NITRO_ROOT_CERT $NITRO_VERIFIER_ID +``` + +This saves output to `deployments/-nitro-verifier.json`. + +To deploy TDX separately: + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-verifier +``` + +To override any TDX verifier input manually, pass all three verifier args: + +```bash +forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ + --sig "run(address,address,bytes32,bytes32)" \ + $OWNER \ + $TDX_RISC0_VERIFIER_ROUTER \ + $TDX_VERIFIER_ID \ + $INTEL_ROOT_CA_HASH \ + --rpc-url $L1_RPC_URL \ + --broadcast \ + --private-key $PRIVATE_KEY +``` + +The script saves output to `deployments/-tdx-verifier.json`. + +To override Nitro inputs manually: + +```bash +forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + --sig "run(address,address,bytes32,bytes32,bytes32)" \ + $OWNER \ + $NITRO_RISC0_VERIFIER_ROUTER \ + $RISC0_SET_BUILDER_IMAGE_ID \ + $NITRO_ROOT_CERT \ + $NITRO_VERIFIER_ID \ + --rpc-url $L1_RPC_URL \ + --broadcast \ + --private-key $PRIVATE_KEY +``` + +Use `DeployTEEVerifiers.s.sol` with `run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)` to override all Nitro and TDX inputs while still deploying both in one broadcast. + +### Step 2: Deploy the TDX multiproof test stack + +Set `DEPLOY_CONFIG_PATH` to the Sepolia deploy config and pass the `TDXVerifier` address from Step 1. The deploy config must also contain the `NitroEnclaveVerifier` address, because TEE proposal proofs now require both Nitro and TDX signatures. `finalSystemOwner` in the deploy config must be the account broadcasting this transaction because the script updates both `TDXVerifier.proofSubmitter` and `NitroEnclaveVerifier.proofSubmitter` to the deployed `TEEProverRegistry`. + +The TDX registry manager is set to `TDX_REGISTRATION_MANAGER`, allowing that address to call `registerTDXSigner(bytes,bytes)`. Register a Nitro signer through `registerSigner(bytes,bytes)` as well before submitting TEE proposal proofs. + +The `deploy-tdx-stack` recipe resolves a recent L2 output root before invoking `DeployDevWithTDX`, then injects the resolved output root and L2 block through `run(address,address,bytes32,uint256)`. Use `L2_OUTPUT_ROOT_RPC_URL` if the `optimism_outputAtBlock` endpoint differs from the L2 execution RPC, and `ASR_ANCHOR_BLOCK_LOOKBACK` to anchor a fixed number of L2 blocks behind head. For a fixed anchor, set both `ASR_ANCHOR_OUTPUT_ROOT` and `ASR_ANCHOR_BLOCK_NUMBER`. + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-stack $TDX_VERIFIER +``` + +If the Nitro verifier in `deploy-config/sepolia.json` is owned by another account, do not edit the shared Sepolia deploy +config. Pass both verifier addresses explicitly instead: + +```bash +just --justfile scripts/multiproof/justfile deploy-tdx-stack-with-verifiers $NITRO_VERIFIER $TDX_VERIFIER +``` + +To override the manager manually, use: + +```bash +export L2_RPC_URL= +export L2_OUTPUT_ROOT_RPC_URL= +ASR_ANCHOR_BLOCK_NUMBER=$(cast block-number --rpc-url $L2_RPC_URL) +ASR_ANCHOR_OUTPUT_ROOT=$(cast rpc optimism_outputAtBlock $(cast to-hex $ASR_ANCHOR_BLOCK_NUMBER) \ + --rpc-url $L2_OUTPUT_ROOT_RPC_URL | jq -r '.outputRoot') + +forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ + --sig "run(address,address,bytes32,uint256)" \ + $TDX_VERIFIER \ + $TDX_REGISTRATION_MANAGER \ + $ASR_ANCHOR_OUTPUT_ROOT \ + $ASR_ANCHOR_BLOCK_NUMBER \ + --rpc-url $L1_RPC_URL \ + --broadcast \ + --private-key $PRIVATE_KEY +``` + +The script saves output to `deployments/-dev-with-tdx.json`. + +### Step 3: Register Nitro and TDX signers + +Register a Nitro signer with a ZK-proven Nitro attestation: + +```bash +cast send $TEE_PROVER_REGISTRY \ + "registerSigner(bytes,bytes)" \ + $NITRO_OUTPUT \ + $NITRO_PROOF_BYTES \ + --rpc-url $L1_RPC_URL \ + --private-key $PRIVATE_KEY +``` + +Once you have the ABI-encoded `TDXVerifierJournal` output and matching RISC Zero proof bytes from the TDX DCAP guest, register the signer through the TDX-aware registry: + +```bash +cast send $TEE_PROVER_REGISTRY \ + "registerTDXSigner(bytes,bytes)" \ + $TDX_OUTPUT \ + $PROOF_BYTES \ + --rpc-url $L1_RPC_URL \ + --private-key $PRIVATE_KEY +``` + +--- + ## Pre-Seeding Games (Post-Deployment) After deploying via either path, you can pre-seed the `DisputeGameFactory` with a chain of `AggregateVerifier` games. This is useful for testing forward traversal at proposer restart — the proposer can walk the linked list of games to find where to resume. diff --git a/scripts/multiproof/justfile b/scripts/multiproof/justfile new file mode 100644 index 000000000..238d8eaf0 --- /dev/null +++ b/scripts/multiproof/justfile @@ -0,0 +1,332 @@ +set dotenv-load + +repo_root := justfile_directory() + "/../.." +justfile_path := justfile_directory() + "/justfile" +default_deploy_config := "deploy-config/sepolia.json" +zeronet_tdx_deploy_config := "deploy-config/zeronet-tdx.json" +sepolia_tdx_stack_deployments := "deployments/11155111-dev-with-tdx.json" +sepolia_tee_verifiers_deployments := "deployments/11155111-tee-verifiers.json" +forge_account := "testnet-admin" +l1_rpc_url := env_var_or_default("L1_RPC_URL", "https://ethereum-full-sepolia-k8s-dev.cbhq.net") +l2_rpc_url := env_var_or_default("L2_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:8545") +l2_output_root_rpc_url := env_var_or_default("L2_OUTPUT_ROOT_RPC_URL", "https://base-sepolia-archive-k8s-dev.cbhq.net:7545") + +sepolia_nitro_risc0_verifier_router := "0xB121B667dd2cf27F95f9F5107137696F56f188f6" +sepolia_tdx_risc0_verifier_router := "0x925d8331ddc0a1F0d96E68CF073DFE1d92b69187" +sepolia_risc0_set_builder_image_id := "0x70909b25db0db00f1d4b4016aeb876f53568a3e5a8e6397cb562d79947a02cc9" +sepolia_tdx_verifier_id := "0xb9681d1f76f5dbf70da84ad06b5b20befa392638060e947965269b6f63ebbf3b" +sepolia_intel_root_ca_hash := "0xa1acc73eb45794fa1734f14d882e91925b6006f79d3bb2460df9d01b333d7009" +sepolia_tdx_registration_manager := "0x44E999A5859c2D12378a349882fAe5805DCE71b9" +zero_bytes32 := "0x0000000000000000000000000000000000000000000000000000000000000000" + +# List available multiproof deployment recipes. +default: + @just --justfile "{{justfile_path}}" --list + +# Deploy the TDXVerifier policy contract. +# +# Required env: +# L1_RPC_URL +# +# Args: +# risc0_router RISC Zero verifier router +# tdx_verifier_id RISC Zero image ID for the TDX DCAP verifier guest +# intel_root_ca_hash Trusted Intel root CA hash consumed by the guest +deploy-tdx-verifier risc0_router=sepolia_tdx_risc0_verifier_router tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as owner/deployer: $owner" + forge script scripts/multiproof/DeployTDXVerifier.s.sol:DeployTDXVerifier \ + --sig "run(address,address,bytes32,bytes32)" \ + "$owner" \ + "{{risc0_router}}" \ + "{{tdx_verifier_id}}" \ + "{{intel_root_ca_hash}}" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Deploy the NitroEnclaveVerifier policy contract and its local RISC Zero set verifier route. +# +# Required args: +# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate +# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest +# +# Optional args: +# risc0_router RISC Zero verifier router +# set_builder_image_id RISC Zero set builder image ID +# nitro_verifier_proof_id Verifier proof ID for batched Nitro proofs; defaults to zero +deploy-nitro-verifier nitro_root_cert nitro_verifier_id risc0_router=sepolia_nitro_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as owner/deployer: $owner" + forge script scripts/multiproof/DeployNitroVerifier.s.sol:DeployNitroVerifier \ + --sig "run(address,address,bytes32,bytes32,bytes32,bytes32)" \ + "$owner" \ + "{{risc0_router}}" \ + "{{set_builder_image_id}}" \ + "{{nitro_root_cert}}" \ + "{{nitro_verifier_id}}" \ + "{{nitro_verifier_proof_id}}" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Deploy both TEE verifier policy contracts in one broadcast. +# +# Required args: +# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate +# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest +# +# Optional args default to the Sepolia TDX testing values in this justfile. +deploy-tee-verifiers nitro_root_cert nitro_verifier_id nitro_risc0_router=sepolia_nitro_risc0_verifier_router tdx_risc0_router=sepolia_tdx_risc0_verifier_router set_builder_image_id=sepolia_risc0_set_builder_image_id nitro_verifier_proof_id=zero_bytes32 tdx_verifier_id=sepolia_tdx_verifier_id intel_root_ca_hash=sepolia_intel_root_ca_hash: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as owner/deployer: $owner" + forge script scripts/multiproof/DeployTEEVerifiers.s.sol:DeployTEEVerifiers \ + --sig "run(address,address,address,bytes32,bytes32,bytes32,bytes32,bytes32,bytes32)" \ + "$owner" \ + "{{nitro_risc0_router}}" \ + "{{tdx_risc0_router}}" \ + "{{set_builder_image_id}}" \ + "{{nitro_root_cert}}" \ + "{{nitro_verifier_id}}" \ + "{{nitro_verifier_proof_id}}" \ + "{{tdx_verifier_id}}" \ + "{{intel_root_ca_hash}}" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Print the Sepolia TDX constants used by these recipes. +tdx-sepolia-config: + @echo "L1_RPC_URL={{l1_rpc_url}}" + @jq -r '"TEE_PROVER_REGISTRY=" + (.TEEProverRegistry // "")' "{{repo_root}}/{{sepolia_tdx_stack_deployments}}" + @echo "NITRO_RISC0_VERIFIER_ROUTER={{sepolia_nitro_risc0_verifier_router}}" + @echo "TDX_RISC0_VERIFIER_ROUTER={{sepolia_tdx_risc0_verifier_router}}" + @echo "RISC0_SET_BUILDER_IMAGE_ID={{sepolia_risc0_set_builder_image_id}}" + @echo "TDX_VERIFIER_ID={{sepolia_tdx_verifier_id}}" + @echo "INTEL_ROOT_CA_HASH={{sepolia_intel_root_ca_hash}}" + @echo "TDX_REGISTRATION_MANAGER={{sepolia_tdx_registration_manager}}" + @echo "L2_RPC_URL={{l2_rpc_url}}" + @echo "L2_OUTPUT_ROOT_RPC_URL={{l2_output_root_rpc_url}}" + +# Deploy the TDX multiproof dev/test stack using the zeronet TDX config. +# +# Required env: +# L1_RPC_URL +# L2_RPC_URL Optional when using this recipe; defaults to Base Sepolia L2. +# L2_OUTPUT_ROOT_RPC_URL Optional when using this recipe; defaults to Base Sepolia op-node/archive RPC. +# ASR_ANCHOR_BLOCK_LOOKBACK Optional L2 block lookback from head; defaults to 0. +# ASR_ANCHOR_BLOCK_NUMBER Optional explicit L2 block. Requires ASR_ANCHOR_OUTPUT_ROOT. +# ASR_ANCHOR_OUTPUT_ROOT Optional explicit output root. Requires ASR_ANCHOR_BLOCK_NUMBER. +# +# Args: +# tdx_verifier TDXVerifier address from deploy-tdx-verifier +# tdx_manager Manager that can call registerTDXSigner() +deploy-tdx-stack tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as deployer: $owner" + + if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 + exit 1 + fi + asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" + asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" + else + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + fi + + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi + + echo "Using ASR anchor output root: $asr_anchor_output_root" + echo "Using ASR anchor L2 block: $asr_anchor_block" + + DEPLOY_CONFIG_PATH="{{deploy_config}}" \ + forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ + --sig "run(address,address,bytes32,uint256)" \ + "{{tdx_verifier}}" \ + "{{tdx_manager}}" \ + "$asr_anchor_output_root" \ + "$asr_anchor_block" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Deploy the TDX multiproof dev/test stack with both verifier addresses supplied explicitly. +# +# Use this when you want to bind the deployed stack to explicit verifier addresses. +# +# Args: +# nitro_verifier NitroEnclaveVerifier address from deploy-nitro-verifier or deploy-tee-verifiers +# tdx_verifier TDXVerifier address from deploy-tdx-verifier or deploy-tee-verifiers +# tdx_manager Manager that can call registerTDXSigner() +deploy-tdx-stack-with-verifiers nitro_verifier tdx_verifier tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + owner="$(cast wallet address --account "{{forge_account}}")" + echo "Using {{forge_account}} as deployer: $owner" + + if [[ -n "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -n "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + if [[ -z "${ASR_ANCHOR_BLOCK_NUMBER:-}" || -z "${ASR_ANCHOR_OUTPUT_ROOT:-}" ]]; then + echo "ASR_ANCHOR_BLOCK_NUMBER and ASR_ANCHOR_OUTPUT_ROOT must be set together" >&2 + exit 1 + fi + asr_anchor_block="$ASR_ANCHOR_BLOCK_NUMBER" + asr_anchor_output_root="$ASR_ANCHOR_OUTPUT_ROOT" + else + lookback="${ASR_ANCHOR_BLOCK_LOOKBACK:-0}" + l2_head="$(cast block-number --rpc-url "{{l2_rpc}}")" + if (( lookback > l2_head )); then + echo "ASR_ANCHOR_BLOCK_LOOKBACK ($lookback) exceeds L2 head ($l2_head)" >&2 + exit 1 + fi + asr_anchor_block=$((l2_head - lookback)) + asr_anchor_block_hex="$(cast to-hex "$asr_anchor_block")" + asr_anchor_output_root="$(cast rpc optimism_outputAtBlock "$asr_anchor_block_hex" --rpc-url "{{l2_output_root_rpc}}" | jq -r '.outputRoot')" + fi + + if [[ -z "$asr_anchor_output_root" || "$asr_anchor_output_root" == "null" || "$asr_anchor_output_root" == "0x0000000000000000000000000000000000000000000000000000000000000000" ]]; then + echo "Invalid ASR anchor output root: $asr_anchor_output_root" >&2 + exit 1 + fi + + echo "Using NitroEnclaveVerifier: {{nitro_verifier}}" + echo "Using TDXVerifier: {{tdx_verifier}}" + echo "Using ASR anchor output root: $asr_anchor_output_root" + echo "Using ASR anchor L2 block: $asr_anchor_block" + + DEPLOY_CONFIG_PATH="{{deploy_config}}" \ + forge script scripts/multiproof/DeployDevWithTDX.s.sol:DeployDevWithTDX \ + --sig "run(address,address,address,bytes32,uint256)" \ + "{{nitro_verifier}}" \ + "{{tdx_verifier}}" \ + "{{tdx_manager}}" \ + "$asr_anchor_output_root" \ + "$asr_anchor_block" \ + --rpc-url "{{l1_rpc_url}}" \ + --broadcast \ + --account "{{forge_account}}" \ + --sender "$owner" + +# Deploy fresh Nitro and TDX verifier policy contracts, then deploy the TDX stack +# against those verifier addresses without editing shared Sepolia deploy config. +# +# Required args: +# nitro_root_cert SHA-256 hash of the AWS Nitro root certificate +# nitro_verifier_id RISC Zero image ID for the Nitro attestation verifier guest +deploy-tdx-system nitro_root_cert nitro_verifier_id tdx_manager=sepolia_tdx_registration_manager deploy_config=zeronet_tdx_deploy_config l2_rpc=l2_rpc_url l2_output_root_rpc=l2_output_root_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + + just --justfile "{{justfile_path}}" deploy-tee-verifiers "{{nitro_root_cert}}" "{{nitro_verifier_id}}" + + nitro_verifier="$(jq -r '.NitroEnclaveVerifier // empty' "{{sepolia_tee_verifiers_deployments}}")" + tdx_verifier="$(jq -r '.TDXVerifier // empty' "{{sepolia_tee_verifiers_deployments}}")" + + if [[ -z "$nitro_verifier" || -z "$tdx_verifier" ]]; then + echo "Missing verifier address in {{sepolia_tee_verifiers_deployments}}" >&2 + exit 1 + fi + + just --justfile "{{justfile_path}}" deploy-tdx-stack-with-verifiers \ + "$nitro_verifier" \ + "$tdx_verifier" \ + "{{tdx_manager}}" \ + "{{deploy_config}}" \ + "{{l2_rpc}}" \ + "{{l2_output_root_rpc}}" + +# Args: +# registry TEEProverRegistry address. Defaults to deployments/11155111-dev-with-tdx.json. +# rpc L1 Sepolia RPC URL. +# +# Query currently registered TEE signer addresses on Sepolia. +tee-registered-signers registry="" rpc=l1_rpc_url: + #!/usr/bin/env bash + set -euo pipefail + cd "{{repo_root}}" + + registry="{{registry}}" + if [[ -z "$registry" ]]; then + registry="$(jq -r '.TEEProverRegistry // empty' "{{sepolia_tdx_stack_deployments}}")" + fi + + if [[ -z "$registry" || "$registry" == "null" ]]; then + echo "Missing TEEProverRegistry address. Pass one explicitly or update {{sepolia_tdx_stack_deployments}}." >&2 + exit 1 + fi + + echo "TEEProverRegistry: $registry" + echo "RPC: {{rpc}}" + + signers=() + while IFS= read -r signer; do + signers+=("$signer") + done < <(cast call "$registry" 'getRegisteredSigners()(address[])' --rpc-url "{{rpc}}" --json | jq -r '.[0][]') + if (( ${#signers[@]} == 0 )); then + echo "No registered TEE signers found." + exit 0 + fi + + printf "%-42s %-6s %-66s %-5s\n" "SIGNER" "TYPE" "IMAGE_HASH" "VALID" + for signer in "${signers[@]}"; do + tee_type_id="$(cast call "$registry" 'signerTEEType(address)(uint8)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" + image_hash="$(cast call "$registry" 'signerImageHash(address)(bytes32)' "$signer" --rpc-url "{{rpc}}" --json | jq -r '.[0]')" + valid_raw="$(cast call "$registry" 'isValidSigner(address)(bool)' "$signer" --rpc-url "{{rpc}}" --json 2>/dev/null || true)" + if [[ -n "$valid_raw" ]]; then + valid="$(echo "$valid_raw" | jq -r '.[0]')" + else + valid="unknown" + fi + + case "$tee_type_id" in + 1) tee_type="NITRO" ;; + 2) tee_type="TDX" ;; + *) tee_type="NONE" ;; + esac + + printf "%-42s %-6s %-66s %-5s\n" "$signer" "$tee_type" "$image_hash" "$valid" + done + +# Print the two-step Sepolia command template. +tdx-sepolia-help: + @echo 'export L1_RPC_URL=' + @echo 'export L2_RPC_URL=' + @echo 'export L2_OUTPUT_ROOT_RPC_URL=' + @echo 'just deploy-nitro-verifier ' + @echo 'just deploy-tdx-verifier' + @echo 'just deploy-tee-verifiers ' + @echo 'just deploy-tdx-system ' + @echo 'just deploy-tdx-stack [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' + @echo 'just deploy-tdx-stack-with-verifiers [tdx-manager] [deploy-config] [l2-rpc] [l2-output-root-rpc]' + @echo 'just tee-registered-signers' + @echo 'just tdx-sepolia-config' diff --git a/snapshots/abi/AggregateVerifier.json b/snapshots/abi/AggregateVerifier.json index 9c991351e..54a0e5a02 100644 --- a/snapshots/abi/AggregateVerifier.json +++ b/snapshots/abi/AggregateVerifier.json @@ -27,9 +27,21 @@ "type": "address" }, { - "internalType": "bytes32", - "name": "teeImageHash", - "type": "bytes32" + "components": [ + { + "internalType": "bytes32", + "name": "nitroHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "tdxHash", + "type": "bytes32" + } + ], + "internalType": "struct AggregateVerifier.TeeHashes", + "name": "teeHashes", + "type": "tuple" }, { "components": [ @@ -230,7 +242,20 @@ }, { "inputs": [], - "name": "TEE_IMAGE_HASH", + "name": "TEE_NITRO_IMAGE_HASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TEE_TDX_IMAGE_HASH", "outputs": [ { "internalType": "bytes32", diff --git a/snapshots/abi/TDXVerifier.json b/snapshots/abi/TDXVerifier.json new file mode 100644 index 000000000..bda2cd07b --- /dev/null +++ b/snapshots/abi/TDXVerifier.json @@ -0,0 +1,808 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint64", + "name": "initialMaxTimeDiff", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "initialRootCaHash", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "initialProofSubmitter", + "type": "address" + }, + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "internalType": "struct ZkCoProcessorConfig", + "name": "config", + "type": "tuple" + }, + { + "internalType": "enum TDXTcbStatus[]", + "name": "initialAllowedTcbStatuses", + "type": "uint8[]" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "enum TDXTcbStatus", + "name": "", + "type": "uint8" + } + ], + "name": "allowedTcbStatuses", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cancelOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "completeOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + } + ], + "name": "getZkConfig", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "internalType": "struct ZkCoProcessorConfig", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxTimeDiff", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "result", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "ownershipHandoverExpiresAt", + "outputs": [ + { + "internalType": "uint256", + "name": "result", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "proofSubmitter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "requestOwnershipHandover", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "rootCaHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "newMaxTimeDiff", + "type": "uint64" + } + ], + "name": "setMaxTimeDiff", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newProofSubmitter", + "type": "address" + } + ], + "name": "setProofSubmitter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "newRootCaHash", + "type": "bytes32" + } + ], + "name": "setRootCaHash", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum TDXTcbStatus", + "name": "status", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "setTcbStatusAllowed", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "internalType": "struct ZkCoProcessorConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "setZkConfiguration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "output", + "type": "bytes" + }, + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "internalType": "bytes", + "name": "proofBytes", + "type": "bytes" + } + ], + "name": "verify", + "outputs": [ + { + "components": [ + { + "internalType": "enum TDXVerificationResult", + "name": "result", + "type": "uint8" + }, + { + "internalType": "enum TDXTcbStatus", + "name": "tcbStatus", + "type": "uint8" + }, + { + "internalType": "uint64", + "name": "timestamp", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "collateralExpiration", + "type": "uint64" + }, + { + "internalType": "bytes32", + "name": "rootCaHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "pckCertHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "tcbInfoHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "qeIdentityHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "publicKey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "mrTdHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "reportDataPrefix", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "reportDataSuffix", + "type": "bytes32" + } + ], + "internalType": "struct TDXVerifierJournal", + "name": "journal", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "", + "type": "uint8" + } + ], + "name": "zkConfig", + "outputs": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "enum TDXTcbStatus", + "name": "tcbStatus", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "pckCertHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "tcbInfoHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "qeIdentityHash", + "type": "bytes32" + } + ], + "name": "AttestationSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "maxTimeDiff", + "type": "uint64" + } + ], + "name": "MaxTimeDiffUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "OwnershipHandoverCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pendingOwner", + "type": "address" + } + ], + "name": "OwnershipHandoverRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "proofSubmitter", + "type": "address" + } + ], + "name": "ProofSubmitterChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "rootCaHash", + "type": "bytes32" + } + ], + "name": "RootCaHashUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum TDXTcbStatus", + "name": "status", + "type": "uint8" + }, + { + "indexed": false, + "internalType": "bool", + "name": "allowed", + "type": "bool" + } + ], + "name": "TcbStatusPolicyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "verifierId", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "aggregatorId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "zkVerifier", + "type": "address" + } + ], + "indexed": false, + "internalType": "struct ZkCoProcessorConfig", + "name": "config", + "type": "tuple" + } + ], + "name": "ZKConfigurationUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "CallerNotProofSubmitter", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "collateralExpiration", + "type": "uint64" + } + ], + "name": "CollateralExpired", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidPublicKey", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "timestampSeconds", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "currentTimestamp", + "type": "uint256" + } + ], + "name": "InvalidTimestamp", + "type": "error" + }, + { + "inputs": [], + "name": "NewOwnerIsZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "NoHandoverRequest", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actual", + "type": "bytes32" + } + ], + "name": "ReportDataMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "expected", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "actual", + "type": "bytes32" + } + ], + "name": "RootCaHashMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "expected", + "type": "address" + }, + { + "internalType": "address", + "name": "actual", + "type": "address" + } + ], + "name": "SignerMismatch", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum TDXVerificationResult", + "name": "result", + "type": "uint8" + } + ], + "name": "TDXVerificationFailed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum TDXTcbStatus", + "name": "status", + "type": "uint8" + } + ], + "name": "TcbStatusNotAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "Unauthorized", + "type": "error" + }, + { + "inputs": [], + "name": "UnknownZkCoprocessor", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroMaxTimeDiff", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroProofSubmitter", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroRootCaHash", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "enum ZkCoProcessorType", + "name": "zkCoprocessor", + "type": "uint8" + } + ], + "name": "ZkVerifierNotConfigured", + "type": "error" + } +] \ No newline at end of file diff --git a/snapshots/abi/TEEProverRegistry.json b/snapshots/abi/TEEProverRegistry.json index 510962c5d..44586ef24 100644 --- a/snapshots/abi/TEEProverRegistry.json +++ b/snapshots/abi/TEEProverRegistry.json @@ -6,6 +6,11 @@ "name": "nitroVerifier", "type": "address" }, + { + "internalType": "contract ITDXVerifier", + "name": "tdxVerifier", + "type": "address" + }, { "internalType": "contract IDisputeGameFactory", "name": "factory", @@ -54,6 +59,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "TDX_VERIFIER", + "outputs": [ + { + "internalType": "contract ITDXVerifier", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -82,7 +100,20 @@ }, { "inputs": [], - "name": "getExpectedImageHash", + "name": "getExpectedNitroImageHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExpectedTDXImageHash", "outputs": [ { "internalType": "bytes32", @@ -235,6 +266,24 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "output", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "proofBytes", + "type": "bytes" + } + ], + "name": "registerTDXSigner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [], "name": "renounceManagement", @@ -299,6 +348,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "signerTEEType", + "outputs": [ + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -447,6 +515,31 @@ "name": "SignerRegistered", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "imageHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "reportDataSuffix", + "type": "bytes32" + } + ], + "name": "TDXSignerRegistered", + "type": "event" + }, { "inputs": [], "name": "AttestationTooOld", @@ -477,9 +570,40 @@ "name": "InvalidPublicKey", "type": "error" }, + { + "inputs": [], + "name": "InvalidTEEType", + "type": "error" + }, { "inputs": [], "name": "PCR0NotFound", "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "signer", + "type": "address" + }, + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "existingTEEType", + "type": "uint8" + }, + { + "internalType": "enum TEEProverRegistry.TEEType", + "name": "newTEEType", + "type": "uint8" + } + ], + "name": "SignerTEETypeMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "TDXVerifierNotSet", + "type": "error" } ] \ No newline at end of file diff --git a/snapshots/semver-lock.json b/snapshots/semver-lock.json index 9caf6f250..01ccf8ab8 100644 --- a/snapshots/semver-lock.json +++ b/snapshots/semver-lock.json @@ -36,8 +36,8 @@ "sourceCodeHash": "0xf122a50487efe9bd5a620262ba20ef4adbca14eeec2af7fd32e6e16739001596" }, "src/L1/proofs/AggregateVerifier.sol:AggregateVerifier": { - "initCodeHash": "0x10bb6a60f21de103d1da9ff310a38f571c53a8113d04a029087648debf3f0341", - "sourceCodeHash": "0xa9866fb867598e444846f2ce1a3460efc1d784751831f0c41c110f33b7ec844d" + "initCodeHash": "0x6e50ad8219dfc59503395bf63d2a4d91c002bd21a1df7c902debedea9c2e13eb", + "sourceCodeHash": "0x802a3645c05ae98232b091f779f6321f60230dd22f536b00dcbacccde6af4d3a" }, "src/L1/proofs/AnchorStateRegistry.sol:AnchorStateRegistry": { "initCodeHash": "0x6f3afd2d0ef97a82ca3111976322b99343a270e54cd4a405028f2f29c75f7fb1", @@ -55,13 +55,17 @@ "initCodeHash": "0x82f42b4d578bfcf9dc35eaa2c4ada04a45e1eca63021bceceb2ec794b12a9dd6", "sourceCodeHash": "0x5b2938048ff85baceb963c54138ce209890d77c63ce8791b48f36c5fda5c81e5" }, + "src/L1/proofs/tee/TDXVerifier.sol:TDXVerifier": { + "initCodeHash": "0xab68cfaae62a40d02a7d096bf1c81ec8ac2c106b19859ffa772a22b477a55af2", + "sourceCodeHash": "0x82a3eff9a7566e483b5f015dc97a88d90bdc4d9d7d49794556106bfc86d55ea0" + }, "src/L1/proofs/tee/TEEProverRegistry.sol:TEEProverRegistry": { - "initCodeHash": "0xfd1942e1c2f59b0aa72b33d698a948a53b6e4cf1040106f173fb5d89f63f57b0", - "sourceCodeHash": "0x410542b3899800d884a2281ef234a28d2dbb707576f2ad7d21f2d36de6290ca1" + "initCodeHash": "0x4b2fb8ba42668b1b9a8ebb99518fc0552703dfda190acde45d771847b450ac65", + "sourceCodeHash": "0x3e763a25f5353756c2ab52f6ae003f8cc7c1da2965aa2fd8b6ec6a0ac0ae6720" }, "src/L1/proofs/tee/TEEVerifier.sol:TEEVerifier": { - "initCodeHash": "0x655576cc21cc5c603d55fb4dd2a2f0ef57b11deeaabd3e539b0a70a5f7e2c9af", - "sourceCodeHash": "0x6c1d839e48cc8497af7b0d74a9566e76000599b14d7db7b3163a69e774a91bb3" + "initCodeHash": "0x5f42455c6d41c0a49e05681a6fa7dbf21eb22c89705fd11b6030952b3227e559", + "sourceCodeHash": "0xcd8358734ff27b0c17af73713b7f3d2b0f348ba0646efc356345bad9fa8d6a8c" }, "src/L1/proofs/v2/FaultDisputeGameV2.sol:FaultDisputeGameV2": { "initCodeHash": "0x6fc59e2da083c9e2093e42b0fda705e8215cc216e4dcedbf728c08f69ec2d3bd", diff --git a/snapshots/storageLayout/TDXVerifier.json b/snapshots/storageLayout/TDXVerifier.json new file mode 100644 index 000000000..2f13d341b --- /dev/null +++ b/snapshots/storageLayout/TDXVerifier.json @@ -0,0 +1,37 @@ +[ + { + "bytes": "8", + "label": "maxTimeDiff", + "offset": 0, + "slot": "0", + "type": "uint64" + }, + { + "bytes": "20", + "label": "proofSubmitter", + "offset": 8, + "slot": "0", + "type": "address" + }, + { + "bytes": "32", + "label": "rootCaHash", + "offset": 0, + "slot": "1", + "type": "bytes32" + }, + { + "bytes": "32", + "label": "zkConfig", + "offset": 0, + "slot": "2", + "type": "mapping(enum ZkCoProcessorType => struct ZkCoProcessorConfig)" + }, + { + "bytes": "32", + "label": "allowedTcbStatuses", + "offset": 0, + "slot": "3", + "type": "mapping(enum TDXTcbStatus => bool)" + } +] \ No newline at end of file diff --git a/snapshots/storageLayout/TEEProverRegistry.json b/snapshots/storageLayout/TEEProverRegistry.json index 93dcbfff3..30836afaa 100644 --- a/snapshots/storageLayout/TEEProverRegistry.json +++ b/snapshots/storageLayout/TEEProverRegistry.json @@ -75,5 +75,12 @@ "offset": 0, "slot": "105", "type": "struct EnumerableSetLib.AddressSet" + }, + { + "bytes": "32", + "label": "signerTEEType", + "offset": 0, + "slot": "106", + "type": "mapping(address => enum TEEProverRegistry.TEEType)" } ] \ No newline at end of file diff --git a/src/L1/proofs/AggregateVerifier.sol b/src/L1/proofs/AggregateVerifier.sol index 84a792af3..f5b5a052e 100644 --- a/src/L1/proofs/AggregateVerifier.sol +++ b/src/L1/proofs/AggregateVerifier.sol @@ -43,6 +43,23 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { bytes32 aggregateHash; } + /// @notice Hashes for the TEE proving images. + struct TeeHashes { + bytes32 nitroHash; + bytes32 tdxHash; + } + + /// @notice Common public inputs used to compute each TEE signature journal. + struct TeeJournalInputs { + address proposer; + bytes32 l1OriginHash; + bytes32 startingRoot; + uint64 startingL2SequenceNumber; + bytes32 endingRoot; + uint64 endingL2SequenceNumber; + bytes intermediateRoots; + } + //////////////////////////////////////////////////////////////// // Constants // //////////////////////////////////////////////////////////////// @@ -65,6 +82,11 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The minimum number of proofs required to resolve the game. uint256 public constant PROOF_THRESHOLD = 1; + + /// @notice TEE proof payload size: + /// nitro image hash(32) + nitro signature(65) + tdx image hash(32) + tdx signature(65). + uint256 internal constant TEE_PROOF_BYTES_LENGTH = 194; + //////////////////////////////////////////////////////////////// // Immutables // //////////////////////////////////////////////////////////////// @@ -80,8 +102,11 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @notice The TEE prover. IVerifier public immutable TEE_VERIFIER; - /// @notice The hash of the TEE image. - bytes32 public immutable TEE_IMAGE_HASH; + /// @notice The hash of the Nitro TEE image. + bytes32 public immutable TEE_NITRO_IMAGE_HASH; + + /// @notice The hash of the TDX TEE image. + bytes32 public immutable TEE_TDX_IMAGE_HASH; /// @notice The ZK prover. IVerifier public immutable ZK_VERIFIER; @@ -255,7 +280,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { /// @param delayedWETH The delayed WETH contract. /// @param teeVerifier The TEE verifier. /// @param zkVerifier The ZK verifier. - /// @param teeImageHash The hash of the TEE image. + /// @param teeHashes The hashes of the Nitro and TDX TEE images. /// @param zkHashes The hashes of the ZK range and aggregate programs. /// @param configHash The hash of the rollup configuration. /// @param l2ChainId The chain ID of the L2 network. @@ -267,7 +292,7 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { IDelayedWETH delayedWETH, IVerifier teeVerifier, IVerifier zkVerifier, - bytes32 teeImageHash, + TeeHashes memory teeHashes, ZkHashes memory zkHashes, bytes32 configHash, uint256 l2ChainId, @@ -286,7 +311,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { DELAYED_WETH = delayedWETH; TEE_VERIFIER = teeVerifier; ZK_VERIFIER = zkVerifier; - TEE_IMAGE_HASH = teeImageHash; + TEE_NITRO_IMAGE_HASH = teeHashes.nitroHash; + TEE_TDX_IMAGE_HASH = teeHashes.tdxHash; ZK_RANGE_HASH = zkHashes.rangeHash; ZK_AGGREGATE_HASH = zkHashes.aggregateHash; CONFIG_HASH = configHash; @@ -877,7 +903,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } /// @notice Verifies a TEE proof for the current game. - /// @param proofBytes The proof: signature(65). + /// @param proofBytes The proof: nitro image hash(32) + nitro signature(65) + tdx image hash(32) + /// + tdx signature(65). function _verifyTeeProof( bytes calldata proofBytes, address proposer, @@ -890,24 +917,51 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { ) internal view + { + if (proofBytes.length != TEE_PROOF_BYTES_LENGTH) revert InvalidProof(); + + bytes32 nitroImageHash = bytes32(proofBytes[:32]); + bytes32 tdxImageHash = bytes32(proofBytes[97:129]); + if (nitroImageHash != TEE_NITRO_IMAGE_HASH || tdxImageHash != TEE_TDX_IMAGE_HASH) revert InvalidProof(); + + TeeJournalInputs memory inputs = TeeJournalInputs({ + proposer: proposer, + l1OriginHash: l1OriginHash, + startingRoot: startingRoot, + startingL2SequenceNumber: startingL2SequenceNumber, + endingRoot: endingRoot, + endingL2SequenceNumber: endingL2SequenceNumber, + intermediateRoots: intermediateRoots + }); + + _verifyTeeSignature(proofBytes[32:97], nitroImageHash, inputs); + _verifyTeeSignature(proofBytes[129:194], tdxImageHash, inputs); + } + + function _verifyTeeSignature( + bytes calldata signature, + bytes32 imageHash, + TeeJournalInputs memory inputs + ) + internal + view { bytes32 journal = keccak256( abi.encodePacked( - proposer, - l1OriginHash, - startingRoot, - startingL2SequenceNumber, - endingRoot, - endingL2SequenceNumber, - intermediateRoots, + inputs.proposer, + inputs.l1OriginHash, + inputs.startingRoot, + inputs.startingL2SequenceNumber, + inputs.endingRoot, + inputs.endingL2SequenceNumber, + inputs.intermediateRoots, CONFIG_HASH, - TEE_IMAGE_HASH + imageHash ) ); - // Validate the proof. - bytes memory proof = abi.encodePacked(proposer, proofBytes); - if (!TEE_VERIFIER.verify(proof, TEE_IMAGE_HASH, journal)) revert InvalidProof(); + bytes memory verifierProof = abi.encodePacked(inputs.proposer, signature); + if (!TEE_VERIFIER.verify(verifierProof, imageHash, journal)) revert InvalidProof(); } /// @notice Verifies a ZK proof for the current game. @@ -1036,8 +1090,8 @@ contract AggregateVerifier is Clone, ReentrancyGuard, ISemver { } /// @notice Semantic version. - /// @custom:semver 0.1.0 + /// @custom:semver 0.2.0 function version() public pure virtual returns (string memory) { - return "0.1.0"; + return "0.2.0"; } } diff --git a/src/L1/proofs/tee/TDXVerifier.sol b/src/L1/proofs/tee/TDXVerifier.sol new file mode 100644 index 000000000..09e9b5195 --- /dev/null +++ b/src/L1/proofs/tee/TDXVerifier.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Ownable } from "@solady/auth/Ownable.sol"; +import { ISP1Verifier } from "interfaces/L1/proofs/zk/ISP1Verifier.sol"; +import { IRiscZeroVerifier } from "lib/risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; + +import { ISemver } from "interfaces/universal/ISemver.sol"; +import { + ITDXVerifier, + TDXTcbStatus, + TDXVerificationResult, + TDXVerifierJournal, + ZkCoProcessorType, + ZkCoProcessorConfig +} from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; + +/// @title TDXVerifier +/// @notice Production-shape Intel TDX DCAP verifier for multiproof signer registration. +/// @dev The heavy TDX work is expected to happen in a ZK guest: quote signature verification, PCK chain +/// validation, TCB info validation, QE identity validation, CRL checks, and extraction of TDREPORT fields. +/// This contract verifies the ZK proof and enforces on-chain policy over the verified journal. +contract TDXVerifier is Ownable, ITDXVerifier, ISemver { + /// @notice Conversion factor from milliseconds to seconds. + uint256 private constant MS_PER_SECOND = 1000; + + /// @notice Maximum accepted age of a TDX quote, in seconds. + uint64 public maxTimeDiff; + + /// @notice Address authorized to submit TDX proofs, expected to be the TDX-aware registry. + address public proofSubmitter; + + /// @notice Hash of the trusted Intel root CA used by the ZK verifier guest. + bytes32 public rootCaHash; + + /// @notice Configuration mapping for each supported ZK coprocessor type. + mapping(ZkCoProcessorType => ZkCoProcessorConfig) public zkConfig; + + /// @inheritdoc ITDXVerifier + mapping(TDXTcbStatus => bool) public allowedTcbStatuses; + + /// @notice Emitted when the trusted Intel root CA hash changes. + event RootCaHashUpdated(bytes32 indexed rootCaHash); + + /// @notice Emitted when the proof submitter changes. + event ProofSubmitterChanged(address indexed proofSubmitter); + + /// @notice Emitted when the quote timestamp tolerance changes. + event MaxTimeDiffUpdated(uint64 maxTimeDiff); + + /// @notice Emitted when a TCB status policy changes. + event TcbStatusPolicyUpdated(TDXTcbStatus indexed status, bool allowed); + + /// @notice Emitted when ZK configuration changes. + event ZKConfigurationUpdated(ZkCoProcessorType indexed zkCoprocessor, ZkCoProcessorConfig config); + + /// @notice Emitted after a TDX attestation journal is accepted. + event AttestationSubmitted( + address indexed signer, + bytes32 indexed imageHash, + TDXTcbStatus indexed tcbStatus, + bytes32 pckCertHash, + bytes32 tcbInfoHash, + bytes32 qeIdentityHash + ); + + /// @notice Thrown when a zero maxTimeDiff is provided. + error ZeroMaxTimeDiff(); + + /// @notice Thrown when a zero address is provided for the proof submitter. + error ZeroProofSubmitter(); + + /// @notice Thrown when a zero root CA hash is provided. + error ZeroRootCaHash(); + + /// @notice Thrown when the caller is not the configured proof submitter. + error CallerNotProofSubmitter(); + + /// @notice Thrown when the ZK coprocessor type is unknown. + error UnknownZkCoprocessor(); + + /// @notice Thrown when the configured ZK verifier address is zero. + error ZkVerifierNotConfigured(ZkCoProcessorType zkCoprocessor); + + /// @notice Thrown when the TDX verifier guest did not report success. + error TDXVerificationFailed(TDXVerificationResult result); + + /// @notice Thrown when the journal root does not match the trusted Intel root. + error RootCaHashMismatch(bytes32 expected, bytes32 actual); + + /// @notice Thrown when the journal's TCB status is not allowed. + error TcbStatusNotAllowed(TDXTcbStatus status); + + /// @notice Thrown when collateral consumed by the ZK guest is expired. + error CollateralExpired(uint64 collateralExpiration); + + /// @notice Thrown when the quote timestamp is outside policy. + error InvalidTimestamp(uint64 timestampSeconds, uint256 currentTimestamp); + + /// @notice Thrown when the public key is not an uncompressed secp256k1 public key. + error InvalidPublicKey(); + + /// @notice Thrown when the journal signer does not match the supplied public key. + error SignerMismatch(address expected, address actual); + + /// @notice Thrown when TDREPORT.REPORTDATA does not bind the supplied public key. + error ReportDataMismatch(bytes32 expected, bytes32 actual); + + constructor( + address owner, + uint64 initialMaxTimeDiff, + bytes32 initialRootCaHash, + address initialProofSubmitter, + ZkCoProcessorType zkCoprocessor, + ZkCoProcessorConfig memory config, + TDXTcbStatus[] memory initialAllowedTcbStatuses + ) { + _initializeOwner(owner); + _setMaxTimeDiff(initialMaxTimeDiff); + _setRootCaHash(initialRootCaHash); + _setProofSubmitter(initialProofSubmitter); + _setZkConfiguration(zkCoprocessor, config); + + for (uint256 i = 0; i < initialAllowedTcbStatuses.length; i++) { + _setTcbStatusAllowed(initialAllowedTcbStatuses[i], true); + } + } + + /// @inheritdoc ITDXVerifier + function getZkConfig(ZkCoProcessorType zkCoprocessor) external view returns (ZkCoProcessorConfig memory) { + return zkConfig[zkCoprocessor]; + } + + /// @notice Sets the trusted Intel root CA hash. + function setRootCaHash(bytes32 newRootCaHash) external onlyOwner { + _setRootCaHash(newRootCaHash); + } + + /// @notice Sets the proof submitter, expected to be the TDX-aware registry. + function setProofSubmitter(address newProofSubmitter) external onlyOwner { + _setProofSubmitter(newProofSubmitter); + } + + /// @notice Sets maximum allowed quote age, in seconds. + function setMaxTimeDiff(uint64 newMaxTimeDiff) external onlyOwner { + _setMaxTimeDiff(newMaxTimeDiff); + } + + /// @notice Sets whether a TDX TCB status is accepted. + function setTcbStatusAllowed(TDXTcbStatus status, bool allowed) external onlyOwner { + _setTcbStatusAllowed(status, allowed); + } + + /// @notice Configures a ZK verifier/program for a coprocessor. + function setZkConfiguration(ZkCoProcessorType zkCoprocessor, ZkCoProcessorConfig memory config) external onlyOwner { + _setZkConfiguration(zkCoprocessor, config); + } + + /// @inheritdoc ITDXVerifier + function verify( + bytes calldata output, + ZkCoProcessorType zkCoprocessor, + bytes calldata proofBytes + ) + external + returns (TDXVerifierJournal memory journal) + { + if (msg.sender != proofSubmitter) revert CallerNotProofSubmitter(); + + _verifyZk(zkCoprocessor, output, proofBytes); + journal = abi.decode(output, (TDXVerifierJournal)); + _verifyJournal(journal); + + emit AttestationSubmitted( + journal.signer, + journal.imageHash, + journal.tcbStatus, + journal.pckCertHash, + journal.tcbInfoHash, + journal.qeIdentityHash + ); + } + + /// @notice Semantic version. + /// @custom:semver 0.2.0 + function version() public pure virtual returns (string memory) { + return "0.2.0"; + } + + function _verifyJournal(TDXVerifierJournal memory journal) internal view { + if (journal.result != TDXVerificationResult.Success) revert TDXVerificationFailed(journal.result); + bytes32 expectedRootCaHash = rootCaHash; + if (journal.rootCaHash != expectedRootCaHash) { + revert RootCaHashMismatch(expectedRootCaHash, journal.rootCaHash); + } + if (!allowedTcbStatuses[journal.tcbStatus]) revert TcbStatusNotAllowed(journal.tcbStatus); + if (journal.collateralExpiration <= block.timestamp) revert CollateralExpired(journal.collateralExpiration); + + uint64 timestamp = journal.timestamp / uint64(MS_PER_SECOND); + if (timestamp + maxTimeDiff <= block.timestamp || timestamp >= block.timestamp) { + revert InvalidTimestamp(timestamp, block.timestamp); + } + + bytes32 publicKeyHash = _derivePublicKeyHash(journal.publicKey); + address signer = address(uint160(uint256(publicKeyHash))); + if (journal.signer != signer) revert SignerMismatch(signer, journal.signer); + if (journal.reportDataPrefix != publicKeyHash) { + revert ReportDataMismatch(publicKeyHash, journal.reportDataPrefix); + } + } + + function _verifyZk( + ZkCoProcessorType zkCoprocessor, + bytes calldata output, + bytes calldata proofBytes + ) + internal + view + { + ZkCoProcessorConfig memory config = zkConfig[zkCoprocessor]; + if (config.zkVerifier == address(0)) revert ZkVerifierNotConfigured(zkCoprocessor); + + if (zkCoprocessor == ZkCoProcessorType.RiscZero) { + IRiscZeroVerifier(config.zkVerifier).verify(proofBytes, config.verifierId, sha256(output)); + } else if (zkCoprocessor == ZkCoProcessorType.Succinct) { + ISP1Verifier(config.zkVerifier).verifyProof(config.verifierId, output, proofBytes); + } else { + revert UnknownZkCoprocessor(); + } + } + + function _derivePublicKeyHash(bytes memory publicKey) internal pure returns (bytes32 publicKeyHash) { + if (publicKey.length != 65 || publicKey[0] != 0x04) revert InvalidPublicKey(); + // Skip the 32-byte length word and the 0x04 uncompressed prefix; hash the 64-byte X||Y. + assembly { + publicKeyHash := keccak256(add(publicKey, 0x21), 64) + } + } + + function _setRootCaHash(bytes32 newRootCaHash) internal { + if (newRootCaHash == bytes32(0)) revert ZeroRootCaHash(); + rootCaHash = newRootCaHash; + emit RootCaHashUpdated(newRootCaHash); + } + + function _setProofSubmitter(address newProofSubmitter) internal { + if (newProofSubmitter == address(0)) revert ZeroProofSubmitter(); + proofSubmitter = newProofSubmitter; + emit ProofSubmitterChanged(newProofSubmitter); + } + + function _setMaxTimeDiff(uint64 newMaxTimeDiff) internal { + if (newMaxTimeDiff == 0) revert ZeroMaxTimeDiff(); + maxTimeDiff = newMaxTimeDiff; + emit MaxTimeDiffUpdated(newMaxTimeDiff); + } + + function _setTcbStatusAllowed(TDXTcbStatus status, bool allowed) internal { + allowedTcbStatuses[status] = allowed; + emit TcbStatusPolicyUpdated(status, allowed); + } + + function _setZkConfiguration(ZkCoProcessorType zkCoprocessor, ZkCoProcessorConfig memory config) internal { + zkConfig[zkCoprocessor] = config; + emit ZKConfigurationUpdated(zkCoprocessor, config); + } +} diff --git a/src/L1/proofs/tee/TEEProverRegistry.sol b/src/L1/proofs/tee/TEEProverRegistry.sol index 2292154e0..800b9dbf3 100644 --- a/src/L1/proofs/tee/TEEProverRegistry.sol +++ b/src/L1/proofs/tee/TEEProverRegistry.sol @@ -9,6 +9,7 @@ import { Pcr, Bytes48 } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { ISemver } from "interfaces/universal/ISemver.sol"; import { EnumerableSetLib } from "lib/solady-v0.0.245/src/utils/EnumerableSetLib.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; @@ -16,15 +17,22 @@ import { OwnableManagedUpgradeable } from "src/universal/OwnableManagedUpgradeab import { GameType } from "src/libraries/bridge/Types.sol"; /// @title TEEProverRegistry -/// @notice Manages TEE signer registration via ZK-verified AWS Nitro attestation. -/// @dev Signers are registered by providing a ZK proof of a valid AWS Nitro attestation document, -/// verified through an external NitroEnclaveVerifier contract (Risc0). +/// @notice Manages TEE signer registration via ZK-verified Nitro or TDX attestation. +/// @dev Nitro signers are registered by providing a ZK proof of a valid AWS Nitro attestation document, +/// verified through an external NitroEnclaveVerifier contract (Risc0). TDX signers are registered +/// through the TDXVerifier configured on the implementation. /// Registration is PCR0-agnostic: any enclave with a valid attestation can register, -/// enabling pre-registration before hardforks. PCR0 enforcement happens at proof-submission +/// enabling pre-registration before hardforks. PCR0 / image enforcement happens at proof-submission /// time in TEEVerifier, which checks signerImageHash against the AggregateVerifier's -/// TEE_IMAGE_HASH. +/// type-specific TEE image hash and requires one Nitro signer plus one TDX signer. contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { using EnumerableSetLib for EnumerableSetLib.AddressSet; + enum TEEType { + NONE, + NITRO, + TDX + } + /// @notice Maximum age of an attestation document (60 minutes), in seconds. uint256 public constant MAX_AGE = 60 minutes; @@ -36,7 +44,10 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice The external NitroEnclaveVerifier contract used for ZK attestation verification. INitroEnclaveVerifier public immutable NITRO_VERIFIER; - /// @notice The DisputeGameFactory used to look up the current AggregateVerifier and its TEE_IMAGE_HASH. + /// @notice The external TDXVerifier contract used for ZK TDX quote verification. + ITDXVerifier public immutable TDX_VERIFIER; + + /// @notice The DisputeGameFactory used to look up the current AggregateVerifier and its TEE image hashes. IDisputeGameFactory public immutable DISPUTE_GAME_FACTORY; /// @notice The game type used to look up the AggregateVerifier in the factory. @@ -48,7 +59,7 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Mapping of signer address to the PCR0 image hash from their attestation. /// @dev Stored at registration time from the ZK-verified attestation document. - /// TEEVerifier checks this against the AggregateVerifier's TEE_IMAGE_HASH at + /// TEEVerifier checks this against the AggregateVerifier's type-specific TEE image hash at /// proof-submission time, so signers automatically become unusable when the /// AggregateVerifier upgrades to a new image hash. isValidSigner also uses /// this for off-chain pre-submission checks. @@ -62,9 +73,15 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// Enables O(1) on-chain enumeration via `getRegisteredSigners()`. EnumerableSetLib.AddressSet internal _registeredSigners; + /// @notice Mapping of signer address to the attestation type used to register it. + mapping(address => TEEType) public signerTEEType; + /// @notice Emitted when a signer is registered. event SignerRegistered(address indexed signer); + /// @notice Emitted when a TDX signer is registered. + event TDXSignerRegistered(address indexed signer, bytes32 indexed imageHash, bytes32 reportDataSuffix); + /// @notice Emitted when a signer is deregistered. event SignerDeregistered(address indexed signer); @@ -89,15 +106,26 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// @notice Thrown when the dispute game factory is not configured. error DisputeGameFactoryNotSet(); - /// @notice Thrown when reading TEE_IMAGE_HASH from the AggregateVerifier fails. + /// @notice Thrown when reading a TEE image hash from the AggregateVerifier fails. error ImageHashReadFailed(); - /// @notice Thrown when setting a game type whose AggregateVerifier has no TEE_IMAGE_HASH. + /// @notice Thrown when setting a game type whose AggregateVerifier has no type-specific TEE image hash. error InvalidGameType(); - constructor(INitroEnclaveVerifier nitroVerifier, IDisputeGameFactory factory) { + /// @notice Thrown when the TDX verifier is not configured. + error TDXVerifierNotSet(); + + /// @notice Thrown when attempting to register a signer with no TEE type. + error InvalidTEEType(); + + /// @notice Thrown when a signer is already registered under another TEE type. + error SignerTEETypeMismatch(address signer, TEEType existingTEEType, TEEType newTEEType); + + constructor(INitroEnclaveVerifier nitroVerifier, ITDXVerifier tdxVerifier, IDisputeGameFactory factory) { if (address(factory) == address(0)) revert DisputeGameFactoryNotSet(); + if (address(tdxVerifier) == address(0)) revert TDXVerifierNotSet(); NITRO_VERIFIER = nitroVerifier; + TDX_VERIFIER = tdxVerifier; DISPUTE_GAME_FACTORY = factory; initialize({ initialOwner: address(0xdEaD), @@ -116,17 +144,14 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } /// @notice Updates the game type used to look up the AggregateVerifier. - /// @dev Validates that the new game type has an AggregateVerifier with a non-zero TEE_IMAGE_HASH. + /// @dev Validates that the new game type has an AggregateVerifier with non-zero type-specific TEE image hashes. /// @param gameType_ The new game type ID. function setGameType(GameType gameType_) external onlyOwner { - // Validate the new game type points to a valid AggregateVerifier with a TEE_IMAGE_HASH - GameType oldGameType = gameType; + if ( + _getExpectedImageHash(gameType_, TEEType.NITRO) == bytes32(0) + || _getExpectedImageHash(gameType_, TEEType.TDX) == bytes32(0) + ) revert InvalidGameType(); gameType = gameType_; - bytes32 imageHash = _getExpectedImageHash(); - if (imageHash == bytes32(0)) { - gameType = oldGameType; - revert InvalidGameType(); - } emit GameTypeUpdated(gameType_); } @@ -136,9 +161,9 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { /// 2. Is less than MAX_AGE old /// Registration is PCR0-agnostic: any enclave with a valid attestation can register. /// This enables pre-registration of new-PCR0 enclaves before a hardfork, eliminating - /// proof-generation delay when the on-chain TEE_IMAGE_HASH rotates. The TEEVerifier + /// proof-generation delay when the on-chain Nitro image hash rotates. The TEEVerifier /// enforces PCR0 correctness at proof-submission time by checking signerImageHash - /// against the AggregateVerifier's TEE_IMAGE_HASH, so pre-registered enclaves cannot + /// against the AggregateVerifier's TEE_NITRO_IMAGE_HASH, so pre-registered enclaves cannot /// produce accepted proofs until the hardfork activates. /// @param output The ABI-encoded VerifierJournal from the ZK proof. /// @param proofBytes The Risc0 ZK proof bytes. @@ -151,26 +176,30 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { // the attestation is generated and when it is submitted to this contract. if (journal.timestamp / MS_PER_SECOND + MAX_AGE <= block.timestamp) revert AttestationTooOld(); - // Extract the attestation's PCR0 and store it for TEEVerifier to check at - // proof-submission time. No comparison against the current TEE_IMAGE_HASH - // here — the registry accepts any valid attestation. bytes32 pcr0Hash = _extractPCR0Hash(journal.pcrs); - // The publicKey is encoded in ANSI X9.62 format: 0x04 || x || y (65 bytes). - // We skip the first byte (0x04 prefix) when hashing to derive the address. + // publicKey is ANSI X9.62 uncompressed: 0x04 || x || y (65 bytes). Skip the prefix and hash + // only the 64-byte x||y to derive the signer address. bytes memory pubKey = journal.publicKey; if (pubKey.length != 65) revert InvalidPublicKey(); bytes32 publicKeyHash; assembly { - // Length is hardcoded to 64 to skip the 0x04 prefix and hash only the x and y coordinates publicKeyHash := keccak256(add(pubKey, 0x21), 64) } address enclaveAddress = address(uint160(uint256(publicKeyHash))); - isRegisteredSigner[enclaveAddress] = true; - signerImageHash[enclaveAddress] = pcr0Hash; - _registeredSigners.add(enclaveAddress); - emit SignerRegistered(enclaveAddress); + _registerSigner(enclaveAddress, pcr0Hash, TEEType.NITRO); + } + + /// @notice Registers a signer using a ZK proof of Intel TDX DCAP quote verification. + /// @param output ABI-encoded TDXVerifierJournal public values from the ZK verifier guest. + /// @param proofBytes ZK proof bytes. + function registerTDXSigner(bytes calldata output, bytes calldata proofBytes) external onlyOwnerOrManager { + TDXVerifierJournal memory journal = TDX_VERIFIER.verify(output, ZkCoProcessorType.RiscZero, proofBytes); + + _registerSigner(journal.signer, journal.imageHash, TEEType.TDX); + + emit TDXSignerRegistered(journal.signer, journal.imageHash, journal.reportDataSuffix); } /// @notice Deregisters a signer. @@ -178,18 +207,22 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { function deregisterSigner(address signer) external onlyOwnerOrManager { delete isRegisteredSigner[signer]; delete signerImageHash[signer]; + delete signerTEEType[signer]; _registeredSigners.remove(signer); emit SignerDeregistered(signer); } /// @notice Checks if an address is a valid signer. /// @dev Defense-in-depth: checks both that the signer is registered AND that their - /// registered image hash matches the current AggregateVerifier's TEE_IMAGE_HASH. + /// registered image hash matches the current AggregateVerifier's type-specific TEE image hash. /// This ensures signers automatically become invalid when the AggregateVerifier upgrades. /// @param signer The address to check. /// @return True if the signer is registered with the current image hash, false otherwise. function isValidSigner(address signer) external view returns (bool) { - return isRegisteredSigner[signer] && signerImageHash[signer] == _getExpectedImageHash(); + if (!isRegisteredSigner[signer]) return false; + TEEType teeType = signerTEEType[signer]; + if (teeType == TEEType.NONE) return false; + return signerImageHash[signer] == _getExpectedImageHash(gameType, teeType); } /// @notice Returns all currently registered signer addresses. @@ -200,10 +233,16 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { return _registeredSigners.values(); } - /// @notice Returns the expected TEE image hash from the current AggregateVerifier. - /// @return The TEE_IMAGE_HASH from the AggregateVerifier registered in the factory. - function getExpectedImageHash() external view returns (bytes32) { - return _getExpectedImageHash(); + /// @notice Returns the expected Nitro TEE image hash from the current AggregateVerifier. + /// @return The TEE_NITRO_IMAGE_HASH from the AggregateVerifier registered in the factory. + function getExpectedNitroImageHash() external view returns (bytes32) { + return _getExpectedImageHash(gameType, TEEType.NITRO); + } + + /// @notice Returns the expected TDX TEE image hash from the current AggregateVerifier. + /// @return The TEE_TDX_IMAGE_HASH from the AggregateVerifier registered in the factory. + function getExpectedTDXImageHash() external view returns (bytes32) { + return _getExpectedImageHash(gameType, TEEType.TDX); } /// @notice Initializes the contract with owner, manager, proposers, and game type. @@ -233,16 +272,34 @@ contract TEEProverRegistry is OwnableManagedUpgradeable, ISemver { } /// @notice Semantic version. - /// @custom:semver 0.5.0 + /// @custom:semver 0.7.0 function version() public pure virtual returns (string memory) { - return "0.5.0"; + return "0.7.0"; + } + + /// @dev Registers a signer and stores the image hash enforced by TEEVerifier at proof-submission time. + function _registerSigner(address signer, bytes32 imageHash, TEEType teeType) internal { + if (teeType == TEEType.NONE) revert InvalidTEEType(); + TEEType existingTEEType = signerTEEType[signer]; + if (existingTEEType != TEEType.NONE && existingTEEType != teeType) { + revert SignerTEETypeMismatch(signer, existingTEEType, teeType); + } + + isRegisteredSigner[signer] = true; + signerImageHash[signer] = imageHash; + signerTEEType[signer] = teeType; + _registeredSigners.add(signer); + emit SignerRegistered(signer); } - /// @dev Reads TEE_IMAGE_HASH from the AggregateVerifier registered in the factory. - function _getExpectedImageHash() internal view returns (bytes32) { - address impl = address(DISPUTE_GAME_FACTORY.gameImpls(gameType)); - // AggregateVerifier.TEE_IMAGE_HASH() selector - (bool success, bytes memory data) = impl.staticcall(abi.encodeWithSignature("TEE_IMAGE_HASH()")); + /// @dev Reads a type-specific TEE image hash from the AggregateVerifier registered in the factory for `gameType_`. + function _getExpectedImageHash(GameType gameType_, TEEType teeType) internal view returns (bytes32) { + if (teeType == TEEType.NONE) revert InvalidTEEType(); + address impl = address(DISPUTE_GAME_FACTORY.gameImpls(gameType_)); + bytes memory callData = teeType == TEEType.NITRO + ? abi.encodeWithSignature("TEE_NITRO_IMAGE_HASH()") + : abi.encodeWithSignature("TEE_TDX_IMAGE_HASH()"); + (bool success, bytes memory data) = impl.staticcall(callData); if (!success || data.length != 32) revert ImageHashReadFailed(); return abi.decode(data, (bytes32)); } diff --git a/src/L1/proofs/tee/TEEVerifier.sol b/src/L1/proofs/tee/TEEVerifier.sol index 4fb94f1a8..92428cf7d 100644 --- a/src/L1/proofs/tee/TEEVerifier.sol +++ b/src/L1/proofs/tee/TEEVerifier.sol @@ -10,19 +10,23 @@ import { TEEProverRegistry } from "./TEEProverRegistry.sol"; import { Verifier } from "../Verifier.sol"; /// @title TEEVerifier -/// @notice Stateless TEE proof verifier that validates signatures against registered signers. +/// @notice Stateless TEE proof verifier that validates Nitro and TDX signatures against registered signers. /// @dev This contract is designed to be used as the TEE_VERIFIER in the AggregateVerifier. -/// It verifies that proofs are signed by enclave addresses registered in TEEProverRegistry -/// via AWS Nitro attestation. PCR0 (enclave image hash) enforcement is handled by -/// AggregateVerifier, which bakes TEE_IMAGE_HASH into the journal that the enclave signs. +/// It verifies one TEE signature at a time against a registered Nitro or TDX signer. /// The contract is intentionally stateless - all state related to output proposals and /// L1 origin verification is managed by the calling contract (e.g., AggregateVerifier). contract TEEVerifier is Verifier, ISemver { /// @notice The TEEProverRegistry contract that manages valid TEE signers. - /// @dev Signers are registered via AWS Nitro attestation in TEEProverRegistry. + /// @dev Signers are registered via Nitro or TDX attestation in TEEProverRegistry. TEEProverRegistry public immutable TEE_PROVER_REGISTRY; - /// @notice Thrown when the recovered signer is not a valid registered signer. + /// @notice Size of an ECDSA signature in bytes. + uint256 internal constant SIGNATURE_SIZE = 65; + + /// @notice Size of a TEE proof: proposer(20) + signature(65). + uint256 internal constant TEE_PROOF_SIZE = 20 + SIGNATURE_SIZE; + + /// @notice Thrown when a recovered signer is not a valid registered signer. error InvalidSigner(address signer); /// @notice Thrown when the signer's registered image hash does not match the claimed imageId. @@ -50,8 +54,7 @@ contract TEEVerifier is Verifier, ISemver { /// @notice Verifies a TEE proof for a state transition. /// @param proofBytes The proof: proposer(20) + signature(65) = 85 bytes. - /// @param imageId The claimed TEE image hash (from the calling AggregateVerifier's TEE_IMAGE_HASH). - /// Validated against the signer's registered image hash to prevent cross-game-type attacks. + /// @param imageId The TEE image hash expected for the recovered signer. /// @param journal The keccak256 hash of the proof's public inputs. /// @return valid Whether the proof is valid. function verify( @@ -65,42 +68,44 @@ contract TEEVerifier is Verifier, ISemver { notNullified returns (bool) { - if (proofBytes.length < 85) revert InvalidProofFormat(); + if (proofBytes.length != TEE_PROOF_SIZE) revert InvalidProofFormat(); address proposer = address(bytes20(proofBytes[0:20])); - bytes calldata signature = proofBytes[20:85]; - - // Recover the signer from the signature - // The signature should be over the journal hash directly (not eth-signed-message prefixed) - (address signer, ECDSA.RecoverError err) = ECDSA.tryRecover(journal, signature); - - if (err != ECDSA.RecoverError.NoError) { - revert InvalidSignature(); - } - if (!TEE_PROVER_REGISTRY.isValidProposer(proposer)) { revert InvalidProposer(proposer); } - // Check that the signer is registered - if (!TEE_PROVER_REGISTRY.isRegisteredSigner(signer)) { - revert InvalidSigner(signer); - } + bytes calldata signature = proofBytes[20:TEE_PROOF_SIZE]; + address signer = _recoverSigner(journal, signature); + _validateSigner(signer, imageId); - // Check that the signer's registered image hash matches the calling AggregateVerifier's imageId. - // This prevents a signer registered under one enclave image from being used in a game + return true; + } + + function _recoverSigner(bytes32 journal, bytes calldata signature) internal pure returns (address signer) { + // The signature should be over the journal hash directly (not eth-signed-message prefixed). + ECDSA.RecoverError err; + (signer, err) = ECDSA.tryRecover(journal, signature); + if (err != ECDSA.RecoverError.NoError) revert InvalidSignature(); + } + + function _validateSigner(address signer, bytes32 imageId) internal view { + // A registered signer always has a non-NONE TEE type, so this single read also + // serves as the registration check (saves an SLOAD versus calling isRegisteredSigner). + TEEProverRegistry.TEEType signerTEEType = TEE_PROVER_REGISTRY.signerTEEType(signer); + if (signerTEEType == TEEProverRegistry.TEEType.NONE) revert InvalidSigner(signer); + + // Prevents a signer registered under one enclave image from being used in a game // that expects a different image (e.g., after an upgrade or across game types). bytes32 registeredImageHash = TEE_PROVER_REGISTRY.signerImageHash(signer); if (registeredImageHash != imageId) { revert ImageIdMismatch(registeredImageHash, imageId); } - - return true; } /// @notice Semantic version. - /// @custom:semver 0.2.0 + /// @custom:semver 0.3.0 function version() public pure virtual returns (string memory) { - return "0.2.0"; + return "0.3.0"; } } diff --git a/test/L1/proofs/AggregateVerifier.t.sol b/test/L1/proofs/AggregateVerifier.t.sol index b30267362..a452398bb 100644 --- a/test/L1/proofs/AggregateVerifier.t.sol +++ b/test/L1/proofs/AggregateVerifier.t.sol @@ -19,6 +19,53 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proof = _generateProof("tee-proof", AggregateVerifier.ProofType.TEE); + bytes memory intermediateRoots = + abi.encodePacked(_generateIntermediateRootsExceptLast(currentL2BlockNumber), rootClaim.raw()); + bytes32 l1OriginHash = blockhash(block.number - 1); + bytes32 startingRoot = keccak256(abi.encode(uint256(0))); + bytes32 saltHash = keccak256("tee-proof"); + bytes memory nitroSignature = abi.encodePacked(saltHash, bytes32(0), uint8(27)); + bytes memory tdxSignature = abi.encodePacked(saltHash, bytes32(uint256(1)), uint8(28)); + bytes32 nitroJournal = keccak256( + abi.encodePacked( + TEE_PROVER, + l1OriginHash, + startingRoot, + uint64(0), + rootClaim.raw(), + uint64(currentL2BlockNumber), + intermediateRoots, + CONFIG_HASH, + TEE_NITRO_IMAGE_HASH + ) + ); + bytes32 tdxJournal = keccak256( + abi.encodePacked( + TEE_PROVER, + l1OriginHash, + startingRoot, + uint64(0), + rootClaim.raw(), + uint64(currentL2BlockNumber), + intermediateRoots, + CONFIG_HASH, + TEE_TDX_IMAGE_HASH + ) + ); + + vm.expectCall( + address(teeVerifier), + abi.encodeCall( + IVerifier.verify, (abi.encodePacked(TEE_PROVER, nitroSignature), TEE_NITRO_IMAGE_HASH, nitroJournal) + ) + ); + vm.expectCall( + address(teeVerifier), + abi.encodeCall( + IVerifier.verify, (abi.encodePacked(TEE_PROVER, tdxSignature), TEE_TDX_IMAGE_HASH, tdxJournal) + ) + ); + AggregateVerifier game = _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proof ); @@ -268,7 +315,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(194)); vm.expectRevert( abi.encodeWithSelector(AggregateVerifier.L1OriginInFuture.selector, l1OriginNumber, block.number) @@ -290,7 +337,7 @@ contract AggregateVerifierTest is BaseTest { Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); + abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, new bytes(194)); vm.expectRevert(abi.encodeWithSelector(AggregateVerifier.L1OriginTooOld.selector, l1OriginNumber, block.number)); _createAggregateVerifierGame( @@ -325,8 +372,12 @@ contract AggregateVerifierTest is BaseTest { bytes32 l1OriginHash = blockhash(l1OriginNumber); Claim rootClaim = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber))); - bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), l1OriginHash, l1OriginNumber, rootClaim.raw()); + bytes memory proofBytes = abi.encodePacked( + uint8(AggregateVerifier.ProofType.TEE), + l1OriginHash, + l1OriginNumber, + _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) + ); _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes @@ -351,8 +402,12 @@ contract AggregateVerifierTest is BaseTest { abi.encode(expectedHash) // returns the blockhash ); - bytes memory proofBytes = - abi.encodePacked(uint8(AggregateVerifier.ProofType.TEE), expectedHash, l1OriginNumber, rootClaim.raw()); + bytes memory proofBytes = abi.encodePacked( + uint8(AggregateVerifier.ProofType.TEE), + expectedHash, + l1OriginNumber, + _generateProofBody("tee-proof", AggregateVerifier.ProofType.TEE) + ); _createAggregateVerifierGame( TEE_PROVER, rootClaim, currentL2BlockNumber, address(anchorStateRegistry), proofBytes @@ -370,7 +425,7 @@ contract AggregateVerifierTest is BaseTest { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, @@ -386,7 +441,7 @@ contract AggregateVerifierTest is BaseTest { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, @@ -402,7 +457,7 @@ contract AggregateVerifierTest is BaseTest { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, diff --git a/test/L1/proofs/BaseTest.t.sol b/test/L1/proofs/BaseTest.t.sol index 5117f6e26..8ab9780e7 100644 --- a/test/L1/proofs/BaseTest.t.sol +++ b/test/L1/proofs/BaseTest.t.sol @@ -49,7 +49,8 @@ contract BaseTest is Test { address public immutable ZK_PROVER = makeAddr("zk-prover"); address public immutable ATTACKER = makeAddr("attacker"); - bytes32 public immutable TEE_IMAGE_HASH = keccak256("tee-image"); + bytes32 public immutable TEE_NITRO_IMAGE_HASH = keccak256("tee-nitro-image"); + bytes32 public immutable TEE_TDX_IMAGE_HASH = keccak256("tee-tdx-image"); bytes32 public immutable ZK_RANGE_HASH = keccak256("zk-range"); bytes32 public immutable ZK_AGGREGATE_HASH = keccak256("zk-aggregate"); bytes32 public immutable CONFIG_HASH = keccak256("config"); @@ -132,7 +133,7 @@ contract BaseTest is Test { IDelayedWETH(payable(address(delayedWETH))), IVerifier(address(teeVerifier)), IVerifier(address(zkVerifier)), - TEE_IMAGE_HASH, + AggregateVerifier.TeeHashes(TEE_NITRO_IMAGE_HASH, TEE_TDX_IMAGE_HASH), AggregateVerifier.ZkHashes(ZK_RANGE_HASH, ZK_AGGREGATE_HASH), CONFIG_HASH, L2_CHAIN_ID, @@ -220,10 +221,42 @@ contract BaseTest is Test { bytes32 l1OriginHash = blockhash(block.number - 1); // Use the previous block number as l1OriginNumber uint256 l1OriginNumber = block.number - 1; - // Add some padding/signature data (65 bytes minimum for a signature) - bytes memory signature = abi.encodePacked(salt, bytes32(0), bytes32(0), uint8(27)); + return abi.encodePacked(uint8(proofType), l1OriginHash, l1OriginNumber, _generateProofBody(salt, proofType)); + } + + function _generateProposalProof( + bytes memory salt, + AggregateVerifier.ProofType proofType + ) + internal + view + returns (bytes memory) + { + return abi.encodePacked(uint8(proofType), _generateProofBody(salt, proofType)); + } - return abi.encodePacked(uint8(proofType), l1OriginHash, l1OriginNumber, signature); + function _generateProofBody( + bytes memory salt, + AggregateVerifier.ProofType proofType + ) + internal + view + returns (bytes memory) + { + if (proofType == AggregateVerifier.ProofType.TEE) { + bytes32 saltHash = keccak256(salt); + return abi.encodePacked( + TEE_NITRO_IMAGE_HASH, + saltHash, + bytes32(0), + uint8(27), + TEE_TDX_IMAGE_HASH, + saltHash, + bytes32(uint256(1)), + uint8(28) + ); + } + return abi.encodePacked(salt, bytes32(0), bytes32(0), uint8(27)); } function _generateIntermediateRootsExceptLast(uint256 l2BlockNumber) internal pure returns (bytes memory) { diff --git a/test/L1/proofs/Challenge.t.sol b/test/L1/proofs/Challenge.t.sol index 27c754758..de0cca167 100644 --- a/test/L1/proofs/Challenge.t.sol +++ b/test/L1/proofs/Challenge.t.sol @@ -80,7 +80,7 @@ contract ChallengeTest is BaseTest { ); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); vm.expectRevert(AggregateVerifier.InvalidProofType.selector); game1.challenge(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); @@ -170,7 +170,7 @@ contract ChallengeTest is BaseTest { ); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); @@ -284,7 +284,7 @@ contract ChallengeTest is BaseTest { _createAggregateVerifierGame(TEE_PROVER, rootClaimB, currentL2BlockNumber, address(gameA), teeProofB); Claim rootNullifyB = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee-nullify-b"))); - bytes memory teeNullifyB = _generateProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); + bytes memory teeNullifyB = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); uint256 lastIdx = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; gameB.nullify(teeNullifyB, lastIdx, rootNullifyB.raw()); assertTrue(teeVerifier.nullified()); diff --git a/test/L1/proofs/Nullify.t.sol b/test/L1/proofs/Nullify.t.sol index 7a5e1a2e3..e0f2d9ccf 100644 --- a/test/L1/proofs/Nullify.t.sol +++ b/test/L1/proofs/Nullify.t.sol @@ -20,7 +20,7 @@ contract NullifyTest is BaseTest { ); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); @@ -87,7 +87,7 @@ contract NullifyTest is BaseTest { assertEq(game.expectedResolution().raw(), block.timestamp + 1 days); Claim rootClaim2 = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "tee2"))); - bytes memory teeProof2 = _generateProof("tee-proof-2", AggregateVerifier.ProofType.TEE); + bytes memory teeProof2 = _generateProposalProof("tee-proof-2", AggregateVerifier.ProofType.TEE); game.nullify(teeProof2, BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1, rootClaim2.raw()); assertEq(uint8(game.status()), uint8(GameStatus.IN_PROGRESS)); @@ -195,7 +195,7 @@ contract NullifyTest is BaseTest { assertEq(gameA.proofCount(), 1); Claim rootClaimNullify = Claim.wrap(keccak256(abi.encode(currentL2BlockNumber, "nullify-b"))); - bytes memory teeProofNullify = _generateProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); + bytes memory teeProofNullify = _generateProposalProof("tee-nullify-b", AggregateVerifier.ProofType.TEE); uint256 lastIntermediateIdx = BLOCK_INTERVAL / INTERMEDIATE_BLOCK_INTERVAL - 1; gameB.nullify(teeProofNullify, lastIntermediateIdx, rootClaimNullify.raw()); diff --git a/test/L1/proofs/TDXVerifier.t.sol b/test/L1/proofs/TDXVerifier.t.sol new file mode 100644 index 000000000..83c74afbd --- /dev/null +++ b/test/L1/proofs/TDXVerifier.t.sol @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; + +import { TDXTcbStatus, TDXVerificationResult, TDXVerifierJournal } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; + +import { TDXVerifier } from "src/L1/proofs/tee/TDXVerifier.sol"; + +contract TDXVerifierTest is Test { + TDXVerifier internal verifier; + + address internal owner; + address internal proofSubmitter; + address internal mockRiscZeroVerifier; + address internal mockSP1Verifier; + + bytes32 internal constant ROOT_CA_HASH = keccak256("intel-root-ca"); + bytes32 internal constant WRONG_ROOT_CA_HASH = keccak256("wrong-root-ca"); + bytes32 internal constant VERIFIER_ID = keccak256("tdx-verifier-id"); + bytes32 internal constant AGGREGATOR_ID = keccak256("tdx-aggregator-id"); + bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); + bytes32 internal constant MRTD_HASH = keccak256("mrtd"); + bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); + + uint64 internal constant MAX_TIME_DIFF = 3600; + uint256 internal constant NOW = 1_700_000_000; + + function setUp() public { + vm.warp(NOW); + + owner = address(this); + proofSubmitter = address(this); + mockRiscZeroVerifier = makeAddr("mock-risc-zero"); + mockSP1Verifier = makeAddr("mock-sp1"); + + TDXTcbStatus[] memory allowedStatuses = new TDXTcbStatus[](2); + allowedStatuses[0] = TDXTcbStatus.UpToDate; + allowedStatuses[1] = TDXTcbStatus.SwHardeningNeeded; + + verifier = new TDXVerifier( + owner, + MAX_TIME_DIFF, + ROOT_CA_HASH, + proofSubmitter, + ZkCoProcessorType.Succinct, + ZkCoProcessorConfig({ verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockSP1Verifier }), + allowedStatuses + ); + } + + function testVerifySucceedsWithSP1ProofAndAllowedJournal() public { + TDXVerifierJournal memory journal = _successJournal(); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + + assertEq(result.signer, journal.signer); + assertEq(result.imageHash, IMAGE_HASH); + assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.UpToDate)); + } + + function testVerifySucceedsWithRiscZeroProof() public { + ZkCoProcessorConfig memory config = ZkCoProcessorConfig({ + verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockRiscZeroVerifier + }); + verifier.setZkConfiguration(ZkCoProcessorType.RiscZero, config); + + TDXVerifierJournal memory journal = _successJournal(); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"5678"; + _mockRiscZeroVerify(VERIFIER_ID, output, proofBytes); + + TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.RiscZero, proofBytes); + + assertEq(result.signer, journal.signer); + assertEq(result.imageHash, IMAGE_HASH); + } + + function testVerifyRevertsIfNotProofSubmitter() public { + bytes memory output = abi.encode(_successJournal()); + + vm.prank(makeAddr("not-submitter")); + vm.expectRevert(TDXVerifier.CallerNotProofSubmitter.selector); + verifier.verify(output, ZkCoProcessorType.Succinct, ""); + } + + function testVerifyRevertsIfZkVerifierNotConfigured() public { + bytes memory output = abi.encode(_successJournal()); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.ZkVerifierNotConfigured.selector, ZkCoProcessorType.RiscZero) + ); + verifier.verify(output, ZkCoProcessorType.RiscZero, ""); + } + + function testVerifyRevertsForUnknownCoprocessor() public { + ZkCoProcessorConfig memory config = + ZkCoProcessorConfig({ verifierId: VERIFIER_ID, aggregatorId: AGGREGATOR_ID, zkVerifier: mockSP1Verifier }); + verifier.setZkConfiguration(ZkCoProcessorType.Unknown, config); + + bytes memory output = abi.encode(_successJournal()); + + vm.expectRevert(TDXVerifier.UnknownZkCoprocessor.selector); + verifier.verify(output, ZkCoProcessorType.Unknown, ""); + } + + function testVerifyRevertsWhenGuestReportsFailure() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.result = TDXVerificationResult.PckCertChainInvalid; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TDXVerificationFailed.selector, journal.result)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenRootCaHashMismatches() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.rootCaHash = WRONG_ROOT_CA_HASH; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.RootCaHashMismatch.selector, ROOT_CA_HASH, WRONG_ROOT_CA_HASH) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenTcbStatusIsNotAllowed() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.TcbStatusNotAllowed.selector, journal.tcbStatus)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testOwnerCanAllowAdditionalTcbStatus() public { + verifier.setTcbStatusAllowed(TDXTcbStatus.ConfigurationNeeded, true); + + TDXVerifierJournal memory journal = _successJournal(); + journal.tcbStatus = TDXTcbStatus.ConfigurationNeeded; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + TDXVerifierJournal memory result = verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + + assertEq(uint256(result.tcbStatus), uint256(TDXTcbStatus.ConfigurationNeeded)); + } + + function testVerifyRevertsWhenCollateralExpired() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.collateralExpiration = uint64(block.timestamp); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.CollateralExpired.selector, journal.collateralExpiration)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenTimestampTooOld() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.timestamp = uint64(block.timestamp - MAX_TIME_DIFF) * 1000; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector( + TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp - MAX_TIME_DIFF), block.timestamp + ) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenTimestampIsFromFuture() public { + TDXVerifierJournal memory journal = _successJournal(); + journal.timestamp = uint64(block.timestamp) * 1000; + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.InvalidTimestamp.selector, uint64(block.timestamp), block.timestamp) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenReportDataDoesNotBindPublicKey() public { + TDXVerifierJournal memory journal = _successJournal(); + bytes32 expected = journal.reportDataPrefix; + journal.reportDataPrefix = keccak256("wrong-report-data"); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert( + abi.encodeWithSelector(TDXVerifier.ReportDataMismatch.selector, expected, journal.reportDataPrefix) + ); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function testVerifyRevertsWhenSignerDoesNotMatchPublicKey() public { + TDXVerifierJournal memory journal = _successJournal(); + address expected = journal.signer; + journal.signer = makeAddr("wrong-signer"); + bytes memory output = abi.encode(journal); + bytes memory proofBytes = hex"1234"; + _mockSP1Verify(VERIFIER_ID, output, proofBytes); + + vm.expectRevert(abi.encodeWithSelector(TDXVerifier.SignerMismatch.selector, expected, journal.signer)); + verifier.verify(output, ZkCoProcessorType.Succinct, proofBytes); + } + + function _successJournal() internal view returns (TDXVerifierJournal memory journal) { + bytes memory publicKey = _publicKey(); + bytes32 publicKeyHash; + assembly { + publicKeyHash := keccak256(add(publicKey, 0x21), 64) + } + + journal = TDXVerifierJournal({ + result: TDXVerificationResult.Success, + tcbStatus: TDXTcbStatus.UpToDate, + timestamp: uint64(block.timestamp - 1) * 1000, + collateralExpiration: uint64(block.timestamp + 1 days), + rootCaHash: ROOT_CA_HASH, + pckCertHash: keccak256("pck-cert"), + tcbInfoHash: keccak256("tcb-info"), + qeIdentityHash: keccak256("qe-identity"), + publicKey: publicKey, + signer: address(uint160(uint256(publicKeyHash))), + imageHash: IMAGE_HASH, + mrTdHash: MRTD_HASH, + reportDataPrefix: publicKeyHash, + reportDataSuffix: REPORT_DATA_SUFFIX + }); + } + + function _publicKey() internal pure returns (bytes memory publicKey) { + publicKey = new bytes(65); + publicKey[0] = 0x04; + for (uint256 i = 1; i < publicKey.length; i++) { + publicKey[i] = bytes1(uint8(i)); + } + } + + function _mockRiscZeroVerify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { + vm.mockCall( + mockRiscZeroVerifier, + abi.encodeWithSelector( + bytes4(keccak256("verify(bytes,bytes32,bytes32)")), proofBytes, programId, sha256(output) + ), + "" + ); + } + + function _mockSP1Verify(bytes32 programId, bytes memory output, bytes memory proofBytes) internal { + vm.mockCall( + mockSP1Verifier, + abi.encodeWithSelector( + bytes4(keccak256("verifyProof(bytes32,bytes,bytes)")), programId, output, proofBytes + ), + "" + ); + } +} diff --git a/test/L1/proofs/TEEProverRegistry.t.sol b/test/L1/proofs/TEEProverRegistry.t.sol index c9c4c74bc..eff980abb 100644 --- a/test/L1/proofs/TEEProverRegistry.t.sol +++ b/test/L1/proofs/TEEProverRegistry.t.sol @@ -9,6 +9,7 @@ import { import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { GameType } from "src/libraries/bridge/Types.sol"; @@ -17,12 +18,14 @@ import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; -/// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. +/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. contract MockAggregateVerifierForRegistry { - bytes32 public TEE_IMAGE_HASH; + bytes32 public TEE_NITRO_IMAGE_HASH; + bytes32 public TEE_TDX_IMAGE_HASH; constructor(bytes32 imageHash) { - TEE_IMAGE_HASH = imageHash; + TEE_NITRO_IMAGE_HASH = imageHash; + TEE_TDX_IMAGE_HASH = imageHash; } } @@ -77,8 +80,9 @@ contract TEEProverRegistryTest is Test { mockFactory.setImpl(TEST_GAME_TYPE, address(mockVerifier)); // Deploy implementation (using DevTEEProverRegistry for test flexibility) - DevTEEProverRegistry impl = - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(address(mockFactory))); + DevTEEProverRegistry impl = new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(mockFactory)) + ); // Deploy proxy admin proxyAdmin = new ProxyAdmin(address(this)); @@ -100,15 +104,16 @@ contract TEEProverRegistryTest is Test { function testInitialization() public view { assertEq(teeProverRegistry.owner(), owner); assertEq(teeProverRegistry.manager(), manager); - assertEq(teeProverRegistry.version(), "0.5.0"); + assertEq(teeProverRegistry.version(), "0.7.0"); } function testInitializationWithProposers() public { address proposer1 = makeAddr("proposer1"); address proposer2 = makeAddr("proposer2"); address proposer3 = makeAddr("proposer3"); - DevTEEProverRegistry impl2 = - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(address(1))); + DevTEEProverRegistry impl2 = new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(1)) + ); ProxyAdmin proxyAdmin2 = new ProxyAdmin(address(this)); address[] memory proposers = new address[](3); proposers[0] = proposer1; @@ -215,6 +220,35 @@ contract TEEProverRegistryTest is Test { teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); assertTrue(teeProverRegistry.isValidSigner(signer)); + assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.NITRO)); + } + + function testAddDevTDXSigner() public { + address signer = makeAddr("dev-tdx-signer"); + + vm.prank(owner); + teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); + + assertTrue(teeProverRegistry.isValidSigner(signer)); + assertEq(uint8(teeProverRegistry.signerTEEType(signer)), uint8(TEEProverRegistry.TEEType.TDX)); + } + + function testRegisteringSameSignerWithDifferentTEETypeFails() public { + address signer = makeAddr("dev-signer"); + + vm.prank(owner); + teeProverRegistry.addDevSigner(signer, TEST_IMAGE_HASH); + + vm.prank(owner); + vm.expectRevert( + abi.encodeWithSelector( + TEEProverRegistry.SignerTEETypeMismatch.selector, + signer, + TEEProverRegistry.TEEType.NITRO, + TEEProverRegistry.TEEType.TDX + ) + ); + teeProverRegistry.addDevTDXSigner(signer, TEST_IMAGE_HASH); } // ============ MAX_AGE Tests ============ diff --git a/test/L1/proofs/TEEProverRegistryTDX.t.sol b/test/L1/proofs/TEEProverRegistryTDX.t.sol new file mode 100644 index 000000000..bc8ad918f --- /dev/null +++ b/test/L1/proofs/TEEProverRegistryTDX.t.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { Test } from "forge-std/Test.sol"; + +import { IDisputeGame } from "interfaces/L1/proofs/IDisputeGame.sol"; +import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; +import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { + ITDXVerifier, + TDXTcbStatus, + TDXVerificationResult, + TDXVerifierJournal +} from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; +import { ZkCoProcessorConfig, ZkCoProcessorType } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { GameType } from "src/libraries/bridge/Types.sol"; + +import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; + +/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. +contract MockAggregateVerifierForTDXRegistry { + bytes32 public TEE_NITRO_IMAGE_HASH; + bytes32 public TEE_TDX_IMAGE_HASH; + + constructor(bytes32 imageHash) { + TEE_NITRO_IMAGE_HASH = imageHash; + TEE_TDX_IMAGE_HASH = imageHash; + } +} + +/// @notice Mock DisputeGameFactory that returns a fixed game implementation. +contract MockDisputeGameFactoryForTDXRegistry { + mapping(uint32 => address) internal _impls; + + function setImpl(uint32 gameType, address impl) external { + _impls[gameType] = impl; + } + + function gameImpls(GameType gameType) external view returns (IDisputeGame) { + return IDisputeGame(_impls[GameType.unwrap(gameType)]); + } +} + +contract MockTDXVerifierForRegistry is ITDXVerifier { + TDXVerifierJournal internal _journal; + + function setJournal(TDXVerifierJournal memory journal) external { + _journal = journal; + } + + function verify( + bytes calldata, + ZkCoProcessorType, + bytes calldata + ) + external + view + returns (TDXVerifierJournal memory) + { + return _journal; + } + + function getZkConfig(ZkCoProcessorType) external pure returns (ZkCoProcessorConfig memory) { + return ZkCoProcessorConfig({ verifierId: bytes32(0), aggregatorId: bytes32(0), zkVerifier: address(0) }); + } + + function allowedTcbStatuses(TDXTcbStatus) external pure returns (bool) { + return true; + } + + function setProofSubmitter(address) external { } +} + +contract TEEProverRegistryTDXTest is Test { + bytes32 internal constant IMAGE_HASH = keccak256("tdx-image"); + bytes32 internal constant REPORT_DATA_SUFFIX = keccak256("multiproof-tdx-poc"); + + function testRegisterTDXSignerStoresImageHash() public { + TDXVerifierJournal memory journal = _successJournal(); + MockTDXVerifierForRegistry verifier = new MockTDXVerifierForRegistry(); + verifier.setJournal(journal); + + MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); + factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); + + TEEProverRegistry registry = new TEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(verifier)), IDisputeGameFactory(address(factory)) + ); + + vm.prank(address(0xdEaD)); + registry.registerTDXSigner("", ""); + + assertTrue(registry.isRegisteredSigner(journal.signer)); + assertEq(registry.signerImageHash(journal.signer), IMAGE_HASH); + assertEq(uint8(registry.signerTEEType(journal.signer)), uint8(TEEProverRegistry.TEEType.TDX)); + assertTrue(registry.isValidSigner(journal.signer)); + } + + function testConstructorRevertsIfTDXVerifierNotSet() public { + MockDisputeGameFactoryForTDXRegistry factory = new MockDisputeGameFactoryForTDXRegistry(); + factory.setImpl(0, address(new MockAggregateVerifierForTDXRegistry(IMAGE_HASH))); + + vm.expectRevert(TEEProverRegistry.TDXVerifierNotSet.selector); + new TEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(0)), IDisputeGameFactory(address(factory)) + ); + } + + function _successJournal() internal pure returns (TDXVerifierJournal memory journal) { + address signer = address(0x1234); + + journal = TDXVerifierJournal({ + result: TDXVerificationResult.Success, + tcbStatus: TDXTcbStatus.UpToDate, + timestamp: 0, + collateralExpiration: 0, + rootCaHash: bytes32(0), + pckCertHash: bytes32(0), + tcbInfoHash: bytes32(0), + qeIdentityHash: bytes32(0), + publicKey: "", + signer: signer, + imageHash: IMAGE_HASH, + mrTdHash: bytes32(0), + reportDataPrefix: bytes32(0), + reportDataSuffix: REPORT_DATA_SUFFIX + }); + } +} diff --git a/test/L1/proofs/TEEVerifier.t.sol b/test/L1/proofs/TEEVerifier.t.sol index 66ab646a2..901f4ad07 100644 --- a/test/L1/proofs/TEEVerifier.t.sol +++ b/test/L1/proofs/TEEVerifier.t.sol @@ -9,6 +9,7 @@ import { import { ProxyAdmin } from "src/universal/ProxyAdmin.sol"; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { IAnchorStateRegistry } from "interfaces/L1/proofs/IAnchorStateRegistry.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; import { GameType } from "src/libraries/bridge/Types.sol"; @@ -19,12 +20,14 @@ import { DevTEEProverRegistry } from "test/mocks/MockDevTEEProverRegistry.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; import { TEEVerifier } from "src/L1/proofs/tee/TEEVerifier.sol"; -/// @notice Mock AggregateVerifier that returns a configurable TEE_IMAGE_HASH. +/// @notice Mock AggregateVerifier that returns configurable TEE image hashes. contract MockAggregateVerifierForVerifier { - bytes32 public TEE_IMAGE_HASH; + bytes32 public TEE_NITRO_IMAGE_HASH; + bytes32 public TEE_TDX_IMAGE_HASH; - constructor(bytes32 imageHash) { - TEE_IMAGE_HASH = imageHash; + constructor(bytes32 nitroImageHash, bytes32 tdxImageHash) { + TEE_NITRO_IMAGE_HASH = nitroImageHash; + TEE_TDX_IMAGE_HASH = tdxImageHash; } } @@ -47,11 +50,15 @@ contract TEEVerifierTest is Test { ProxyAdmin public proxyAdmin; MockAnchorStateRegistry public anchorStateRegistry; - // Test signer - we'll derive address from private key - uint256 internal constant SIGNER_PRIVATE_KEY = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; - address internal signerAddress; + uint256 internal constant NITRO_SIGNER_PRIVATE_KEY = + 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + uint256 internal constant TDX_SIGNER_PRIVATE_KEY = + 0x2234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; + address internal nitroSignerAddress; + address internal tdxSignerAddress; - bytes32 internal constant IMAGE_ID = keccak256("test-image-id"); + bytes32 internal constant NITRO_IMAGE_ID = keccak256("test-nitro-image-id"); + bytes32 internal constant TDX_IMAGE_ID = keccak256("test-tdx-image-id"); uint32 internal constant TEST_GAME_TYPE = 621; address internal immutable PROPOSER = makeAddr("proposer"); @@ -60,17 +67,19 @@ contract TEEVerifierTest is Test { function setUp() public { owner = address(this); - // Derive signer address from private key - signerAddress = vm.addr(SIGNER_PRIVATE_KEY); + nitroSignerAddress = vm.addr(NITRO_SIGNER_PRIVATE_KEY); + tdxSignerAddress = vm.addr(TDX_SIGNER_PRIVATE_KEY); // Deploy mock factory and verifier - MockAggregateVerifierForVerifier mockVerifier = new MockAggregateVerifierForVerifier(IMAGE_ID); + MockAggregateVerifierForVerifier mockVerifier = + new MockAggregateVerifierForVerifier(NITRO_IMAGE_ID, TDX_IMAGE_ID); MockDisputeGameFactoryForVerifier mockFactory = new MockDisputeGameFactoryForVerifier(); mockFactory.setImpl(TEST_GAME_TYPE, address(mockVerifier)); // Deploy implementation (NitroEnclaveVerifier not needed for dev signer tests) - DevTEEProverRegistry impl = - new DevTEEProverRegistry(INitroEnclaveVerifier(address(0)), IDisputeGameFactory(address(mockFactory))); + DevTEEProverRegistry impl = new DevTEEProverRegistry( + INitroEnclaveVerifier(address(0)), ITDXVerifier(address(1)), IDisputeGameFactory(address(mockFactory)) + ); // Deploy proxy admin proxyAdmin = new ProxyAdmin(address(this)); @@ -86,8 +95,9 @@ contract TEEVerifierTest is Test { teeProverRegistry = DevTEEProverRegistry(address(proxy)); - // Register the signer with the correct image hash - teeProverRegistry.addDevSigner(signerAddress, IMAGE_ID); + // Register one Nitro signer and one TDX signer with the correct image hash. + teeProverRegistry.addDevSigner(nitroSignerAddress, NITRO_IMAGE_ID); + teeProverRegistry.addDevTDXSigner(tdxSignerAddress, TDX_IMAGE_ID); // Set the proposer as valid teeProverRegistry.setProposer(PROPOSER, true); @@ -99,48 +109,37 @@ contract TEEVerifierTest is Test { ); } - function testVerifyValidSignature() public view { - // Create a journal hash + function testVerifyValidNitroSignature() public view { bytes32 journal = keccak256("test-journal"); + bytes memory proofBytes = _proofBytes(journal, NITRO_SIGNER_PRIVATE_KEY); + bool result = verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); + assertTrue(result); + } - // Sign the journal with the signer's private key - (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_PRIVATE_KEY, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - // Construct proof: proposer(20) + signature(65) = 85 bytes - bytes memory proofBytes = abi.encodePacked(PROPOSER, signature); - - // Verify should return true regardless of imageId (enforced via journal hash, not registry) - bool result = verifier.verify(proofBytes, IMAGE_ID, journal); + function testVerifyValidTDXSignature() public view { + bytes32 journal = keccak256("test-journal"); + bytes memory proofBytes = _proofBytes(journal, TDX_SIGNER_PRIVATE_KEY); + bool result = verifier.verify(proofBytes, TDX_IMAGE_ID, journal); assertTrue(result); } function testVerifyFailsWithInvalidSignature() public { bytes32 journal = keccak256("test-journal"); - - // Create an invalid signature (all zeros except v) bytes memory invalidSignature = new bytes(65); invalidSignature[64] = bytes1(uint8(27)); // Set v to 27 - bytes memory proofBytes = abi.encodePacked(PROPOSER, invalidSignature); + bytes memory proofBytes = _buildProof(PROPOSER, invalidSignature); vm.expectRevert(TEEVerifier.InvalidSignature.selector); - verifier.verify(proofBytes, IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } function testVerifyFailsWithInvalidProposer() public { - // Create a journal hash bytes32 journal = keccak256("test-journal"); - - // Sign the journal with the signer's private key - (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_PRIVATE_KEY, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - // Construct proof: proposer(20) + signature(65) = 85 bytes - bytes memory proofBytes = abi.encodePacked(address(0), signature); + bytes memory proofBytes = _buildProof(address(0), _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidProposer.selector, address(0))); - verifier.verify(proofBytes, IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } function testVerifyFailsWithUnregisteredSigner() public { @@ -150,40 +149,54 @@ contract TEEVerifierTest is Test { bytes32 journal = keccak256("test-journal"); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(unregisteredKey, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - bytes memory proofBytes = abi.encodePacked(PROPOSER, signature); + bytes memory proofBytes = _buildProof(PROPOSER, _signature(unregisteredKey, journal)); vm.expectRevert(abi.encodeWithSelector(TEEVerifier.InvalidSigner.selector, unregisteredSigner)); - verifier.verify(proofBytes, IMAGE_ID, journal); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); } function testVerifyFailsWithImageIdMismatch() public { bytes32 journal = keccak256("test-journal"); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_PRIVATE_KEY, journal); - bytes memory signature = abi.encodePacked(r, s, v); - - bytes memory proofBytes = abi.encodePacked(PROPOSER, signature); - - // Different imageId should fail — signer was registered with IMAGE_ID + // Different imageId should fail — Nitro signer was registered with NITRO_IMAGE_ID bytes32 wrongImageId = keccak256("different-image"); - vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, IMAGE_ID, wrongImageId)); + bytes memory proofBytes = _proofBytes(journal, NITRO_SIGNER_PRIVATE_KEY); + vm.expectRevert(abi.encodeWithSelector(TEEVerifier.ImageIdMismatch.selector, NITRO_IMAGE_ID, wrongImageId)); verifier.verify(proofBytes, wrongImageId, journal); } + function testVerifyFailsWhenTEETypeIsIncludedInProof() public { + bytes32 journal = keccak256("test-journal"); + bytes memory proofBytes = abi.encodePacked(PROPOSER, uint8(3), _signature(NITRO_SIGNER_PRIVATE_KEY, journal)); + + vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); + verifier.verify(proofBytes, NITRO_IMAGE_ID, journal); + } + function testVerifyFailsWithInvalidProofFormat() public { bytes32 journal = keccak256("test-journal"); - // Proof too short (less than 85 bytes) + // Proof too short (less than proposer + two signatures). bytes memory shortProof = new bytes(50); vm.expectRevert(TEEVerifier.InvalidProofFormat.selector); - verifier.verify(shortProof, IMAGE_ID, journal); + verifier.verify(shortProof, NITRO_IMAGE_ID, journal); } function testConstants() public view { assertEq(address(verifier.TEE_PROVER_REGISTRY()), address(teeProverRegistry)); } + + function _proofBytes(bytes32 journal, uint256 signerPrivateKey) internal view returns (bytes memory) { + return _buildProof(PROPOSER, _signature(signerPrivateKey, journal)); + } + + function _signature(uint256 privateKey, bytes32 journal) internal pure returns (bytes memory) { + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, journal); + return abi.encodePacked(r, s, v); + } + + function _buildProof(address proposer, bytes memory signature) internal pure returns (bytes memory) { + return abi.encodePacked(proposer, signature); + } } diff --git a/test/mocks/MockDevTEEProverRegistry.sol b/test/mocks/MockDevTEEProverRegistry.sol index 523a96159..66bff358c 100644 --- a/test/mocks/MockDevTEEProverRegistry.sol +++ b/test/mocks/MockDevTEEProverRegistry.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.15; import { INitroEnclaveVerifier } from "interfaces/L1/proofs/tee/INitroEnclaveVerifier.sol"; import { IDisputeGameFactory } from "interfaces/L1/proofs/IDisputeGameFactory.sol"; -import { EnumerableSetLib } from "lib/solady-v0.0.245/src/utils/EnumerableSetLib.sol"; +import { ITDXVerifier } from "interfaces/L1/proofs/tee/ITDXVerifier.sol"; import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; @@ -12,25 +12,29 @@ import { TEEProverRegistry } from "src/L1/proofs/tee/TEEProverRegistry.sol"; /// @dev This contract adds addDevSigner() which bypasses AWS Nitro attestation verification. /// DO NOT deploy this contract to production networks. contract DevTEEProverRegistry is TEEProverRegistry { - using EnumerableSetLib for EnumerableSetLib.AddressSet; - constructor( INitroEnclaveVerifier nitroVerifier, + ITDXVerifier tdxVerifier, IDisputeGameFactory factory ) - TEEProverRegistry(nitroVerifier, factory) + TEEProverRegistry(nitroVerifier, tdxVerifier, factory) { } - /// @notice Registers a signer for testing (bypasses attestation verification). + /// @notice Registers a Nitro signer for testing (bypasses attestation verification). /// @dev Only callable by owner. For development/testing use only. /// The imageHash parameter is stored so isValidSigner() can validate against - /// the current AggregateVerifier's TEE_IMAGE_HASH. + /// the current AggregateVerifier's TEE_NITRO_IMAGE_HASH. /// @param signer The address of the signer to register. /// @param imageHash The TEE image hash to associate with this signer. function addDevSigner(address signer, bytes32 imageHash) external onlyOwner { - isRegisteredSigner[signer] = true; - signerImageHash[signer] = imageHash; - _registeredSigners.add(signer); - emit SignerRegistered(signer); + _registerSigner(signer, imageHash, TEEType.NITRO); + } + + /// @notice Registers a TDX signer for testing (bypasses attestation verification). + /// @dev Only callable by owner. For development/testing use only. + /// @param signer The address of the signer to register. + /// @param imageHash The TEE image hash to associate with this signer. + function addDevTDXSigner(address signer, bytes32 imageHash) external onlyOwner { + _registerSigner(signer, imageHash, TEEType.TDX); } } diff --git a/test/opcm/DeployImplementations.t.sol b/test/opcm/DeployImplementations.t.sol index e91760bdc..7eefb2ebd 100644 --- a/test/opcm/DeployImplementations.t.sol +++ b/test/opcm/DeployImplementations.t.sol @@ -174,10 +174,12 @@ contract DeployImplementations_Test is Test, FeatureFlags { _faultGameV2SplitDepth, // faultGameV2SplitDepth (bounded) _faultGameV2ClockExtension, // faultGameV2ClockExtension (bounded) _faultGameV2MaxClockDuration, // faultGameV2MaxClockDuration (bounded) - bytes32(uint256(1)), // teeImageHash + bytes32(uint256(1)), // teeNitroImageHash + bytes32(uint256(2)), // teeTdxImageHash bytes32(0), // multiproofConfigHash 621, // multiproofGameType address(0), // nitroEnclaveVerifier + address(1), // tdxVerifier 8453, // l2ChainID 100, // multiproofBlockInterval 10, // multiproofIntermediateBlockInterval @@ -315,6 +317,11 @@ contract DeployImplementations_Test is Test, FeatureFlags { vm.expectRevert("DeployImplementations: mipsVersion not set"); deployImplementations.run(input); + input = defaultInput(); + input.tdxVerifier = address(0); + vm.expectRevert("DeployImplementations: tdxVerifier not set"); + deployImplementations.run(input); + input = defaultInput(); input.superchainConfigProxy = ISuperchainConfig(address(0)); vm.expectRevert("DeployImplementations: superchainConfigProxy not set"); @@ -398,10 +405,12 @@ contract DeployImplementations_Test is Test, FeatureFlags { 30, // faultGameV2SplitDepth 10800, // faultGameV2ClockExtension 302400, // faultGameV2MaxClockDuration - bytes32(uint256(1)), // teeImageHash + bytes32(uint256(1)), // teeNitroImageHash + bytes32(uint256(2)), // teeTdxImageHash bytes32(0), // multiproofConfigHash 621, // multiproofGameType address(0), // nitroEnclaveVerifier + address(1), // tdxVerifier 8453, // l2ChainID 100, // multiproofBlockInterval 10, // multiproofIntermediateBlockInterval diff --git a/test/opcm/DeployOPChain.t.sol b/test/opcm/DeployOPChain.t.sol index fb3697cd7..aaf13d95b 100644 --- a/test/opcm/DeployOPChain.t.sol +++ b/test/opcm/DeployOPChain.t.sol @@ -87,10 +87,12 @@ contract DeployOPChain_TestBase is Test, FeatureFlags { faultGameV2SplitDepth: 30, faultGameV2ClockExtension: 10800, faultGameV2MaxClockDuration: 302400, - teeImageHash: bytes32(uint256(1)), + teeNitroImageHash: bytes32(uint256(1)), + teeTdxImageHash: bytes32(uint256(2)), multiproofConfigHash: bytes32(0), multiproofGameType: 621, nitroEnclaveVerifier: address(0), + tdxVerifier: address(1), l2ChainID: 8453, multiproofBlockInterval: 100, multiproofIntermediateBlockInterval: 10,