sp1_verifier/groth16/mod.rs
1mod converter;
2pub mod error;
3mod verify;
4
5use bn::Fr;
6pub(crate) use converter::{load_groth16_proof_from_bytes, load_groth16_verifying_key_from_bytes};
7pub(crate) use verify::*;
8
9use error::Groth16Error;
10
11use crate::{
12 blake3_hash, constants::VK_HASH_PREFIX_LENGTH, decode_sp1_vkey_hash, error::Error,
13 hash_public_inputs, hash_public_inputs_with_fn,
14};
15
16use alloc::vec::Vec;
17use sha2::{Digest, Sha256};
18
19#[cfg(feature = "ark")]
20pub mod ark_converter;
21
22/// A verifier for Groth16 zero-knowledge proofs.
23#[derive(Debug)]
24pub struct Groth16Verifier;
25impl Groth16Verifier {
26 /// Verifies an SP1 Groth16 proof, as generated by the SP1 SDK.
27 ///
28 /// # Arguments
29 ///
30 /// * `proof` - The proof bytes.
31 /// * `public_inputs` - The SP1 public inputs.
32 /// * `sp1_vkey_hash` - The SP1 vkey hash. This is generated in the following manner:
33 ///
34 /// ```ignore
35 /// use sp1_sdk::ProverClient;
36 /// let client = ProverClient::new();
37 /// let (pk, vk) = client.setup(ELF);
38 /// let sp1_vkey_hash = vk.bytes32();
39 /// ```
40 /// * `groth16_vk` - The Groth16 verifying key bytes. Usually this will be the
41 /// [`static@crate::GROTH16_VK_BYTES`] constant, which is the Groth16 verifying key for the
42 /// current SP1 version.
43 ///
44 /// # Returns
45 ///
46 /// A success [`Result`] if verification succeeds, or a [`Groth16Error`] if verification fails.
47 pub fn verify(
48 proof: &[u8],
49 sp1_public_inputs: &[u8],
50 sp1_vkey_hash: &str,
51 groth16_vk: &[u8],
52 ) -> Result<(), Groth16Error> {
53 if proof.len() < VK_HASH_PREFIX_LENGTH {
54 return Err(Groth16Error::GeneralError(Error::InvalidData));
55 }
56
57 // Hash the vk and get the first 4 bytes.
58 let groth16_vk_hash: [u8; 4] = Sha256::digest(groth16_vk)[..VK_HASH_PREFIX_LENGTH]
59 .try_into()
60 .map_err(|_| Groth16Error::GeneralError(Error::InvalidData))?;
61
62 // Check to make sure that this proof was generated by the groth16 proving key corresponding
63 // to the given groth16_vk.
64 //
65 // SP1 prepends the raw Groth16 proof with the first 4 bytes of the groth16 vkey to
66 // facilitate this check.
67 if groth16_vk_hash != proof[..VK_HASH_PREFIX_LENGTH] {
68 return Err(Groth16Error::Groth16VkeyHashMismatch);
69 }
70
71 let sp1_vkey_hash = decode_sp1_vkey_hash(sp1_vkey_hash)?;
72
73 // It is computationally infeasible to find two distinct inputs, one processed with
74 // SHA256 and the other with Blake3, that yield the same hash value.
75 if Self::verify_gnark_proof(
76 &proof[VK_HASH_PREFIX_LENGTH..],
77 &[sp1_vkey_hash, hash_public_inputs(sp1_public_inputs)],
78 groth16_vk,
79 )
80 .is_ok()
81 {
82 return Ok(())
83 }
84
85 Self::verify_gnark_proof(
86 &proof[VK_HASH_PREFIX_LENGTH..],
87 &[sp1_vkey_hash, hash_public_inputs_with_fn(sp1_public_inputs, blake3_hash)],
88 groth16_vk,
89 )
90 }
91
92 /// Verifies a Gnark Groth16 proof using raw byte inputs.
93 ///
94 /// WARNING: if you're verifying an SP1 proof, you should use [`verify`] instead.
95 /// This is a lower-level verification method that works directly with raw bytes rather than
96 /// the SP1 SDK's data structures.
97 ///
98 /// # Arguments
99 ///
100 /// * `proof` - The raw Groth16 proof bytes (without the 4-byte vkey hash prefix)
101 /// * `public_inputs` - The public inputs to the circuit
102 /// * `groth16_vk` - The Groth16 verifying key bytes
103 ///
104 /// # Returns
105 ///
106 /// A [`Result`] containing unit `()` if the proof is valid,
107 /// or a [`Groth16Error`] if verification fails.
108 ///
109 /// # Note
110 ///
111 /// This method expects the raw proof bytes without the 4-byte vkey hash prefix that
112 /// [`verify`] checks. If you have a complete proof with the prefix, use [`verify`] instead.
113 pub fn verify_gnark_proof(
114 proof: &[u8],
115 public_inputs: &[[u8; 32]],
116 groth16_vk: &[u8],
117 ) -> Result<(), Groth16Error> {
118 let proof = load_groth16_proof_from_bytes(proof)?;
119 let groth16_vk = load_groth16_verifying_key_from_bytes(groth16_vk)?;
120
121 let public_inputs =
122 public_inputs.iter().map(|input| Fr::from_slice(input).unwrap()).collect::<Vec<_>>();
123 verify_groth16_algebraic(&groth16_vk, &proof, &public_inputs)
124 }
125}