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, VK_ROOT_BYTES,
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::from_env();
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 Self::verify_with_exit_code(proof, sp1_public_inputs, sp1_vkey_hash, groth16_vk, [0u8; 32])
54 }
55
56 /// Verifies an SP1 Groth16 proof with an expected exit code. Only use this if you're trying to
57 /// verify a program that panics. Otherwise use [`verify`].
58 ///
59 /// # Arguments
60 ///
61 /// * `proof` - The proof bytes.
62 /// * `public_inputs` - The SP1 public inputs.
63 /// * `sp1_vkey_hash` - The SP1 vkey hash. This is generated in the following manner:
64 ///
65 /// ```ignore
66 /// use sp1_sdk::ProverClient;
67 /// let client = ProverClient::from_env();
68 /// let (pk, vk) = client.setup(ELF);
69 /// let sp1_vkey_hash = vk.bytes32();
70 /// ```
71 /// * `groth16_vk` - The Groth16 verifying key bytes. Usually this will be the
72 /// [`static@crate::GROTH16_VK_BYTES`] constant, which is the Groth16 verifying key for the
73 /// current SP1 version.
74 /// * `expected_exit_code` - The expected exit code to verify against.
75 ///
76 /// # Returns
77 ///
78 /// A success [`Result`] if verification succeeds, or a [`Groth16Error`] if verification fails.
79 pub fn verify_with_exit_code(
80 proof: &[u8],
81 sp1_public_inputs: &[u8],
82 sp1_vkey_hash: &str,
83 groth16_vk: &[u8],
84 expected_exit_code: [u8; 32],
85 ) -> Result<(), Groth16Error> {
86 if proof.len() < VK_HASH_PREFIX_LENGTH + 32 + 32 + 32 {
87 return Err(Groth16Error::GeneralError(Error::InvalidData));
88 }
89
90 // Hash the vk and get the first 4 bytes.
91 let groth16_vk_hash: [u8; 4] = Sha256::digest(groth16_vk)[..VK_HASH_PREFIX_LENGTH]
92 .try_into()
93 .map_err(|_| Groth16Error::GeneralError(Error::InvalidData))?;
94
95 // Check to make sure that this proof was generated by the groth16 proving key corresponding
96 // to the given groth16_vk.
97 //
98 // SP1 prepends the raw Groth16 proof with the first 4 bytes of the groth16 vkey to
99 // facilitate this check.
100 if groth16_vk_hash != proof[..VK_HASH_PREFIX_LENGTH] {
101 return Err(Groth16Error::Groth16VkeyHashMismatch);
102 }
103
104 let sp1_vkey_hash = decode_sp1_vkey_hash(sp1_vkey_hash)?;
105
106 let exit_code: [u8; 32] = proof[VK_HASH_PREFIX_LENGTH..VK_HASH_PREFIX_LENGTH + 32]
107 .try_into()
108 .map_err(|_| Groth16Error::GeneralError(Error::InvalidData))?;
109
110 let vk_root: [u8; 32] = proof[VK_HASH_PREFIX_LENGTH + 32..VK_HASH_PREFIX_LENGTH + 64]
111 .try_into()
112 .map_err(|_| Groth16Error::GeneralError(Error::InvalidData))?;
113
114 let proof_nonce: [u8; 32] = proof[VK_HASH_PREFIX_LENGTH + 64..VK_HASH_PREFIX_LENGTH + 96]
115 .try_into()
116 .map_err(|_| Groth16Error::GeneralError(Error::InvalidData))?;
117
118 if vk_root != *VK_ROOT_BYTES {
119 return Err(Groth16Error::VkeyRootMismatch);
120 }
121
122 if exit_code != expected_exit_code {
123 return Err(Groth16Error::ExitCodeMismatch);
124 }
125
126 // It is computationally infeasible to find two distinct inputs, one processed with
127 // SHA256 and the other with Blake3, that yield the same hash value.
128 if Self::verify_gnark_proof(
129 &proof[VK_HASH_PREFIX_LENGTH + 96..],
130 &[
131 sp1_vkey_hash,
132 hash_public_inputs(sp1_public_inputs),
133 exit_code,
134 vk_root,
135 proof_nonce,
136 ],
137 groth16_vk,
138 )
139 .is_ok()
140 {
141 return Ok(());
142 }
143
144 Self::verify_gnark_proof(
145 &proof[VK_HASH_PREFIX_LENGTH + 96..],
146 &[
147 sp1_vkey_hash,
148 hash_public_inputs_with_fn(sp1_public_inputs, blake3_hash),
149 exit_code,
150 vk_root,
151 proof_nonce,
152 ],
153 groth16_vk,
154 )
155 }
156
157 /// Verifies a Gnark Groth16 proof using raw byte inputs.
158 ///
159 /// WARNING: if you're verifying an SP1 proof, you should use [`verify`] instead.
160 /// This is a lower-level verification method that works directly with raw bytes rather than
161 /// the SP1 SDK's data structures.
162 ///
163 /// # Arguments
164 ///
165 /// * `proof` - The raw Groth16 proof bytes (without the 4-byte vkey hash prefix)
166 /// * `public_inputs` - The public inputs to the circuit
167 /// * `groth16_vk` - The Groth16 verifying key bytes
168 ///
169 /// # Returns
170 ///
171 /// A [`Result`] containing unit `()` if the proof is valid,
172 /// or a [`Groth16Error`] if verification fails.
173 ///
174 /// # Note
175 ///
176 /// This method expects the raw proof bytes without the 4-byte vkey hash prefix that
177 /// [`verify`] checks. If you have a complete proof with the prefix, use [`verify`] instead.
178 pub fn verify_gnark_proof(
179 proof: &[u8],
180 public_inputs: &[[u8; 32]],
181 groth16_vk: &[u8],
182 ) -> Result<(), Groth16Error> {
183 let proof = load_groth16_proof_from_bytes(proof)?;
184 let groth16_vk = load_groth16_verifying_key_from_bytes(groth16_vk)?;
185
186 let public_inputs = public_inputs
187 .iter()
188 .map(|input| Fr::from_slice(input))
189 .collect::<Result<Vec<_>, _>>()
190 .map_err(|e| Groth16Error::GeneralError(crate::groth16::Error::Field(e)))?;
191
192 verify_groth16_algebraic(&groth16_vk, &proof, &public_inputs)
193 }
194}