-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(protocol): add ComposeVerifier (#17961)
- Loading branch information
Showing
7 changed files
with
290 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
packages/protocol/contracts/mainnet/rollup/verifiers/MainnetTeeAnyVerifier.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected] | ||
contract MainnetTeeAnyVerifier is TeeAnyVerifier, RollupAddressCache { | ||
function _getAddress(uint64 _chainId, bytes32 _name) internal view override returns (address) { | ||
return getAddress(_chainId, _name, super._getAddress); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
packages/protocol/contracts/mainnet/rollup/verifiers/MainnetZkAnyVerifier.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected] | ||
contract MainnetZkAnyVerifier is ZkAnyVerifier, RollupAddressCache { | ||
function _getAddress(uint64 _chainId, bytes32 _name) internal view override returns (address) { | ||
return getAddress(_chainId, _name, super._getAddress); | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
packages/protocol/contracts/verifiers/compose/ComposeVerifier.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected] | ||
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_); | ||
} |
24 changes: 24 additions & 0 deletions
24
packages/protocol/contracts/verifiers/compose/TeeAnyVerifier.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected] | ||
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; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
packages/protocol/contracts/verifiers/compose/ZKAnyVerifier.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected] | ||
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; | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
packages/protocol/test/verifiers/compose/ComposeVerifeir.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |