1use super::{
9 challenge_var_to_fp_elem_var, poly, BatchProofVar, ChallengesFpElemVar, ChallengesVar,
10 NonNativeFieldInfo, PcsInfoVar, ProofEvaluationsVar, ScalarsAndBasesVar, VerifyingKeyVar,
11};
12use crate::{
13 circuit::transcript::RescueTranscriptVar, constants::EXTRA_TRANSCRIPT_MSG_LABEL,
14 errors::PlonkError,
15};
16use ark_ec::{
17 pairing::Pairing,
18 short_weierstrass::{Affine, SWCurveConfig as SWParam},
19};
20use ark_ff::PrimeField;
21use ark_poly::{EvaluationDomain, Radix2EvaluationDomain};
22use ark_std::{format, vec, vec::Vec};
23use jf_relation::{
24 gadgets::{
25 ecc::{PointVariable, SWToTEConParam},
26 ultraplonk::mod_arith::{FpElem, FpElemVar},
27 },
28 Circuit, CircuitError,
29 CircuitError::ParameterError,
30 PlonkCircuit,
31};
32use jf_rescue::RescueParameter;
33use jf_utils::{bytes_to_field_elements, field_switching};
34
35pub(super) fn aggregate_poly_commitments_circuit<E, F>(
54 circuit: &mut PlonkCircuit<F>,
55 vks: &[&VerifyingKeyVar<E>],
56 challenges: &ChallengesFpElemVar<F>,
57 poly_evals: &[FpElemVar<F>; 3],
58 batch_proof: &BatchProofVar<F>,
59 alpha_bases: &[FpElemVar<F>],
60 non_native_field_info: NonNativeFieldInfo<F>,
61) -> Result<(ScalarsAndBasesVar<F>, Vec<FpElemVar<F>>), CircuitError>
62where
63 E: Pairing<BaseField = F>,
64 F: PrimeField,
65{
66 if vks.len() != batch_proof.len() {
67 return Err(ParameterError(format!(
68 "the number of verification keys {} != the number of instances {}",
69 vks.len(),
70 batch_proof.len()
71 )));
72 }
73
74 let mut scalars_and_bases = poly::linearization_scalars_and_bases_circuit(
76 circuit,
77 vks,
78 challenges,
79 poly_evals,
80 batch_proof,
81 alpha_bases,
82 non_native_field_info,
83 )?;
84 let mut v_base = challenges.v;
86 let mut uv_base = challenges.u;
88
89 let mut v_and_uv_basis = vec![];
91
92 for (i, vk) in vks.iter().enumerate() {
93 for &poly_comm in batch_proof.wires_poly_comms_vec[i].iter() {
96 v_and_uv_basis.push(v_base);
97 add_poly_comm_circuit(
98 circuit,
99 &mut scalars_and_bases,
100 &mut v_base,
101 &poly_comm,
102 &challenges.v,
103 &non_native_field_info.modulus_fp_elem,
104 )?;
105 }
106 let num_wire_types = batch_proof.wires_poly_comms_vec[i].len();
108 for &poly_comm in vk.sigma_comms.iter().take(num_wire_types - 1) {
109 v_and_uv_basis.push(v_base);
110 add_poly_comm_circuit(
111 circuit,
112 &mut scalars_and_bases,
113 &mut v_base,
114 &poly_comm,
115 &challenges.v,
116 &non_native_field_info.modulus_fp_elem,
117 )?;
118 }
119
120 v_and_uv_basis.push(uv_base);
122 add_poly_comm_circuit(
123 circuit,
124 &mut scalars_and_bases,
125 &mut uv_base,
126 &batch_proof.prod_perm_poly_comms_vec[i],
127 &challenges.v,
128 &non_native_field_info.modulus_fp_elem,
129 )?;
130 }
131
132 Ok((scalars_and_bases, v_and_uv_basis))
133}
134
135pub(super) fn aggregate_evaluations_circuit<F>(
139 circuit: &mut PlonkCircuit<F>,
140 lin_poly_constant: &FpElemVar<F>,
141 poly_evals_vec: &[ProofEvaluationsVar<F>],
142 non_native_field_info: NonNativeFieldInfo<F>,
143 buffer_v_and_uv_basis: &[FpElemVar<F>],
144) -> Result<FpElemVar<F>, CircuitError>
145where
146 F: PrimeField,
147{
148 let mut result = circuit.mod_negate(lin_poly_constant, &non_native_field_info.modulus_in_f)?;
149 let mut v_and_uv_basis = buffer_v_and_uv_basis.iter();
150
151 for poly_evals in poly_evals_vec.iter() {
152 for wire_eval in poly_evals.wires_evals.iter() {
154 add_pcs_eval_circuit(
155 circuit,
156 &mut result,
157 v_and_uv_basis
158 .next()
159 .ok_or(PlonkError::IteratorOutOfRange)?,
160 wire_eval,
161 &non_native_field_info.modulus_fp_elem,
162 )?;
163 }
164 for sigma_eval in poly_evals.wire_sigma_evals.iter() {
165 add_pcs_eval_circuit(
166 circuit,
167 &mut result,
168 v_and_uv_basis
169 .next()
170 .ok_or(PlonkError::IteratorOutOfRange)?,
171 sigma_eval,
172 &non_native_field_info.modulus_fp_elem,
173 )?;
174 }
175 add_pcs_eval_circuit(
177 circuit,
178 &mut result,
179 v_and_uv_basis
180 .next()
181 .ok_or(PlonkError::IteratorOutOfRange)?,
182 &poly_evals.perm_next_eval,
183 &non_native_field_info.modulus_fp_elem,
184 )?;
185 }
186 if v_and_uv_basis.next().is_some() {
188 Err(PlonkError::IteratorOutOfRange)?;
189 }
190 Ok(result)
191}
192
193pub(super) fn compute_challenges_vars<E, F, P>(
196 circuit: &mut PlonkCircuit<F>,
197 verify_keys: &[&VerifyingKeyVar<E>],
198 public_inputs: &[&[FpElemVar<F>]],
199 batch_proof: &BatchProofVar<F>,
200 extra_transcript_init_msg: &Option<Vec<u8>>,
201 non_native_field_info: NonNativeFieldInfo<F>,
202) -> Result<ChallengesFpElemVar<F>, CircuitError>
203where
204 E: Pairing<BaseField = F, G1Affine = Affine<P>>,
205 F: RescueParameter + SWToTEConParam,
206 P: SWParam<BaseField = F>,
207{
208 if verify_keys.len() != batch_proof.len() || verify_keys.len() != public_inputs.len() {
209 return Err(ParameterError(format!(
210 "the number of verification keys = {}; the number of instances = {}; the number of public inputs = {}",
211 verify_keys.len(),
212 batch_proof.len(),
213 public_inputs.len(),
214 )));
215 }
216 let mut transcript_var = RescueTranscriptVar::new(circuit);
217 if let Some(msg) = extra_transcript_init_msg {
218 let msg_fs = bytes_to_field_elements::<_, F>(msg.as_ref());
219 let msg_vars = msg_fs
220 .iter()
221 .map(|x| circuit.create_variable(*x))
222 .collect::<Result<Vec<_>, _>>()?;
223 transcript_var.append_message_vars(EXTRA_TRANSCRIPT_MSG_LABEL, &msg_vars)?;
224 }
225 for (&vk, &pi) in verify_keys.iter().zip(public_inputs.iter()) {
226 transcript_var.append_vk_and_pub_input_vars::<E>(circuit, vk, pi)?;
227 }
228 for wires_poly_comms in batch_proof.wires_poly_comms_vec.iter() {
229 transcript_var.append_commitments_vars(b"witness_poly_comms", wires_poly_comms)?;
230 }
231
232 let beta = transcript_var.get_challenge_var::<E>(b"beta", circuit)?;
233 let gamma = transcript_var.get_challenge_var::<E>(b"gamma", circuit)?;
234 for prod_perm_poly_comm in batch_proof.prod_perm_poly_comms_vec.iter() {
235 transcript_var.append_commitment_var(b"perm_poly_comms", prod_perm_poly_comm)?;
236 }
237
238 let alpha = transcript_var.get_challenge_var::<E>(b"alpha", circuit)?;
239 transcript_var
240 .append_commitments_vars(b"quot_poly_comms", &batch_proof.split_quot_poly_comms)?;
241 let zeta = transcript_var.get_challenge_var::<E>(b"zeta", circuit)?;
242 for poly_evals in batch_proof.poly_evals_vec.iter() {
243 transcript_var.append_proof_evaluations_vars(circuit, poly_evals)?;
244 }
245
246 let v = transcript_var.get_challenge_var::<E>(b"v", circuit)?;
247 transcript_var.append_commitment_var(b"open_proof", &batch_proof.opening_proof)?;
248 transcript_var
249 .append_commitment_var(b"shifted_open_proof", &batch_proof.shifted_opening_proof)?;
250 let u = transcript_var.get_challenge_var::<E>(b"u", circuit)?;
251
252 let challenge_var = ChallengesVar {
254 alpha,
255 beta,
256 gamma,
257 zeta,
258 v,
259 u,
260 };
261
262 let challenge_fp_elem_var =
263 challenge_var_to_fp_elem_var(circuit, &challenge_var, &non_native_field_info)?;
264 Ok(challenge_fp_elem_var)
265}
266
267#[allow(clippy::too_many_arguments)]
269pub(super) fn prepare_pcs_info_var<E, F, P>(
270 circuit: &mut PlonkCircuit<F>,
271 verify_keys: &[&VerifyingKeyVar<E>],
272 public_inputs: &[&[FpElemVar<F>]],
273 batch_proof: &BatchProofVar<F>,
274 extra_transcript_init_msg: &Option<Vec<u8>>,
275
276 domain: Radix2EvaluationDomain<E::ScalarField>,
277 non_native_field_info: NonNativeFieldInfo<F>,
278) -> Result<PcsInfoVar<F>, CircuitError>
279where
280 E: Pairing<BaseField = F, G1Affine = Affine<P>>,
281 F: RescueParameter + SWToTEConParam,
282 P: SWParam<BaseField = F>,
283{
284 if verify_keys.len() != batch_proof.len() || verify_keys.len() != public_inputs.len() {
285 return Err(ParameterError(format!(
286 "the number of verification keys = {}; the number of instances = {}; the number of public inputs = {}",
287 verify_keys.len(),
288 batch_proof.len(),
289 public_inputs.len(),
290 )));
291 }
292
293 for (i, (&pub_input, &vk)) in public_inputs.iter().zip(verify_keys.iter()).enumerate() {
294 if pub_input.len() != vk.num_inputs {
295 return Err(ParameterError(format!(
296 "the circuit pub_input length {} != the {}-th verification key's pub_input length {}",
297 pub_input.len(),
298 i,
299 vk.num_inputs,
300 )));
301 }
302
303 if vk.domain_size != domain.size() {
304 return Err(ParameterError(format!(
305 "the domain size {} of the {}-th verification key is different from {}",
306 vk.domain_size,
307 i,
308 domain.size(),
309 )));
310 }
311 }
312
313 let challenges_fp_elem_var = compute_challenges_vars::<E, F, P>(
315 circuit,
316 verify_keys,
317 public_inputs,
318 batch_proof,
319 extra_transcript_init_msg,
320 non_native_field_info,
321 )?;
322
323 let alpha_bases = compute_alpha_basis(
325 circuit,
326 challenges_fp_elem_var.alphas[2],
327 verify_keys.len(),
328 non_native_field_info,
329 )?;
330
331 let evals = poly::evaluate_poly_helper::<E, F>(
333 circuit,
334 &challenges_fp_elem_var.zeta,
335 domain.size(),
336 non_native_field_info,
337 )?;
338
339 let lin_poly_constant = poly::compute_lin_poly_constant_term_circuit(
341 circuit,
342 domain.size(),
343 &challenges_fp_elem_var,
344 verify_keys,
345 public_inputs,
346 batch_proof,
347 &evals,
348 &alpha_bases,
349 non_native_field_info,
350 )?;
351
352 let (comm_scalars_and_bases, v_and_uv_basis) = aggregate_poly_commitments_circuit(
354 circuit,
355 verify_keys,
356 &challenges_fp_elem_var,
357 &evals,
358 batch_proof,
359 &alpha_bases,
360 non_native_field_info,
361 )?;
362 let eval = aggregate_evaluations_circuit::<_>(
363 circuit,
364 &lin_poly_constant,
365 &batch_proof.poly_evals_vec,
366 non_native_field_info,
367 &v_and_uv_basis,
368 )?;
369
370 let group_gen = FpElem::new(
372 &field_switching(&domain.group_gen),
373 non_native_field_info.m,
374 non_native_field_info.two_power_m,
375 )?;
376 let next_point = circuit.mod_mul_constant(
377 &challenges_fp_elem_var.zeta,
378 &group_gen,
379 &non_native_field_info.modulus_fp_elem,
380 )?;
381
382 Ok(PcsInfoVar {
383 u: challenges_fp_elem_var.u,
384 eval_point: challenges_fp_elem_var.zeta,
385 next_eval_point: next_point,
386 eval,
387 comm_scalars_and_bases,
388 opening_proof: batch_proof.opening_proof,
389 shifted_opening_proof: batch_proof.shifted_opening_proof,
390 })
391}
392
393#[inline]
396fn add_poly_comm_circuit<F>(
397 circuit: &mut PlonkCircuit<F>,
398 scalars_and_bases: &mut ScalarsAndBasesVar<F>,
399 random_combiner: &mut FpElemVar<F>,
400 comm: &PointVariable,
401 r: &FpElemVar<F>,
402 p: &FpElem<F>,
403) -> Result<(), CircuitError>
404where
405 F: PrimeField,
406{
407 scalars_and_bases.scalars.push(*random_combiner);
408 scalars_and_bases.bases.push(*comm);
409 *random_combiner = circuit.mod_mul(random_combiner, r, p)?;
410 Ok(())
411}
412
413#[inline]
416fn add_pcs_eval_circuit<F>(
417 circuit: &mut PlonkCircuit<F>,
418 result: &mut FpElemVar<F>,
419 random_combiner: &FpElemVar<F>,
420 eval: &FpElemVar<F>,
421 p: &FpElem<F>,
422) -> Result<(), CircuitError>
423where
424 F: PrimeField,
425{
426 let tmp = circuit.mod_mul(random_combiner, eval, p)?;
427 *result = circuit.mod_add(result, &tmp, p)?;
428
429 Ok(())
430}
431
432#[inline]
434fn compute_alpha_basis<F: PrimeField>(
435 circuit: &mut PlonkCircuit<F>,
436 alpha_to_3: FpElemVar<F>,
437 len: usize,
438 non_native_field_info: NonNativeFieldInfo<F>,
439) -> Result<Vec<FpElemVar<F>>, CircuitError> {
440 let mut res = Vec::new();
441 let mut alpha_base_elem_var = FpElemVar::<F>::one(
442 circuit,
443 non_native_field_info.m,
444 non_native_field_info.two_power_m,
445 );
446 res.push(alpha_base_elem_var);
447 for _ in 0..len - 1 {
448 alpha_base_elem_var = circuit.mod_mul(
449 &alpha_base_elem_var,
450 &alpha_to_3,
451 &non_native_field_info.modulus_fp_elem,
452 )?;
453 res.push(alpha_base_elem_var);
454 }
455 Ok(res)
456}
457
458#[cfg(test)]
459mod test {
460 use super::*;
461 use crate::{
462 proof_system::{
463 batch_arg::{new_mergeable_circuit_for_test, BatchArgument},
464 structs::VerifyingKey,
465 PlonkKzgSnark, UniversalSNARK,
466 },
467 transcript::{PlonkTranscript, RescueTranscript},
468 };
469 use ark_bls12_377::Bls12_377;
470 use ark_ec::{short_weierstrass::SWCurveConfig, twisted_edwards::TECurveConfig};
471 use ark_ff::BigInteger as _;
472 use ark_std::{vec, UniformRand};
473 use jf_relation::MergeableCircuitType;
474 use jf_utils::test_rng;
475
476 const RANGE_BIT_LEN_FOR_TEST: usize = 16;
477 #[test]
478 fn test_compute_challenges_vars_circuit() -> Result<(), CircuitError> {
479 test_compute_challenges_vars_circuit_helper::<Bls12_377, _, _, RescueTranscript<_>>()
480 }
481
482 fn test_compute_challenges_vars_circuit_helper<E, F, P, T>() -> Result<(), CircuitError>
483 where
484 E: Pairing<BaseField = F, G1Affine = Affine<P>>,
485 F: RescueParameter + SWToTEConParam,
486 P: SWCurveConfig<BaseField = F> + TECurveConfig,
487 T: PlonkTranscript<F>,
488 {
489 let rng = &mut test_rng();
491 let n = 128;
492 let max_degree = n + 2;
493 let srs = PlonkKzgSnark::<E>::universal_setup_for_testing(max_degree, rng)?;
494
495 let shared_public_input = E::ScalarField::rand(rng);
497 let mut instances_type_a = vec![];
498 let mut instances_type_b = vec![];
499 for i in 32..50 {
500 let circuit = new_mergeable_circuit_for_test::<E>(
501 shared_public_input,
502 i,
503 MergeableCircuitType::TypeA,
504 )?;
505 let instance =
506 BatchArgument::setup_instance(&srs, circuit, MergeableCircuitType::TypeA)?;
507 instances_type_a.push(instance);
508
509 let circuit = new_mergeable_circuit_for_test::<E>(
510 shared_public_input,
511 i,
512 MergeableCircuitType::TypeB,
513 )?;
514 let instance =
515 BatchArgument::setup_instance(&srs, circuit, MergeableCircuitType::TypeB)?;
516 instances_type_b.push(instance);
517 }
518 let batch_proof =
520 BatchArgument::batch_prove::<_, T>(rng, &instances_type_a, &instances_type_b)?;
521
522 let vks_type_a: Vec<&VerifyingKey<E>> = instances_type_a
524 .iter()
525 .map(|pred| pred.verify_key_ref())
526 .collect();
527 let vks_type_b: Vec<&VerifyingKey<E>> = instances_type_b
528 .iter()
529 .map(|pred| pred.verify_key_ref())
530 .collect();
531 let merged_vks = BatchArgument::aggregate_verify_keys(&vks_type_a, &vks_type_b)?;
532 assert!(BatchArgument::aggregate_verify_keys(&vks_type_a[1..], &vks_type_b).is_err());
534
535 let open_key_ref = &vks_type_a[0].open_key;
537 let beta_g_ref = &srs.powers_of_g[1];
538 let blinding_factor = E::ScalarField::rand(rng);
539 let (inner1, inner2) = BatchArgument::partial_verify::<T>(
540 beta_g_ref,
541 &open_key_ref.g,
542 &merged_vks,
543 &[shared_public_input],
544 &batch_proof,
545 blinding_factor,
546 )?;
547 assert!(BatchArgument::decide(open_key_ref, inner1, inner2)?);
548
549 let mut circuit = PlonkCircuit::<E::BaseField>::new_ultra_plonk(RANGE_BIT_LEN_FOR_TEST);
553
554 let m = 128;
556 let two_power_m = Some(E::BaseField::from(2u8).pow([m as u64]));
557
558 let fr_modulus_bits = <E::ScalarField as PrimeField>::MODULUS.to_bytes_le();
559 let modulus_in_f = F::from_le_bytes_mod_order(&fr_modulus_bits);
560 let modulus_fp_elem = FpElem::new(&modulus_in_f, m, two_power_m)?;
561 let non_native_field_info = NonNativeFieldInfo::<F> {
562 m,
563 two_power_m,
564 modulus_in_f,
565 modulus_fp_elem,
566 };
567
568 let vk_vars = merged_vks
570 .iter()
571 .map(|x| VerifyingKeyVar::new(&mut circuit, x))
572 .collect::<Result<Vec<_>, _>>()?;
573 let merged_vks_ref: Vec<&VerifyingKeyVar<E>> = vk_vars.iter().collect();
574
575 let shared_public_input_var =
576 circuit.create_public_variable(field_switching(&shared_public_input))?;
577 let shared_public_input_fp_elem_var =
578 [FpElemVar::new_unchecked(&mut circuit, shared_public_input_var, m, two_power_m)?; 1];
579 let shared_public_input_fp_elem_var_ref = shared_public_input_fp_elem_var.as_ref();
580
581 let batch_proof_vars = batch_proof.create_variables(&mut circuit, m, two_power_m)?;
583
584 let _challenges_fp_elem_var = compute_challenges_vars::<E, F, P>(
585 &mut circuit,
586 &merged_vks_ref,
587 &[shared_public_input_fp_elem_var_ref; 18],
588 &batch_proof_vars,
589 &None,
590 non_native_field_info,
591 )?;
592
593 let tmp = field_switching(&shared_public_input);
594 let public_inputs = [tmp];
595
596 assert!(
597 circuit
598 .check_circuit_satisfiability(public_inputs.as_ref())
599 .is_ok(),
600 "{:?}",
601 circuit.check_circuit_satisfiability(public_inputs.as_ref())
602 );
603
604 Ok(())
605 }
606}