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 sample_random_instance_witness(
582 &self,
583 ck: &CommitmentKey<E>,
584 ) -> Result<(RelaxedR1CSInstance<E>, RelaxedR1CSWitness<E>), NovaError> {
585 let Z = (0..self.num_vars + self.num_io + 1)
587 .into_par_iter()
588 .map(|_| E::Scalar::random(&mut OsRng))
589 .collect::<Vec<E::Scalar>>();
590
591 let r_W = E::Scalar::random(&mut OsRng);
592 let r_E = E::Scalar::random(&mut OsRng);
593
594 let u = Z[self.num_vars];
595
596 let (AZ, BZ, CZ) = self.multiply_vec(&Z)?;
598
599 let E = AZ
600 .par_iter()
601 .zip(BZ.par_iter())
602 .zip(CZ.par_iter())
603 .map(|((az, bz), cz)| *az * *bz - u * *cz)
604 .collect::<Vec<E::Scalar>>();
605
606 let (comm_W, comm_E) = rayon::join(
608 || CE::<E>::commit(ck, &Z[..self.num_vars], &r_W),
609 || CE::<E>::commit(ck, &E, &r_E),
610 );
611
612 Ok((
613 RelaxedR1CSInstance {
614 comm_W,
615 comm_E,
616 u,
617 X: Z[self.num_vars + 1..].to_vec(),
618 },
619 RelaxedR1CSWitness {
620 W: Z[..self.num_vars].to_vec(),
621 r_W,
622 E,
623 r_E,
624 },
625 ))
626 }
627}
628
629impl<E: Engine> R1CSWitness<E> {
630 pub fn new(S: &R1CSShape<E>, W: &[E::Scalar]) -> Result<R1CSWitness<E>, NovaError> {
632 let mut W = W.to_vec();
633 W.resize(S.num_vars, E::Scalar::ZERO);
634
635 Ok(R1CSWitness {
636 W,
637 r_W: E::Scalar::random(&mut OsRng),
638 })
639 }
640
641 pub fn W(&self) -> &[E::Scalar] {
645 &self.W
646 }
647
648 pub fn r_W(&self) -> &E::Scalar {
650 &self.r_W
651 }
652
653 pub fn commit(&self, ck: &CommitmentKey<E>) -> Commitment<E> {
655 CE::<E>::commit(ck, &self.W, &self.r_W)
656 }
657
658 pub fn pad(&self, S: &R1CSShape<E>) -> R1CSWitness<E> {
660 let mut W = self.W.clone();
661 W.extend(vec![E::Scalar::ZERO; S.num_vars - W.len()]);
662
663 Self { W, r_W: self.r_W }
664 }
665}
666
667impl<E: Engine> R1CSInstance<E> {
668 pub fn new(
670 S: &R1CSShape<E>,
671 comm_W: &Commitment<E>,
672 X: &[E::Scalar],
673 ) -> Result<R1CSInstance<E>, NovaError> {
674 if S.num_io != X.len() {
675 Err(NovaError::InvalidInputLength)
676 } else {
677 Ok(R1CSInstance {
678 comm_W: *comm_W,
679 X: X.to_owned(),
680 })
681 }
682 }
683
684 pub fn comm_W(&self) -> &Commitment<E> {
686 &self.comm_W
687 }
688
689 pub fn X(&self) -> &[E::Scalar] {
693 &self.X
694 }
695}
696
697impl<E: Engine> TranscriptReprTrait<E::GE> for R1CSInstance<E> {
698 fn to_transcript_bytes(&self) -> Vec<u8> {
699 [
700 self.comm_W.to_transcript_bytes(),
701 self.X.as_slice().to_transcript_bytes(),
702 ]
703 .concat()
704 }
705}
706
707impl<E: Engine> AbsorbInROTrait<E> for R1CSInstance<E> {
708 fn absorb_in_ro(&self, ro: &mut E::RO) {
709 self.comm_W.absorb_in_ro(ro);
710
711 for x in &self.X {
714 ro.absorb(scalar_as_base::<E>(*x));
715 }
716 }
717}
718
719impl<E: Engine> AbsorbInRO2Trait<E> for R1CSInstance<E> {
720 fn absorb_in_ro2(&self, ro: &mut E::RO2) {
721 self.comm_W.absorb_in_ro2(ro);
723
724 for x in &self.X {
725 ro.absorb(*x);
726 }
727 }
728}
729
730impl<E: Engine> RelaxedR1CSWitness<E> {
731 pub fn default(S: &R1CSShape<E>) -> RelaxedR1CSWitness<E> {
733 RelaxedR1CSWitness {
734 W: vec![E::Scalar::ZERO; S.num_vars],
735 r_W: E::Scalar::ZERO,
736 E: vec![E::Scalar::ZERO; S.num_cons],
737 r_E: E::Scalar::ZERO,
738 }
739 }
740
741 pub fn from_r1cs_witness(S: &R1CSShape<E>, witness: &R1CSWitness<E>) -> RelaxedR1CSWitness<E> {
743 RelaxedR1CSWitness {
744 W: witness.W.clone(),
745 r_W: witness.r_W,
746 E: vec![E::Scalar::ZERO; S.num_cons],
747 r_E: E::Scalar::ZERO,
748 }
749 }
750
751 pub fn new(
758 S: &R1CSShape<E>,
759 W: Vec<E::Scalar>,
760 r_W: E::Scalar,
761 E: Vec<E::Scalar>,
762 r_E: E::Scalar,
763 ) -> Result<RelaxedR1CSWitness<E>, NovaError> {
764 if W.len() != S.num_vars {
765 return Err(NovaError::InvalidWitnessLength);
766 }
767 if E.len() != S.num_cons {
768 return Err(NovaError::InvalidWitnessLength);
769 }
770 Ok(RelaxedR1CSWitness { W, r_W, E, r_E })
771 }
772
773 pub fn W(&self) -> &[E::Scalar] {
777 &self.W
778 }
779
780 pub fn E(&self) -> &[E::Scalar] {
784 &self.E
785 }
786
787 pub fn commit(&self, ck: &CommitmentKey<E>) -> (Commitment<E>, Commitment<E>) {
789 (
790 CE::<E>::commit(ck, &self.W, &self.r_W),
791 CE::<E>::commit(ck, &self.E, &self.r_E),
792 )
793 }
794
795 pub fn fold(
797 &self,
798 W2: &R1CSWitness<E>,
799 T: &[E::Scalar],
800 r_T: &E::Scalar,
801 r: &E::Scalar,
802 ) -> Result<RelaxedR1CSWitness<E>, NovaError> {
803 let (W1, r_W1, E1, r_E1) = (&self.W, &self.r_W, &self.E, &self.r_E);
804 let (W2, r_W2) = (&W2.W, &W2.r_W);
805
806 if W1.len() != W2.len() {
807 return Err(NovaError::InvalidWitnessLength);
808 }
809
810 let W = W1
811 .par_iter()
812 .zip(W2)
813 .map(|(a, b)| *a + *r * *b)
814 .collect::<Vec<E::Scalar>>();
815 let E = E1
816 .par_iter()
817 .zip(T)
818 .map(|(a, b)| *a + *r * *b)
819 .collect::<Vec<E::Scalar>>();
820
821 let r_W = *r_W1 + *r * r_W2;
822 let r_E = *r_E1 + *r * r_T;
823
824 Ok(RelaxedR1CSWitness { W, r_W, E, r_E })
825 }
826
827 pub fn fold_relaxed(
830 &self,
831 W2: &RelaxedR1CSWitness<E>,
832 T: &[E::Scalar],
833 r_T: &E::Scalar,
834 r: &E::Scalar,
835 ) -> Result<RelaxedR1CSWitness<E>, NovaError> {
836 let (W1, r_W1, E1, r_E1) = (&self.W, &self.r_W, &self.E, &self.r_E);
837 let (W2, r_W2, E2, r_E2) = (&W2.W, &W2.r_W, &W2.E, &W2.r_E);
838
839 if W1.len() != W2.len() {
840 return Err(NovaError::InvalidWitnessLength);
841 }
842
843 let W = W1
844 .par_iter()
845 .zip(W2)
846 .map(|(a, b)| *a + *r * *b)
847 .collect::<Vec<E::Scalar>>();
848 let E = E1
849 .par_iter()
850 .zip(T)
851 .zip(E2.par_iter())
852 .map(|((a, b), c)| *a + *r * *b + *r * *r * *c)
853 .collect::<Vec<E::Scalar>>();
854
855 let r_W = *r_W1 + *r * r_W2;
856 let r_E = *r_E1 + *r * r_T + *r * *r * *r_E2;
857
858 Ok(RelaxedR1CSWitness { W, r_W, E, r_E })
859 }
860
861 pub fn pad(&self, S: &R1CSShape<E>) -> RelaxedR1CSWitness<E> {
863 let mut W = self.W.clone();
864 W.extend(vec![E::Scalar::ZERO; S.num_vars - W.len()]);
865
866 let mut E = self.E.clone();
867 E.extend(vec![E::Scalar::ZERO; S.num_cons - E.len()]);
868
869 Self {
870 W,
871 r_W: self.r_W,
872 E,
873 r_E: self.r_E,
874 }
875 }
876
877 pub fn into_W_E(self) -> (Vec<E::Scalar>, Vec<E::Scalar>) {
879 (self.W, self.E)
880 }
881
882 pub fn derandomize(&self) -> (Self, E::Scalar, E::Scalar) {
884 (
885 RelaxedR1CSWitness {
886 W: self.W.clone(),
887 r_W: E::Scalar::ZERO,
888 E: self.E.clone(),
889 r_E: E::Scalar::ZERO,
890 },
891 self.r_W,
892 self.r_E,
893 )
894 }
895}
896
897impl<E: Engine> RelaxedR1CSInstance<E> {
898 pub fn default(_ck: &CommitmentKey<E>, S: &R1CSShape<E>) -> RelaxedR1CSInstance<E> {
900 let (comm_W, comm_E) = (Commitment::<E>::default(), Commitment::<E>::default());
901 RelaxedR1CSInstance {
902 comm_W,
903 comm_E,
904 u: E::Scalar::ZERO,
905 X: vec![E::Scalar::ZERO; S.num_io],
906 }
907 }
908
909 pub fn from_r1cs_instance(
911 ck: &CommitmentKey<E>,
912 S: &R1CSShape<E>,
913 instance: &R1CSInstance<E>,
914 ) -> RelaxedR1CSInstance<E> {
915 let mut r_instance = RelaxedR1CSInstance::default(ck, S);
916 r_instance.comm_W = instance.comm_W;
917 r_instance.u = E::Scalar::ONE;
918 r_instance.X.clone_from(&instance.X);
919
920 r_instance
921 }
922
923 pub fn comm_W(&self) -> &Commitment<E> {
927 &self.comm_W
928 }
929
930 pub fn comm_E(&self) -> &Commitment<E> {
934 &self.comm_E
935 }
936
937 pub fn X(&self) -> &[E::Scalar] {
941 &self.X
942 }
943
944 pub fn u(&self) -> E::Scalar {
948 self.u
949 }
950
951 pub fn new(
958 S: &R1CSShape<E>,
959 comm_W: Commitment<E>,
960 comm_E: Commitment<E>,
961 u: E::Scalar,
962 X: Vec<E::Scalar>,
963 ) -> Result<RelaxedR1CSInstance<E>, NovaError> {
964 if X.len() != S.num_io {
965 return Err(NovaError::InvalidInputLength);
966 }
967 Ok(RelaxedR1CSInstance {
968 comm_W,
969 comm_E,
970 u,
971 X,
972 })
973 }
974
975 pub fn from_r1cs_instance_unchecked(
977 comm_W: &Commitment<E>,
978 X: &[E::Scalar],
979 ) -> RelaxedR1CSInstance<E> {
980 RelaxedR1CSInstance {
981 comm_W: *comm_W,
982 comm_E: Commitment::<E>::default(),
983 u: E::Scalar::ONE,
984 X: X.to_vec(),
985 }
986 }
987
988 pub fn fold(
990 &self,
991 U2: &R1CSInstance<E>,
992 comm_T: &Commitment<E>,
993 r: &E::Scalar,
994 ) -> RelaxedR1CSInstance<E> {
995 let (X1, u1, comm_W_1, comm_E_1) =
996 (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone());
997 let (X2, comm_W_2) = (&U2.X, &U2.comm_W);
998
999 let X = X1
1001 .par_iter()
1002 .zip(X2)
1003 .map(|(a, b)| *a + *r * *b)
1004 .collect::<Vec<E::Scalar>>();
1005 let comm_W = *comm_W_1 + *comm_W_2 * *r;
1006 let comm_E = *comm_E_1 + *comm_T * *r;
1007 let u = *u1 + *r;
1008
1009 RelaxedR1CSInstance {
1010 comm_W,
1011 comm_E,
1012 X,
1013 u,
1014 }
1015 }
1016
1017 pub fn fold_relaxed(
1019 &self,
1020 U2: &RelaxedR1CSInstance<E>,
1021 comm_T: &Commitment<E>,
1022 r: &E::Scalar,
1023 ) -> RelaxedR1CSInstance<E> {
1024 let (X1, u1, comm_W_1, comm_E_1) =
1025 (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone());
1026 let (X2, u2, comm_W_2, comm_E_2) = (&U2.X, &U2.u, &U2.comm_W, &U2.comm_E);
1027
1028 let X = X1
1030 .par_iter()
1031 .zip(X2)
1032 .map(|(a, b)| *a + *r * *b)
1033 .collect::<Vec<E::Scalar>>();
1034 let comm_W = *comm_W_1 + *comm_W_2 * *r;
1035 let comm_E = *comm_E_1 + *comm_T * *r + *comm_E_2 * *r * *r;
1036 let u = *u1 + *r * *u2;
1037
1038 RelaxedR1CSInstance {
1039 comm_W,
1040 comm_E,
1041 X,
1042 u,
1043 }
1044 }
1045
1046 pub fn derandomize(
1048 &self,
1049 dk: &DerandKey<E>,
1050 r_W: &E::Scalar,
1051 r_E: &E::Scalar,
1052 ) -> RelaxedR1CSInstance<E> {
1053 RelaxedR1CSInstance {
1054 comm_W: CE::<E>::derandomize(dk, &self.comm_W, r_W),
1055 comm_E: CE::<E>::derandomize(dk, &self.comm_E, r_E),
1056 X: self.X.clone(),
1057 u: self.u,
1058 }
1059 }
1060}
1061
1062impl<E: Engine> TranscriptReprTrait<E::GE> for RelaxedR1CSInstance<E> {
1063 fn to_transcript_bytes(&self) -> Vec<u8> {
1064 [
1065 self.comm_W.to_transcript_bytes(),
1066 self.comm_E.to_transcript_bytes(),
1067 self.u.to_transcript_bytes(),
1068 self.X.as_slice().to_transcript_bytes(),
1069 ]
1070 .concat()
1071 }
1072}
1073
1074impl<E: Engine> AbsorbInROTrait<E> for RelaxedR1CSInstance<E> {
1075 fn absorb_in_ro(&self, ro: &mut E::RO) {
1076 self.comm_W.absorb_in_ro(ro);
1077 self.comm_E.absorb_in_ro(ro);
1078 ro.absorb(scalar_as_base::<E>(self.u));
1079
1080 for x in &self.X {
1082 let limbs: Vec<E::Scalar> = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap();
1083 for limb in limbs {
1084 ro.absorb(scalar_as_base::<E>(limb));
1085 }
1086 }
1087 }
1088}
1089
1090#[cfg(test)]
1091mod tests {
1092 use ff::Field;
1093
1094 use super::*;
1095 use crate::{
1096 provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine},
1097 r1cs::sparse::SparseMatrix,
1098 traits::{snark::default_ck_hint, Engine},
1099 };
1100
1101 fn tiny_r1cs<E: Engine>(num_vars: usize) -> R1CSShape<E> {
1102 let one = <E::Scalar as Field>::ONE;
1103 let (num_cons, num_vars, num_io, A, B, C) = {
1104 let num_cons = 4;
1105 let num_io = 2;
1106
1107 let mut A: Vec<(usize, usize, E::Scalar)> = Vec::new();
1119 let mut B: Vec<(usize, usize, E::Scalar)> = Vec::new();
1120 let mut C: Vec<(usize, usize, E::Scalar)> = Vec::new();
1121
1122 A.push((0, num_vars + 1, one));
1125 B.push((0, num_vars + 1, one));
1126 C.push((0, 0, one));
1127
1128 A.push((1, 0, one));
1131 B.push((1, num_vars + 1, one));
1132 C.push((1, 1, one));
1133
1134 A.push((2, 1, one));
1137 A.push((2, num_vars + 1, one));
1138 B.push((2, num_vars, one));
1139 C.push((2, 2, one));
1140
1141 A.push((3, 2, one));
1144 A.push((3, num_vars, one + one + one + one + one));
1145 B.push((3, num_vars, one));
1146 C.push((3, num_vars + 2, one));
1147
1148 (num_cons, num_vars, num_io, A, B, C)
1149 };
1150
1151 let rows = num_cons;
1153 let cols = num_vars + num_io + 1;
1154
1155 let res = R1CSShape::new(
1156 num_cons,
1157 num_vars,
1158 num_io,
1159 SparseMatrix::new(&A, rows, cols),
1160 SparseMatrix::new(&B, rows, cols),
1161 SparseMatrix::new(&C, rows, cols),
1162 );
1163 assert!(res.is_ok());
1164 res.unwrap()
1165 }
1166
1167 fn test_pad_tiny_r1cs_with<E: Engine>() {
1168 let padded_r1cs = tiny_r1cs::<E>(3).pad();
1169 assert!(padded_r1cs.is_regular_shape());
1170
1171 let expected_r1cs = tiny_r1cs::<E>(4);
1172
1173 assert_eq!(padded_r1cs, expected_r1cs);
1174 }
1175
1176 #[test]
1177 fn test_pad_tiny_r1cs() {
1178 test_pad_tiny_r1cs_with::<PallasEngine>();
1179 test_pad_tiny_r1cs_with::<Bn256EngineKZG>();
1180 test_pad_tiny_r1cs_with::<Secp256k1Engine>();
1181 }
1182
1183 fn test_random_sample_with<E: Engine>() {
1184 let S = tiny_r1cs::<E>(4);
1185 let ck = R1CSShape::commitment_key(&[&S], &[&*default_ck_hint()]).unwrap();
1186 let (inst, wit) = S.sample_random_instance_witness(&ck).unwrap();
1187 assert!(S.is_sat_relaxed(&ck, &inst, &wit).is_ok());
1188 }
1189
1190 #[test]
1191 fn test_random_sample() {
1192 test_random_sample_with::<PallasEngine>();
1193 test_random_sample_with::<Bn256EngineKZG>();
1194 test_random_sample_with::<Secp256k1Engine>();
1195 }
1196}