use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb}; use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_r1cs_std::{fields::fp::FpVar, uint8::UInt8, ToConstraintFieldGadget}; use ark_relations::r1cs::SynthesisError; use ark_std::{cfg_into_iter, log2, One}; use rayon::prelude::*; use super::{constants::RUNNING, utils::pow_i, CommittedInstance, CommittedInstanceVar, Witness}; use crate::{ arith::{r1cs::R1CS, Arith}, folding::circuits::CF1, transcript::AbsorbNonNative, utils::vec::is_zero_vec, Error, }; // Implements the trait for absorbing ProtoGalaxy's CommittedInstance. impl Absorb for CommittedInstance where C::ScalarField: Absorb, { fn to_sponge_bytes(&self, dest: &mut Vec) { C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest); } fn to_sponge_field_elements(&self, dest: &mut Vec) { self.phi .to_native_sponge_field_elements_as_vec() .to_sponge_field_elements(dest); self.betas.to_sponge_field_elements(dest); self.e.to_sponge_field_elements(dest); self.x.to_sponge_field_elements(dest); } } // Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit. impl AbsorbGadget for CommittedInstanceVar { fn to_sponge_bytes(&self) -> Result>, SynthesisError> { FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?) } fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { Ok([ self.phi.to_constraint_field()?, self.betas.to_sponge_field_elements()?, self.e.to_sponge_field_elements()?, self.x.to_sponge_field_elements()?, ] .concat()) } } /// Implements `Arith` for R1CS, where the witness is of type [`Witness`], and /// the committed instance is of type [`CommittedInstance`]. /// /// Due to the error term `CommittedInstance.e`, R1CS here is considered as a /// relaxed R1CS. /// /// See `nova/traits.rs` for the rationale behind the design. impl Arith>, CommittedInstance> for R1CS> { type Evaluation = Vec>; fn eval_relation( &self, w: &Witness>, u: &CommittedInstance, ) -> Result { self.eval_at_z(&[&[C::ScalarField::one()][..], &u.x, &w.w].concat()) } fn check_evaluation( _w: &Witness, u: &CommittedInstance, e: Vec, ) -> Result<(), Error> { let ok = if TYPE == RUNNING { if u.betas.len() != log2(e.len()) as usize { return Err(Error::NotSameLength( "instance.betas.len()".to_string(), u.betas.len(), "log2(e.len())".to_string(), log2(e.len()) as usize, )); } u.e == cfg_into_iter!(e) .enumerate() .map(|(i, e_i)| pow_i(i, &u.betas) * e_i) .sum::>() } else { is_zero_vec(&e) }; ok.then_some(()).ok_or(Error::NotSatisfied) } } #[cfg(test)] pub mod tests { use super::*; use ark_bn254::{Fr, G1Projective as Projective}; use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; use ark_relations::r1cs::ConstraintSystem; use ark_std::UniformRand; use rand::Rng; /// test that checks the native CommittedInstance.to_sponge_{bytes,field_elements} /// vs the R1CS constraints version #[test] pub fn test_committed_instance_to_sponge_preimage() { let mut rng = ark_std::test_rng(); let t = rng.gen::() as usize; let io_len = rng.gen::() as usize; let ci = CommittedInstance:: { phi: Projective::rand(&mut rng), betas: (0..t).map(|_| Fr::rand(&mut rng)).collect(), e: Fr::rand(&mut rng), x: (0..io_len).map(|_| Fr::rand(&mut rng)).collect(), }; let bytes = ci.to_sponge_bytes_as_vec(); let field_elements = ci.to_sponge_field_elements_as_vec(); let cs = ConstraintSystem::::new_ref(); let ciVar = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(ci.clone())) .unwrap(); let bytes_var = ciVar.to_sponge_bytes().unwrap(); let field_elements_var = ciVar.to_sponge_field_elements().unwrap(); assert!(cs.is_satisfied().unwrap()); // check that the natively computed and in-circuit computed hashes match assert_eq!(bytes_var.value().unwrap(), bytes); assert_eq!(field_elements_var.value().unwrap(), field_elements); } }