diff --git a/contracts/libs/zkp/Groth16VerifierHelper.sol b/contracts/libs/zkp/Groth16VerifierHelper.sol index acbfb766..c25310e8 100644 --- a/contracts/libs/zkp/Groth16VerifierHelper.sol +++ b/contracts/libs/zkp/Groth16VerifierHelper.sol @@ -5,9 +5,9 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; /** * @notice This library is used to simplify the interaction with autogenerated contracts - * that use [snarkjs](https://www.npmjs.com/package/snarkjs) to verify Groth16 ZK proofs. + * that use [hardhat-zkit](https://github.com/dl-solarity/hardhat-zkit) to verify Groth16 ZK proofs. * - * The main problem with these contracts is that the verification function always has the same signature, except for one parameter. + * The main problem with the ZK verifier contracts is that the verification function always has the same signature, except for one parameter. * The `input` parameter is a static array `uint256`, the size of which depends on the number of public outputs of ZK proof, * therefore the signatures of the verification functions may be different for different schemes. * @@ -17,6 +17,22 @@ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; library Groth16VerifierHelper { using Strings for uint256; + /** + * @notice Structure representing a Groth16 proof. + * @param proofPoints The proof data points + * @param publicSignals The public signals associated with the proof + */ + struct Groth16Proof { + ProofPoints proofPoints; + uint256[] publicSignals; + } + + /** + * @notice Represents proof points used in a Groth16 proof. + * @param a_ the A point of the Groth16 ZK proof + * @param b_ the B point of the Groth16 ZK proof + * @param c_ the C point of the Groth16 ZK proof + */ struct ProofPoints { uint256[2] a; uint256[2][2] b; @@ -28,10 +44,32 @@ library Groth16VerifierHelper { /** * @notice Function to call the `verifyProof` function on the `verifier` contract. - * The ZK proof points are wrapped in a structure for convenience + * The Groth16 ZK proof is wrapped in a structure for convenience + * @param verifier_ The address of the verifier contract + * @param groth16Proof_ The Groth16 proof to be verified + * @return true if the proof is valid, false - otherwise + */ + function verifyProof( + address verifier_, + Groth16Proof memory groth16Proof_ + ) internal view returns (bool) { + return + _verifyProof( + verifier_, + groth16Proof_.proofPoints.a, + groth16Proof_.proofPoints.b, + groth16Proof_.proofPoints.c, + groth16Proof_.publicSignals, + groth16Proof_.publicSignals.length + ); + } + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract. + * The Groth16 ZK proof points are wrapped in a structure for convenience * @param verifier_ the address of the autogenerated `Verifier` contract - * @param proofPoints_ the ProofPoints struct with ZK proof points - * @param pubSignals_ the array of the ZK proof public signals + * @param proofPoints_ the ProofPoints struct with Groth16 ZK proof points + * @param pubSignals_ the array of the Groth16 ZK proof public signals * @return true if the proof is valid, false - otherwise */ function verifyProof( @@ -53,10 +91,10 @@ library Groth16VerifierHelper { /** * @notice Function to call the `verifyProof` function on the `verifier` contract * @param verifier_ the address of the autogenerated `Verifier` contract - * @param a_ the A point of the ZK proof - * @param b_ the B point of the ZK proof - * @param c_ the C point of the ZK proof - * @param pubSignals_ the array of the ZK proof public signals + * @param a_ the A point of the Groth16 ZK proof + * @param b_ the B point of the Groth16 ZK proof + * @param c_ the C point of the Groth16 ZK proof + * @param pubSignals_ the array of the Groth16 ZK proof public signals * @return true if the proof is valid, false - otherwise */ function verifyProof( @@ -71,11 +109,36 @@ library Groth16VerifierHelper { /** * @notice Function to call the `verifyProof` function on the `verifier` contract. - * The ZK proof points are wrapped in a structure for convenience + * The Groth16 ZK proof is wrapped in a structure for convenience + * The length of the `groth16Proof_.publicSignals` arr must be strictly equal to `pubSignalsCount_` + * @param verifier_ The address of the verifier contract. + * @param groth16Proof_ The Groth16 proof to be verified. + * @param pubSignalsCount_ The expected number of public signals. + * @return Whether the proof is valid or not. + */ + function verifyProofSafe( + address verifier_, + Groth16Proof memory groth16Proof_, + uint256 pubSignalsCount_ + ) internal view returns (bool) { + return + _verifyProofSafe( + verifier_, + groth16Proof_.proofPoints.a, + groth16Proof_.proofPoints.b, + groth16Proof_.proofPoints.c, + groth16Proof_.publicSignals, + pubSignalsCount_ + ); + } + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract. + * The Groth16 ZK proof points are wrapped in a structure for convenience * The length of the `pubSignals_` arr must be strictly equal to `pubSignalsCount_` * @param verifier_ the address of the autogenerated `Verifier` contract - * @param proofPoints_ the ProofPoints struct with ZK proof points - * @param pubSignals_ the array of the ZK proof public signals + * @param proofPoints_ the ProofPoints struct with Groth16 ZK proof points + * @param pubSignals_ the array of the Groth16 ZK proof public signals * @param pubSignalsCount_ the number of public signals * @return true if the proof is valid, false - otherwise */ @@ -85,11 +148,8 @@ library Groth16VerifierHelper { uint256[] memory pubSignals_, uint256 pubSignalsCount_ ) internal view returns (bool) { - if (pubSignals_.length != pubSignalsCount_) - revert InvalidPublicSignalsCount(pubSignals_.length, pubSignalsCount_); - return - _verifyProof( + _verifyProofSafe( verifier_, proofPoints_.a, proofPoints_.b, @@ -103,10 +163,10 @@ library Groth16VerifierHelper { * @notice Function to call the `verifyProof` function on the `verifier` contract * The length of the `pubSignals_` arr must be strictly equal to `pubSignalsCount_` * @param verifier_ the address of the autogenerated `Verifier` contract - * @param a_ the A point of the ZK proof - * @param b_ the B point of the ZK proof - * @param c_ the C point of the ZK proof - * @param pubSignals_ the array of the ZK proof public signals + * @param a_ the A point of the Groth16 ZK proof + * @param b_ the B point of the Groth16 ZK proof + * @param c_ the C point of the Groth16 ZK proof + * @param pubSignals_ the array of the Groth16 ZK proof public signals * @param pubSignalsCount_ the number of public signals * @return true if the proof is valid, false - otherwise */ @@ -118,8 +178,18 @@ library Groth16VerifierHelper { uint256[] memory pubSignals_, uint256 pubSignalsCount_ ) internal view returns (bool) { - if (pubSignals_.length != pubSignalsCount_) - revert InvalidPublicSignalsCount(pubSignals_.length, pubSignalsCount_); + return _verifyProofSafe(verifier_, a_, b_, c_, pubSignals_, pubSignalsCount_); + } + + function _verifyProofSafe( + address verifier_, + uint256[2] memory a_, + uint256[2][2] memory b_, + uint256[2] memory c_, + uint256[] memory pubSignals_, + uint256 pubSignalsCount_ + ) private view returns (bool) { + _checkPublicSignalsLength(pubSignals_, pubSignalsCount_); return _verifyProof(verifier_, a_, b_, c_, pubSignals_, pubSignalsCount_); } @@ -149,4 +219,12 @@ library Groth16VerifierHelper { return abi.decode(returnData_, (bool)); } + + function _checkPublicSignalsLength( + uint256[] memory pubSignals_, + uint256 expectedPubSignalsCount_ + ) private pure { + if (pubSignals_.length != expectedPubSignalsCount_) + revert InvalidPublicSignalsCount(pubSignals_.length, expectedPubSignalsCount_); + } } diff --git a/contracts/libs/zkp/PlonkVerifierHelper.sol b/contracts/libs/zkp/PlonkVerifierHelper.sol new file mode 100644 index 00000000..247a783e --- /dev/null +++ b/contracts/libs/zkp/PlonkVerifierHelper.sol @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +/** + * @notice This library is used to simplify the interaction with autogenerated contracts + * that use [hardhat-zkit](https://github.com/dl-solarity/hardhat-zkit) to verify Plonk ZK proofs. + * + * The main problem with the ZK verifier contracts is that the verification function always has the same signature, except for one parameter. + * The `input` parameter is a static array `uint256`, the size of which depends on the number of public outputs of ZK proof, + * therefore the signatures of the verification functions may be different for different schemes. + * + * With this library there is no need to create many different interfaces for each circuit. + * Moreover, the library functions accept dynamic arrays of public signals, so you don't need to convert them manually to static ones. + */ +library PlonkVerifierHelper { + using Strings for uint256; + + /** + * @notice Structure representing a Plonk proof. + * @param proofPoints The proof data points + * @param publicSignals The public signals associated with the proof + */ + struct PlonkProof { + ProofPoints proofPoints; + uint256[] publicSignals; + } + + /** + * @notice Structure holding the proof data points. + * @param proofData The array of proof-related data + */ + struct ProofPoints { + uint256[24] proofData; + } + + error InvalidPublicSignalsCount(uint256 arrayLength, uint256 pubSignalsCount); + error FailedToCallVerifyProof(); + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract. + * The Plonk ZK proof is wrapped in a structure for convenience + * @param verifier_ The address of the verifier contract + * @param plonkProof_ The Plonk proof to be verified + * @return true if the proof is valid, false - otherwise + */ + function verifyProof( + address verifier_, + PlonkProof memory plonkProof_ + ) internal view returns (bool) { + return + _verifyProof( + verifier_, + plonkProof_.proofPoints.proofData, + plonkProof_.publicSignals, + plonkProof_.publicSignals.length + ); + } + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract. + * The Plonk ZK proof points are wrapped in a structure for convenience + * @param verifier_ the address of the autogenerated `Verifier` contract + * @param proofPoints_ the ProofPoints struct with Plonk ZK proof points + * @param pubSignals_ the array of the Plonk ZK proof public signals + * @return true if the proof is valid, false - otherwise + */ + function verifyProof( + address verifier_, + ProofPoints memory proofPoints_, + uint256[] memory pubSignals_ + ) internal view returns (bool) { + return _verifyProof(verifier_, proofPoints_.proofData, pubSignals_, pubSignals_.length); + } + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract + * @param verifier_ the address of the autogenerated `Verifier` contract + * @param proofData_ the Plonk proof data + * @param pubSignals_ the array of the Plonk ZK proof public signals + * @return true if the proof is valid, false - otherwise + */ + function verifyProof( + address verifier_, + uint256[24] memory proofData_, + uint256[] memory pubSignals_ + ) internal view returns (bool) { + return _verifyProof(verifier_, proofData_, pubSignals_, pubSignals_.length); + } + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract. + * The Plonk ZK proof is wrapped in a structure for convenience. + * The length of the `plonkProof_.publicSignals` arr must be strictly equal to `pubSignalsCount_` + * @param verifier_ The address of the verifier contract + * @param plonkProof_ The Plonk proof to be verified + * @param pubSignalsCount_ The expected number of public signals + * @return true if the proof is valid, false - otherwise + */ + function verifyProofSafe( + address verifier_, + PlonkProof memory plonkProof_, + uint256 pubSignalsCount_ + ) internal view returns (bool) { + return + _verifyProofSafe( + verifier_, + plonkProof_.proofPoints.proofData, + plonkProof_.publicSignals, + pubSignalsCount_ + ); + } + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract. + * The Plonk proof data are wrapped in a structure for code consistency + * The length of the `pubSignals_` arr must be strictly equal to `pubSignalsCount_` + * @param verifier_ the address of the autogenerated `Verifier` contract + * @param proofPoints_ the ProofPoints struct with the Plonk proof data + * @param pubSignals_ the array of the Plonk ZK proof public signals + * @param pubSignalsCount_ the number of public signals + * @return true if the proof is valid, false - otherwise + */ + function verifyProofSafe( + address verifier_, + ProofPoints memory proofPoints_, + uint256[] memory pubSignals_, + uint256 pubSignalsCount_ + ) internal view returns (bool) { + return _verifyProofSafe(verifier_, proofPoints_.proofData, pubSignals_, pubSignalsCount_); + } + + /** + * @notice Function to call the `verifyProof` function on the `verifier` contract + * The length of the `pubSignals_` arr must be strictly equal to `pubSignalsCount_` + * @param verifier_ the address of the autogenerated `Verifier` contract + * @param proofData_ the Plonk proof data + * @param pubSignals_ the array of the Plonk ZK proof public signals + * @param pubSignalsCount_ the number of public signals + * @return true if the proof is valid, false - otherwise + */ + function verifyProofSafe( + address verifier_, + uint256[24] memory proofData_, + uint256[] memory pubSignals_, + uint256 pubSignalsCount_ + ) internal view returns (bool) { + return _verifyProofSafe(verifier_, proofData_, pubSignals_, pubSignalsCount_); + } + + function _verifyProofSafe( + address verifier_, + uint256[24] memory proofData_, + uint256[] memory pubSignals_, + uint256 pubSignalsCount_ + ) private view returns (bool) { + _checkPublicSignalsLength(pubSignals_, pubSignalsCount_); + + return _verifyProof(verifier_, proofData_, pubSignals_, pubSignalsCount_); + } + + function _verifyProof( + address verifier_, + uint256[24] memory proofData_, + uint256[] memory pubSignals_, + uint256 pubSignalsCount_ + ) private view returns (bool) { + string memory funcSign_ = string( + abi.encodePacked("verifyProof(uint256[24],uint256[", pubSignalsCount_.toString(), "])") + ); + + /// @dev We have to use abi.encodePacked to encode a dynamic array as a static array (without offset and length) + (bool success_, bytes memory returnData_) = verifier_.staticcall( + abi.encodePacked(abi.encodeWithSignature(funcSign_, proofData_), pubSignals_) + ); + + if (!success_) revert FailedToCallVerifyProof(); + + return abi.decode(returnData_, (bool)); + } + + function _checkPublicSignalsLength( + uint256[] memory pubSignals_, + uint256 expectedPubSignalsCount_ + ) private pure { + if (pubSignals_.length != expectedPubSignalsCount_) + revert InvalidPublicSignalsCount(pubSignals_.length, expectedPubSignalsCount_); + } +} diff --git a/contracts/mock/libs/zkp/snarkjs/Groth16VerifierHelperMock.sol b/contracts/mock/libs/zkp/Groth16VerifierHelperMock.sol similarity index 65% rename from contracts/mock/libs/zkp/snarkjs/Groth16VerifierHelperMock.sol rename to contracts/mock/libs/zkp/Groth16VerifierHelperMock.sol index de248929..350e5e8b 100644 --- a/contracts/mock/libs/zkp/snarkjs/Groth16VerifierHelperMock.sol +++ b/contracts/mock/libs/zkp/Groth16VerifierHelperMock.sol @@ -2,12 +2,19 @@ // solhint-disable pragma solidity ^0.8.21; -import {Groth16VerifierHelper} from "../../../../libs/zkp/Groth16VerifierHelper.sol"; +import {Groth16VerifierHelper} from "../../../libs/zkp/Groth16VerifierHelper.sol"; contract Groth16VerifierHelperMock { using Groth16VerifierHelper for address; - function verifyProofStruct( + function verifyProofGroth16ProofStruct( + address verifier_, + Groth16VerifierHelper.Groth16Proof memory groth16Proof_ + ) external view returns (bool) { + return verifier_.verifyProof(groth16Proof_); + } + + function verifyProofPointsStruct( address verifier_, Groth16VerifierHelper.ProofPoints memory proofPoints_, uint256[] memory pubSignals_ @@ -25,7 +32,15 @@ contract Groth16VerifierHelperMock { return verifier_.verifyProof(a_, b_, c_, pubSignals_); } - function verifyProofStructSafe( + function verifyProofGroth16ProofStructSafe( + address verifier_, + Groth16VerifierHelper.Groth16Proof memory groth16Proof_, + uint256 pubSignalsCount_ + ) external view returns (bool) { + return verifier_.verifyProofSafe(groth16Proof_, pubSignalsCount_); + } + + function verifyProofPointsStructSafe( address verifier_, Groth16VerifierHelper.ProofPoints memory proofPoints_, uint256[] memory pubSignals_, diff --git a/contracts/mock/libs/zkp/snarkjs/Groth16VerifierMock.sol b/contracts/mock/libs/zkp/Groth16VerifierMock.sol similarity index 100% rename from contracts/mock/libs/zkp/snarkjs/Groth16VerifierMock.sol rename to contracts/mock/libs/zkp/Groth16VerifierMock.sol diff --git a/contracts/mock/libs/zkp/PlonkVerifierHelperMock.sol b/contracts/mock/libs/zkp/PlonkVerifierHelperMock.sol new file mode 100644 index 00000000..3b2c5d3e --- /dev/null +++ b/contracts/mock/libs/zkp/PlonkVerifierHelperMock.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +// solhint-disable +pragma solidity ^0.8.21; + +import {PlonkVerifierHelper} from "../../../libs/zkp/PlonkVerifierHelper.sol"; + +contract PlonkVerifierHelperMock { + using PlonkVerifierHelper for address; + + function verifyProofPlonkProofStruct( + address verifier_, + PlonkVerifierHelper.PlonkProof memory plonkProof_ + ) external view returns (bool) { + return verifier_.verifyProof(plonkProof_); + } + + function verifyProofPointsStruct( + address verifier_, + PlonkVerifierHelper.ProofPoints memory proofPoints_, + uint256[] memory pubSignals_ + ) external view returns (bool) { + return verifier_.verifyProof(proofPoints_, pubSignals_); + } + + function verifyProof( + address verifier_, + uint256[24] memory proofData_, + uint256[] memory pubSignals_ + ) external view returns (bool) { + return verifier_.verifyProof(proofData_, pubSignals_); + } + + function verifyProofPlonkProofStructSafe( + address verifier_, + PlonkVerifierHelper.PlonkProof memory plonkProof_, + uint256 pubSignalsCount_ + ) external view returns (bool) { + return verifier_.verifyProofSafe(plonkProof_, pubSignalsCount_); + } + + function verifyProofPointsStructSafe( + address verifier_, + PlonkVerifierHelper.ProofPoints memory proofPoints_, + uint256[] memory pubSignals_, + uint256 pubSignalsCount_ + ) external view returns (bool) { + return verifier_.verifyProofSafe(proofPoints_, pubSignals_, pubSignalsCount_); + } + + function verifyProofSafe( + address verifier_, + uint256[24] memory proofData_, + uint256[] memory pubSignals_, + uint256 pubSignalsCount_ + ) external view returns (bool) { + return verifier_.verifyProofSafe(proofData_, pubSignals_, pubSignalsCount_); + } +} diff --git a/contracts/mock/libs/zkp/PlonkVerifierMock.sol b/contracts/mock/libs/zkp/PlonkVerifierMock.sol new file mode 100644 index 00000000..a6017d60 --- /dev/null +++ b/contracts/mock/libs/zkp/PlonkVerifierMock.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +// solhint-disable +pragma solidity ^0.8.21; + +contract BasePlonkVerifierMock { + bool public verifyResult; + uint256[] public expectedInputs; + + error InvalidInputs(); + + constructor(bool verifyResult_, uint256[] memory expectedInputs_) { + verifyResult = verifyResult_; + expectedInputs = expectedInputs_; + } + + function setVerifyResult(bool newResult_) external { + verifyResult = newResult_; + } + + function setExpectedInputs(uint256[] memory newExpectedInputs_) external { + expectedInputs = newExpectedInputs_; + } +} + +contract PlonkVerifier2Mock is BasePlonkVerifierMock { + constructor( + bool verifyResult_, + uint256[] memory expectedInputs_ + ) BasePlonkVerifierMock(verifyResult_, expectedInputs_) {} + + function verifyProof( + uint256[24] memory, + uint256[2] memory inputs_ + ) external view returns (bool) { + for (uint256 i = 0; i < inputs_.length; i++) { + if (inputs_[i] != expectedInputs[i]) revert InvalidInputs(); + } + + return verifyResult; + } +} + +contract PlonkVerifier3Mock is BasePlonkVerifierMock { + constructor( + bool verifyResult_, + uint256[] memory expectedInputs_ + ) BasePlonkVerifierMock(verifyResult_, expectedInputs_) {} + + function verifyProof( + uint256[24] memory, + uint256[3] memory inputs_ + ) external view returns (bool) { + for (uint256 i = 0; i < inputs_.length; i++) { + if (inputs_[i] != expectedInputs[i]) revert InvalidInputs(); + } + + return verifyResult; + } +} diff --git a/package-lock.json b/package-lock.json index 31f07496..2bc47db6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@solarity/solidity-lib", - "version": "3.0.0-rc.2", + "version": "3.0.0-rc.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@solarity/solidity-lib", - "version": "3.0.0-rc.2", + "version": "3.0.0-rc.3", "license": "MIT", "dependencies": { "@openzeppelin/contracts": "5.2.0", diff --git a/package.json b/package.json index 68c5f81c..f0631d1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@solarity/solidity-lib", - "version": "3.0.0-rc.2", + "version": "3.0.0-rc.3", "license": "MIT", "author": "Distributed Lab", "readme": "README.md", diff --git a/test/libs/zkp/Groth16VerifierHelper.test.ts b/test/libs/zkp/Groth16VerifierHelper.test.ts index 018990ac..4919af12 100644 --- a/test/libs/zkp/Groth16VerifierHelper.test.ts +++ b/test/libs/zkp/Groth16VerifierHelper.test.ts @@ -19,6 +19,8 @@ describe("Groth16VerifierHelper", () => { ]; const c = <[number, number]>[30, 40]; + const proofPoints = { a, b, c }; + const pubSignals2 = [2, 4]; const pubSignals3 = [3, 6, 9]; @@ -39,15 +41,25 @@ describe("Groth16VerifierHelper", () => { describe("verifyProof", () => { it("should correctly call verifyProof function", async () => { - const contractInterface = expect( - await verifierHelper.verifyProofStruct(await verifier3.getAddress(), { a, b, c }, pubSignals3), + expect( + await verifierHelper.verifyProofGroth16ProofStruct(await verifier3.getAddress(), { + proofPoints, + publicSignals: pubSignals3, + }), ).to.be.true; - + expect(await verifierHelper.verifyProofPointsStruct(await verifier3.getAddress(), proofPoints, pubSignals3)).to.be + .true; expect(await verifierHelper.verifyProof(await verifier3.getAddress(), a, b, c, pubSignals3)).to.be.true; await verifier2.setVerifyResult(false); - expect(await verifierHelper.verifyProofStruct(await verifier2.getAddress(), { a, b, c }, pubSignals2)).to.be + expect( + await verifierHelper.verifyProofGroth16ProofStruct(await verifier2.getAddress(), { + proofPoints, + publicSignals: pubSignals2, + }), + ).to.be.false; + expect(await verifierHelper.verifyProofPointsStruct(await verifier2.getAddress(), proofPoints, pubSignals2)).to.be .false; expect(await verifierHelper.verifyProof(await verifier2.getAddress(), a, b, c, pubSignals2)).to.be.false; }); @@ -55,7 +67,15 @@ describe("Groth16VerifierHelper", () => { it("should get exception if failed to call verifyProof function", async () => { const wrongPubSignals = [1, 1, 2, 3]; - await expect(verifierHelper.verifyProofStruct(await verifier2.getAddress(), { a, b, c }, wrongPubSignals)) + await expect( + verifierHelper.verifyProofGroth16ProofStruct(await verifier2.getAddress(), { + proofPoints, + publicSignals: wrongPubSignals, + }), + ) + .to.be.revertedWithCustomError(verifierHelper, "FailedToCallVerifyProof") + .withArgs(); + await expect(verifierHelper.verifyProofPointsStruct(await verifier2.getAddress(), proofPoints, wrongPubSignals)) .to.be.revertedWithCustomError(verifierHelper, "FailedToCallVerifyProof") .withArgs(); await expect(verifierHelper.verifyProof(await verifier3.getAddress(), a, b, c, wrongPubSignals)) @@ -66,22 +86,48 @@ describe("Groth16VerifierHelper", () => { describe("verifyProofSafe", () => { it("should correctly call verifyProof function with additional checks", async () => { - expect(await verifierHelper.verifyProofStructSafe(await verifier3.getAddress(), { a, b, c }, pubSignals3, 3)).to - .be.true; + expect( + await verifierHelper.verifyProofGroth16ProofStructSafe( + await verifier3.getAddress(), + { proofPoints, publicSignals: pubSignals3 }, + 3, + ), + ).to.be.true; + expect( + await verifierHelper.verifyProofPointsStructSafe(await verifier3.getAddress(), proofPoints, pubSignals3, 3), + ).to.be.true; expect(await verifierHelper.verifyProofSafe(await verifier3.getAddress(), a, b, c, pubSignals3, 3)).to.be.true; await verifier2.setVerifyResult(false); - expect(await verifierHelper.verifyProofStructSafe(await verifier2.getAddress(), { a, b, c }, pubSignals2, 2)).to - .be.false; + expect( + await verifierHelper.verifyProofGroth16ProofStructSafe( + await verifier2.getAddress(), + { proofPoints, publicSignals: pubSignals2 }, + 2, + ), + ).to.be.false; + expect( + await verifierHelper.verifyProofPointsStructSafe(await verifier2.getAddress(), proofPoints, pubSignals2, 2), + ).to.be.false; expect(await verifierHelper.verifyProofSafe(await verifier2.getAddress(), a, b, c, pubSignals2, 2)).to.be.false; }); it("should get an exception if it passes invalid public signals arr", async () => { - await expect(verifierHelper.verifyProofStructSafe(await verifier2.getAddress(), { a, b, c }, pubSignals2, 4)) + await expect( + verifierHelper.verifyProofGroth16ProofStructSafe( + await verifier2.getAddress(), + { proofPoints, publicSignals: pubSignals2 }, + 4, + ), + ) + .to.be.revertedWithCustomError(verifierHelper, "InvalidPublicSignalsCount") + .withArgs(pubSignals2.length, 4); + await expect( + verifierHelper.verifyProofPointsStructSafe(await verifier2.getAddress(), proofPoints, pubSignals2, 4), + ) .to.be.revertedWithCustomError(verifierHelper, "InvalidPublicSignalsCount") .withArgs(pubSignals2.length, 4); - await expect(verifierHelper.verifyProofSafe(await verifier3.getAddress(), a, b, c, pubSignals3, 4)) .to.be.revertedWithCustomError(verifierHelper, "InvalidPublicSignalsCount") .withArgs(pubSignals3.length, 4); diff --git a/test/libs/zkp/PlonkVerifierHelper.test.ts b/test/libs/zkp/PlonkVerifierHelper.test.ts new file mode 100644 index 00000000..41102591 --- /dev/null +++ b/test/libs/zkp/PlonkVerifierHelper.test.ts @@ -0,0 +1,130 @@ +import { ethers } from "hardhat"; +import { expect } from "chai"; + +import { Reverter } from "@/test/helpers/reverter"; + +import { PlonkVerifierHelperMock, PlonkVerifier2Mock, PlonkVerifier3Mock } from "@ethers-v6"; + +describe("PlonkVerifierHelper", () => { + const reverter = new Reverter(); + + let verifierHelper: PlonkVerifierHelperMock; + let verifier2: PlonkVerifier2Mock; + let verifier3: PlonkVerifier3Mock; + + const proofData = new Array(24).fill(10); + const proofPoints = { proofData }; + + const pubSignals2 = [2, 4]; + const pubSignals3 = [3, 6, 9]; + + before("setup", async () => { + const PlonkVerifierHelperMock = await ethers.getContractFactory("PlonkVerifierHelperMock"); + const PlonkVerifier2Mock = await ethers.getContractFactory("PlonkVerifier2Mock"); + const PlonkVerifier3Mock = await ethers.getContractFactory("PlonkVerifier3Mock"); + + verifierHelper = await PlonkVerifierHelperMock.deploy(); + + verifier2 = await PlonkVerifier2Mock.deploy(true, pubSignals2); + verifier3 = await PlonkVerifier3Mock.deploy(true, pubSignals3); + + await reverter.snapshot(); + }); + + afterEach(reverter.revert); + + describe("verifyProof", () => { + it("should correctly call verifyProof function", async () => { + expect( + await verifierHelper.verifyProofPlonkProofStruct(await verifier3.getAddress(), { + proofPoints, + publicSignals: pubSignals3, + }), + ).to.be.true; + expect(await verifierHelper.verifyProofPointsStruct(await verifier3.getAddress(), proofPoints, pubSignals3)).to.be + .true; + expect(await verifierHelper.verifyProof(await verifier3.getAddress(), proofData, pubSignals3)).to.be.true; + + await verifier2.setVerifyResult(false); + + expect( + await verifierHelper.verifyProofPlonkProofStruct(await verifier2.getAddress(), { + proofPoints, + publicSignals: pubSignals2, + }), + ).to.be.false; + expect(await verifierHelper.verifyProofPointsStruct(await verifier2.getAddress(), proofPoints, pubSignals2)).to.be + .false; + expect(await verifierHelper.verifyProof(await verifier2.getAddress(), proofData, pubSignals2)).to.be.false; + }); + + it("should get exception if failed to call verifyProof function", async () => { + const wrongPubSignals = [1, 1, 2, 3]; + + await expect( + verifierHelper.verifyProofPlonkProofStruct(await verifier2.getAddress(), { + proofPoints, + publicSignals: wrongPubSignals, + }), + ) + .to.be.revertedWithCustomError(verifierHelper, "FailedToCallVerifyProof") + .withArgs(); + await expect(verifierHelper.verifyProofPointsStruct(await verifier2.getAddress(), proofPoints, wrongPubSignals)) + .to.be.revertedWithCustomError(verifierHelper, "FailedToCallVerifyProof") + .withArgs(); + await expect(verifierHelper.verifyProof(await verifier3.getAddress(), proofData, wrongPubSignals)) + .to.be.revertedWithCustomError(verifierHelper, "FailedToCallVerifyProof") + .withArgs(); + }); + }); + + describe("verifyProofSafe", () => { + it("should correctly call verifyProof function with additional checks", async () => { + expect( + await verifierHelper.verifyProofPlonkProofStructSafe( + await verifier3.getAddress(), + { proofPoints, publicSignals: pubSignals3 }, + 3, + ), + ).to.be.true; + expect( + await verifierHelper.verifyProofPointsStructSafe(await verifier3.getAddress(), proofPoints, pubSignals3, 3), + ).to.be.true; + expect(await verifierHelper.verifyProofSafe(await verifier3.getAddress(), proofData, pubSignals3, 3)).to.be.true; + + await verifier2.setVerifyResult(false); + + expect( + await verifierHelper.verifyProofPlonkProofStructSafe( + await verifier2.getAddress(), + { proofPoints, publicSignals: pubSignals2 }, + 2, + ), + ).to.be.false; + expect( + await verifierHelper.verifyProofPointsStructSafe(await verifier2.getAddress(), proofPoints, pubSignals2, 2), + ).to.be.false; + expect(await verifierHelper.verifyProofSafe(await verifier2.getAddress(), proofData, pubSignals2, 2)).to.be.false; + }); + + it("should get an exception if it passes invalid public signals arr", async () => { + await expect( + verifierHelper.verifyProofPlonkProofStructSafe( + await verifier2.getAddress(), + { proofPoints, publicSignals: pubSignals2 }, + 4, + ), + ) + .to.be.revertedWithCustomError(verifierHelper, "InvalidPublicSignalsCount") + .withArgs(pubSignals2.length, 4); + await expect( + verifierHelper.verifyProofPointsStructSafe(await verifier2.getAddress(), proofPoints, pubSignals2, 4), + ) + .to.be.revertedWithCustomError(verifierHelper, "InvalidPublicSignalsCount") + .withArgs(pubSignals2.length, 4); + await expect(verifierHelper.verifyProofSafe(await verifier3.getAddress(), proofData, pubSignals3, 4)) + .to.be.revertedWithCustomError(verifierHelper, "InvalidPublicSignalsCount") + .withArgs(pubSignals3.length, 4); + }); + }); +});