sonobe/folding-schemes/src/folding/protogalaxy/traits.rs
folding-schemes/src/folding/protogalaxy/traits.rsRaw
1use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb};
2use ark_ec::CurveGroup;
3use ark_ff::PrimeField;
4use ark_r1cs_std::{fields::fp::FpVar, uint8::UInt8, ToConstraintFieldGadget};
5use ark_relations::r1cs::SynthesisError;
6use ark_std::{cfg_into_iter, log2, One};
7use rayon::prelude::*;
8
9use super::{constants::RUNNING, utils::pow_i, CommittedInstance, CommittedInstanceVar, Witness};
10use crate::{
11 arith::{r1cs::R1CS, Arith},
12 folding::circuits::CF1,
13 transcript::AbsorbNonNative,
14 utils::vec::is_zero_vec,
15 Error,
16};
17
18// Implements the trait for absorbing ProtoGalaxy's CommittedInstance.
19impl<C: CurveGroup, const TYPE: bool> Absorb for CommittedInstance<C, TYPE>
20where
21 C::ScalarField: Absorb,
22{
23 fn to_sponge_bytes(&self, dest: &mut Vec<u8>) {
24 C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest);
25 }
26
27 fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
28 self.phi
29 .to_native_sponge_field_elements_as_vec()
30 .to_sponge_field_elements(dest);
31 self.betas.to_sponge_field_elements(dest);
32 self.e.to_sponge_field_elements(dest);
33 self.x.to_sponge_field_elements(dest);
34 }
35}
36
37// Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit.
38impl<C: CurveGroup, const TYPE: bool> AbsorbGadget<C::ScalarField>
39 for CommittedInstanceVar<C, TYPE>
40{
41 fn to_sponge_bytes(&self) -> Result<Vec<UInt8<C::ScalarField>>, SynthesisError> {
42 FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?)
43 }
44
45 fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
46 Ok([
47 self.phi.to_constraint_field()?,
48 self.betas.to_sponge_field_elements()?,
49 self.e.to_sponge_field_elements()?,
50 self.x.to_sponge_field_elements()?,
51 ]
52 .concat())
53 }
54}
55
56/// Implements `Arith` for R1CS, where the witness is of type [`Witness`], and
57/// the committed instance is of type [`CommittedInstance`].
58///
59/// Due to the error term `CommittedInstance.e`, R1CS here is considered as a
60/// relaxed R1CS.
61///
62/// See `nova/traits.rs` for the rationale behind the design.
63impl<C: CurveGroup, const TYPE: bool> Arith<Witness<CF1<C>>, CommittedInstance<C, TYPE>>
64 for R1CS<CF1<C>>
65{
66 type Evaluation = Vec<CF1<C>>;
67
68 fn eval_relation(
69 &self,
70 w: &Witness<CF1<C>>,
71 u: &CommittedInstance<C, TYPE>,
72 ) -> Result<Self::Evaluation, Error> {
73 self.eval_at_z(&[&[C::ScalarField::one()][..], &u.x, &w.w].concat())
74 }
75
76 fn check_evaluation(
77 _w: &Witness<C::ScalarField>,
78 u: &CommittedInstance<C, TYPE>,
79 e: Vec<C::ScalarField>,
80 ) -> Result<(), Error> {
81 let ok = if TYPE == RUNNING {
82 if u.betas.len() != log2(e.len()) as usize {
83 return Err(Error::NotSameLength(
84 "instance.betas.len()".to_string(),
85 u.betas.len(),
86 "log2(e.len())".to_string(),
87 log2(e.len()) as usize,
88 ));
89 }
90
91 u.e == cfg_into_iter!(e)
92 .enumerate()
93 .map(|(i, e_i)| pow_i(i, &u.betas) * e_i)
94 .sum::<CF1<C>>()
95 } else {
96 is_zero_vec(&e)
97 };
98 ok.then_some(()).ok_or(Error::NotSatisfied)
99 }
100}
101
102#[cfg(test)]
103pub mod tests {
104 use super::*;
105 use ark_bn254::{Fr, G1Projective as Projective};
106 use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
107 use ark_relations::r1cs::ConstraintSystem;
108 use ark_std::UniformRand;
109 use rand::Rng;
110
111 /// test that checks the native CommittedInstance.to_sponge_{bytes,field_elements}
112 /// vs the R1CS constraints version
113 #[test]
114 pub fn test_committed_instance_to_sponge_preimage() {
115 let mut rng = ark_std::test_rng();
116
117 let t = rng.gen::<u8>() as usize;
118 let io_len = rng.gen::<u8>() as usize;
119
120 let ci = CommittedInstance::<Projective, true> {
121 phi: Projective::rand(&mut rng),
122 betas: (0..t).map(|_| Fr::rand(&mut rng)).collect(),
123 e: Fr::rand(&mut rng),
124 x: (0..io_len).map(|_| Fr::rand(&mut rng)).collect(),
125 };
126
127 let bytes = ci.to_sponge_bytes_as_vec();
128 let field_elements = ci.to_sponge_field_elements_as_vec();
129
130 let cs = ConstraintSystem::<Fr>::new_ref();
131
132 let ciVar =
133 CommittedInstanceVar::<Projective, true>::new_witness(cs.clone(), || Ok(ci.clone()))
134 .unwrap();
135 let bytes_var = ciVar.to_sponge_bytes().unwrap();
136 let field_elements_var = ciVar.to_sponge_field_elements().unwrap();
137
138 assert!(cs.is_satisfied().unwrap());
139
140 // check that the natively computed and in-circuit computed hashes match
141 assert_eq!(bytes_var.value().unwrap(), bytes);
142 assert_eq!(field_elements_var.value().unwrap(), field_elements);
143 }
144}