Skip to content

Commit 819e5ee

Browse files
authored
Add VerifierHelper library (#32)
* Add VerifierHelper library and unit tests to it * Remove useless code and update comments * Move VerifierHelper library to another folder, update dependecies
1 parent 30b5095 commit 819e5ee

File tree

5 files changed

+312
-6
lines changed

5 files changed

+312
-6
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
import "@openzeppelin/contracts/utils/Strings.sol";
5+
6+
/**
7+
* @notice This library is needed to simplify the interaction with autogenerated contracts that use [snarkjs](https://www.npmjs.com/package/snarkjs) to verify ZK proofs
8+
* The main problem with these contracts is that the verification function always has the same signature, except for one parameter.
9+
* The `input` parameter is a static array `uint256`, the size of which depends on the number of public outputs of ZK proof,
10+
* therefore the signatures of the verification functions may be different for different schemes.
11+
*
12+
* With this library there is no need to create many different interfaces for each circuit.
13+
* Also, the library functions accept dynamic arrays of public signals, so you don't need to convert them manually to static ones.
14+
*/
15+
library VerifierHelper {
16+
using Strings for uint256;
17+
18+
struct ProofPoints {
19+
uint256[2] a;
20+
uint256[2][2] b;
21+
uint256[2] c;
22+
}
23+
24+
/**
25+
* @notice Function to call the `verifyProof` function on the `verifier` contract.
26+
* The ZK proof points are wrapped in a structure for convenience
27+
* @param verifier_ the address of the autogenerated `Verifier` contract
28+
* @param pubSignals_ the array of the ZK proof public signals
29+
* @param proofPoints_ the ProofPoints struct with ZK proof points
30+
* @return true if the proof is valid, false - otherwise
31+
*/
32+
function verifyProof(
33+
address verifier_,
34+
uint256[] memory pubSignals_,
35+
ProofPoints memory proofPoints_
36+
) internal view returns (bool) {
37+
return
38+
_verifyProof(
39+
verifier_,
40+
proofPoints_.a,
41+
proofPoints_.b,
42+
proofPoints_.c,
43+
pubSignals_,
44+
pubSignals_.length
45+
);
46+
}
47+
48+
/**
49+
* @notice Function to call the `verifyProof` function on the `verifier` contract
50+
* @param verifier_ the address of the autogenerated `Verifier` contract
51+
* @param pubSignals_ the array of the ZK proof public signals
52+
* @param a_ the A point of the ZK proof
53+
* @param b_ the B point of the ZK proof
54+
* @param c_ the C point of the ZK proof
55+
* @return true if the proof is valid, false - otherwise
56+
*/
57+
function verifyProof(
58+
address verifier_,
59+
uint256[] memory pubSignals_,
60+
uint256[2] memory a_,
61+
uint256[2][2] memory b_,
62+
uint256[2] memory c_
63+
) internal view returns (bool) {
64+
return _verifyProof(verifier_, a_, b_, c_, pubSignals_, pubSignals_.length);
65+
}
66+
67+
/**
68+
* @notice Function to call the `verifyProof` function on the `verifier` contract.
69+
* The ZK proof points are wrapped in a structure for convenience
70+
* The length of the `pubSignals_` arr must be strictly equal to `pubSignalsCount_`
71+
* @param verifier_ the address of the autogenerated `Verifier` contract
72+
* @param pubSignals_ the array of the ZK proof public signals
73+
* @param proofPoints_ the ProofPoints struct with ZK proof points
74+
* @param pubSignalsCount_ the number of public signals
75+
* @return true if the proof is valid, false - otherwise
76+
*/
77+
function verifyProofSafe(
78+
address verifier_,
79+
uint256[] memory pubSignals_,
80+
ProofPoints memory proofPoints_,
81+
uint256 pubSignalsCount_
82+
) internal view returns (bool) {
83+
require(
84+
pubSignals_.length == pubSignalsCount_,
85+
"VerifierHelper: invalid public signals count"
86+
);
87+
88+
return
89+
_verifyProof(
90+
verifier_,
91+
proofPoints_.a,
92+
proofPoints_.b,
93+
proofPoints_.c,
94+
pubSignals_,
95+
pubSignalsCount_
96+
);
97+
}
98+
99+
/**
100+
* @notice Function to call the `verifyProof` function on the `verifier` contract
101+
* The length of the `pubSignals_` arr must be strictly equal to `pubSignalsCount_`
102+
* @param verifier_ the address of the autogenerated `Verifier` contract
103+
* @param pubSignals_ the array of the ZK proof public signals
104+
* @param a_ the A point of the ZK proof
105+
* @param b_ the B point of the ZK proof
106+
* @param c_ the C point of the ZK proof
107+
* @param pubSignalsCount_ the number of public signals
108+
* @return true if the proof is valid, false - otherwise
109+
*/
110+
function verifyProofSafe(
111+
address verifier_,
112+
uint256[] memory pubSignals_,
113+
uint256[2] memory a_,
114+
uint256[2][2] memory b_,
115+
uint256[2] memory c_,
116+
uint256 pubSignalsCount_
117+
) internal view returns (bool) {
118+
require(
119+
pubSignals_.length == pubSignalsCount_,
120+
"VerifierHelper: invalid public signals count"
121+
);
122+
123+
return _verifyProof(verifier_, a_, b_, c_, pubSignals_, pubSignalsCount_);
124+
}
125+
126+
function _verifyProof(
127+
address verifier_,
128+
uint256[2] memory a_,
129+
uint256[2][2] memory b_,
130+
uint256[2] memory c_,
131+
uint256[] memory pubSignals_,
132+
uint256 pubSignalsCount_
133+
) private view returns (bool) {
134+
string memory funcSign_ = string(
135+
abi.encodePacked(
136+
"verifyProof(uint256[2],uint256[2][2],uint256[2],uint256[",
137+
pubSignalsCount_.toString(),
138+
"])"
139+
)
140+
);
141+
142+
(bool success_, bytes memory returnData_) = verifier_.staticcall(
143+
abi.encodeWithSignature(funcSign_, a_, b_, c_, pubSignals_)
144+
);
145+
146+
require(success_, "VerifierHelper: failed to call verifyProof function");
147+
148+
return abi.decode(returnData_, (bool));
149+
}
150+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
import "../../../../libs/zkp/snarkjs/VerifierHelper.sol";
5+
6+
contract VerifierHelperMock {
7+
using VerifierHelper for address;
8+
9+
function verifyProof(
10+
address verifier_,
11+
uint256[] memory pubSignals_,
12+
VerifierHelper.ProofPoints memory proofPoints_
13+
) external view returns (bool) {
14+
return verifier_.verifyProof(pubSignals_, proofPoints_);
15+
}
16+
17+
function verifyProof(
18+
address verifier_,
19+
uint256[] memory pubSignals_,
20+
uint256[2] memory a_,
21+
uint256[2][2] memory b_,
22+
uint256[2] memory c_
23+
) external view returns (bool) {
24+
return verifier_.verifyProof(pubSignals_, a_, b_, c_);
25+
}
26+
27+
function verifyProofSafe(
28+
address verifier_,
29+
uint256[] memory pubSignals_,
30+
VerifierHelper.ProofPoints memory proofPoints_,
31+
uint256 pubSignalsCount_
32+
) external view returns (bool) {
33+
return verifier_.verifyProofSafe(pubSignals_, proofPoints_, pubSignalsCount_);
34+
}
35+
36+
function verifyProofSafe(
37+
address verifier_,
38+
uint256[] memory pubSignals_,
39+
uint256[2] memory a_,
40+
uint256[2][2] memory b_,
41+
uint256[2] memory c_,
42+
uint256 pubSignalsCount_
43+
) external view returns (bool) {
44+
return verifier_.verifyProofSafe(pubSignals_, a_, b_, c_, pubSignalsCount_);
45+
}
46+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.4;
3+
4+
contract BaseVerifierMock {
5+
bool public verifyResult;
6+
7+
constructor(bool verifyResult_) {
8+
verifyResult = verifyResult_;
9+
}
10+
11+
function setVerifyResult(bool newResult_) external {
12+
verifyResult = newResult_;
13+
}
14+
}
15+
16+
contract Verifier2Mock is BaseVerifierMock {
17+
constructor(bool verifyResult_) BaseVerifierMock(verifyResult_) {}
18+
19+
function verifyProof(
20+
uint256[2] memory,
21+
uint256[2][2] memory,
22+
uint256[2] memory,
23+
uint256[2] memory
24+
) external view returns (bool) {
25+
return verifyResult;
26+
}
27+
}
28+
29+
contract Verifier3Mock is BaseVerifierMock {
30+
constructor(bool verifyResult_) BaseVerifierMock(verifyResult_) {}
31+
32+
function verifyProof(
33+
uint256[2] memory,
34+
uint256[2][2] memory,
35+
uint256[2] memory,
36+
uint256[3] memory
37+
) external view returns (bool) {
38+
return verifyResult;
39+
}
40+
}

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const { assert } = require("chai");
2+
const truffleAssert = require("truffle-assertions");
3+
4+
const VerifierHelperMock = artifacts.require("VerifierHelperMock");
5+
const Verifier2Mock = artifacts.require("Verifier2Mock");
6+
const Verifier3Mock = artifacts.require("Verifier3Mock");
7+
8+
VerifierHelperMock.numberFormat = "BigNumber";
9+
10+
describe("VerifierHelper", () => {
11+
let verifierHelper;
12+
let verifier2;
13+
let verifier3;
14+
15+
const a = [10, 20];
16+
const b = [
17+
[1, 2],
18+
[3, 4],
19+
];
20+
const c = [30, 40];
21+
const pubSignals2 = [2, 4];
22+
const pubSignals3 = [3, 6, 9];
23+
24+
beforeEach("setup", async () => {
25+
verifierHelper = await VerifierHelperMock.new();
26+
27+
verifier2 = await Verifier2Mock.new(true);
28+
verifier3 = await Verifier3Mock.new(true);
29+
});
30+
31+
describe("verifyProof", () => {
32+
it("should correctly call verifyProof function", async () => {
33+
assert.isTrue(await verifierHelper.verifyProof(verifier3.address, pubSignals3, [a, b, c]));
34+
assert.isTrue(await verifierHelper.verifyProof(verifier3.address, pubSignals3, a, b, c));
35+
36+
await verifier2.setVerifyResult(false);
37+
38+
assert.isFalse(await verifierHelper.verifyProof(verifier2.address, pubSignals2, [a, b, c]));
39+
assert.isFalse(await verifierHelper.verifyProof(verifier2.address, pubSignals2, a, b, c));
40+
});
41+
42+
it("should get exception if failed to call verifyProof function", async () => {
43+
const reason = "VerifierHelper: failed to call verifyProof function";
44+
45+
const wrongPubSignals = [1, 1, 2, 3];
46+
47+
await truffleAssert.reverts(verifierHelper.verifyProof(verifier2.address, wrongPubSignals, [a, b, c]), reason);
48+
await truffleAssert.reverts(verifierHelper.verifyProof(verifier3.address, wrongPubSignals, a, b, c), reason);
49+
});
50+
});
51+
52+
describe("verifyProofSafe", () => {
53+
it("should correctly call verifyProof function with additional checks", async () => {
54+
assert.isTrue(await verifierHelper.verifyProofSafe(verifier3.address, pubSignals3, [a, b, c], 3));
55+
assert.isTrue(await verifierHelper.verifyProofSafe(verifier3.address, pubSignals3, a, b, c, 3));
56+
57+
await verifier2.setVerifyResult(false);
58+
59+
assert.isFalse(await verifierHelper.verifyProofSafe(verifier2.address, pubSignals2, [a, b, c], 2));
60+
assert.isFalse(await verifierHelper.verifyProofSafe(verifier2.address, pubSignals2, a, b, c, 2));
61+
});
62+
63+
it("should get an exception if it passes invalid public signals arr", async () => {
64+
const reason = "VerifierHelper: invalid public signals count";
65+
66+
await truffleAssert.reverts(verifierHelper.verifyProofSafe(verifier2.address, pubSignals2, [a, b, c], 4), reason);
67+
await truffleAssert.reverts(verifierHelper.verifyProofSafe(verifier3.address, pubSignals3, a, b, c, 4), reason);
68+
});
69+
});
70+
});

0 commit comments

Comments
 (0)