spawn_zk_snarks/groth16.rs
1// Copyright (c) 2023 spawn-zk-snarks developers
2//
3// Licensed under the MIT License
4// <LICENSE-MIT or http://opensource.org/licenses/MIT>
5
6use ark_bn254::{Bn254, Fr};
7use ark_groth16::{
8 PreparedVerifyingKey, Proof as ArkProof, ProvingKey, VerifyingKey,
9};
10use ark_snark::SNARK;
11use ark_groth16::Groth16;
12use ark_ec::pairing::Pairing;
13use ark_serialize::CanonicalSerialize;
14use ark_std::rand::thread_rng;
15use thiserror::Error;
16use crate::circuits::{CircuitError, ArithmeticCircuit};
17
18/// Represents all possible errors that can occur during Groth16 operations.
19///
20/// This enum provides a comprehensive set of errors that might occur during
21/// setup, proving, verification, and other related operations.
22#[derive(Error, Debug)]
23pub enum Groth16Error {
24 /// Occurs when the setup phase fails, typically due to invalid parameters
25 /// or insufficient randomness.
26 #[error("Setup failed")]
27 SetupError,
28
29 /// Occurs when proof generation fails, usually due to invalid witness
30 /// or constraint system issues.
31 #[error("Proving failed")]
32 ProvingError,
33
34 /// Occurs when proof verification fails, indicating either an invalid proof
35 /// or mismatched public inputs.
36 #[error("Verification failed")]
37 VerificationError,
38
39 /// Wraps an error from the underlying circuit implementation.
40 /// This can occur during constraint generation or witness computation.
41 #[error("Circuit error: {0}")]
42 CircuitError(#[from] CircuitError),
43
44 /// Occurs when serialization or deserialization of proofs, keys,
45 /// or other data structures fails.
46 #[error("Serialization error")]
47 SerializationError,
48}
49
50/// Contains all necessary parameters for the Groth16 proving system.
51///
52/// This structure holds the proving key, verifying key, and prepared verifying key
53/// needed for generating and verifying zero-knowledge proofs.
54#[derive(Debug)]
55pub struct Groth16Setup<E: Pairing> {
56 /// The proving key used to generate proofs. This contains the secret parameters
57 /// and should be kept private in some applications.
58 pub proving_key: ProvingKey<E>,
59
60 /// The verification key used to verify proofs. This can be made public
61 /// and distributed to verifiers.
62 pub verifying_key: VerifyingKey<E>,
63
64 /// A preprocessed version of the verification key that allows for
65 /// more efficient proof verification.
66 pub prepared_verifying_key: PreparedVerifyingKey<E>,
67}
68
69impl Groth16Setup<Bn254> {
70 /// Creates a new Groth16 setup for a circuit with the specified parameters.
71 ///
72 /// This function generates the proving and verification keys necessary for
73 /// creating and verifying zero-knowledge proofs.
74 ///
75 /// # Arguments
76 /// * `num_constraints` - Number of constraints in the arithmetic circuit
77 /// * `num_variables` - Number of variables used in the circuit
78 ///
79 /// # Returns
80 /// * `Result<Self, Groth16Error>` - The setup parameters or an error
81 ///
82 /// # Example
83 /// ```
84 /// # use spawn_zk_snarks::Groth16Setup;
85 /// let setup = Groth16Setup::new(3, 2)?;
86 /// ```
87 pub fn new(num_constraints: usize, num_variables: usize) -> Result<Self, Groth16Error> {
88 let circuit: ArithmeticCircuit<Fr> = ArithmeticCircuit::new(num_constraints, num_variables, 1);
89 let rng = &mut thread_rng();
90
91 let (pk, vk) = Groth16::<Bn254>::circuit_specific_setup(
92 circuit,
93 rng,
94 ).map_err(|_| Groth16Error::SetupError)?;
95
96 let pvk = match Groth16::<Bn254>::process_vk(&vk) {
97 Ok(pvk) => pvk,
98 Err(_) => return Err(Groth16Error::SetupError),
99 };
100
101 Ok(Self {
102 proving_key: pk,
103 verifying_key: vk,
104 prepared_verifying_key: pvk,
105 })
106 }
107
108 /// Generates a zero-knowledge proof using the provided inputs and witness.
109 ///
110 /// # Arguments
111 /// * `inputs` - Public inputs to the circuit
112 /// * `witness` - Private witness values
113 ///
114 /// # Returns
115 /// * `Result<ArkProof<Bn254>, Groth16Error>` - The generated proof or an error
116 ///
117 /// # Example
118 /// ```
119 /// # use spawn_zk_snarks::{Groth16Setup, Fr};
120 /// # let setup = Groth16Setup::new(3, 2)?;
121 /// let proof = setup.prove(&inputs, &witness)?;
122 /// ```
123 pub fn prove(&self, inputs: &[Fr], witness: &[Fr]) -> Result<ArkProof<Bn254>, Groth16Error> {
124 let rng = &mut thread_rng();
125
126 let circuit = ArithmeticCircuit {
127 num_constraints: inputs.len(),
128 num_variables: witness.len(),
129 num_inputs: 1,
130 witness: witness.to_vec(),
131 };
132
133 Groth16::<Bn254>::create_random_proof_with_reduction(
134 circuit,
135 &self.proving_key,
136 rng,
137 ).map_err(|_| Groth16Error::ProvingError)
138 }
139
140 /// Verifies a zero-knowledge proof against the provided public inputs.
141 ///
142 /// # Arguments
143 /// * `proof` - The proof to verify
144 /// * `public_inputs` - Public inputs used in the proof
145 ///
146 /// # Returns
147 /// * `Result<bool, Groth16Error>` - Whether the proof is valid
148 ///
149 /// # Example
150 /// ```
151 /// # use spawn_zk_snarks::Groth16Setup;
152 /// # let setup = Groth16Setup::new(3, 2)?;
153 /// let is_valid = setup.verify(&proof, &public_inputs)?;
154 /// assert!(is_valid);
155 /// ```
156 pub fn verify(
157 &self,
158 proof: &ArkProof<Bn254>,
159 public_inputs: &[Fr],
160 ) -> Result<bool, Groth16Error> {
161 <Groth16<Bn254> as SNARK<Fr>>::verify_with_processed_vk(
162 &self.prepared_verifying_key,
163 public_inputs,
164 proof,
165 ).map_err(|_| Groth16Error::VerificationError)
166 }
167
168 /// Converts a proof to EVM-compatible format for on-chain verification.
169 ///
170 /// # Arguments
171 /// * `proof` - The proof to convert
172 ///
173 /// # Returns
174 /// * `Result<Vec<u8>, Groth16Error>` - The serialized proof bytes
175 pub fn proof_to_evm_format(proof: &ArkProof<Bn254>) -> Result<Vec<u8>, Groth16Error> {
176 let mut bytes = Vec::new();
177 proof.serialize_compressed(&mut bytes)
178 .map_err(|_| Groth16Error::SerializationError)?;
179 Ok(bytes)
180 }
181
182 /// Estimates the gas cost for verifying a proof on Ethereum.
183 ///
184 /// # Arguments
185 /// * `proof_size` - Size of the proof in bytes
186 /// * `num_inputs` - Number of public inputs
187 ///
188 /// # Returns
189 /// * `u64` - Estimated gas cost in units
190 pub fn estimate_verification_gas(proof_size: usize, num_inputs: usize) -> u64 {
191 let base_cost = 150_000;
192 let input_cost = num_inputs as u64 * 1_000;
193 let data_cost = proof_size as u64 * 16;
194 base_cost + input_cost + data_cost
195 }
196}