Skip to main content

🔐 Groth16 Verifier Helper

Introduction​

The Verifier Helper library is designed to simplify working with auto-generated verifier contracts from hardhat-zkit to verify Groth16 ZK proofs.

Implementation​

Groth16 verifier contracts usually have a function verifyProof(uint256[2] a_, uint256[2][2] b_, uint256[2] c_, uint256[<number-of-public-inputs>] inputs_) where the inputs_ is a static array of public inputs the circuit accepts. If a developer needs to interact with more than one circuit verifier, it will be necessary to create many different Solidity interfaces, which is not particularly user-friendly. The Groth16VerifierHelper library solves this problem.

Under the hood, the Groth16VerifierHelper library wraps the verifier staticcall by constructing public signals dynamically. The length of the static array inputs_ inside the verifyProof function is determined by the length of the dynamic array that was passed to the library function.

Functions​

To use the Groth16VerifierHelper library, you need to import it.

import "@solarity/solidity-lib/libs/zkp/Groth16VerifierHelper.sol";

And optionally bind it to the type with the using statement.

using Groth16VerifierHelper for address;

verifyProof​

function verifyProof(
address verifier_,
Groth16Proof memory groth16Proof_
) internal view returns (bool);
function verifyProof(
address verifier_,
ProofPoints memory proofPoints_,
uint256[] memory pubSignals_
) internal view returns (bool);
function verifyProof(
address verifier_,
uint256[2] memory a_,
uint256[2][2] memory b_,
uint256[2] memory c_,
uint256[] memory pubSignals_
) internal view returns (bool);

Description​

These functions verify the ZK proof by calling the verifyProof function on the verifier's address by converting the dynamic array of public inputs into a static one. The functions will revert if the passed verifier address does not have the required verifyProof method.

Example​

import "@solarity/solidity-lib/libs/zkp/Groth16VerifierHelper.sol";

contract ZKPValidator {
using Groth16VerifierHelper for address;

function verifyProof(
address verifier_,
Groth16VerifierHelper.Groth16Proof memory groth16Proof_
) external view returns (bool) {
return verifier_.verifyProof(groth16Proof_);
}

function verifyZKP(
address verifier_,
Groth16VerifierHelper.ProofPoints memory proofPoints_,
uint256[] memory inputs_
) external view returns (bool) {
return verifier_.verifyProof(proofPoints_, inputs_);
}

function verifyZKP(
address verifier_,
uint256[2] a_,
uint256[2][2] b_,
uint256[2] c_,
uint256[] memory inputs_
) external view returns (bool) {
return verifier_.verifyProof(a_, b_, c_, inputs_);
}
}

ZKPValidator public validator;

validator = new ZKPValidator();

address verifierAddr_ = 0x0Ef6437FC3441A5936f347313D5043FE487a8B54;

uint256[2] a_ = [10, 20];
uint256[2][2] b_ = [[1, 2], [3, 4]];
uint256[2] c_ = [30, 40];

Groth16VerifierHelper.ProofPoints memory proofPoints_ = Groth16VerifierHelper.ProofPoints(
a_,
b_,
c_
);

uint256[] inputs_ = new uint256[](3);

inputs_[0] = 100;
inputs_[1] = 200;
inputs_[2] = 300;

Groth16VerifierHelper.Groth16Proof memory proof_ = Groth16VerifierHelper.Groth16Proof(
proofPoints_,
inputs_
);

validator.verifyZKP(verifierAddr_, proof_); // true
validator.verifyZKP(verifierAddr_, proofPoints_, inputs_); // true
validator.verifyZKP(verifierAddr_, a_, b_, c_, inputs_); // true

validator.verifyZKP(address(validator_), a_, b_, c_, inputs_);
// Reverts with "Groth16VerifierHelper: failed to call verifyProof function"

verifyProofSafe​

 function verifyProofSafe(
address verifier_,
Groth16Proof memory groth16Proof_,
uint256 pubSignalsCount_
) internal view returns (bool);
function verifyProofSafe(
address verifier_,
ProofPoints memory proofPoints_,
uint256[] memory pubSignals_,
uint256 pubSignalsCount_
) internal view returns (bool);
function verifyProofSafe(
address verifier_,
uint256[2] memory a_,
uint256[2][2] memory b_,
uint256[2] memory c_,
uint256[] memory pubSignals_,
uint256 pubSignalsCount_
) internal view returns (bool);

Description​

These functions work in a similar way to the ones above. However, there is one extra layer of protection: they will revert if the pubSignals_ array length is not equal to the pubSignalsCount_.