use core::{borrow::Borrow, hash::Hash, marker::PhantomData}; use crate::{ crh::{ bowe_hopwood::{BoweHopwoodPedersenCRH, BoweHopwoodPedersenParameters, CHUNK_SIZE}, pedersen::PedersenWindow, FixedLengthCRHGadget, }, Vec, }; use algebra_core::{groups::Group, Field}; use r1cs_core::{ConstraintSystem, SynthesisError}; use r1cs_std::{alloc::AllocGadget, groups::GroupGadget, uint8::UInt8}; use r1cs_std::bits::boolean::Boolean; #[derive(Derivative)] #[derivative(Clone( bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget" ))] pub struct BoweHopwoodPedersenCRHGadgetParameters< G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget, > { params: BoweHopwoodPedersenParameters, _group_g: PhantomData, _engine: PhantomData, _window: PhantomData, } pub struct BoweHopwoodPedersenCRHGadget< G: Group, ConstraintF: Field, GG: GroupGadget, > { _group: PhantomData<*const G>, _group_gadget: PhantomData<*const GG>, _engine: PhantomData, } impl FixedLengthCRHGadget, ConstraintF> for BoweHopwoodPedersenCRHGadget where ConstraintF: Field, G: Group + Hash, GG: GroupGadget, W: PedersenWindow, { type OutputGadget = GG; type ParametersGadget = BoweHopwoodPedersenCRHGadgetParameters; fn check_evaluation_gadget>( cs: CS, parameters: &Self::ParametersGadget, input: &[UInt8], ) -> Result { // Pad the input if it is not the current length. let mut input_in_bits: Vec<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect(); if (input_in_bits.len()) % CHUNK_SIZE != 0 { let current_length = input_in_bits.len(); for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) { input_in_bits.push(Boolean::constant(false)); } } assert!(input_in_bits.len() % CHUNK_SIZE == 0); assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS); for generators in parameters.params.generators.iter() { assert_eq!(generators.len(), W::WINDOW_SIZE); } // Allocate new variable for the result. let input_in_bits = input_in_bits .chunks(W::WINDOW_SIZE * CHUNK_SIZE) .map(|x| x.chunks(CHUNK_SIZE).collect::>()) .collect::>(); let result = GG::precomputed_base_3_bit_signed_digit_scalar_mul( cs, ¶meters.params.generators, &input_in_bits, )?; Ok(result) } } impl> AllocGadget, ConstraintF> for BoweHopwoodPedersenCRHGadgetParameters { fn alloc_constant>( _cs: CS, val: T, ) -> Result where T: Borrow>, { let params = val.borrow().clone(); Ok(BoweHopwoodPedersenCRHGadgetParameters { params, _group_g: PhantomData, _engine: PhantomData, _window: PhantomData, }) } fn alloc>( cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let params = value_gen()?.borrow().clone(); Self::alloc_constant(cs, params) } fn alloc_input>( _cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let params = value_gen()?.borrow().clone(); Ok(BoweHopwoodPedersenCRHGadgetParameters { params, _group_g: PhantomData, _engine: PhantomData, _window: PhantomData, }) } } #[cfg(test)] mod test { use rand::Rng; use crate::crh::{ bowe_hopwood::{constraints::BoweHopwoodPedersenCRHGadget, BoweHopwoodPedersenCRH}, pedersen::PedersenWindow, FixedLengthCRH, FixedLengthCRHGadget, }; use algebra::{ jubjub::{Fq as Fr, JubJubProjective as JubJub}, test_rng, ProjectiveCurve, }; use r1cs_core::ConstraintSystem; use r1cs_std::{ alloc::AllocGadget, jubjub::JubJubGadget, test_constraint_system::TestConstraintSystem, uint8::UInt8, }; type TestCRH = BoweHopwoodPedersenCRH; type TestCRHGadget = BoweHopwoodPedersenCRHGadget; #[derive(Clone, PartialEq, Eq, Hash)] pub(super) struct Window; impl PedersenWindow for Window { const WINDOW_SIZE: usize = 63; const NUM_WINDOWS: usize = 8; } fn generate_input, R: Rng>( mut cs: CS, rng: &mut R, ) -> ([u8; 189], Vec) { let mut input = [1u8; 189]; 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()); } }