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}