Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: verifier: Add batch verification tests #33

Merged
merged 1 commit into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/verifier/Verifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ contract Verifier {
res = BN254.add(res, permTerm);

// Add in the quotient polynomial contribution
BN254.G1Point memory quotientTerm = plonkStep9QuotientTerm(vk.n, challenges.zeta, vanishingEval, proof);
BN254.G1Point memory quotientTerm = plonkStep9QuotientTerm(challenges.zeta, vanishingEval, proof);
res = BN254.add(res, quotientTerm);
return res;
}
Expand Down Expand Up @@ -491,7 +491,6 @@ contract Verifier {

/// @notice Compute the quotient polynomial contribution to the linearized polynomial relation
function plonkStep9QuotientTerm(
uint256 n,
BN254.ScalarField zeta,
BN254.ScalarField vanishingEval,
PlonkProof memory proof
Expand Down
104 changes: 104 additions & 0 deletions test/Verifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,96 @@ contract VerifierTest is VerifierTestUtils {
require(res, "Original proof verification should have succeeded");
}

/// @notice Test that batch verification fails if any proof in the batch is invalid
function testInvalidBatchVerification() public {
// First generate the verification keys for the circuits
compileRustBinary("test/rust-reference-impls/verifier/Cargo.toml");

// Generate batch test data
(PlonkProof[] memory proofs, BN254.ScalarField[][] memory publicInputs, VerificationKey[] memory vks) =
generateBatchProofData();

// Randomly select a proof to modify
uint256 proofToModify = randomUint(proofs.length);
PlonkProof memory invalidProof = clonePlonkProof(proofs[proofToModify]);

// Randomly select which part of the proof to modify
uint256 modType = randomUint(8);
BN254.G1Point memory dummyG1Point = BN254.P1();
BN254.ScalarField dummyScalar = BN254.ScalarField.wrap(1);

if (modType == 0) {
// Modify a wire commitment
uint256 randomIdx = randomUint(NUM_WIRE_TYPES);
invalidProof.wire_comms[randomIdx] = dummyG1Point;
} else if (modType == 1) {
// Modify z_comm
invalidProof.z_comm = dummyG1Point;
} else if (modType == 2) {
// Modify a quotient commitment
uint256 randomIdx = randomUint(NUM_WIRE_TYPES);
invalidProof.quotient_comms[randomIdx] = dummyG1Point;
} else if (modType == 3) {
// Modify w_zeta
invalidProof.w_zeta = dummyG1Point;
} else if (modType == 4) {
// Modify w_zeta_omega
invalidProof.w_zeta_omega = dummyG1Point;
} else if (modType == 5) {
// Modify a wire evaluation
uint256 randomIdx = randomUint(NUM_WIRE_TYPES);
invalidProof.wire_evals[randomIdx] = dummyScalar;
} else if (modType == 6) {
// Modify a sigma evaluation
uint256 randomIdx = randomUint(NUM_WIRE_TYPES - 1);
invalidProof.sigma_evals[randomIdx] = dummyScalar;
} else {
// Modify z_bar
invalidProof.z_bar = dummyScalar;
}

// Replace the selected proof with the invalid one
proofs[proofToModify] = invalidProof;

// Verify the batch - should fail
bool res = verifier.batchVerify(proofs, publicInputs, vks);
require(!res, "Proof verification should have failed");
}

/// @notice Test the case in which a public input is modified
function testModifiedPublicInput() public {
// First generate the verification keys for the circuits
compileRustBinary("test/rust-reference-impls/verifier/Cargo.toml");
VerificationKey memory vkey = getPermutationVkey();

// Generate data for the permutation circuit
uint256 randomChallenge = randomFelt();
uint256[5] memory statement;
uint256[5] memory witness;
for (uint256 i = 0; i < 5; i++) {
statement[i] = randomFelt();
witness[5 - i - 1] = statement[i];
}

// Get the proof and public input
PlonkProof memory proof = getPermutationProof(randomChallenge, statement, witness);

// Verify the proof
BN254.ScalarField[] memory publicInputs = new BN254.ScalarField[](6);
publicInputs[0] = BN254.ScalarField.wrap(randomChallenge);
for (uint256 i = 0; i < 5; i++) {
publicInputs[i + 1] = BN254.ScalarField.wrap(statement[i]);
}

// Modify the public input
uint256 randomIdx = randomUint(publicInputs.length);
publicInputs[randomIdx] = BN254.ScalarField.wrap(randomFelt());

// Verify the proof
bool res = verifier.verify(proof, publicInputs, vkey);
require(!res, "Proof verification should have failed");
}

// --- Valid Test Cases --- //

/// @notice Test the verifier against a reference implementation on the mul-two circuit
Expand Down Expand Up @@ -386,4 +476,18 @@ contract VerifierTest is VerifierTestUtils {
bool res = verifier.verify(proof, publicInputs, vkey);
require(res, "Proof verification should have succeeded");
}

/// @notice Test batch verification against all three circuits
function testBatchVerification() public {
// First generate the verification keys for the circuits
compileRustBinary("test/rust-reference-impls/verifier/Cargo.toml");

// Generate batch test data
(PlonkProof[] memory proofs, BN254.ScalarField[][] memory publicInputs, VerificationKey[] memory vks) =
generateBatchProofData();

// Verify the batch
bool res = verifier.batchVerify(proofs, publicInputs, vks);
require(res, "Proof verification should have succeeded");
}
}
49 changes: 49 additions & 0 deletions test/utils/VerifierTestUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,53 @@ contract VerifierTestUtils is TestUtils {
z_bar: original.z_bar
});
}

/// @dev Helper function to generate a batch of proofs, verification keys, and public inputs
function generateBatchProofData()
internal
returns (PlonkProof[] memory proofs, BN254.ScalarField[][] memory publicInputs, VerificationKey[] memory vks)
{
proofs = new PlonkProof[](3);
vks = new VerificationKey[](3);
publicInputs = new BN254.ScalarField[][](3);

// Generate mul-two proof and data
uint256 a = randomFelt();
uint256 b = randomFelt();
(uint256 c, PlonkProof memory mulTwoProof) = getMulTwoProof(a, b);
proofs[0] = mulTwoProof;
vks[0] = getMulTwoVkey();
publicInputs[0] = new BN254.ScalarField[](1);
publicInputs[0][0] = BN254.ScalarField.wrap(c);

// Generate sum-pow proof and data
uint256[10] memory inputs;
for (uint256 i = 0; i < 10; i++) {
inputs[i] = randomFelt();
}
(BN254.ScalarField sumPow, PlonkProof memory sumPowProof) = getSumPowProof(inputs);
proofs[1] = sumPowProof;
vks[1] = getSumPowVkey();
publicInputs[1] = new BN254.ScalarField[](1);
publicInputs[1][0] = sumPow;

// Generate permutation proof and data
uint256 randomChallenge = randomFelt();
uint256[5] memory statement;
uint256[5] memory witness;
for (uint256 i = 0; i < 5; i++) {
statement[i] = randomFelt();
witness[5 - i - 1] = statement[i];
}
PlonkProof memory permutationProof = getPermutationProof(randomChallenge, statement, witness);
proofs[2] = permutationProof;
vks[2] = getPermutationVkey();
publicInputs[2] = new BN254.ScalarField[](6);
publicInputs[2][0] = BN254.ScalarField.wrap(randomChallenge);
for (uint256 i = 0; i < 5; i++) {
publicInputs[2][i + 1] = BN254.ScalarField.wrap(statement[i]);
}

return (proofs, publicInputs, vks);
}
}
Loading