1use super::{read_instances, write_instances, CircuitExt, PlonkSuccinctVerifier, Snark};
2#[cfg(feature = "display")]
3use ark_std::{end_timer, start_timer};
4use halo2_base::halo2_proofs;
5pub use halo2_base::poseidon::hasher::spec::OptimizedPoseidonSpec;
6use halo2_proofs::{
7 circuit::Layouter,
8 halo2curves::{
9 bn256::{Bn256, Fr, G1Affine},
10 group::ff::Field,
11 },
12 plonk::{
13 create_proof, keygen_vk, verify_proof, Circuit, ConstraintSystem, Error, ProvingKey,
14 VerifyingKey,
15 },
16 poly::{
17 commitment::{ParamsProver, Prover, Verifier},
18 kzg::{
19 commitment::{KZGCommitmentScheme, ParamsKZG},
20 msm::DualMSM,
21 multiopen::{ProverGWC, ProverSHPLONK, VerifierGWC, VerifierSHPLONK},
22 strategy::{AccumulatorStrategy, GuardKZG},
23 },
24 VerificationStrategy,
25 },
26};
27use itertools::Itertools;
28use lazy_static::lazy_static;
29use rand::{rngs::StdRng, SeedableRng};
30use snark_verifier::{
31 cost::CostEstimation,
32 loader::native::NativeLoader,
33 pcs::{
34 kzg::{KzgAccumulator, KzgAsVerifyingKey, KzgSuccinctVerifyingKey},
35 AccumulationScheme, PolynomialCommitmentScheme, Query,
36 },
37 system::halo2::{compile, Config},
38 util::arithmetic::Rotation,
39 util::transcript::TranscriptWrite,
40 verifier::plonk::{PlonkProof, PlonkProtocol},
41};
42use std::{
43 fs::{self, File},
44 marker::PhantomData,
45 path::Path,
46};
47
48pub mod aggregation;
49pub mod utils;
50
51const T: usize = 3;
55const RATE: usize = 2;
56const R_F: usize = 8; const R_P: usize = 57; const SECURE_MDS: usize = 0;
59
60pub type PoseidonTranscript<L, S> =
61 snark_verifier::system::halo2::transcript::halo2::PoseidonTranscript<
62 G1Affine,
63 L,
64 S,
65 T,
66 RATE,
67 R_F,
68 R_P,
69 >;
70
71lazy_static! {
72 pub static ref POSEIDON_SPEC: OptimizedPoseidonSpec<Fr, T, RATE> =
73 OptimizedPoseidonSpec::new::<R_F, R_P, SECURE_MDS>();
74}
75
76pub fn gen_proof<'params, C, P, V>(
80 params: &'params ParamsKZG<Bn256>,
82 pk: &ProvingKey<G1Affine>,
83 circuit: C,
84 instances: Vec<Vec<Fr>>,
85 path: Option<(&Path, &Path)>,
86) -> Vec<u8>
87where
88 C: Circuit<Fr>,
89 P: Prover<'params, KZGCommitmentScheme<Bn256>>,
90 V: Verifier<
91 'params,
92 KZGCommitmentScheme<Bn256>,
93 Guard = GuardKZG<'params, Bn256>,
94 MSMAccumulator = DualMSM<'params, Bn256>,
95 >,
96{
97 if let Some((instance_path, proof_path)) = path {
98 let cached_instances = read_instances(instance_path);
99 if matches!(cached_instances, Ok(tmp) if tmp == instances) && proof_path.exists() {
100 #[cfg(feature = "display")]
101 let read_time = start_timer!(|| format!("Reading proof from {proof_path:?}"));
102
103 let proof = fs::read(proof_path).unwrap();
104
105 #[cfg(feature = "display")]
106 end_timer!(read_time);
107 return proof;
108 }
109 }
110
111 let instances = instances.iter().map(Vec::as_slice).collect_vec();
112
113 #[cfg(feature = "display")]
114 let proof_time = start_timer!(|| "Create proof");
115
116 let mut transcript =
117 PoseidonTranscript::<NativeLoader, Vec<u8>>::from_spec(vec![], POSEIDON_SPEC.clone());
118 let rng = StdRng::from_entropy();
119 create_proof::<_, P, _, _, _, _>(params, pk, &[circuit], &[&instances], rng, &mut transcript)
120 .unwrap();
121 let proof = transcript.finalize();
122
123 #[cfg(feature = "display")]
124 end_timer!(proof_time);
125
126 assert!(
128 {
129 let mut transcript_read = PoseidonTranscript::<NativeLoader, &[u8]>::from_spec(
130 &proof[..],
131 POSEIDON_SPEC.clone(),
132 );
133 VerificationStrategy::<_, V>::finalize(
134 verify_proof::<_, V, _, _, _>(
135 params.verifier_params(),
136 pk.get_vk(),
137 AccumulatorStrategy::new(params.verifier_params()),
138 &[instances.as_slice()],
139 &mut transcript_read,
140 )
141 .unwrap(),
142 )
143 },
144 "SNARK proof failed to verify"
145 );
146
147 if let Some((instance_path, proof_path)) = path {
148 write_instances(&instances, instance_path);
149 fs::write(proof_path, &proof).unwrap();
150 }
151
152 proof
153}
154
155pub fn gen_proof_gwc<C: Circuit<Fr>>(
159 params: &ParamsKZG<Bn256>,
160 pk: &ProvingKey<G1Affine>,
161 circuit: C,
162 instances: Vec<Vec<Fr>>,
163 path: Option<(&Path, &Path)>,
164) -> Vec<u8> {
165 gen_proof::<C, ProverGWC<_>, VerifierGWC<_>>(params, pk, circuit, instances, path)
166}
167
168pub fn gen_proof_shplonk<C: Circuit<Fr>>(
172 params: &ParamsKZG<Bn256>,
173 pk: &ProvingKey<G1Affine>,
174 circuit: C,
175 instances: Vec<Vec<Fr>>,
176 path: Option<(&Path, &Path)>,
177) -> Vec<u8> {
178 gen_proof::<C, ProverSHPLONK<_>, VerifierSHPLONK<_>>(params, pk, circuit, instances, path)
179}
180
181pub fn gen_snark<'params, ConcreteCircuit, P, V>(
186 params: &'params ParamsKZG<Bn256>,
187 pk: &ProvingKey<G1Affine>,
188 circuit: ConcreteCircuit,
189 path: Option<impl AsRef<Path>>,
190) -> Snark
191where
192 ConcreteCircuit: CircuitExt<Fr>,
193 P: Prover<'params, KZGCommitmentScheme<Bn256>>,
194 V: Verifier<
195 'params,
196 KZGCommitmentScheme<Bn256>,
197 Guard = GuardKZG<'params, Bn256>,
198 MSMAccumulator = DualMSM<'params, Bn256>,
199 >,
200{
201 if let Some(path) = &path {
202 if let Ok(snark) = read_snark(path) {
203 return snark;
204 }
205 }
206 let protocol = compile(
207 params,
208 pk.get_vk(),
209 Config::kzg()
210 .with_num_instance(circuit.num_instance())
211 .with_accumulator_indices(ConcreteCircuit::accumulator_indices()),
212 );
213
214 let instances = circuit.instances();
215 let proof = gen_proof::<ConcreteCircuit, P, V>(params, pk, circuit, instances.clone(), None);
216
217 let snark = Snark::new(protocol, instances, proof);
218 if let Some(path) = &path {
219 let f = File::create(path).unwrap();
220 #[cfg(feature = "display")]
221 let write_time = start_timer!(|| "Write SNARK");
222 bincode::serialize_into(f, &snark).unwrap();
223 #[cfg(feature = "display")]
224 end_timer!(write_time);
225 }
226 #[allow(clippy::let_and_return)]
227 snark
228}
229
230pub fn gen_snark_gwc<ConcreteCircuit: CircuitExt<Fr>>(
235 params: &ParamsKZG<Bn256>,
236 pk: &ProvingKey<G1Affine>,
237 circuit: ConcreteCircuit,
238 path: Option<impl AsRef<Path>>,
239) -> Snark {
240 gen_snark::<ConcreteCircuit, ProverGWC<_>, VerifierGWC<_>>(params, pk, circuit, path)
241}
242
243pub fn gen_snark_shplonk<ConcreteCircuit: CircuitExt<Fr>>(
248 params: &ParamsKZG<Bn256>,
249 pk: &ProvingKey<G1Affine>,
250 circuit: ConcreteCircuit,
251 path: Option<impl AsRef<Path>>,
252) -> Snark {
253 gen_snark::<ConcreteCircuit, ProverSHPLONK<_>, VerifierSHPLONK<_>>(params, pk, circuit, path)
254}
255
256pub fn read_snark(path: impl AsRef<Path>) -> Result<Snark, bincode::Error> {
260 let f = File::open(path).map_err(Box::<bincode::ErrorKind>::from)?;
261 bincode::deserialize_from(f)
262}
263
264pub trait NativeKzgAccumulationScheme:
265 PolynomialCommitmentScheme<
266 G1Affine,
267 NativeLoader,
268 VerifyingKey = KzgSuccinctVerifyingKey<G1Affine>,
269 Output = KzgAccumulator<G1Affine, NativeLoader>,
270 > + AccumulationScheme<
271 G1Affine,
272 NativeLoader,
273 Accumulator = KzgAccumulator<G1Affine, NativeLoader>,
274 VerifyingKey = KzgAsVerifyingKey,
275 > + CostEstimation<G1Affine, Input = Vec<Query<Rotation>>>
276{
277}
278
279impl NativeKzgAccumulationScheme for crate::GWC {}
280impl NativeKzgAccumulationScheme for crate::SHPLONK {}
281
282pub fn gen_dummy_snark<ConcreteCircuit, AS>(
284 params: &ParamsKZG<Bn256>,
285 vk: Option<&VerifyingKey<G1Affine>>,
286 num_instance: Vec<usize>,
287 circuit_params: ConcreteCircuit::Params,
288) -> Snark
289where
290 ConcreteCircuit: CircuitExt<Fr>,
291 ConcreteCircuit::Params: Clone,
292 AS: NativeKzgAccumulationScheme,
293{
294 #[derive(Clone)]
295 struct CsProxy<F: Field, C: Circuit<F>> {
296 params: C::Params,
297 _marker: PhantomData<F>,
298 }
299
300 impl<F: Field, C: Circuit<F>> CsProxy<F, C> {
301 pub fn new(params: C::Params) -> Self {
302 Self { params, _marker: PhantomData }
303 }
304 }
305
306 impl<F: Field, C: CircuitExt<F>> Circuit<F> for CsProxy<F, C>
307 where
308 C::Params: Clone,
309 {
310 type Config = C::Config;
311 type FloorPlanner = C::FloorPlanner;
312 type Params = C::Params;
313
314 fn without_witnesses(&self) -> Self {
315 Self::new(self.params.clone())
316 }
317
318 fn params(&self) -> Self::Params {
319 self.params.clone()
320 }
321
322 fn configure_with_params(
323 meta: &mut ConstraintSystem<F>,
324 params: Self::Params,
325 ) -> Self::Config {
326 C::configure_with_params(meta, params)
327 }
328
329 fn configure(_: &mut ConstraintSystem<F>) -> Self::Config {
330 unreachable!("must use configure_with_params")
331 }
332
333 fn synthesize(
334 &self,
335 config: Self::Config,
336 mut layouter: impl Layouter<F>,
337 ) -> Result<(), Error> {
338 layouter.assign_region(
341 || "",
342 |mut region| {
343 for q in C::selectors(&config).iter() {
344 q.enable(&mut region, 0)?;
345 }
346 Ok(())
347 },
348 )?;
349 Ok(())
350 }
351 }
352
353 let dummy_vk = vk
354 .is_none()
355 .then(|| keygen_vk(params, &CsProxy::<Fr, ConcreteCircuit>::new(circuit_params)).unwrap());
356
357 gen_dummy_snark_from_vk::<AS>(
358 params,
359 vk.or(dummy_vk.as_ref()).unwrap(),
360 num_instance,
361 ConcreteCircuit::accumulator_indices(),
362 )
363}
364
365pub fn gen_dummy_snark_from_vk<AS>(
372 params: &ParamsKZG<Bn256>,
373 vk: &VerifyingKey<G1Affine>,
374 num_instance: Vec<usize>,
375 accumulator_indices: Option<Vec<(usize, usize)>>,
376) -> Snark
377where
378 AS: NativeKzgAccumulationScheme,
379{
380 let protocol = compile(
381 params,
382 vk,
383 Config::kzg().with_num_instance(num_instance).with_accumulator_indices(accumulator_indices),
384 );
385 gen_dummy_snark_from_protocol::<AS>(protocol)
386}
387
388pub fn gen_dummy_snark_from_protocol<AS>(protocol: PlonkProtocol<G1Affine>) -> Snark
395where
396 AS: NativeKzgAccumulationScheme,
397{
398 let instances = protocol.num_instance.iter().map(|&n| vec![Fr::default(); n]).collect();
399 let proof = {
400 let mut transcript = PoseidonTranscript::<NativeLoader, _>::new::<SECURE_MDS>(Vec::new());
401 for _ in 0..protocol
402 .num_witness
403 .iter()
404 .chain(Some(&protocol.quotient.num_chunk()))
405 .sum::<usize>()
406 {
407 transcript.write_ec_point(G1Affine::default()).unwrap();
408 }
409 for _ in 0..protocol.evaluations.len() {
410 transcript.write_scalar(Fr::default()).unwrap();
411 }
412 let queries = PlonkProof::<G1Affine, NativeLoader, AS>::empty_queries(&protocol);
413 for _ in 0..AS::estimate_cost(&queries).num_commitment {
414 transcript.write_ec_point(G1Affine::default()).unwrap();
415 }
416 transcript.finalize()
417 };
418
419 Snark::new(protocol, instances, proof)
420}