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

feat: Implement verify_groth16 & prove_groth16 on MockProver #745

Merged
merged 18 commits into from
May 17, 2024
Merged
3 changes: 2 additions & 1 deletion prover/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ mod tests {
tracing::info!("prove core");
let stdin = SP1Stdin::new();
let core_proof = prover.prove_core(&pk, &stdin)?;
let public_values = core_proof.public_values.clone();

tracing::info!("verify core");
prover.verify(&core_proof.proof, &vk)?;
Expand Down Expand Up @@ -747,7 +748,7 @@ mod tests {
let groth16_proof = prover.wrap_groth16(wrapped_bn254_proof, &artifacts_dir);
println!("{:?}", groth16_proof);

prover.verify_groth16(&groth16_proof, &vk, &artifacts_dir)?;
prover.verify_groth16(&groth16_proof, &vk, &public_values, &artifacts_dir)?;

Ok(())
}
Expand Down
42 changes: 35 additions & 7 deletions prover/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@ use p3_baby_bear::BabyBear;
use p3_field::{AbstractField, PrimeField};
use sp1_core::{
air::PublicValues,
io::SP1PublicValues,
stark::{MachineProof, MachineVerificationError, StarkGenericConfig},
utils::BabyBearPoseidon2,
};
use sp1_recursion_core::{air::RecursionPublicValues, stark::config::BabyBearPoseidon2Outer};
use sp1_recursion_gnark_ffi::{Groth16Proof, Groth16Prover};
use thiserror::Error;

use crate::{
CoreSC, HashableKey, OuterSC, SP1CoreProofData, SP1Prover, SP1ReduceProof, SP1VerifyingKey,
};

#[derive(Error, Debug)]
ratankaliani marked this conversation as resolved.
Show resolved Hide resolved
pub enum Groth16VerificationError {
#[error("the verifying key does not match the inner groth16 proof's committed verifying key")]
InvalidVerificationKey,
#[error("the public values in the sp1 proof do not match the public values in the inner groth16 proof")]
InvalidPublicValues,
}

impl SP1Prover {
/// Verify a core proof by verifying the shards, verifying lookup bus, verifying that the
/// shards are contiguous and complete.
Expand Down Expand Up @@ -202,12 +212,12 @@ impl SP1Prover {
Ok(())
}

/// Verifies a Groth16 proof. Additionally, verifies that the hash of VK matches the VK hash
/// specified by the proof's public inputs.
/// Verifies a Groth16 proof using the circuit artifacts in the build directory.
pub fn verify_groth16(
&self,
proof: &Groth16Proof,
vk: &SP1VerifyingKey,
public_values: &SP1PublicValues,
build_dir: &PathBuf,
) -> Result<()> {
let prover = Groth16Prover::new();
Expand All @@ -218,12 +228,30 @@ impl SP1Prover {
// Verify the proof with the corresponding public inputs.
prover.verify(proof, &vkey_hash, &committed_values_digest, build_dir);

// Verify that the vk hash of the SP1VerifyingKey matches the vk hash specified in the proof.
let vk_hash = vk.hash_bn254().as_canonical_biguint();
if vk_hash != vkey_hash {
return Err(anyhow::Error::msg("vk hash mismatch"));
}
verify_groth16_public_inputs(vk, public_values, &proof.public_inputs)?;

Ok(())
}
}

/// Verify the vk_hash and public_values_hash in the public inputs of the Groth16Proof match the expected values.
ratankaliani marked this conversation as resolved.
Show resolved Hide resolved
pub fn verify_groth16_public_inputs(
vk: &SP1VerifyingKey,
public_values: &SP1PublicValues,
groth16_public_inputs: &[String],
) -> Result<()> {
let expected_vk_hash = BigUint::from_str(&groth16_public_inputs[0])?;
let expected_public_values_hash = BigUint::from_str(&groth16_public_inputs[1])?;

let vk_hash = vk.hash_bn254().as_canonical_biguint();
if vk_hash != expected_vk_hash {
return Err(Groth16VerificationError::InvalidVerificationKey.into());
}

let public_values_hash = public_values.hash();
if public_values_hash != expected_public_values_hash {
return Err(Groth16VerificationError::InvalidPublicValues.into());
}

Ok(())
}
13 changes: 13 additions & 0 deletions sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,4 +525,17 @@ mod tests {
let proof = client.prove(&pk, stdin).unwrap();
client.verify(&proof, &vk).unwrap();
}

#[test]
fn test_e2e_prove_groth16_mock() {
utils::setup_logger();
let client = ProverClient::mock();
let elf =
include_bytes!("../../examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf");
let (pk, vk) = client.setup(elf);
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let proof = client.prove_groth16(&pk, stdin).unwrap();
client.verify_groth16(&proof, &vk).unwrap();
}
}
22 changes: 19 additions & 3 deletions sdk/src/provers/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use crate::{
SP1ProofVerificationError, SP1ProofWithPublicValues, SP1ProvingKey, SP1VerifyingKey,
};
use anyhow::Result;
use sp1_prover::{SP1Prover, SP1Stdin};
use p3_field::PrimeField;
use sp1_prover::{
verify::verify_groth16_public_inputs, Groth16Proof, HashableKey, SP1Prover, SP1Stdin,
};

/// An implementation of [crate::ProverClient] that can generate mock proofs.
pub struct MockProver {
Expand Down Expand Up @@ -50,7 +53,19 @@ impl Prover for MockProver {
}

fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result<SP1Groth16Proof> {
todo!()
let public_values = SP1Prover::execute(&pk.elf, &stdin)?;
Ok(SP1Groth16Proof {
proof: Groth16Proof {
public_inputs: [
pk.vk.hash_bn254().as_canonical_biguint().to_string(),
public_values.hash().to_string(),
],
encoded_proof: "".to_string(),
raw_proof: "".to_string(),
},
stdin,
public_values,
})
}

fn prove_plonk(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result<SP1PlonkProof> {
Expand All @@ -73,7 +88,8 @@ impl Prover for MockProver {
Ok(())
}

fn verify_groth16(&self, _proof: &SP1Groth16Proof, _vkey: &SP1VerifyingKey) -> Result<()> {
fn verify_groth16(&self, proof: &SP1Groth16Proof, vkey: &SP1VerifyingKey) -> Result<()> {
verify_groth16_public_inputs(vkey, &proof.public_values, &proof.proof.public_inputs)?;
Ok(())
}

Expand Down
16 changes: 2 additions & 14 deletions sdk/src/provers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ mod local;
mod mock;
mod network;

use std::str::FromStr;

use crate::{SP1CompressedProof, SP1Groth16Proof, SP1PlonkProof, SP1Proof};
use anyhow::Result;
pub use local::LocalProver;
pub use mock::MockProver;
pub use network::NetworkProver;
use num_bigint::BigUint;
use sp1_core::stark::MachineVerificationError;
use sp1_prover::CoreSC;
use sp1_prover::SP1CoreProofData;
Expand Down Expand Up @@ -59,7 +56,7 @@ pub trait Prover: Send + Sync {
.map_err(|e| e.into())
}

/// Verify that a SP1 Groth16 proof is valid. Additionally, verify the public inputs of the Groth16Proof match
/// Verify that a SP1 Groth16 proof is valid. Verify that the public inputs of the Groth16Proof match
/// the hash of the VK and the committed public values of the SP1ProofWithPublicValues.
fn verify_groth16(&self, proof: &SP1Groth16Proof, vkey: &SP1VerifyingKey) -> Result<()> {
let sp1_prover = self.sp1_prover();
Expand All @@ -69,16 +66,7 @@ pub trait Prover: Send + Sync {
} else {
sp1_prover::build::groth16_artifacts_dir()
};
sp1_prover.verify_groth16(&proof.proof, vkey, &groth16_aritfacts)?;

// Verify that the public values of the SP1ProofWithPublicValues match the committed public
// values of the Groth16 proof.
let pv_biguint = proof.public_values.hash();
let committed_values_digest = BigUint::from_str(&proof.proof.public_inputs[1])?;

if pv_biguint != committed_values_digest {
return Err(anyhow::Error::msg("public values hash mismatch"));
}
sp1_prover.verify_groth16(&proof.proof, vkey, &proof.public_values, &groth16_aritfacts)?;

Ok(())
}
Expand Down
Loading