diff --git a/prover/src/lib.rs b/prover/src/lib.rs index dcc3ca3071..b33a897182 100644 --- a/prover/src/lib.rs +++ b/prover/src/lib.rs @@ -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)?; @@ -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(()) } diff --git a/prover/src/verify.rs b/prover/src/verify.rs index 8740e7ee36..279fb356a3 100644 --- a/prover/src/verify.rs +++ b/prover/src/verify.rs @@ -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)] +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. @@ -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(); @@ -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. +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(()) +} diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 58ae33b989..1249fc0c88 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -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(); + } } diff --git a/sdk/src/provers/mock.rs b/sdk/src/provers/mock.rs index 9f25eafd38..390c494240 100644 --- a/sdk/src/provers/mock.rs +++ b/sdk/src/provers/mock.rs @@ -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 { @@ -50,7 +53,19 @@ impl Prover for MockProver { } fn prove_groth16(&self, pk: &SP1ProvingKey, stdin: SP1Stdin) -> Result { - 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 { @@ -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(()) } diff --git a/sdk/src/provers/mod.rs b/sdk/src/provers/mod.rs index d2b9b35949..8475a0f7aa 100644 --- a/sdk/src/provers/mod.rs +++ b/sdk/src/provers/mod.rs @@ -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; @@ -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(); @@ -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(()) }