use crate::{ crh::{ pedersen::{PedersenCRH, PedersenParameters, PedersenWindow}, FixedLengthCRHGadget, }, Vec, }; use algebra_core::{Field, Group}; use r1cs_core::{ConstraintSystem, SynthesisError}; use r1cs_std::prelude::*; use core::{borrow::Borrow, marker::PhantomData}; #[derive(Derivative)] #[derivative(Clone( bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget" ))] pub struct PedersenCRHGadgetParameters< G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget, > { params: PedersenParameters, _group_g: PhantomData, _engine: PhantomData, _window: PhantomData, } pub struct PedersenCRHGadget> { #[doc(hideen)] _group: PhantomData<*const G>, #[doc(hideen)] _group_gadget: PhantomData<*const GG>, #[doc(hideen)] _engine: PhantomData, } impl FixedLengthCRHGadget, ConstraintF> for PedersenCRHGadget where ConstraintF: Field, G: Group, GG: GroupGadget, W: PedersenWindow, { type OutputGadget = GG; type ParametersGadget = PedersenCRHGadgetParameters; fn check_evaluation_gadget>( cs: CS, parameters: &Self::ParametersGadget, input: &[UInt8], ) -> Result { let mut padded_input = input.to_vec(); // Pad the input if it is not the current length. if input.len() * 8 < W::WINDOW_SIZE * W::NUM_WINDOWS { let current_length = input.len(); for _ in current_length..(W::WINDOW_SIZE * W::NUM_WINDOWS / 8) { padded_input.push(UInt8::constant(0u8)); } } assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS); assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS); // Allocate new variable for the result. let input_in_bits: Vec<_> = padded_input .iter() .flat_map(|byte| byte.into_bits_le()) .collect(); let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE); let result = GG::precomputed_base_multiscalar_mul(cs, ¶meters.params.generators, input_in_bits)?; Ok(result) } } impl> AllocGadget, ConstraintF> for PedersenCRHGadgetParameters { fn alloc>( _cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let params = value_gen()?.borrow().clone(); Ok(PedersenCRHGadgetParameters { params, _group_g: PhantomData, _engine: PhantomData, _window: PhantomData, }) } fn alloc_input>( _cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let params = value_gen()?.borrow().clone(); Ok(PedersenCRHGadgetParameters { params, _group_g: PhantomData, _engine: PhantomData, _window: PhantomData, }) } } #[cfg(test)] mod test { use crate::crh::{ pedersen::{constraints::PedersenCRHGadget, PedersenCRH, PedersenWindow}, FixedLengthCRH, FixedLengthCRHGadget, }; use algebra::{ jubjub::{Fq as Fr, JubJubProjective as JubJub}, test_rng, ProjectiveCurve, }; use r1cs_core::ConstraintSystem; use r1cs_std::{ jubjub::JubJubGadget, prelude::*, test_constraint_system::TestConstraintSystem, }; use rand::Rng; type TestCRH = PedersenCRH; type TestCRHGadget = PedersenCRHGadget; #[derive(Clone, PartialEq, Eq, Hash)] pub(super) struct Window; impl PedersenWindow for Window { const WINDOW_SIZE: usize = 128; const NUM_WINDOWS: usize = 8; } fn generate_input, R: Rng>( mut cs: CS, rng: &mut R, ) -> ([u8; 128], Vec) { let mut input = [1u8; 128]; rng.fill_bytes(&mut input); let mut input_bytes = vec![]; for (byte_i, input_byte) in input.iter().enumerate() { let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i)); input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap()); } (input, input_bytes) } #[test] fn crh_primitive_gadget_test() { let rng = &mut test_rng(); let mut cs = TestConstraintSystem::::new(); let (input, input_bytes) = generate_input(&mut cs, rng); println!("number of constraints for input: {}", cs.num_constraints()); let parameters = TestCRH::setup(rng).unwrap(); let primitive_result = TestCRH::evaluate(¶meters, &input).unwrap(); let gadget_parameters = >::ParametersGadget::alloc( &mut cs.ns(|| "gadget_parameters"), || Ok(¶meters), ) .unwrap(); println!( "number of constraints for input + params: {}", cs.num_constraints() ); let gadget_result = >::check_evaluation_gadget( &mut cs.ns(|| "gadget_evaluation"), &gadget_parameters, &input_bytes, ) .unwrap(); println!("number of constraints total: {}", cs.num_constraints()); let primitive_result = primitive_result.into_affine(); assert_eq!(primitive_result.x, gadget_result.x.value.unwrap()); assert_eq!(primitive_result.y, gadget_result.y.value.unwrap()); assert!(cs.is_satisfied()); } }