use crate::{ commitment::pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness}, crh::pedersen::PedersenWindow, }; use algebra_core::{ fields::{Field, PrimeField}, to_bytes, Group, ToBytes, }; use r1cs_core::{ConstraintSystem, SynthesisError}; use crate::commitment::CommitmentGadget; use core::{borrow::Borrow, marker::PhantomData}; use r1cs_std::prelude::*; #[derive(Derivative)] #[derivative(Clone(bound = "G: Group, W: PedersenWindow, ConstraintF: Field"))] pub struct PedersenCommitmentGadgetParameters { params: PedersenParameters, #[doc(hidden)] _group: PhantomData, #[doc(hidden)] _engine: PhantomData, #[doc(hidden)] _window: PhantomData, } #[derive(Clone, Debug)] pub struct PedersenRandomnessGadget(Vec); pub struct PedersenCommitmentGadget>( #[doc(hidden)] PhantomData<*const G>, #[doc(hidden)] PhantomData<*const GG>, PhantomData, ); impl CommitmentGadget, ConstraintF> for PedersenCommitmentGadget where ConstraintF: PrimeField, G: Group, GG: GroupGadget, W: PedersenWindow, { type OutputGadget = GG; type ParametersGadget = PedersenCommitmentGadgetParameters; type RandomnessGadget = PedersenRandomnessGadget; fn check_commitment_gadget>( mut cs: CS, parameters: &Self::ParametersGadget, input: &[UInt8], r: &Self::RandomnessGadget, ) -> Result { assert!((input.len() * 8) <= (W::WINDOW_SIZE * W::NUM_WINDOWS)); let mut padded_input = input.to_vec(); // Pad if input length is less than `W::WINDOW_SIZE * W::NUM_WINDOWS`. 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 commitment output. 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 mut result = GG::precomputed_base_multiscalar_mul( cs.ns(|| "multiexp"), ¶meters.params.generators, input_in_bits, )?; // Compute h^r let rand_bits: Vec<_> = r.0.iter().flat_map(|byte| byte.into_bits_le()).collect(); result.precomputed_base_scalar_mul( cs.ns(|| "Randomizer"), rand_bits .iter() .zip(¶meters.params.randomness_generator), )?; Ok(result) } } impl AllocGadget, ConstraintF> for PedersenCommitmentGadgetParameters where G: Group, W: PedersenWindow, ConstraintF: PrimeField, { fn alloc>( _cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let temp = value_gen()?; let parameters = temp.borrow().clone(); Ok(PedersenCommitmentGadgetParameters { params: parameters, _group: PhantomData, _engine: PhantomData, _window: PhantomData, }) } fn alloc_input>( _cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let temp = value_gen()?; let parameters = temp.borrow().clone(); Ok(PedersenCommitmentGadgetParameters { params: parameters, _group: PhantomData, _engine: PhantomData, _window: PhantomData, }) } } impl AllocGadget, ConstraintF> for PedersenRandomnessGadget where G: Group, ConstraintF: PrimeField, { fn alloc>( cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let temp = value_gen()?; let randomness = to_bytes![temp.borrow().0].unwrap(); Ok(PedersenRandomnessGadget(UInt8::alloc_vec(cs, &randomness)?)) } fn alloc_input>( cs: CS, value_gen: F, ) -> Result where F: FnOnce() -> Result, T: Borrow>, { let temp = value_gen()?; let randomness = to_bytes![temp.borrow().0].unwrap(); Ok(PedersenRandomnessGadget(UInt8::alloc_input_vec( cs, &randomness, )?)) } } #[cfg(test)] mod test { use algebra::{ jubjub::{Fq, Fr, JubJubProjective as JubJub}, test_rng, ProjectiveCurve, UniformRand, }; use crate::{ commitment::{ pedersen::{ constraints::PedersenCommitmentGadget, PedersenCommitment, PedersenRandomness, }, CommitmentGadget, CommitmentScheme, }, crh::pedersen::PedersenWindow, }; use r1cs_core::ConstraintSystem; use r1cs_std::{ jubjub::JubJubGadget, prelude::*, test_constraint_system::TestConstraintSystem, }; #[test] fn commitment_gadget_test() { let mut cs = TestConstraintSystem::::new(); #[derive(Clone, PartialEq, Eq, Hash)] pub(super) struct Window; impl PedersenWindow for Window { const WINDOW_SIZE: usize = 4; const NUM_WINDOWS: usize = 8; } let input = [1u8; 4]; let rng = &mut test_rng(); type TestCOMM = PedersenCommitment; type TestCOMMGadget = PedersenCommitmentGadget; let randomness = PedersenRandomness(Fr::rand(rng)); let parameters = PedersenCommitment::::setup(rng).unwrap(); let primitive_result = PedersenCommitment::::commit(¶meters, &input, &randomness).unwrap(); 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()); } let randomness = >::RandomnessGadget::alloc( &mut cs.ns(|| "gadget_randomness"), || Ok(&randomness), ) .unwrap(); let gadget_parameters = >::ParametersGadget::alloc( &mut cs.ns(|| "gadget_parameters"), || Ok(¶meters), ) .unwrap(); let gadget_result = >::check_commitment_gadget( &mut cs.ns(|| "gadget_evaluation"), &gadget_parameters, &input_bytes, &randomness, ) .unwrap(); 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()); } }