penumbra_sdk_proof_setup/single/
mod.rs1#![deny(clippy::unwrap_used)]
3mod dlog;
5pub(crate) mod group;
6pub mod log;
7mod phase1;
8mod phase2;
9
10use ark_ec::{CurveGroup, Group};
11use ark_ff::Zero;
12use ark_groth16::data_structures::ProvingKey;
13use ark_groth16::VerifyingKey;
14use ark_poly::EvaluationDomain;
15use ark_poly::Radix2EvaluationDomain;
16use ark_relations::r1cs::ConstraintMatrices;
17
18use ark_serialize::CanonicalDeserialize;
19use ark_serialize::CanonicalSerialize;
20use decaf377::Bls12_377;
21
22pub use dlog::Proof as DLogProof;
23
24pub use phase1::CRSElements as Phase1CRSElements;
25pub use phase1::Contribution as Phase1Contribution;
26pub(crate) use phase1::LinkingProof;
27pub use phase1::RawCRSElements as Phase1RawCRSElements;
28pub use phase1::RawContribution as Phase1RawContribution;
29
30pub use phase2::CRSElements as Phase2CRSElements;
31pub use phase2::Contribution as Phase2Contribution;
32pub use phase2::RawCRSElements as Phase2RawCRSElements;
33pub use phase2::RawContribution as Phase2RawContribution;
34
35use group::{F, G1, G2};
36
37use anyhow::{anyhow, Result};
38
39#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)]
40pub struct ExtraTransitionInformation {
41 u_1: Vec<G1>,
43 v_1: Vec<G1>,
45 v_2: Vec<G2>,
47 p_1: Vec<G1>,
49}
50
51pub fn circuit_degree(circuit: &ConstraintMatrices<F>) -> Result<usize> {
55 let circuit_size = circuit.num_constraints + circuit.num_instance_variables;
56 Radix2EvaluationDomain::<group::F>::compute_size_of_domain(circuit_size)
57 .ok_or_else(|| anyhow!("Circuit of size {} is too large", circuit_size))
58}
59
60pub fn transition(
65 phase1: &phase1::CRSElements,
66 circuit: &ConstraintMatrices<F>,
67) -> Result<(ExtraTransitionInformation, phase2::CRSElements)> {
68 let circuit_size = circuit.num_constraints + circuit.num_instance_variables;
143 let domain: Radix2EvaluationDomain<F> =
144 Radix2EvaluationDomain::new(circuit_size).ok_or_else(|| {
145 anyhow!(
146 "Failed to create evaluation domain size (at least) {}",
147 circuit_size
148 )
149 })?;
150 let domain_size = domain.size();
151 if phase1.degree < domain_size {
153 return Err(anyhow!(
154 "Phase1 elements not large enough: expected >= {}, found {}",
155 domain_size,
156 phase1.degree
157 ));
158 }
159
160 let mut extracting_poly: Vec<_> = phase1.raw.x_1.iter().copied().take(domain_size).collect();
163 domain.ifft_in_place(&mut extracting_poly);
164 let lagrange_1 = extracting_poly;
165
166 let mut extracting_poly: Vec<_> = phase1.raw.x_2.iter().copied().take(domain_size).collect();
168 domain.ifft_in_place(&mut extracting_poly);
169 let lagrange_2 = extracting_poly;
170
171 let mut extracting_poly: Vec<_> = phase1
173 .raw
174 .alpha_x_1
175 .iter()
176 .copied()
177 .take(domain_size)
178 .collect();
179 domain.ifft_in_place(&mut extracting_poly);
180 let alpha_lagrange_1 = extracting_poly;
181
182 let mut extracting_poly: Vec<_> = phase1
184 .raw
185 .beta_x_1
186 .iter()
187 .copied()
188 .take(domain_size)
189 .collect();
190 domain.ifft_in_place(&mut extracting_poly);
191 let beta_lagrange_1 = extracting_poly;
192
193 let qap_num_variables = (circuit.num_instance_variables - 1) + circuit.num_witness_variables;
199 let mut p_1 = vec![G1::zero(); qap_num_variables + 1];
200 let mut u_1 = vec![G1::zero(); qap_num_variables + 1];
202 let mut v_1 = vec![G1::zero(); qap_num_variables + 1];
203 let mut v_2 = vec![G2::zero(); qap_num_variables + 1];
204
205 {
208 let start = 0;
209 let end = circuit.num_instance_variables;
210 let num_constraints = circuit.num_constraints;
211 p_1[start..end]
215 .copy_from_slice(&beta_lagrange_1[(start + num_constraints)..(end + num_constraints)]);
216 u_1[start..end]
218 .copy_from_slice(&lagrange_1[(start + num_constraints)..(end + num_constraints)])
219 }
220
221 for i in 0..circuit.num_constraints {
223 for &(ref coeff, j) in &circuit.a[i] {
224 p_1[j] += beta_lagrange_1[i] * coeff;
225 u_1[j] += lagrange_1[i] * coeff;
226 }
227 for &(ref coeff, j) in &circuit.b[i] {
228 p_1[j] += alpha_lagrange_1[i] * coeff;
229 v_1[j] += lagrange_1[i] * coeff;
230 v_2[j] += lagrange_2[i] * coeff;
231 }
232 for &(ref coeff, j) in &circuit.c[i] {
233 p_1[j] += lagrange_1[i] * coeff;
234 }
235 }
236
237 let t: Vec<_> = (0..(domain_size - 1))
239 .map(|i| phase1.raw.x_1[i + domain_size] - phase1.raw.x_1[i])
240 .collect();
241
242 Ok((
243 ExtraTransitionInformation {
244 u_1,
245 v_1,
246 v_2,
247 p_1: p_1.clone(),
248 },
249 phase2::CRSElements {
250 raw: phase2::RawCRSElements {
251 delta_1: G1::generator(),
252 delta_2: G2::generator(),
253 inv_delta_p_1: p_1,
254 inv_delta_t_1: t,
255 },
256 },
257 ))
258}
259
260pub fn combine(
264 circuit: &ConstraintMatrices<F>,
265 phase1out: &Phase1CRSElements,
266 phase2out: &Phase2CRSElements,
267 extra: &ExtraTransitionInformation,
268) -> ProvingKey<Bls12_377> {
269 let vk = VerifyingKey {
270 alpha_g1: phase1out.raw.alpha_1.into_affine(),
271 beta_g2: phase1out.raw.beta_2.into_affine(),
272 gamma_g2: G2::generator().into_affine(),
273 delta_g2: phase2out.raw.delta_2.into_affine(),
274 gamma_abc_g1: G1::normalize_batch(&extra.p_1[..circuit.num_instance_variables]),
275 };
276 ProvingKey {
277 vk,
278 beta_g1: phase1out.raw.beta_1.into_affine(),
279 delta_g1: phase2out.raw.delta_1.into_affine(),
280 a_query: G1::normalize_batch(&extra.u_1),
281 b_g1_query: G1::normalize_batch(&extra.v_1),
282 b_g2_query: G2::normalize_batch(&extra.v_2),
283 h_query: G1::normalize_batch(&phase2out.raw.inv_delta_t_1),
284 l_query: G1::normalize_batch(
285 &phase2out.raw.inv_delta_p_1[circuit.num_instance_variables..],
286 ),
287 }
288}
289
290#[cfg(test)]
291mod test {
292 use ark_ff::One;
293 use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16};
294 use ark_r1cs_std::{
295 eq::EqGadget,
296 fields::fp::FpVar,
297 prelude::{AllocVar, Boolean},
298 };
299 use ark_relations::r1cs::{self, ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef};
300 use ark_snark::SNARK;
301 use rand_core::{OsRng, SeedableRng};
302
303 use crate::single::log::Hashable;
304
305 use super::*;
306
307 #[derive(Clone, Copy, Debug)]
308 struct TestCircuit {
309 x: F,
310 y: F,
311 pub x_plus_y: F,
312 }
313
314 impl ConstraintSynthesizer<F> for TestCircuit {
315 fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> r1cs::Result<()> {
316 let x_var = FpVar::new_witness(cs.clone(), || Ok(self.x))?;
318 let y_var = FpVar::new_witness(cs.clone(), || Ok(self.y))?;
319
320 let x_plus_y_var = FpVar::new_input(cs, || Ok(self.x_plus_y))?;
322
323 let add_output = x_var + y_var;
324
325 x_plus_y_var.conditional_enforce_equal(&add_output, &Boolean::TRUE)
326 }
327 }
328
329 #[test]
330 fn test_can_generate_keys_through_ceremony() -> anyhow::Result<()> {
331 let circuit = TestCircuit {
332 x: F::one(),
333 y: F::one(),
334 x_plus_y: F::from(2u64),
335 };
336 let cs = ConstraintSystem::new_ref();
337 cs.set_optimization_goal(r1cs::OptimizationGoal::Constraints);
338 cs.set_mode(r1cs::SynthesisMode::Setup);
339 circuit.generate_constraints(cs.clone())?;
340 cs.finalize();
341
342 let matrices = cs
343 .to_matrices()
344 .ok_or_else(|| anyhow!("Failed to generate constraint matrices."))?;
345
346 let mut rng = rand_chacha::ChaChaRng::seed_from_u64(1776);
347
348 let degree = circuit_degree(&matrices)?;
349 let phase1root = Phase1CRSElements::root(degree);
350
351 let phase1contribution = Phase1Contribution::make(&mut rng, phase1root.hash(), &phase1root);
353 let phase1contribution = Phase1Contribution::make(
354 &mut rng,
355 phase1contribution.hash(),
356 &phase1contribution.new_elements,
357 );
358
359 let (extra, phase2root) = transition(&phase1contribution.new_elements, &matrices)?;
360
361 let phase2contribution = Phase2Contribution::make(&mut rng, phase2root.hash(), &phase2root);
362 let phase2contribution = Phase2Contribution::make(
363 &mut rng,
364 phase2contribution.hash(),
365 &phase2contribution.new_elements,
366 );
367
368 let pk = combine(
369 &matrices,
370 &phase1contribution.new_elements,
371 &phase2contribution.new_elements,
372 &extra,
373 );
374
375 let proof = Groth16::<Bls12_377, LibsnarkReduction>::prove(&pk, circuit, &mut OsRng)
376 .map_err(|err| anyhow!(err))?;
377
378 let public_inputs = vec![circuit.x_plus_y];
379 let ok = Groth16::<Bls12_377, LibsnarkReduction>::verify(&pk.vk, &public_inputs, &proof)?;
380 assert!(ok);
381
382 Ok(())
383 }
384}