diff --git a/packages/protocol/contracts/layer1/automata-attestation/AttestationVerifier.sol b/packages/protocol/contracts/layer1/automata-attestation/AttestationVerifier.sol index 60c21fff1ec..75f852ef22b 100644 --- a/packages/protocol/contracts/layer1/automata-attestation/AttestationVerifier.sol +++ b/packages/protocol/contracts/layer1/automata-attestation/AttestationVerifier.sol @@ -38,30 +38,28 @@ contract AttestationVerifier is IAttestationVerifier, EssentialContract { bytes calldata _report, bytes32 _userData, bytes calldata _ext - ) + ) external { if (address(automataDcapAttestation) == address(0)) return; - (bool succ, bytes memory output) = automataDcapAttestation - .verifyAndAttestOnChain(_report); + (bool succ, bytes memory output) = automataDcapAttestation.verifyAndAttestOnChain(_report); if (!succ) revert INVALID_REPORT(); if (output.length < 32) revert INVALID_REPORT_DATA(); bytes32 quoteBodyLast32; assembly { - quoteBodyLast32 := mload( - add(add(output, 0x20), sub(mload(output), 32)) - ) + quoteBodyLast32 := mload(add(add(output, 0x20), sub(mload(output), 32))) } ExtTpmInfo memory info = abi.decode(_ext, (ExtTpmInfo)); bytes32 dataWithNonce = sha256(abi.encodePacked(info.akDer, _userData)); - if (quoteBodyLast32 != dataWithNonce) revert REPORT_DATA_MISMATCH(quoteBodyLast32, dataWithNonce); - // TODO: verify tpm info + if (quoteBodyLast32 != dataWithNonce) { + revert REPORT_DATA_MISMATCH(quoteBodyLast32, dataWithNonce); + } if (checkPcr10 && !trustedPcr10[info.pcr10]) revert INVALID_PRC10(info.pcr10); } -} \ No newline at end of file +} diff --git a/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationV2.sol b/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationV2.sol index 502c9b80326..9a9fad7b764 100644 --- a/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationV2.sol +++ b/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationV2.sol @@ -2,24 +2,28 @@ pragma solidity ^0.8.24; /** - * @title Interface standard that implement attestation contracts whose verification logic can be implemented + * @title Interface standard that implement attestation contracts whose verification logic can be + * implemented * both on-chain and with Risc0 ZK proofs * @notice The interface simply provides two verification methods for a given attestation input. * The user can either pay a possibly hefty gas cost to fully verify an attestation fully on-chain * OR - * Provides ZK proofs from executing an off-chain program where the verification of such attestation is conducted. + * Provides ZK proofs from executing an off-chain program where the verification of such attestation + * is conducted. * @dev should also implement Risc0 Guest Program to use this interface. * See https://dev.risczero.com/api/blockchain-integration/bonsai-on-eth to learn more */ interface IAttestationV2 { /** * @notice full on-chain verification for an attestation - * @dev must further specify the structure of inputs/outputs, to be serialized and passed to this method + * @dev must further specify the structure of inputs/outputs, to be serialized and passed to + * this method * @param input - serialized raw input as defined by the project * @return success - whether the quote has been successfully verified or not - * @return output - the output upon completion of verification. The output data may require + * @return output - the output upon completion of verification. The output data may require * post-processing by the consumer. - * For verification failures, the output is simply a UTF-8 encoded string, describing the reason for failure. + * For verification failures, the output is simply a UTF-8 encoded string, describing the reason + * for failure. * @dev can directly type cast the failed output as a string */ function verifyAndAttestOnChain(bytes calldata input) @@ -44,4 +48,4 @@ interface IAttestationV2 { ) external returns (bool success, bytes memory output); -} \ No newline at end of file +} diff --git a/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationVerifier.sol b/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationVerifier.sol index d2602a16115..14a34350600 100644 --- a/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationVerifier.sol +++ b/packages/protocol/contracts/layer1/automata-attestation/interfaces/IAttestationVerifier.sol @@ -16,5 +16,10 @@ interface IAttestationVerifier { error INVALID_PRC10(bytes32 pcr10); function setImagePcr10(bytes32 _pcr10, bool _trusted) external; - function verifyAttestation(bytes calldata _report, bytes32 _userData, bytes calldata ext) external; + function verifyAttestation( + bytes calldata _report, + bytes32 _userData, + bytes calldata ext + ) + external; } diff --git a/packages/protocol/contracts/layer1/verifiers/IProverRegistry.sol b/packages/protocol/contracts/layer1/verifiers/IProverRegistry.sol index 3817f2cd091..75184a376c7 100644 --- a/packages/protocol/contracts/layer1/verifiers/IProverRegistry.sol +++ b/packages/protocol/contracts/layer1/verifiers/IProverRegistry.sol @@ -29,7 +29,6 @@ interface IProverRegistry { uint256 referenceBlockNumber; bytes32 referenceBlockHash; bytes32 binHash; - bytes ext; } @@ -52,27 +51,19 @@ interface IProverRegistry { error INVALID_PRC10(bytes32 pcr10); event InstanceAdded( - uint256 indexed id, - address indexed instance, - address replaced, - uint256 validUntil + uint256 indexed id, address indexed instance, address replaced, uint256 validUntil ); event VerifyProof(uint256 proofs); /// @notice register prover instance with quote - function register( - bytes calldata _report, - ReportData calldata _data - ) external; + function register(bytes calldata _report, ReportData calldata _data) external; - /// TODO: should we need to add teeType? /// @notice validate whether the prover with (instanceID, address) function checkProver( uint256 _instanceID, address _proverAddr - ) external view returns (ProverInstance memory); - - /// TODO: each proof should coming from different teeType - /// @notice verify multiple proofs in one call - function verifyProofs(Proof[] calldata _proofs) external; -} \ No newline at end of file + ) + external + view + returns (ProverInstance memory); +} diff --git a/packages/protocol/contracts/layer1/verifiers/ProverRegistryVerifier.sol b/packages/protocol/contracts/layer1/verifiers/ProverRegistryVerifier.sol index ed4f04c647c..aaab36d7776 100644 --- a/packages/protocol/contracts/layer1/verifiers/ProverRegistryVerifier.sol +++ b/packages/protocol/contracts/layer1/verifiers/ProverRegistryVerifier.sol @@ -28,7 +28,7 @@ contract ProverRegistryVerifier is IVerifier, IProverRegistry, EssentialContract address _verifierAddr, uint256 _attestValiditySeconds, uint256 _maxBlockNumberDiff - ) + ) external initializer { @@ -55,12 +55,7 @@ contract ProverRegistryVerifier is IVerifier, IProverRegistry, EssentialContract } /// @notice register prover instance with quote - function register( - bytes calldata _report, - ReportData calldata _data - ) - external - { + function register(bytes calldata _report, ReportData calldata _data) external { _checkBlockNumber(_data.referenceBlockNumber, _data.referenceBlockHash); bytes32 dataHash = keccak256(abi.encode(_data)); @@ -74,11 +69,7 @@ contract ProverRegistryVerifier is IVerifier, IProverRegistry, EssentialContract nextInstanceId += 1; uint256 validUnitl = block.timestamp + attestValiditySeconds; - attestedProvers[instanceID] = ProverInstance( - _data.addr, - validUnitl, - _data.teeType - ); + attestedProvers[instanceID] = ProverInstance(_data.addr, validUnitl, _data.teeType); emit InstanceAdded(instanceID, _data.addr, address(0), validUnitl); } @@ -125,37 +116,14 @@ contract ProverRegistryVerifier is IVerifier, IProverRegistry, EssentialContract notImplemented { } - /// TODO: each proof should coming from different teeType - /// @notice verify multiple proofs in one call - function verifyProofs(Proof[] calldata _proofs) - external - onlyFromNamedEither(LibStrings.B_TAIKO, LibStrings.B_TIER_TDX) - { - require(_proofs.length >= 1, "missing proofs"); - for (uint i = 0; i < _proofs.length; i++) { - IProverRegistry.SignedPoe calldata poe = _proofs[i].poe; - address oldInstance = ECDSA.recover( - LibPublicInput.hashPublicInputs( - poe.transition, address(this), poe.newInstance, _proofs[i].ctx.prover,_proofs[i].ctx.metaHash, uniFiChainId() - ), - poe.signature - ); - - ProverInstance memory prover = checkProver(poe.id, oldInstance); - if (poe.teeType != prover.teeType) revert PROVER_TYPE_MISMATCH(); - if (oldInstance != poe.newInstance) { - attestedProvers[poe.id].addr = poe.newInstance; - emit InstanceAdded(poe.id, oldInstance, poe.newInstance, prover.validUntil); - } - } - - emit VerifyProof(_proofs.length); - } - function checkProver( uint256 _instanceID, address _proverAddr - ) public view returns (ProverInstance memory) { + ) + public + view + returns (ProverInstance memory) + { ProverInstance memory prover; if (_instanceID == 0) revert PROVER_INVALID_INSTANCE_ID(_instanceID); if (_proverAddr == address(0)) revert PROVER_INVALID_ADDR(_proverAddr); @@ -169,16 +137,16 @@ contract ProverRegistryVerifier is IVerifier, IProverRegistry, EssentialContract return ITaikoL1(resolve(LibStrings.B_TAIKO, false)).getConfig().chainId; } - // Due to the inherent unpredictability of blockHash, it mitigates the risk of mass-generation - // of attestation reports in a short time frame, preventing their delayed and gradual exploitation. - // This function will make sure the attestation report generated in recent ${maxBlockNumberDiff} blocks - function _checkBlockNumber( - uint256 blockNumber, - bytes32 blockHash - ) private view { + // Due to the inherent unpredictability of blockHash, it mitigates the risk of mass-generation + // of attestation reports in a short time frame, preventing their delayed and gradual + // exploitation. + // This function will make sure the attestation report generated in recent ${maxBlockNumberDiff} + // blocks + function _checkBlockNumber(uint256 blockNumber, bytes32 blockHash) private view { if (blockNumber >= block.number) revert INVALID_BLOCK_NUMBER(); - if (block.number - blockNumber >= maxBlockNumberDiff) + if (block.number - blockNumber >= maxBlockNumberDiff) { revert BLOCK_NUMBER_OUT_OF_DATE(); + } if (blockhash(blockNumber) != blockHash) revert BLOCK_NUMBER_MISMATCH(); } -} \ No newline at end of file +} diff --git a/packages/protocol/script/layer1/DeployProtocolOnL1.s.sol b/packages/protocol/script/layer1/DeployProtocolOnL1.s.sol index e71a3d061a7..92ff5a1d138 100644 --- a/packages/protocol/script/layer1/DeployProtocolOnL1.s.sol +++ b/packages/protocol/script/layer1/DeployProtocolOnL1.s.sol @@ -298,7 +298,9 @@ contract DeployProtocolOnL1 is DeployCapability { if (keccak256(abi.encode(vm.envString("TIER_PROVIDER"))) == keccak256(abi.encode("devnet"))) { taikoL1 = TaikoL1(address(new DevnetTaikoL1())); - } else if (keccak256(abi.encode(vm.envString("TIER_PROVIDER"))) == keccak256(abi.encode("testnet"))) { + } else if ( + keccak256(abi.encode(vm.envString("TIER_PROVIDER"))) == keccak256(abi.encode("testnet")) + ) { taikoL1 = TaikoL1(address(new TestnetUniFiL1())); } else { taikoL1 = TaikoL1(address(new TaikoL1())); @@ -336,13 +338,18 @@ contract DeployProtocolOnL1 is DeployCapability { address attestationVerifier = deployProxy({ name: "attestation_verifier", impl: address(new AttestationVerifier()), - data: abi.encodeCall(AttestationVerifier.init, (owner, vm.envAddress("AUTOMATA_DCAP_ATTESTATION"), true)) + data: abi.encodeCall( + AttestationVerifier.init, (owner, vm.envAddress("AUTOMATA_DCAP_ATTESTATION"), true) + ) }); deployProxy({ name: "tier_tdx", impl: address(new ProverRegistryVerifier()), - data: abi.encodeCall(ProverRegistryVerifier.init, (owner, rollupAddressManager, attestationVerifier, 3600, 25)), + data: abi.encodeCall( + ProverRegistryVerifier.init, + (owner, rollupAddressManager, attestationVerifier, 3600, 25) + ), registerTo: rollupAddressManager }); } diff --git a/packages/protocol/script/layer1/DeployProverRegistryVerifier.s.sol b/packages/protocol/script/layer1/DeployProverRegistryVerifier.s.sol index 6b1a34acd20..a32ea62d7a2 100644 --- a/packages/protocol/script/layer1/DeployProverRegistryVerifier.s.sol +++ b/packages/protocol/script/layer1/DeployProverRegistryVerifier.s.sol @@ -23,10 +23,18 @@ contract DeployProverRegistryVerifier is DeployCapability { deployProxy({ name: "tier_tdx", impl: address(proverRegistryVerifier), - data: abi.encodeCall(ProverRegistryVerifier.init, (msg.sender, rollupAddressManager, attestationVerifier, attestationValiditySeconds, maxBlockNumberDiff)), - registerTo: rollupAddressManager + data: abi.encodeCall( + ProverRegistryVerifier.init, + ( + msg.sender, + rollupAddressManager, + attestationVerifier, + attestationValiditySeconds, + maxBlockNumberDiff + ) + ) }); vm.stopBroadcast(); } -} \ No newline at end of file +} diff --git a/packages/protocol/test/layer1/TaikoL1Test.sol b/packages/protocol/test/layer1/TaikoL1Test.sol index d83f508cf0e..5216d88805b 100644 --- a/packages/protocol/test/layer1/TaikoL1Test.sol +++ b/packages/protocol/test/layer1/TaikoL1Test.sol @@ -1,10 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; +import "../../contracts/layer1/automata-attestation/AttestationVerifier.sol"; import "../../contracts/layer1/based/TaikoL1.sol"; import "../../contracts/layer1/token/TaikoToken.sol"; import "../../contracts/layer1/verifiers/SgxVerifier.sol"; import "../../contracts/layer1/verifiers/SP1Verifier.sol"; +import "../../contracts/layer1/verifiers/ProverRegistryVerifier.sol"; import "../../contracts/layer1/verifiers/Risc0Verifier.sol"; import "../../contracts/layer1/provers/GuardianProver.sol"; import "../../contracts/layer1/team/airdrop/ERC20Airdrop.sol"; diff --git a/packages/protocol/test/layer1/automata-attestation/MockAutomataDcapAttestation.sol b/packages/protocol/test/layer1/automata-attestation/MockAutomataDcapAttestation.sol new file mode 100644 index 00000000000..72f5b9f22d5 --- /dev/null +++ b/packages/protocol/test/layer1/automata-attestation/MockAutomataDcapAttestation.sol @@ -0,0 +1,27 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { IAttestationV2 } from + "../../../contracts/layer1/automata-attestation/interfaces/IAttestationV2.sol"; + +/// @title MockAutomataDcapAttestation +contract MockAutomataDcapAttestation is IAttestationV2 { + error FUNC_NOT_IMPLEMENTED(); + + function verifyAndAttestOnChain(bytes calldata input) + external + returns (bool success, bytes memory output) + { + return (true, input); + } + + function verifyAndAttestWithZKProof( + bytes calldata journal, + bytes calldata seal + ) + external + returns (bool success, bytes memory output) + { + revert FUNC_NOT_IMPLEMENTED(); + } +} diff --git a/packages/protocol/test/layer1/verifiers/ProverRegistryVerifier.t.sol b/packages/protocol/test/layer1/verifiers/ProverRegistryVerifier.t.sol new file mode 100644 index 00000000000..cd03a08b56a --- /dev/null +++ b/packages/protocol/test/layer1/verifiers/ProverRegistryVerifier.t.sol @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "../based/TaikoL1TestBase.sol"; +import "../../../contracts/layer1/verifiers/IProverRegistry.sol"; +import "../../../contracts/layer1/automata-attestation/interfaces/IAttestationV2.sol"; +import "../../../contracts/layer1/automata-attestation/interfaces/IAttestationVerifier.sol"; +import "../automata-attestation/MockAutomataDcapAttestation.sol"; + +contract ProverRegistryVerifierTest is TaikoL1TestBase { + bytes fakeReport = hex"01020203"; + address KNOWN_ADDRESS = address(0xAAAAAFE838B80D164535CD4d50058E456A4f9E16); + uint256 KNOWN_ADDRESS_PRIV_KEY = + 0xde9b0c39e60bb0404347b588c6891947db2c873942b553d5d15c03ea30c04c63; + address KNOWN_ADDRESS_V2 = address(0x3B65e7318AC8ba9B7e488f581aAC5E64DC70eC9b); + uint256 KNOWN_ADDRESS_PRIV_KEY_V2 = + 0xc793ff2348b2667c400eded183c779a846997dc79d644ed336d26adadfd9fd80; + + AttestationVerifier mockAttestationVerifier; + AttestationVerifier attestationVerifier; + IAttestationV2 mockAutomataDcapAttestation; + ProverRegistryVerifier pv; + + function deployTaikoL1() internal override returns (TaikoL1) { + return + TaikoL1(payable(deployProxy({ name: "taiko", impl: address(new TaikoL1()), data: "" }))); + } + + function setUp() public override { + super.setUp(); + + mockAutomataDcapAttestation = new MockAutomataDcapAttestation(); + + mockAttestationVerifier = AttestationVerifier( + deployProxy({ + name: "mock_attestation_verifier", + impl: address(new AttestationVerifier()), + data: abi.encodeCall(AttestationVerifier.init, (address(0), address(0), true)) + }) + ); + + attestationVerifier = AttestationVerifier( + deployProxy({ + name: "attestation_verifier", + impl: address(new AttestationVerifier()), + data: abi.encodeCall( + AttestationVerifier.init, (address(0), address(mockAutomataDcapAttestation), true) + ) + }) + ); + attestationVerifier.setCheckPcr10(true); + + pv = ProverRegistryVerifier( + deployProxy({ + name: "tier_tdx", + impl: address(new ProverRegistryVerifier()), + data: abi.encodeCall( + ProverRegistryVerifier.init, + (address(0), address(addressManager), address(mockAttestationVerifier), 86_400, 25) + ) + }) + ); + + registerAddress("tier_tdx", address(pv)); + } + + function _reportData(uint256 teeType) + internal + view + returns (IProverRegistry.ReportData memory) + { + uint256 refBlockNumber = block.number - 1; + bytes32 refBlockHash = blockhash(refBlockNumber); + bytes32 binHash = bytes32(0); + bytes memory ext = new bytes(0); + IProverRegistry.ReportData memory data = IProverRegistry.ReportData( + address(KNOWN_ADDRESS), teeType, refBlockNumber, refBlockHash, binHash, ext + ); + return data; + } + + function _proofContext() internal view returns (IVerifier.Context memory ctx) { + ctx = IVerifier.Context({ + metaHash: bytes32("ab"), + blobHash: bytes32("cd"), + prover: KNOWN_ADDRESS, + msgSender: KNOWN_ADDRESS, + blockId: 10, + isContesting: false, + blobUsed: false + }); + } + + function _proofTransition() internal view returns (TaikoData.Transition memory transition) { + transition = TaikoData.Transition({ + parentHash: bytes32("12"), + blockHash: bytes32("34"), + stateRoot: bytes32("56"), + graffiti: bytes32("78") + }); + } + + function _tierProof( + uint32 id, + uint16 tier, + address newSigner, + IVerifier.Context memory _ctx, + TaikoData.Transition memory _trans + ) + internal + view + returns (TaikoData.TierProof memory proof) + { + uint64 chainId = L1.getConfig().chainId; + bytes32 signedHash = LibPublicInput.hashPublicInputs( + _trans, address(pv), newSigner, _ctx.prover, _ctx.metaHash, chainId + ); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(KNOWN_ADDRESS_PRIV_KEY, signedHash); + bytes memory signature = abi.encodePacked(r, s, v); + + proof = TaikoData.TierProof({ + tier: tier, + data: abi.encodePacked(id, newSigner, signature) + }); + } + + function test_upgrade() external { + address verifierAddr1 = address(new ProverRegistryVerifier()); + address verifierAddr2 = address(new ProverRegistryVerifier()); + ProverRegistryVerifier verifier = ProverRegistryVerifier( + deployProxy({ + name: "tier_tdx_upgrade", + impl: verifierAddr1, + data: abi.encodeCall( + ProverRegistryVerifier.init, + (address(0), address(addressManager), address(mockAttestationVerifier), 86_400, 25) + ) + }) + ); + assertEq(verifier.maxBlockNumberDiff(), 25); + assertEq(verifier.attestValiditySeconds(), 86_400); + + + + verifier.upgradeToAndCall(verifierAddr2, abi.encodeCall( + ProverRegistryVerifier.reinitialize, + (2, address(mockAttestationVerifier), 123, 10) + )); + assertEq(verifier.maxBlockNumberDiff(), 10); + assertEq(verifier.attestValiditySeconds(), 123); + } + + function test_register() external { + uint256 expectInstanceId = pv.nextInstanceId() + 1; + pv.register(fakeReport, _reportData(1)); + + IProverRegistry.ProverInstance memory prover = + pv.checkProver(expectInstanceId, KNOWN_ADDRESS); + assertEq(prover.addr, KNOWN_ADDRESS, "prover registered"); + assertEq(block.timestamp + 86_400, prover.validUntil, "valid time mismatch"); + } + + function test_registerCornerCases() external { + IProverRegistry.ReportData memory reportData = _reportData(1); + reportData.referenceBlockNumber = block.number; + vm.expectRevert(abi.encodeWithSignature("INVALID_BLOCK_NUMBER()")); + pv.register(fakeReport, reportData); + + reportData.referenceBlockNumber = block.number - 1; + reportData.referenceBlockHash = 0x0; + vm.expectRevert(abi.encodeWithSignature("BLOCK_NUMBER_MISMATCH()")); + pv.register(fakeReport, reportData); + + reportData.referenceBlockHash = blockhash(reportData.referenceBlockNumber); + pv.register(hex"010d0d0d", reportData); // should success + vm.expectRevert(abi.encodeWithSignature("REPORT_USED()")); + pv.register(hex"010d0d0d", reportData); + + vm.roll(block.number + 50); + vm.expectRevert(abi.encodeWithSignature("BLOCK_NUMBER_OUT_OF_DATE()")); + pv.register(fakeReport, reportData); + } + + function test_verifyProof() external { + uint32 id = uint32(pv.nextInstanceId()) + 1; + pv.register(hex"01020202", _reportData(100)); + + IVerifier.Context memory _ctx = _proofContext(); + TaikoData.Transition memory _trans = _proofTransition(); + TaikoData.TierProof memory _proof = _tierProof(id, 100, KNOWN_ADDRESS_V2, _ctx, _trans); + + vm.stopPrank(); + + // Caller should be TaikoL1 contract + vm.startPrank(address(L1)); + + pv.verifyProof(_ctx, _trans, _proof); + } + + function test_verifyProofReverts() external { + uint32 id = uint32(pv.nextInstanceId()) + 1; + pv.register(hex"0102020304", _reportData(101)); + + IVerifier.Context memory _ctx = _proofContext(); + TaikoData.Transition memory _trans = _proofTransition(); + TaikoData.TierProof memory _proof = _tierProof(id, 100, KNOWN_ADDRESS, _ctx, _trans); + + vm.expectRevert(abi.encodeWithSignature("RESOLVER_DENIED()")); + pv.verifyProof(_ctx, _trans, _proof); + + vm.stopPrank(); + vm.startPrank(address(L1)); + + _proof.data = new bytes(0); + vm.expectRevert(abi.encodeWithSignature("PROVER_INVALID_PROOF()")); + pv.verifyProof(_ctx, _trans, _proof); + + _proof = _tierProof(id, 100, KNOWN_ADDRESS, _ctx, _trans); + vm.expectRevert(abi.encodeWithSignature("PROVER_TYPE_MISMATCH()")); + pv.verifyProof(_ctx, _trans, _proof); + + _ctx.metaHash = bytes32("abc"); + vm.expectRevert(); + pv.verifyProof(_ctx, _trans, _proof); + + _ctx.metaHash = bytes32("ab"); + _proof = _tierProof(id + 1, 100, KNOWN_ADDRESS, _ctx, _trans); + vm.expectRevert(); + pv.verifyProof(_ctx, _trans, _proof); + + _proof.data = new bytes(80); + vm.expectRevert(abi.encodeWithSignature("PROVER_INVALID_PROOF()")); + pv.verifyProof(_ctx, _trans, _proof); + } + + function test_chainID() external { + assertEq(pv.uniFiChainId(), L1.getConfig().chainId); + } + + function test_attestation() external { + bytes32 pcr10 = bytes32("pcr10"); + bytes32 userData = bytes32("userData"); + IAttestationVerifier.ExtTpmInfo memory tpm = IAttestationVerifier.ExtTpmInfo({ + akDer: new bytes(0), + quote: new bytes(0), + signature: new bytes(0), + pcr10: pcr10 + }); + bytes memory ext = abi.encode(tpm); + bytes32 reportData = sha256(abi.encodePacked(tpm.akDer, userData)); + bytes32 wrongReportData = sha256(abi.encodePacked(tpm.akDer, bytes32("wrongUserData"))); + bytes memory mockReport = abi.encodePacked(reportData); + + attestationVerifier.setCheckPcr10(true); + vm.expectRevert(abi.encodeWithSignature("INVALID_PRC10(bytes32)", pcr10)); + attestationVerifier.verifyAttestation(mockReport, userData, ext); + + { + // should pass when disabled the pcr10 checking + attestationVerifier.setCheckPcr10(false); + attestationVerifier.verifyAttestation(mockReport, userData, ext); + attestationVerifier.setCheckPcr10(true); + } + + attestationVerifier.setImagePcr10(pcr10, true); + attestationVerifier.verifyAttestation(mockReport, userData, ext); + + vm.expectRevert( + abi.encodeWithSignature( + "REPORT_DATA_MISMATCH(bytes32,bytes32)", reportData, wrongReportData + ) + ); + attestationVerifier.verifyAttestation(mockReport, bytes32("wrongUserData"), ext); + + vm.expectRevert(abi.encodeWithSignature("INVALID_REPORT_DATA()")); + attestationVerifier.verifyAttestation(bytes("invalidReport"), userData, ext); + } +}