arnaucube
arnaucube/sonobePublic Self-hosted repository
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::{
  5    eq::EqGadget,
  6    fields::{fp::FpVar, FieldVar},
  7    uint8::UInt8,
  8    ToConstraintFieldGadget,
  9};
 10use ark_relations::r1cs::SynthesisError;
 11use ark_std::{cfg_into_iter, log2, One};
 12use rayon::prelude::*;
 13
 14use super::{
 15    constants::RUNNING,
 16    utils::{pow_i, pow_i_var},
 17    CommittedInstance, CommittedInstanceVar, Witness, WitnessVar,
 18};
 19use crate::{
 20    arith::{
 21        r1cs::{circuits::R1CSMatricesVar, R1CS},
 22        Arith, ArithGadget,
 23    },
 24    folding::circuits::CF1,
 25    transcript::AbsorbNonNative,
 26    utils::vec::is_zero_vec,
 27    Error,
 28};
 29
 30// Implements the trait for absorbing ProtoGalaxy's CommittedInstance.
 31impl<C: CurveGroup, const TYPE: bool> Absorb for CommittedInstance<C, TYPE>
 32where
 33    C::ScalarField: Absorb,
 34{
 35    fn to_sponge_bytes(&self, dest: &mut Vec<u8>) {
 36        C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest);
 37    }
 38
 39    fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
 40        self.phi
 41            .to_native_sponge_field_elements_as_vec()
 42            .to_sponge_field_elements(dest);
 43        self.betas.to_sponge_field_elements(dest);
 44        self.e.to_sponge_field_elements(dest);
 45        self.x.to_sponge_field_elements(dest);
 46    }
 47}
 48
 49// Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit.
 50impl<C: CurveGroup, const TYPE: bool> AbsorbGadget<C::ScalarField>
 51    for CommittedInstanceVar<C, TYPE>
 52{
 53    fn to_sponge_bytes(&self) -> Result<Vec<UInt8<C::ScalarField>>, SynthesisError> {
 54        FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?)
 55    }
 56
 57    fn to_sponge_field_elements(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
 58        Ok([
 59            self.phi.to_constraint_field()?,
 60            self.betas.to_sponge_field_elements()?,
 61            self.e.to_sponge_field_elements()?,
 62            self.x.to_sponge_field_elements()?,
 63        ]
 64        .concat())
 65    }
 66}
 67
 68/// Implements `Arith` for R1CS, where the witness is of type [`Witness`], and
 69/// the committed instance is of type [`CommittedInstance`].
 70///
 71/// Due to the error term `CommittedInstance.e`, R1CS here is considered as a
 72/// relaxed R1CS.
 73///
 74/// See `nova/traits.rs` for the rationale behind the design.
 75impl<C: CurveGroup, const TYPE: bool> Arith<Witness<CF1<C>>, CommittedInstance<C, TYPE>>
 76    for R1CS<CF1<C>>
 77{
 78    type Evaluation = Vec<CF1<C>>;
 79
 80    fn eval_relation(
 81        &self,
 82        w: &Witness<CF1<C>>,
 83        u: &CommittedInstance<C, TYPE>,
 84    ) -> Result<Self::Evaluation, Error> {
 85        self.eval_at_z(&[&[C::ScalarField::one()][..], &u.x, &w.w].concat())
 86    }
 87
 88    fn check_evaluation(
 89        _w: &Witness<C::ScalarField>,
 90        u: &CommittedInstance<C, TYPE>,
 91        e: Vec<C::ScalarField>,
 92    ) -> Result<(), Error> {
 93        let ok = if TYPE == RUNNING {
 94            if u.betas.len() != log2(e.len()) as usize {
 95                return Err(Error::NotSameLength(
 96                    "instance.betas.len()".to_string(),
 97                    u.betas.len(),
 98                    "log2(e.len())".to_string(),
 99                    log2(e.len()) as usize,
100                ));
101            }
102
103            u.e == cfg_into_iter!(e)
104                .enumerate()
105                .map(|(i, e_i)| pow_i(i, &u.betas) * e_i)
106                .sum::<CF1<C>>()
107        } else {
108            is_zero_vec(&e)
109        };
110        ok.then_some(()).ok_or(Error::NotSatisfied)
111    }
112}
113
114/// Unlike its native counterpart, we only need to support running instances in
115/// circuit, as the decider circuit only checks running instance satisfiability.
116impl<C: CurveGroup> ArithGadget<WitnessVar<CF1<C>>, CommittedInstanceVar<C, RUNNING>>
117    for R1CSMatricesVar<CF1<C>, FpVar<CF1<C>>>
118{
119    type Evaluation = (Vec<FpVar<CF1<C>>>, Vec<FpVar<CF1<C>>>);
120
121    fn eval_relation(
122        &self,
123        w: &WitnessVar<CF1<C>>,
124        u: &CommittedInstanceVar<C, RUNNING>,
125    ) -> Result<Self::Evaluation, SynthesisError> {
126        self.eval_at_z(&[&[FpVar::one()][..], &u.x, &w.W].concat())
127    }
128
129    fn enforce_evaluation(
130        _w: &WitnessVar<C::ScalarField>,
131        u: &CommittedInstanceVar<C, RUNNING>,
132        (AzBz, uCz): Self::Evaluation,
133    ) -> Result<(), SynthesisError> {
134        let mut e = vec![];
135        for (i, (l, r)) in AzBz.iter().zip(uCz).enumerate() {
136            e.push(pow_i_var(i, &u.betas) * (l - r));
137        }
138        // Call `sum` on a vector instead of computing the sum in the above loop
139        // to avoid stack overflow (the cause of this is similar to issue #80
140        // https://github.com/privacy-scaling-explorations/sonobe/issues/80)
141        e.iter().sum::<FpVar<_>>().enforce_equal(&u.e)
142    }
143}
144
145#[cfg(test)]
146pub mod tests {
147    use super::*;
148    use ark_bn254::{Fr, G1Projective as Projective};
149    use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
150    use ark_relations::r1cs::ConstraintSystem;
151    use ark_std::UniformRand;
152    use rand::Rng;
153
154    /// test that checks the native CommittedInstance.to_sponge_{bytes,field_elements}
155    /// vs the R1CS constraints version
156    #[test]
157    pub fn test_committed_instance_to_sponge_preimage() {
158        let mut rng = ark_std::test_rng();
159
160        let t = rng.gen::<u8>() as usize;
161        let io_len = rng.gen::<u8>() as usize;
162
163        let ci = CommittedInstance::<Projective, true> {
164            phi: Projective::rand(&mut rng),
165            betas: (0..t).map(|_| Fr::rand(&mut rng)).collect(),
166            e: Fr::rand(&mut rng),
167            x: (0..io_len).map(|_| Fr::rand(&mut rng)).collect(),
168        };
169
170        let bytes = ci.to_sponge_bytes_as_vec();
171        let field_elements = ci.to_sponge_field_elements_as_vec();
172
173        let cs = ConstraintSystem::<Fr>::new_ref();
174
175        let ciVar =
176            CommittedInstanceVar::<Projective, true>::new_witness(cs.clone(), || Ok(ci.clone()))
177                .unwrap();
178        let bytes_var = ciVar.to_sponge_bytes().unwrap();
179        let field_elements_var = ciVar.to_sponge_field_elements().unwrap();
180
181        assert!(cs.is_satisfied().unwrap());
182
183        // check that the natively computed and in-circuit computed hashes match
184        assert_eq!(bytes_var.value().unwrap(), bytes);
185        assert_eq!(field_elements_var.value().unwrap(), field_elements);
186    }
187}