diff --git a/packages/protocol/contracts/L1/tiers/TierProviderBase.sol b/packages/protocol/contracts/L1/tiers/TierProviderBase.sol index aab08f2d16..1a177558b5 100644 --- a/packages/protocol/contracts/L1/tiers/TierProviderBase.sol +++ b/packages/protocol/contracts/L1/tiers/TierProviderBase.sol @@ -59,6 +59,17 @@ abstract contract TierProviderBase is ITierProvider { }); } + if (_tierId == LibTiers.TIER_TEE_ANY) { + return ITierProvider.Tier({ + verifierName: LibStrings.B_TIER_TEE_ANY, + validityBond: 150 ether, // TAIKO + contestBond: 984.375 ether, // = 150 TAIKO * 6.5625 + cooldownWindow: 1440, // 24 hours + provingWindow: GRACE_PERIOD + 60, // 1 hour + maxBlocksToVerifyPerProof: 0 + }); + } + if (_tierId == LibTiers.TIER_ZKVM_RISC0) { return ITierProvider.Tier({ verifierName: LibStrings.B_TIER_ZKVM_RISC0, @@ -81,6 +92,17 @@ abstract contract TierProviderBase is ITierProvider { }); } + if (_tierId == LibTiers.TIER_ZKVM_ANY) { + return ITierProvider.Tier({ + verifierName: LibStrings.B_TIER_ZKVM_ANY, + validityBond: 250 ether, // TAIKO + contestBond: 1640.625 ether, // = 250 TAIKO * 6.5625 + cooldownWindow: 1440, // 24 hours + provingWindow: GRACE_PERIOD + 180, // 3 hours + maxBlocksToVerifyPerProof: 0 + }); + } + if (_tierId == LibTiers.TIER_GUARDIAN_MINORITY) { return ITierProvider.Tier({ verifierName: LibStrings.B_TIER_GUARDIAN_MINORITY, diff --git a/packages/protocol/contracts/mainnet/rollup/verifiers/MainnetTeeAnyVerifier.sol b/packages/protocol/contracts/mainnet/rollup/verifiers/MainnetTeeAnyVerifier.sol new file mode 100644 index 0000000000..5b47d3cd98 --- /dev/null +++ b/packages/protocol/contracts/mainnet/rollup/verifiers/MainnetTeeAnyVerifier.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "../../../verifiers/compose/TeeAnyVerifier.sol"; +import "../../addrcache/RollupAddressCache.sol"; + +/// @title MainnetTeeAnyVerifier +/// @custom:security-contact security@taiko.xyz +contract MainnetTeeAnyVerifier is TeeAnyVerifier, RollupAddressCache { + function _getAddress(uint64 _chainId, bytes32 _name) internal view override returns (address) { + return getAddress(_chainId, _name, super._getAddress); + } +} diff --git a/packages/protocol/contracts/mainnet/rollup/verifiers/MainnetZkAnyVerifier.sol b/packages/protocol/contracts/mainnet/rollup/verifiers/MainnetZkAnyVerifier.sol new file mode 100644 index 0000000000..225016f93c --- /dev/null +++ b/packages/protocol/contracts/mainnet/rollup/verifiers/MainnetZkAnyVerifier.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "../../../verifiers/compose/ZkAnyVerifier.sol"; +import "../../addrcache/RollupAddressCache.sol"; + +/// @title MainnetZkAnyVerifier +/// @custom:security-contact security@taiko.xyz +contract MainnetZkAnyVerifier is ZkAnyVerifier, RollupAddressCache { + function _getAddress(uint64 _chainId, bytes32 _name) internal view override returns (address) { + return getAddress(_chainId, _name, super._getAddress); + } +} diff --git a/packages/protocol/contracts/verifiers/compose/ComposeVerifier.sol b/packages/protocol/contracts/verifiers/compose/ComposeVerifier.sol new file mode 100644 index 0000000000..51c06be1f0 --- /dev/null +++ b/packages/protocol/contracts/verifiers/compose/ComposeVerifier.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "../../common/EssentialContract.sol"; +import "../IVerifier.sol"; + +/// @title ComposeVerifier +/// @notice This contract is an abstract verifier that composes multiple sub-verifiers to validate +/// proofs. +/// It ensures that a set of sub-proofs are verified by their respective verifiers before +/// considering the overall proof as valid. +/// @custom:security-contact security@taiko.xyz +abstract contract ComposeVerifier is EssentialContract, IVerifier { + struct SubProof { + address verifier; + bytes proof; + } + + event InvalidSubProof(address indexed verifier, bytes returnData); + + error INSUFFICIENT_PROOF(); + + /// @notice Initializes the contract. + /// @param _owner The owner of this contract. msg.sender will be used if this value is zero. + /// @param _rollupAddressManager The address of the {AddressManager} contract. + function init(address _owner, address _rollupAddressManager) external initializer { + __Essential_init(_owner, _rollupAddressManager); + } + + /// @notice Verifies one or more sub-proofs. + /// @param _ctx The context of the proof verification. + /// @param _tran The transition to verify. + /// @param _proof The proof to verify. + function verifyProof( + Context calldata _ctx, + TaikoData.Transition calldata _tran, + TaikoData.TierProof calldata _proof + ) + external + { + (address[] memory verifiers, uint256 threshold) = getSubVerifiersAndThreshold(); + + for (uint256 i; i < verifiers.length; ++i) { + // Store the value 1 in the temporary storage slot using inline assembly + uint256 slot = uint256(uint160(verifiers[i])); + if (slot != 0) { + assembly { + tstore(slot, 1) + } + } + } + + SubProof[] memory subproofs = abi.decode(_proof.data, (SubProof[])); + uint256 numSuccesses; + + for (uint256 i; i < subproofs.length; ++i) { + uint256 slot = uint256(uint160(subproofs[i].verifier)); + + assembly { + switch tload(slot) + case 1 { tstore(slot, 0) } + default { + let message := "INVALID_VERIFIER" + mstore(0x0, message) + revert(0x0, 0x20) + } + } + + (bool success, bytes memory returnData) = subproofs[i].verifier.call( + abi.encodeCall( + IVerifier.verifyProof, + (_ctx, _tran, TaikoData.TierProof(_proof.tier, subproofs[i].proof)) + ) + ); + if (success) { + unchecked { + numSuccesses += 1; + } + } else { + emit InvalidSubProof(subproofs[i].verifier, returnData); + } + } + + if (numSuccesses < threshold) { + revert INSUFFICIENT_PROOF(); + } + } + + /// @notice Returns the list of sub-verifiers and calculates the threshold. + /// @return verifiers_ An array of addresses of sub-verifiers. + /// @return threshold_ The threshold number of successful verifications required. + function getSubVerifiersAndThreshold() + public + view + virtual + returns (address[] memory verifiers_, uint256 threshold_); +} diff --git a/packages/protocol/contracts/verifiers/compose/TeeAnyVerifier.sol b/packages/protocol/contracts/verifiers/compose/TeeAnyVerifier.sol new file mode 100644 index 0000000000..cb4a49690f --- /dev/null +++ b/packages/protocol/contracts/verifiers/compose/TeeAnyVerifier.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "../../common/LibStrings.sol"; +import "./ComposeVerifier.sol"; + +/// @title TeeAnyVerifier +/// @notice This contract is a verifier for the Mainnet ZkVM that composes RiscZero and SP1 +/// Verifiers. +/// @custom:security-contact security@taiko.xyz +contract TeeAnyVerifier is EssentialContract, ComposeVerifier { + /// @inheritdoc ComposeVerifier + function getSubVerifiersAndThreshold() + public + view + override + returns (address[] memory verifiers_, uint256 threshold_) + { + verifiers_ = new address[](2); + verifiers_[0] = resolve(LibStrings.B_TIER_SGX, false); + verifiers_[1] = resolve(LibStrings.B_TIER_TDX, false); + threshold_ = 1; + } +} diff --git a/packages/protocol/contracts/verifiers/compose/ZKAnyVerifier.sol b/packages/protocol/contracts/verifiers/compose/ZKAnyVerifier.sol new file mode 100644 index 0000000000..91cdc50936 --- /dev/null +++ b/packages/protocol/contracts/verifiers/compose/ZKAnyVerifier.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "../../common/EssentialContract.sol"; +import "../../common/LibStrings.sol"; +import "./ComposeVerifier.sol"; + +/// @title ZkAnyVerifier +/// @notice This contract is a verifier for the Mainnet ZkVM that composes RiscZero and SP1 +/// Verifiers. +/// @custom:security-contact security@taiko.xyz +contract ZkAnyVerifier is EssentialContract, ComposeVerifier { + /// @inheritdoc ComposeVerifier + function getSubVerifiersAndThreshold() + public + view + override + returns (address[] memory verifiers_, uint256 threshold_) + { + verifiers_ = new address[](2); + verifiers_[0] = resolve(LibStrings.B_TIER_ZKVM_RISC0, false); + verifiers_[1] = resolve(LibStrings.B_TIER_ZKVM_SP1, false); + threshold_ = 1; + } +} diff --git a/packages/protocol/test/verifiers/compose/ComposeVerifeir.t.sol b/packages/protocol/test/verifiers/compose/ComposeVerifeir.t.sol new file mode 100644 index 0000000000..a84ad65817 --- /dev/null +++ b/packages/protocol/test/verifiers/compose/ComposeVerifeir.t.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.24; + +import "../../TaikoTest.sol"; +import "../../../contracts/verifiers/compose/ComposeVerifier.sol"; + +contract ComposeVerifierForTest is ComposeVerifier { + uint256 private threshold; + address[] private verifiers; + + function setThreshold(uint256 _threshold) external { + threshold = _threshold; + } + + function getSubVerifiersAndThreshold() + public + view + override + returns (address[] memory, uint256) + { + return (verifiers, threshold); + } + + function addSubVerifier(address _verifier) external { + verifiers.push(_verifier); + } +} + +contract MockVerifier is IVerifier { + bool private shouldSucceed; + + constructor(bool _shouldSucceed) { + shouldSucceed = _shouldSucceed; + } + + function verifyProof( + Context calldata, + TaikoData.Transition calldata, + TaikoData.TierProof calldata + ) + external + view + override + { + if (!shouldSucceed) { + revert("MockVerifier: Verification failed"); + } + } +} + +contract ComposeVerifierTest is TaikoTest { + ComposeVerifierForTest private composeVerifier; + + IVerifier.Context private ctx; + TaikoData.Transition private tran; + TaikoData.TierProof proof; + address private verifier1; + address private verifier2; + address private verifier3; + + function setUp() public { + verifier1 = address(new MockVerifier(true)); + verifier2 = address(new MockVerifier(false)); + verifier3 = address(new MockVerifier(true)); + + composeVerifier = new ComposeVerifierForTest(); + composeVerifier.addSubVerifier(verifier1); + composeVerifier.addSubVerifier(verifier2); + composeVerifier.addSubVerifier(verifier3); + + ComposeVerifier.SubProof[] memory subProofs = new ComposeVerifier.SubProof[](3); + subProofs[0] = ComposeVerifier.SubProof(verifier1, ""); + subProofs[1] = ComposeVerifier.SubProof(verifier2, ""); + subProofs[2] = ComposeVerifier.SubProof(verifier3, ""); + + proof = TaikoData.TierProof({ tier: 1, data: abi.encode(subProofs) }); + } + + function test_composeVerifeir_All() public { + composeVerifier.setThreshold(3); + + // Expect the verification to fail because not all verifiers succeed + vm.expectRevert(ComposeVerifier.INSUFFICIENT_PROOF.selector); + composeVerifier.verifyProof(ctx, tran, proof); + } + + function test_composeVerifeir_Majority() public { + composeVerifier.setThreshold(2); + composeVerifier.verifyProof(ctx, tran, proof); + } + + function test_composeVerifeir_One() public { + composeVerifier.setThreshold(1); + composeVerifier.verifyProof(ctx, tran, proof); + } +}