Skip to content

Commit

Permalink
verifier: [Plonk step 3] Verify all public inputs (#16)
Browse files Browse the repository at this point in the history
* verifier: (Plonk step 3) Verify all public inputs

* test: Verifier: Constantize expected revert messages
  • Loading branch information
joeykraut authored Feb 12, 2025
1 parent 361bb81 commit 816fd1f
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 20 deletions.
15 changes: 13 additions & 2 deletions src/verifier/Verifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ contract Verifier {
/// @notice Verify a batch of Plonk proofs using the arithmetization defined in `mpc-jellyfish`:
/// https://github.com/renegade-fi/mpc-jellyfish
/// @param proof The proof to verify
/// @param publicInputs The public inputs to the proof
/// @return True if the proof is valid, false otherwise
function verify(PlonkProof memory proof) public view returns (bool) {
function verify(PlonkProof memory proof, BN254.ScalarField[] memory publicInputs) public pure returns (bool) {
plonkStep1And2(proof);
plonkStep3(publicInputs);
}

/// @notice Step 1 of the plonk verification algorithm
/// @notice Step 1 and 2 of the plonk verification algorithm
/// @notice Verify that the G_1 points are on the curve
function plonkStep1And2(PlonkProof memory proof) internal pure {
// Check that the commitments to the wire polynomials are on the curve
Expand Down Expand Up @@ -52,4 +54,13 @@ contract Verifier {
// Check that the evaluation of the grand product polynomial is in the scalar field
BN254.validateScalarField(proof.z_bar);
}

/// @notice Step 3 of the plonk verification algorithm
/// @notice Verify that the public inputs to the proof are all in the scalar field
function plonkStep3(BN254.ScalarField[] memory publicInputs) internal pure {
// Check that the public inputs are all in the scalar field
for (uint256 i = 0; i < publicInputs.length; i++) {
BN254.validateScalarField(publicInputs[i]);
}
}
}
92 changes: 74 additions & 18 deletions test/Verifier.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ contract VerifierTest is TestUtils {
Verifier public verifier;
TestUtils public testUtils;

bytes constant INVALID_G1_POINT = "Bn254: invalid G1 point";
bytes constant INVALID_SCALAR = "Bn254: invalid scalar field";

function setUp() public {
verifier = new Verifier();
}

/// @notice Test that the verifier properly validates all inputs in step 1 of Plonk verification
function testMalformedInputs() public {
/// @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
BN254.G1Point memory validPoint = BN254.P1();
BN254.G1Point memory invalidPoint = BN254.G1Point({x: BN254.BaseField.wrap(42), y: BN254.BaseField.wrap(0)});
Expand All @@ -40,6 +43,9 @@ contract VerifierTest is TestUtils {
}
}

// Create a valid proof
BN254.ScalarField[] memory publicInputs = new BN254.ScalarField[](1);
publicInputs[0] = validScalar;
PlonkProof memory proof = PlonkProof({
wire_comms: wire_comms,
z_comm: validPoint,
Expand All @@ -54,57 +60,107 @@ contract VerifierTest is TestUtils {
// Test Case 1: Invalid wire commitment
uint256 invalidIdx = randomUint(NUM_WIRE_TYPES);
proof.wire_comms[invalidIdx] = invalidPoint;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_G1_POINT);
verifier.verify(proof, publicInputs);
proof.wire_comms[invalidIdx] = validPoint; // Reset

// Test Case 2: Invalid z commitment
invalidIdx = randomUint(NUM_WIRE_TYPES);
proof.z_comm = invalidPoint;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_G1_POINT);
verifier.verify(proof, publicInputs);
proof.z_comm = validPoint; // Reset

// Test Case 3: Invalid quotient commitment
invalidIdx = randomUint(NUM_WIRE_TYPES);
proof.quotient_comms[invalidIdx] = invalidPoint;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_G1_POINT);
verifier.verify(proof, publicInputs);
proof.quotient_comms[invalidIdx] = validPoint; // Reset

// Test Case 4: Invalid w_zeta
invalidIdx = randomUint(NUM_WIRE_TYPES);
proof.w_zeta = invalidPoint;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_G1_POINT);
verifier.verify(proof, publicInputs);
proof.w_zeta = validPoint; // Reset

// Test Case 5: Invalid w_zeta_omega
invalidIdx = randomUint(NUM_WIRE_TYPES);
proof.w_zeta_omega = invalidPoint;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_G1_POINT);
verifier.verify(proof, publicInputs);
proof.w_zeta_omega = validPoint; // Reset

// Test Case 6: Invalid wire evaluation
invalidIdx = randomUint(NUM_WIRE_TYPES);
proof.wire_evals[invalidIdx] = invalidScalar;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_SCALAR);
verifier.verify(proof, publicInputs);
proof.wire_evals[invalidIdx] = validScalar; // Reset

// Test Case 7: Invalid sigma evaluation
invalidIdx = randomUint(NUM_WIRE_TYPES - 1);
proof.sigma_evals[invalidIdx] = invalidScalar;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_SCALAR);
verifier.verify(proof, publicInputs);
proof.sigma_evals[invalidIdx] = validScalar; // Reset

// Test Case 8: Invalid z_bar
invalidIdx = randomUint(NUM_WIRE_TYPES);
proof.z_bar = invalidScalar;
vm.expectRevert();
verifier.verify(proof);
vm.expectRevert(INVALID_SCALAR);
verifier.verify(proof, publicInputs);
proof.z_bar = validScalar; // Reset
}

/// @notice Test that the verifier properly validates public inputs in step 3 of Plonk verification
function testInvalidPublicInputs() public {
uint256 NUM_PUBLIC_INPUTS = 3;

// Create a valid scalar and EC point to use as a base
BN254.G1Point memory validPoint = BN254.P1();
BN254.ScalarField validScalar = BN254.ScalarField.wrap(1);
BN254.ScalarField invalidScalar = BN254.ScalarField.wrap(BN254.R_MOD);

// Create fixed-size arrays for a valid proof
BN254.G1Point[NUM_WIRE_TYPES] memory wire_comms;
BN254.G1Point[NUM_WIRE_TYPES] memory quotient_comms;
BN254.ScalarField[NUM_WIRE_TYPES] memory wire_evals;
BN254.ScalarField[NUM_WIRE_TYPES - 1] memory sigma_evals;

// Fill arrays with valid values
for (uint256 i = 0; i < NUM_WIRE_TYPES; i++) {
wire_comms[i] = validPoint;
quotient_comms[i] = validPoint;
wire_evals[i] = validScalar;
if (i < NUM_WIRE_TYPES - 1) {
sigma_evals[i] = validScalar;
}
}

// Create a valid proof
PlonkProof memory proof = PlonkProof({
wire_comms: wire_comms,
z_comm: validPoint,
quotient_comms: quotient_comms,
w_zeta: validPoint,
w_zeta_omega: validPoint,
wire_evals: wire_evals,
sigma_evals: sigma_evals,
z_bar: validScalar
});

// Test Case: Invalid public input
BN254.ScalarField[] memory publicInputs = new BN254.ScalarField[](NUM_PUBLIC_INPUTS);
for (uint256 i = 0; i < NUM_PUBLIC_INPUTS; i++) {
publicInputs[i] = validScalar;
}

// Try a random position with an invalid scalar
uint256 invalidIdx = randomUint(NUM_PUBLIC_INPUTS);
publicInputs[invalidIdx] = invalidScalar;
vm.expectRevert(INVALID_SCALAR);
verifier.verify(proof, publicInputs);
}
}

0 comments on commit 816fd1f

Please sign in to comment.