1use crate::traits::evm_serde::EvmCompatSerde;
3use crate::{
4 constants::{BN_LIMB_WIDTH, BN_N_LIMBS},
5 digest::{DigestComputer, SimpleDigestible},
6 errors::NovaError,
7 gadgets::{
8 nonnative::{bignat::nat_to_limbs, util::f_to_nat},
9 utils::scalar_as_base,
10 },
11 traits::{
12 commitment::CommitmentEngineTrait, AbsorbInRO2Trait, AbsorbInROTrait, Engine, ROTrait,
13 TranscriptReprTrait,
14 },
15 Commitment, CommitmentKey, DerandKey, CE,
16};
17use core::cmp::max;
18use ff::Field;
19use once_cell::sync::OnceCell;
20use rand_core::OsRng;
21use rayon::prelude::*;
22use serde::{Deserialize, Serialize};
23use serde_with::serde_as;
24
25mod sparse;
26pub use sparse::SparseMatrix;
27
28#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30pub struct R1CSShape<E: Engine> {
31 pub(crate) num_cons: usize,
32 pub(crate) num_vars: usize,
33 pub(crate) num_io: usize,
34 pub(crate) A: SparseMatrix<E::Scalar>,
35 pub(crate) B: SparseMatrix<E::Scalar>,
36 pub(crate) C: SparseMatrix<E::Scalar>,
37 #[serde(skip, default = "OnceCell::new")]
38 pub(crate) digest: OnceCell<E::Scalar>,
39}
40
41impl<E: Engine> SimpleDigestible for R1CSShape<E> {}
42
43#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
45pub struct R1CSWitness<E: Engine> {
46 pub(crate) W: Vec<E::Scalar>,
47 pub(crate) r_W: E::Scalar,
48}
49
50#[serde_as]
52#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
53#[serde(bound = "")]
54pub struct R1CSInstance<E: Engine> {
55 pub(crate) comm_W: Commitment<E>,
56 #[serde_as(as = "Vec<EvmCompatSerde>")]
57 pub(crate) X: Vec<E::Scalar>,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
62pub struct RelaxedR1CSWitness<E: Engine> {
63 pub(crate) W: Vec<E::Scalar>,
64 pub(crate) r_W: E::Scalar,
65 pub(crate) E: Vec<E::Scalar>,
66 pub(crate) r_E: E::Scalar,
67}
68
69#[serde_as]
71#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(bound = "")]
73pub struct RelaxedR1CSInstance<E: Engine> {
74 pub(crate) comm_W: Commitment<E>,
75 pub(crate) comm_E: Commitment<E>,
76 #[serde_as(as = "Vec<EvmCompatSerde>")]
77 pub(crate) X: Vec<E::Scalar>,
78 #[serde_as(as = "EvmCompatSerde")]
79 pub(crate) u: E::Scalar,
80}
81
82pub type CommitmentKeyHint<E> = dyn Fn(&R1CSShape<E>) -> usize;
84
85impl<E: Engine> R1CSShape<E> {
86 pub fn num_cons(&self) -> usize {
90 self.num_cons
91 }
92
93 pub fn num_vars(&self) -> usize {
97 self.num_vars
98 }
99
100 pub fn num_io(&self) -> usize {
104 self.num_io
105 }
106
107 pub fn A(&self) -> &SparseMatrix<E::Scalar> {
109 &self.A
110 }
111
112 pub fn B(&self) -> &SparseMatrix<E::Scalar> {
114 &self.B
115 }
116
117 pub fn C(&self) -> &SparseMatrix<E::Scalar> {
119 &self.C
120 }
121
122 pub fn new(
124 num_cons: usize,
125 num_vars: usize,
126 num_io: usize,
127 A: SparseMatrix<E::Scalar>,
128 B: SparseMatrix<E::Scalar>,
129 C: SparseMatrix<E::Scalar>,
130 ) -> Result<R1CSShape<E>, NovaError> {
131 let is_valid = |num_cons: usize,
132 num_vars: usize,
133 num_io: usize,
134 M: &SparseMatrix<E::Scalar>|
135 -> Result<Vec<()>, NovaError> {
136 M.iter()
137 .map(|(row, col, _val)| {
138 if row >= num_cons || col > num_io + num_vars {
139 Err(NovaError::InvalidIndex)
140 } else {
141 Ok(())
142 }
143 })
144 .collect::<Result<Vec<()>, NovaError>>()
145 };
146
147 is_valid(num_cons, num_vars, num_io, &A)?;
148 is_valid(num_cons, num_vars, num_io, &B)?;
149 is_valid(num_cons, num_vars, num_io, &C)?;
150
151 Ok(R1CSShape {
152 num_cons,
153 num_vars,
154 num_io,
155 A,
156 B,
157 C,
158 digest: OnceCell::new(),
159 })
160 }
161
162 fn compute_max_ck_size(shapes: &[&R1CSShape<E>], ck_floors: &[&CommitmentKeyHint<E>]) -> usize {
170 assert!(
171 !shapes.is_empty(),
172 "commitment_key requires at least one R1CS shape"
173 );
174 assert_eq!(
175 shapes.len(),
176 ck_floors.len(),
177 "shapes and ck_floors must have the same length"
178 );
179
180 shapes
181 .iter()
182 .zip(ck_floors.iter())
183 .map(|(shape, ck_floor)| {
184 let num_cons = shape.num_cons;
185 let num_vars = shape.num_vars;
186 let ck_hint = ck_floor(shape);
187 max(max(num_cons, num_vars), ck_hint)
188 })
189 .max()
190 .unwrap() }
192
193 pub fn commitment_key(
214 shapes: &[&R1CSShape<E>],
215 ck_floors: &[&CommitmentKeyHint<E>],
216 ) -> Result<CommitmentKey<E>, crate::errors::NovaError> {
217 let max_size = Self::compute_max_ck_size(shapes, ck_floors);
218 E::CE::setup(b"ck", max_size)
219 }
220
221 #[cfg(feature = "io")]
244 pub fn commitment_key_from_ptau_dir(
245 shapes: &[&R1CSShape<E>],
246 ck_floors: &[&CommitmentKeyHint<E>],
247 ptau_dir: &std::path::Path,
248 ) -> Result<CommitmentKey<E>, crate::errors::NovaError>
249 where
250 E::GE: crate::provider::traits::PairingGroup,
251 {
252 use std::fs::File;
253 use std::io::BufReader;
254
255 if shapes.is_empty() {
256 return Err(crate::errors::NovaError::PtauFileError(
257 "commitment_key_from_ptau_dir requires at least one R1CS shape".to_string(),
258 ));
259 }
260 if shapes.len() != ck_floors.len() {
261 return Err(crate::errors::NovaError::PtauFileError(format!(
262 "Mismatched lengths: shapes.len() = {}, ck_floors.len() = {}",
263 shapes.len(),
264 ck_floors.len(),
265 )));
266 }
267
268 let max_size = Self::compute_max_ck_size(shapes, ck_floors);
269
270 let min_power = max_size.next_power_of_two().trailing_zeros();
272
273 let mut ptau_path = None;
280 for power in min_power..=crate::provider::ptau::MAX_PPOT_POWER {
281 let pruned = ptau_dir.join(format!("ppot_pruned_{:02}.ptau", power));
283 if pruned.exists() {
284 ptau_path = Some(pruned);
285 break;
286 }
287
288 let original = if power == crate::provider::ptau::MAX_PPOT_POWER {
290 ptau_dir.join("ppot_0080_final.ptau")
291 } else {
292 ptau_dir.join(format!("ppot_0080_{:02}.ptau", power))
293 };
294 if original.exists() {
295 ptau_path = Some(original);
296 break;
297 }
298 }
299
300 let ptau_path = ptau_path.ok_or_else(|| {
301 crate::errors::NovaError::PtauFileError(format!(
302 "No suitable ptau file found in {:?}. Need at least {} generators (power >= {}). \
303 Expected files named ppot_pruned_XX.ptau or ppot_0080_XX.ptau where XX >= {:02}",
304 ptau_dir, max_size, min_power, min_power
305 ))
306 })?;
307
308 let file = File::open(&ptau_path).map_err(|e| {
309 crate::errors::NovaError::PtauFileError(format!(
310 "Failed to open {}: {}",
311 ptau_path.display(),
312 e
313 ))
314 })?;
315 let mut reader = BufReader::new(file);
316
317 E::CE::load_setup(&mut reader, b"ck", max_size)
318 .map_err(|e| crate::errors::NovaError::PtauFileError(e.to_string()))
319 }
320
321 pub fn digest(&self) -> E::Scalar {
323 self
324 .digest
325 .get_or_try_init(|| DigestComputer::new(self).digest())
326 .cloned()
327 .expect("Failure in retrieving digest")
328 }
329
330 #[inline]
333 pub(crate) fn is_regular_shape(&self) -> bool {
334 let cons_valid = self.num_cons.next_power_of_two() == self.num_cons;
335 let vars_valid = self.num_vars.next_power_of_two() == self.num_vars;
336 let io_lt_vars = self.num_io < self.num_vars;
337 cons_valid && vars_valid && io_lt_vars
338 }
339
340 pub fn multiply_vec(
342 &self,
343 z: &[E::Scalar],
344 ) -> Result<(Vec<E::Scalar>, Vec<E::Scalar>, Vec<E::Scalar>), NovaError> {
345 if z.len() != self.num_io + self.num_vars + 1 {
346 return Err(NovaError::InvalidWitnessLength);
347 }
348
349 let (Az, (Bz, Cz)) = rayon::join(
350 || self.A.multiply_vec(z),
351 || rayon::join(|| self.B.multiply_vec(z), || self.C.multiply_vec(z)),
352 );
353
354 Ok((Az, Bz, Cz))
355 }
356
357 pub fn is_sat_relaxed(
359 &self,
360 ck: &CommitmentKey<E>,
361 U: &RelaxedR1CSInstance<E>,
362 W: &RelaxedR1CSWitness<E>,
363 ) -> Result<(), NovaError> {
364 assert_eq!(W.W.len(), self.num_vars);
365 assert_eq!(W.E.len(), self.num_cons);
366 assert_eq!(U.X.len(), self.num_io);
367
368 let res_eq = {
370 let z = [W.W.clone(), vec![U.u], U.X.clone()].concat();
371 let (Az, Bz, Cz) = self.multiply_vec(&z)?;
372 assert_eq!(Az.len(), self.num_cons);
373 assert_eq!(Bz.len(), self.num_cons);
374 assert_eq!(Cz.len(), self.num_cons);
375
376 (0..self.num_cons).all(|i| Az[i] * Bz[i] == U.u * Cz[i] + W.E[i])
377 };
378
379 let res_comm = {
381 let (comm_W, comm_E) = rayon::join(
382 || CE::<E>::commit(ck, &W.W, &W.r_W),
383 || CE::<E>::commit(ck, &W.E, &W.r_E),
384 );
385 U.comm_W == comm_W && U.comm_E == comm_E
386 };
387
388 if !res_eq {
389 return Err(NovaError::UnSat {
390 reason: "Relaxed R1CS is unsatisfiable".to_string(),
391 });
392 }
393
394 if !res_comm {
395 return Err(NovaError::UnSat {
396 reason: "Invalid commitments".to_string(),
397 });
398 }
399
400 Ok(())
401 }
402
403 pub fn is_sat(
405 &self,
406 ck: &CommitmentKey<E>,
407 U: &R1CSInstance<E>,
408 W: &R1CSWitness<E>,
409 ) -> Result<(), NovaError> {
410 assert_eq!(W.W.len(), self.num_vars);
411 assert_eq!(U.X.len(), self.num_io);
412
413 let res_eq = {
415 let z = [W.W.clone(), vec![E::Scalar::ONE], U.X.clone()].concat();
416 let (Az, Bz, Cz) = self.multiply_vec(&z)?;
417 assert_eq!(Az.len(), self.num_cons);
418 assert_eq!(Bz.len(), self.num_cons);
419 assert_eq!(Cz.len(), self.num_cons);
420
421 (0..self.num_cons).all(|i| Az[i] * Bz[i] == Cz[i])
422 };
423
424 let res_comm = U.comm_W == CE::<E>::commit(ck, &W.W, &W.r_W);
426
427 if !res_eq {
428 return Err(NovaError::UnSat {
429 reason: "R1CS is unsatisfiable".to_string(),
430 });
431 }
432
433 if !res_comm {
434 return Err(NovaError::UnSat {
435 reason: "Invalid commitment".to_string(),
436 });
437 }
438
439 Ok(())
440 }
441
442 pub fn commit_T(
445 &self,
446 ck: &CommitmentKey<E>,
447 U1: &RelaxedR1CSInstance<E>,
448 W1: &RelaxedR1CSWitness<E>,
449 U2: &R1CSInstance<E>,
450 W2: &R1CSWitness<E>,
451 r_T: &E::Scalar,
452 ) -> Result<(Vec<E::Scalar>, Commitment<E>), NovaError> {
453 let Z1 = [W1.W.clone(), vec![U1.u], U1.X.clone()].concat();
454 let Z2 = [W2.W.clone(), vec![E::Scalar::ONE], U2.X.clone()].concat();
455
456 let Z = Z1
459 .into_par_iter()
460 .zip(Z2.into_par_iter())
461 .map(|(z1, z2)| z1 + z2)
462 .collect::<Vec<E::Scalar>>();
463 let u = U1.u + E::Scalar::ONE; let (AZ, BZ, CZ) = self.multiply_vec(&Z)?;
466
467 let T = AZ
468 .par_iter()
469 .zip(BZ.par_iter())
470 .zip(CZ.par_iter())
471 .zip(W1.E.par_iter())
472 .map(|(((az, bz), cz), e)| *az * *bz - u * *cz - *e)
473 .collect::<Vec<E::Scalar>>();
474
475 let comm_T = CE::<E>::commit(ck, &T, r_T);
476
477 Ok((T, comm_T))
478 }
479
480 pub fn commit_T_relaxed(
483 &self,
484 ck: &CommitmentKey<E>,
485 U1: &RelaxedR1CSInstance<E>,
486 W1: &RelaxedR1CSWitness<E>,
487 U2: &RelaxedR1CSInstance<E>,
488 W2: &RelaxedR1CSWitness<E>,
489 r_T: &E::Scalar,
490 ) -> Result<(Vec<E::Scalar>, Commitment<E>), NovaError> {
491 let Z1 = [W1.W.clone(), vec![U1.u], U1.X.clone()].concat();
492 let Z2 = [W2.W.clone(), vec![U2.u], U2.X.clone()].concat();
493
494 let Z = Z1
497 .into_par_iter()
498 .zip(Z2.into_par_iter())
499 .map(|(z1, z2)| z1 + z2)
500 .collect::<Vec<E::Scalar>>();
501 let u = U1.u + U2.u;
502
503 let (AZ, BZ, CZ) = self.multiply_vec(&Z)?;
504
505 let T = AZ
506 .par_iter()
507 .zip(BZ.par_iter())
508 .zip(CZ.par_iter())
509 .zip(W1.E.par_iter())
510 .zip(W2.E.par_iter())
511 .map(|((((az, bz), cz), e1), e2)| *az * *bz - u * *cz - *e1 - *e2)
512 .collect::<Vec<E::Scalar>>();
513
514 let comm_T = CE::<E>::commit(ck, &T, r_T);
515
516 Ok((T, comm_T))
517 }
518
519 pub fn pad(&self) -> Self {
522 if self.is_regular_shape() {
524 return self.clone();
525 }
526
527 let m = max(max(self.num_vars, self.num_cons), self.num_io).next_power_of_two();
529
530 if self.num_vars == m {
533 return R1CSShape {
534 num_cons: m,
535 num_vars: m,
536 num_io: self.num_io,
537 A: self.A.clone(),
538 B: self.B.clone(),
539 C: self.C.clone(),
540 digest: OnceCell::new(),
541 };
542 }
543
544 let num_vars_padded = m;
546 let num_cons_padded = m;
547
548 let apply_pad = |mut M: SparseMatrix<E::Scalar>| -> SparseMatrix<E::Scalar> {
549 M.indices.par_iter_mut().for_each(|c| {
550 if *c >= self.num_vars {
551 *c += num_vars_padded - self.num_vars
552 }
553 });
554
555 M.cols += num_vars_padded - self.num_vars;
556
557 let ex = {
558 let nnz = M.indptr.last().unwrap();
559 vec![*nnz; num_cons_padded - self.num_cons]
560 };
561 M.indptr.extend(ex);
562 M
563 };
564
565 let A_padded = apply_pad(self.A.clone());
566 let B_padded = apply_pad(self.B.clone());
567 let C_padded = apply_pad(self.C.clone());
568
569 R1CSShape {
570 num_cons: num_cons_padded,
571 num_vars: num_vars_padded,
572 num_io: self.num_io,
573 A: A_padded,
574 B: B_padded,
575 C: C_padded,
576 digest: OnceCell::new(),
577 }
578 }
579
580 pub fn pad_nonsquare(&self) -> Self {
587 if self.is_regular_shape() {
588 return self.clone();
589 }
590
591 let num_vars_padded = max(self.num_vars, self.num_io + 1).next_power_of_two();
592 let num_cons_padded = self.num_cons.next_power_of_two();
593
594 let apply_pad = |mut M: SparseMatrix<E::Scalar>| -> SparseMatrix<E::Scalar> {
595 if num_vars_padded > self.num_vars {
596 M.indices.par_iter_mut().for_each(|c| {
597 if *c >= self.num_vars {
598 *c += num_vars_padded - self.num_vars
599 }
600 });
601 M.cols += num_vars_padded - self.num_vars;
602 }
603
604 if num_cons_padded > self.num_cons {
605 let ex = {
606 let nnz = M.indptr.last().unwrap();
607 vec![*nnz; num_cons_padded - self.num_cons]
608 };
609 M.indptr.extend(ex);
610 }
611 M
612 };
613
614 let A_padded = apply_pad(self.A.clone());
615 let B_padded = apply_pad(self.B.clone());
616 let C_padded = apply_pad(self.C.clone());
617
618 R1CSShape {
619 num_cons: num_cons_padded,
620 num_vars: num_vars_padded,
621 num_io: self.num_io,
622 A: A_padded,
623 B: B_padded,
624 C: C_padded,
625 digest: OnceCell::new(),
626 }
627 }
628
629 pub fn sample_random_instance_witness(
631 &self,
632 ck: &CommitmentKey<E>,
633 ) -> Result<(RelaxedR1CSInstance<E>, RelaxedR1CSWitness<E>), NovaError> {
634 let Z = (0..self.num_vars + self.num_io + 1)
636 .into_par_iter()
637 .map(|_| E::Scalar::random(&mut OsRng))
638 .collect::<Vec<E::Scalar>>();
639
640 let r_W = E::Scalar::random(&mut OsRng);
641 let r_E = E::Scalar::random(&mut OsRng);
642
643 let u = Z[self.num_vars];
644
645 let (AZ, BZ, CZ) = self.multiply_vec(&Z)?;
647
648 let E = AZ
649 .par_iter()
650 .zip(BZ.par_iter())
651 .zip(CZ.par_iter())
652 .map(|((az, bz), cz)| *az * *bz - u * *cz)
653 .collect::<Vec<E::Scalar>>();
654
655 let (comm_W, comm_E) = rayon::join(
657 || CE::<E>::commit(ck, &Z[..self.num_vars], &r_W),
658 || CE::<E>::commit(ck, &E, &r_E),
659 );
660
661 Ok((
662 RelaxedR1CSInstance {
663 comm_W,
664 comm_E,
665 u,
666 X: Z[self.num_vars + 1..].to_vec(),
667 },
668 RelaxedR1CSWitness {
669 W: Z[..self.num_vars].to_vec(),
670 r_W,
671 E,
672 r_E,
673 },
674 ))
675 }
676}
677
678impl<E: Engine> R1CSWitness<E> {
679 pub fn new(S: &R1CSShape<E>, W: &[E::Scalar]) -> Result<R1CSWitness<E>, NovaError> {
681 let mut W = W.to_vec();
682 W.resize(S.num_vars, E::Scalar::ZERO);
683
684 Ok(R1CSWitness {
685 W,
686 r_W: E::Scalar::random(&mut OsRng),
687 })
688 }
689
690 pub fn W(&self) -> &[E::Scalar] {
694 &self.W
695 }
696
697 pub fn r_W(&self) -> &E::Scalar {
699 &self.r_W
700 }
701
702 pub fn commit(&self, ck: &CommitmentKey<E>) -> Commitment<E> {
704 CE::<E>::commit(ck, &self.W, &self.r_W)
705 }
706
707 pub fn pad(&self, S: &R1CSShape<E>) -> R1CSWitness<E> {
709 let mut W = self.W.clone();
710 W.extend(vec![E::Scalar::ZERO; S.num_vars - W.len()]);
711
712 Self { W, r_W: self.r_W }
713 }
714}
715
716impl<E: Engine> R1CSInstance<E> {
717 pub fn new(
719 S: &R1CSShape<E>,
720 comm_W: &Commitment<E>,
721 X: &[E::Scalar],
722 ) -> Result<R1CSInstance<E>, NovaError> {
723 if S.num_io != X.len() {
724 Err(NovaError::InvalidInputLength)
725 } else {
726 Ok(R1CSInstance {
727 comm_W: *comm_W,
728 X: X.to_owned(),
729 })
730 }
731 }
732
733 pub fn comm_W(&self) -> &Commitment<E> {
735 &self.comm_W
736 }
737
738 pub fn X(&self) -> &[E::Scalar] {
742 &self.X
743 }
744}
745
746impl<E: Engine> TranscriptReprTrait<E::GE> for R1CSInstance<E> {
747 fn to_transcript_bytes(&self) -> Vec<u8> {
748 [
749 self.comm_W.to_transcript_bytes(),
750 self.X.as_slice().to_transcript_bytes(),
751 ]
752 .concat()
753 }
754}
755
756impl<E: Engine> AbsorbInROTrait<E> for R1CSInstance<E> {
757 fn absorb_in_ro(&self, ro: &mut E::RO) {
758 self.comm_W.absorb_in_ro(ro);
759
760 for x in &self.X {
763 ro.absorb(scalar_as_base::<E>(*x));
764 }
765 }
766}
767
768impl<E: Engine> AbsorbInRO2Trait<E> for R1CSInstance<E> {
769 fn absorb_in_ro2(&self, ro: &mut E::RO2) {
770 self.comm_W.absorb_in_ro2(ro);
772
773 for x in &self.X {
774 ro.absorb(*x);
775 }
776 }
777}
778
779impl<E: Engine> RelaxedR1CSWitness<E> {
780 pub fn default(S: &R1CSShape<E>) -> RelaxedR1CSWitness<E> {
782 RelaxedR1CSWitness {
783 W: vec![E::Scalar::ZERO; S.num_vars],
784 r_W: E::Scalar::ZERO,
785 E: vec![E::Scalar::ZERO; S.num_cons],
786 r_E: E::Scalar::ZERO,
787 }
788 }
789
790 pub fn from_r1cs_witness(S: &R1CSShape<E>, witness: &R1CSWitness<E>) -> RelaxedR1CSWitness<E> {
792 RelaxedR1CSWitness {
793 W: witness.W.clone(),
794 r_W: witness.r_W,
795 E: vec![E::Scalar::ZERO; S.num_cons],
796 r_E: E::Scalar::ZERO,
797 }
798 }
799
800 pub fn new(
807 S: &R1CSShape<E>,
808 W: Vec<E::Scalar>,
809 r_W: E::Scalar,
810 E: Vec<E::Scalar>,
811 r_E: E::Scalar,
812 ) -> Result<RelaxedR1CSWitness<E>, NovaError> {
813 if W.len() != S.num_vars {
814 return Err(NovaError::InvalidWitnessLength);
815 }
816 if E.len() != S.num_cons {
817 return Err(NovaError::InvalidWitnessLength);
818 }
819 Ok(RelaxedR1CSWitness { W, r_W, E, r_E })
820 }
821
822 pub fn W(&self) -> &[E::Scalar] {
826 &self.W
827 }
828
829 pub fn E(&self) -> &[E::Scalar] {
833 &self.E
834 }
835
836 pub fn commit(&self, ck: &CommitmentKey<E>) -> (Commitment<E>, Commitment<E>) {
838 (
839 CE::<E>::commit(ck, &self.W, &self.r_W),
840 CE::<E>::commit(ck, &self.E, &self.r_E),
841 )
842 }
843
844 pub fn fold(
846 &self,
847 W2: &R1CSWitness<E>,
848 T: &[E::Scalar],
849 r_T: &E::Scalar,
850 r: &E::Scalar,
851 ) -> Result<RelaxedR1CSWitness<E>, NovaError> {
852 let (W1, r_W1, E1, r_E1) = (&self.W, &self.r_W, &self.E, &self.r_E);
853 let (W2, r_W2) = (&W2.W, &W2.r_W);
854
855 if W1.len() != W2.len() {
856 return Err(NovaError::InvalidWitnessLength);
857 }
858
859 let W = W1
860 .par_iter()
861 .zip(W2)
862 .map(|(a, b)| *a + *r * *b)
863 .collect::<Vec<E::Scalar>>();
864 let E = E1
865 .par_iter()
866 .zip(T)
867 .map(|(a, b)| *a + *r * *b)
868 .collect::<Vec<E::Scalar>>();
869
870 let r_W = *r_W1 + *r * r_W2;
871 let r_E = *r_E1 + *r * r_T;
872
873 Ok(RelaxedR1CSWitness { W, r_W, E, r_E })
874 }
875
876 pub fn fold_relaxed(
879 &self,
880 W2: &RelaxedR1CSWitness<E>,
881 T: &[E::Scalar],
882 r_T: &E::Scalar,
883 r: &E::Scalar,
884 ) -> Result<RelaxedR1CSWitness<E>, NovaError> {
885 let (W1, r_W1, E1, r_E1) = (&self.W, &self.r_W, &self.E, &self.r_E);
886 let (W2, r_W2, E2, r_E2) = (&W2.W, &W2.r_W, &W2.E, &W2.r_E);
887
888 if W1.len() != W2.len() {
889 return Err(NovaError::InvalidWitnessLength);
890 }
891
892 let W = W1
893 .par_iter()
894 .zip(W2)
895 .map(|(a, b)| *a + *r * *b)
896 .collect::<Vec<E::Scalar>>();
897 let E = E1
898 .par_iter()
899 .zip(T)
900 .zip(E2.par_iter())
901 .map(|((a, b), c)| *a + *r * *b + *r * *r * *c)
902 .collect::<Vec<E::Scalar>>();
903
904 let r_W = *r_W1 + *r * r_W2;
905 let r_E = *r_E1 + *r * r_T + *r * *r * *r_E2;
906
907 Ok(RelaxedR1CSWitness { W, r_W, E, r_E })
908 }
909
910 pub fn pad(&self, S: &R1CSShape<E>) -> RelaxedR1CSWitness<E> {
912 let mut W = self.W.clone();
913 W.extend(vec![E::Scalar::ZERO; S.num_vars - W.len()]);
914
915 let mut E = self.E.clone();
916 E.extend(vec![E::Scalar::ZERO; S.num_cons - E.len()]);
917
918 Self {
919 W,
920 r_W: self.r_W,
921 E,
922 r_E: self.r_E,
923 }
924 }
925
926 pub fn into_W_E(self) -> (Vec<E::Scalar>, Vec<E::Scalar>) {
928 (self.W, self.E)
929 }
930
931 pub fn derandomize(&self) -> (Self, E::Scalar, E::Scalar) {
933 (
934 RelaxedR1CSWitness {
935 W: self.W.clone(),
936 r_W: E::Scalar::ZERO,
937 E: self.E.clone(),
938 r_E: E::Scalar::ZERO,
939 },
940 self.r_W,
941 self.r_E,
942 )
943 }
944}
945
946impl<E: Engine> RelaxedR1CSInstance<E> {
947 pub fn default(_ck: &CommitmentKey<E>, S: &R1CSShape<E>) -> RelaxedR1CSInstance<E> {
949 let (comm_W, comm_E) = (Commitment::<E>::default(), Commitment::<E>::default());
950 RelaxedR1CSInstance {
951 comm_W,
952 comm_E,
953 u: E::Scalar::ZERO,
954 X: vec![E::Scalar::ZERO; S.num_io],
955 }
956 }
957
958 pub fn from_r1cs_instance(
960 ck: &CommitmentKey<E>,
961 S: &R1CSShape<E>,
962 instance: &R1CSInstance<E>,
963 ) -> RelaxedR1CSInstance<E> {
964 let mut r_instance = RelaxedR1CSInstance::default(ck, S);
965 r_instance.comm_W = instance.comm_W;
966 r_instance.u = E::Scalar::ONE;
967 r_instance.X.clone_from(&instance.X);
968
969 r_instance
970 }
971
972 pub fn comm_W(&self) -> &Commitment<E> {
976 &self.comm_W
977 }
978
979 pub fn comm_E(&self) -> &Commitment<E> {
983 &self.comm_E
984 }
985
986 pub fn X(&self) -> &[E::Scalar] {
990 &self.X
991 }
992
993 pub fn u(&self) -> E::Scalar {
997 self.u
998 }
999
1000 pub fn new(
1007 S: &R1CSShape<E>,
1008 comm_W: Commitment<E>,
1009 comm_E: Commitment<E>,
1010 u: E::Scalar,
1011 X: Vec<E::Scalar>,
1012 ) -> Result<RelaxedR1CSInstance<E>, NovaError> {
1013 if X.len() != S.num_io {
1014 return Err(NovaError::InvalidInputLength);
1015 }
1016 Ok(RelaxedR1CSInstance {
1017 comm_W,
1018 comm_E,
1019 u,
1020 X,
1021 })
1022 }
1023
1024 pub fn from_r1cs_instance_unchecked(
1026 comm_W: &Commitment<E>,
1027 X: &[E::Scalar],
1028 ) -> RelaxedR1CSInstance<E> {
1029 RelaxedR1CSInstance {
1030 comm_W: *comm_W,
1031 comm_E: Commitment::<E>::default(),
1032 u: E::Scalar::ONE,
1033 X: X.to_vec(),
1034 }
1035 }
1036
1037 pub fn fold(
1039 &self,
1040 U2: &R1CSInstance<E>,
1041 comm_T: &Commitment<E>,
1042 r: &E::Scalar,
1043 ) -> RelaxedR1CSInstance<E> {
1044 let (X1, u1, comm_W_1, comm_E_1) =
1045 (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone());
1046 let (X2, comm_W_2) = (&U2.X, &U2.comm_W);
1047
1048 let X = X1
1050 .par_iter()
1051 .zip(X2)
1052 .map(|(a, b)| *a + *r * *b)
1053 .collect::<Vec<E::Scalar>>();
1054 let comm_W = *comm_W_1 + *comm_W_2 * *r;
1055 let comm_E = *comm_E_1 + *comm_T * *r;
1056 let u = *u1 + *r;
1057
1058 RelaxedR1CSInstance {
1059 comm_W,
1060 comm_E,
1061 X,
1062 u,
1063 }
1064 }
1065
1066 pub fn fold_relaxed(
1068 &self,
1069 U2: &RelaxedR1CSInstance<E>,
1070 comm_T: &Commitment<E>,
1071 r: &E::Scalar,
1072 ) -> RelaxedR1CSInstance<E> {
1073 let (X1, u1, comm_W_1, comm_E_1) =
1074 (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone());
1075 let (X2, u2, comm_W_2, comm_E_2) = (&U2.X, &U2.u, &U2.comm_W, &U2.comm_E);
1076
1077 let X = X1
1079 .par_iter()
1080 .zip(X2)
1081 .map(|(a, b)| *a + *r * *b)
1082 .collect::<Vec<E::Scalar>>();
1083 let comm_W = *comm_W_1 + *comm_W_2 * *r;
1084 let comm_E = *comm_E_1 + *comm_T * *r + *comm_E_2 * *r * *r;
1085 let u = *u1 + *r * *u2;
1086
1087 RelaxedR1CSInstance {
1088 comm_W,
1089 comm_E,
1090 X,
1091 u,
1092 }
1093 }
1094
1095 pub fn derandomize(
1097 &self,
1098 dk: &DerandKey<E>,
1099 r_W: &E::Scalar,
1100 r_E: &E::Scalar,
1101 ) -> RelaxedR1CSInstance<E> {
1102 RelaxedR1CSInstance {
1103 comm_W: CE::<E>::derandomize(dk, &self.comm_W, r_W),
1104 comm_E: CE::<E>::derandomize(dk, &self.comm_E, r_E),
1105 X: self.X.clone(),
1106 u: self.u,
1107 }
1108 }
1109}
1110
1111impl<E: Engine> TranscriptReprTrait<E::GE> for RelaxedR1CSInstance<E> {
1112 fn to_transcript_bytes(&self) -> Vec<u8> {
1113 [
1114 self.comm_W.to_transcript_bytes(),
1115 self.comm_E.to_transcript_bytes(),
1116 self.u.to_transcript_bytes(),
1117 self.X.as_slice().to_transcript_bytes(),
1118 ]
1119 .concat()
1120 }
1121}
1122
1123impl<E: Engine> AbsorbInROTrait<E> for RelaxedR1CSInstance<E> {
1124 fn absorb_in_ro(&self, ro: &mut E::RO) {
1125 self.comm_W.absorb_in_ro(ro);
1126 self.comm_E.absorb_in_ro(ro);
1127 ro.absorb(scalar_as_base::<E>(self.u));
1128
1129 for x in &self.X {
1131 let limbs: Vec<E::Scalar> = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap();
1132 for limb in limbs {
1133 ro.absorb(scalar_as_base::<E>(limb));
1134 }
1135 }
1136 }
1137}
1138
1139#[cfg(test)]
1140mod tests {
1141 use ff::Field;
1142
1143 use super::*;
1144 use crate::{
1145 provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine},
1146 r1cs::sparse::SparseMatrix,
1147 traits::{snark::default_ck_hint, Engine},
1148 };
1149
1150 fn tiny_r1cs<E: Engine>(num_vars: usize) -> R1CSShape<E> {
1151 let one = <E::Scalar as Field>::ONE;
1152 let (num_cons, num_vars, num_io, A, B, C) = {
1153 let num_cons = 4;
1154 let num_io = 2;
1155
1156 let mut A: Vec<(usize, usize, E::Scalar)> = Vec::new();
1168 let mut B: Vec<(usize, usize, E::Scalar)> = Vec::new();
1169 let mut C: Vec<(usize, usize, E::Scalar)> = Vec::new();
1170
1171 A.push((0, num_vars + 1, one));
1174 B.push((0, num_vars + 1, one));
1175 C.push((0, 0, one));
1176
1177 A.push((1, 0, one));
1180 B.push((1, num_vars + 1, one));
1181 C.push((1, 1, one));
1182
1183 A.push((2, 1, one));
1186 A.push((2, num_vars + 1, one));
1187 B.push((2, num_vars, one));
1188 C.push((2, 2, one));
1189
1190 A.push((3, 2, one));
1193 A.push((3, num_vars, one + one + one + one + one));
1194 B.push((3, num_vars, one));
1195 C.push((3, num_vars + 2, one));
1196
1197 (num_cons, num_vars, num_io, A, B, C)
1198 };
1199
1200 let rows = num_cons;
1202 let cols = num_vars + num_io + 1;
1203
1204 let res = R1CSShape::new(
1205 num_cons,
1206 num_vars,
1207 num_io,
1208 SparseMatrix::new(&A, rows, cols),
1209 SparseMatrix::new(&B, rows, cols),
1210 SparseMatrix::new(&C, rows, cols),
1211 );
1212 assert!(res.is_ok());
1213 res.unwrap()
1214 }
1215
1216 fn test_pad_tiny_r1cs_with<E: Engine>() {
1217 let padded_r1cs = tiny_r1cs::<E>(3).pad();
1218 assert!(padded_r1cs.is_regular_shape());
1219
1220 let expected_r1cs = tiny_r1cs::<E>(4);
1221
1222 assert_eq!(padded_r1cs, expected_r1cs);
1223 }
1224
1225 #[test]
1226 fn test_pad_tiny_r1cs() {
1227 test_pad_tiny_r1cs_with::<PallasEngine>();
1228 test_pad_tiny_r1cs_with::<Bn256EngineKZG>();
1229 test_pad_tiny_r1cs_with::<Secp256k1Engine>();
1230 }
1231
1232 fn test_pad_nonsquare_with<E: Engine>() {
1233 let S = tiny_r1cs::<E>(8);
1237 let padded = S.pad_nonsquare();
1238 assert!(padded.is_regular_shape());
1239 assert_eq!(padded.num_cons, 4);
1240 assert_eq!(padded.num_vars, 8);
1241
1242 let S2 = tiny_r1cs::<E>(3);
1245 let padded2 = S2.pad_nonsquare();
1246 assert!(padded2.is_regular_shape());
1247 assert_eq!(padded2.num_cons, 4);
1248 assert_eq!(padded2.num_vars, 4);
1249
1250 let ck = R1CSShape::commitment_key(&[&padded], &[&*default_ck_hint()]).unwrap();
1252 let (inst, wit) = padded.sample_random_instance_witness(&ck).unwrap();
1253 assert!(padded.is_sat_relaxed(&ck, &inst, &wit).is_ok());
1254 }
1255
1256 #[test]
1257 fn test_pad_nonsquare() {
1258 test_pad_nonsquare_with::<PallasEngine>();
1259 test_pad_nonsquare_with::<Bn256EngineKZG>();
1260 test_pad_nonsquare_with::<Secp256k1Engine>();
1261 }
1262
1263 fn test_random_sample_with<E: Engine>() {
1264 let S = tiny_r1cs::<E>(4);
1265 let ck = R1CSShape::commitment_key(&[&S], &[&*default_ck_hint()]).unwrap();
1266 let (inst, wit) = S.sample_random_instance_witness(&ck).unwrap();
1267 assert!(S.is_sat_relaxed(&ck, &inst, &wit).is_ok());
1268 }
1269
1270 #[test]
1271 fn test_random_sample() {
1272 test_random_sample_with::<PallasEngine>();
1273 test_random_sample_with::<Bn256EngineKZG>();
1274 test_random_sample_with::<Secp256k1Engine>();
1275 }
1276}