Skip to content

Commit

Permalink
test: utils: VerifierTestUtils: Add test utils for verifier reference
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykraut committed Feb 17, 2025
1 parent 7e14bf2 commit ca4c287
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 115 deletions.
123 changes: 8 additions & 115 deletions test/Verifier.t.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import { BN254 } from "solidity-bn254/BN254.sol";

import { TestUtils } from "./utils/TestUtils.sol";
import { VerifierTestUtils } from "./utils/VerifierTestUtils.sol";
import { Verifier } from "../src/verifier/Verifier.sol";
import { PlonkProof, NUM_WIRE_TYPES, NUM_SELECTORS, VerificationKey } from "../src/verifier/Types.sol";

contract VerifierTest is TestUtils {
import { BN254 } from "solidity-bn254/BN254.sol";
import { console2 } from "forge-std/console2.sol";

contract VerifierTest is VerifierTestUtils {
Verifier public verifier;
TestUtils public testUtils;

Expand All @@ -19,37 +20,6 @@ contract VerifierTest is TestUtils {
verifier = new Verifier();
}

/// @dev Creates a mock verification key for testing
function createMockVerificationKey() internal pure returns (VerificationKey memory) {
BN254.G1Point memory validPoint = BN254.P1();
BN254.ScalarField validScalar = BN254.ScalarField.wrap(1);

// Create arrays for the verification key
BN254.G1Point[NUM_SELECTORS] memory q_comms;
BN254.G1Point[NUM_WIRE_TYPES] memory sigma_comms;
BN254.ScalarField[NUM_WIRE_TYPES] memory k;

// Fill arrays with valid values
for (uint256 i = 0; i < NUM_SELECTORS; i++) {
q_comms[i] = validPoint;
}
for (uint256 i = 0; i < NUM_WIRE_TYPES; i++) {
sigma_comms[i] = validPoint;
k[i] = validScalar;
}

return VerificationKey({
n: 8, // Small power of 2 for testing
l: 1, // Single public input
k: k,
q_comms: q_comms,
sigma_comms: sigma_comms,
g: validPoint,
h: BN254.P2(),
x_h: BN254.P2()
});
}

/// @notice Test that the verifier properly validates all proof components in step 1 of Plonk verification
function testMalformedProof() public {
// Create a valid scalar and EC point to use as a base
Expand Down Expand Up @@ -248,86 +218,9 @@ contract VerifierTest is TestUtils {
}

/// @notice Test the verifier against a reference implementation
function testVerifierAgainstReferenceImpl() public {
// First compile the binary
function testVerifierMulTwo() public {
// First generate the verification key for the circuit
compileRustBinary("test/rust-reference-impls/verifier/Cargo.toml");

// Run the reference implementation to generate a proof
string[] memory args = new string[](6);
args[0] = "./test/rust-reference-impls/target/debug/verifier";
args[1] = "mul-two";
args[2] = "prove";
args[3] = "2"; // a
args[4] = "3"; // b
args[5] = "6"; // c = a * b

// The Rust binary will output a single hex string prefixed with "RES:"
string memory response = runBinaryGetResponse(args);

// Split the response to get the proof
string[] memory parts = vm.split(response, "RES:");
require(parts.length == 2, "Invalid output format");

// Decode the proof
PlonkProof memory proof = abi.decode(vm.parseBytes(parts[1]), (PlonkProof));

// Create a mock verification key
VerificationKey memory vk = createMockVerificationKey();

// Create the public inputs
BN254.ScalarField[] memory publicInputs = new BN254.ScalarField[](1);
publicInputs[0] = BN254.ScalarField.wrap(6); // c = a * b = 2 * 3 = 6

// Print proof structure details
console2.log("\nProof structure:");
console2.log("wire_comms length: %d", proof.wire_comms.length);
console2.log("quotient_comms length: %d", proof.quotient_comms.length);
console2.log("wire_evals length: %d", proof.wire_evals.length);
console2.log("sigma_evals length: %d", proof.sigma_evals.length);

// Print wire commitments
console2.log("\nWire commitments:");
for (uint256 i = 0; i < proof.wire_comms.length; i++) {
console2.log("wire_comms[%d].x: %d", i, BN254.BaseField.unwrap(proof.wire_comms[i].x));
console2.log("wire_comms[%d].y: %d", i, BN254.BaseField.unwrap(proof.wire_comms[i].y));
}

// Print wire evaluations
console2.log("\nWire evaluations:");
for (uint256 i = 0; i < proof.wire_evals.length; i++) {
console2.log("wire_evals[%d]: %d", i, BN254.ScalarField.unwrap(proof.wire_evals[i]));
}

// Print sigma evaluations
console2.log("\nSigma evaluations:");
for (uint256 i = 0; i < proof.sigma_evals.length; i++) {
console2.log("sigma_evals[%d]: %d", i, BN254.ScalarField.unwrap(proof.sigma_evals[i]));
}

// Print verification key details
console2.log("\nVerification key details:");
console2.log("n: %d", vk.n);
console2.log("l: %d", vk.l);
for (uint256 i = 0; i < vk.k.length; i++) {
console2.log("k[%d]: %d", i, BN254.ScalarField.unwrap(vk.k[i]));
}

// Print public inputs
console2.log("\nPublic inputs:");
for (uint256 i = 0; i < publicInputs.length; i++) {
console2.log("publicInputs[%d]: %d", i, BN254.ScalarField.unwrap(publicInputs[i]));
}

// Try to verify the proof
try verifier.verify(proof, publicInputs, vk) returns (bool result) {
console2.log("\nVerification result: %s", result ? "success" : "failure");
require(result, "Proof verification failed");
} catch Error(string memory reason) {
console2.log("\nVerification failed with reason: %s", reason);
revert(reason);
} catch (bytes memory) {
console2.log("\nVerification failed with no reason");
revert("Verification failed with no reason");
}
VerificationKey memory vkey = getMulTwoVkey();
}
}
108 changes: 108 additions & 0 deletions test/utils/VerifierTestUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { Test } from "forge-std/Test.sol";
import { TestUtils } from "./TestUtils.sol";
import { VerificationKey, NUM_SELECTORS, NUM_WIRE_TYPES, PlonkProof } from "../../src/verifier/Types.sol";
import { BN254 } from "solidity-bn254/BN254.sol";
import { BN254Helpers } from "../../src/verifier/BN254Helpers.sol";
import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol";

contract VerifierTestUtils is TestUtils {
// ---------
// | Mocks |
// ---------

/// @dev Creates a mock verification key for testing
function createMockVerificationKey() internal pure returns (VerificationKey memory) {
BN254.G1Point memory validPoint = BN254.P1();
BN254.ScalarField validScalar = BN254.ScalarField.wrap(1);

// Create arrays for the verification key
BN254.G1Point[NUM_SELECTORS] memory q_comms;
BN254.G1Point[NUM_WIRE_TYPES] memory sigma_comms;
BN254.ScalarField[NUM_WIRE_TYPES] memory k;

// Fill arrays with valid values
for (uint256 i = 0; i < NUM_SELECTORS; i++) {
q_comms[i] = validPoint;
}
for (uint256 i = 0; i < NUM_WIRE_TYPES; i++) {
sigma_comms[i] = validPoint;
k[i] = validScalar;
}

return VerificationKey({
n: 8, // Small power of 2 for testing
l: 1, // Single public input
k: k,
q_comms: q_comms,
sigma_comms: sigma_comms,
g: validPoint,
h: BN254.P2(),
x_h: BN254.P2()
});
}

// -----------------------------
// | Reference Implementations |
// -----------------------------

/// @dev Run the reference implementation to generate a vkey for the mulTwo circuit
function getMulTwoVkey() internal returns (VerificationKey memory) {
string[] memory args = new string[](3);
args[0] = "./test/rust-reference-impls/target/debug/verifier";
args[1] = "mul-two";
args[2] = "print-vkey";

string memory response = runBinaryGetResponse(args);
return abi.decode(vm.parseBytes(response), (VerificationKey));
}

/// @dev Run the reference implementation to generate a proof for the mulTwo circuit
function getMulTwoProof(uint256 a, uint256 b) internal returns (PlonkProof memory) {
uint256 c = a * b;
string[] memory args = new string[](6);
args[0] = "./test/rust-reference-impls/target/debug/verifier";
args[1] = "mul-two";
args[2] = "prove";
args[3] = Strings.toString(a);
args[4] = Strings.toString(b);
args[5] = Strings.toString(c);

string memory response = runBinaryGetResponse(args);
return abi.decode(vm.parseBytes(response), (PlonkProof));
}

/// @dev Run the reference implementation to generate a vkey for the sumPow circuit
function getSumPowVkey() internal returns (VerificationKey memory) {
string[] memory args = new string[](3);
args[0] = "./test/rust-reference-impls/target/debug/verifier";
args[1] = "sum-pow";
args[2] = "print-vkey";

string memory response = runBinaryGetResponse(args);
return abi.decode(vm.parseBytes(response), (VerificationKey));
}

/// @dev Run the reference implementation to generate a proof for the sumPow circuit
function getSumPowProof(uint256[10] memory witness) internal returns (PlonkProof memory) {
BN254.ScalarField sum = BN254.ScalarField.wrap(0);
for (uint256 i = 0; i < witness.length; i++) {
sum = BN254.add(sum, BN254.ScalarField.wrap(witness[i]));
}
BN254.ScalarField expected = BN254Helpers.fifthPower(sum);

string[] memory args = new string[](6);
args[0] = "./test/rust-reference-impls/target/debug/verifier";
args[1] = "sum-pow";
args[2] = "prove";
for (uint256 i = 0; i < witness.length; i++) {
args[3 + i] = Strings.toString(witness[i]);
}
args[13] = Strings.toString(BN254.ScalarField.unwrap(expected));

string memory response = runBinaryGetResponse(args);
return abi.decode(vm.parseBytes(response), (PlonkProof));
}
}

0 comments on commit ca4c287

Please sign in to comment.