use crate::{ commitment::pedersen::{Commitment, Parameters, Randomness}, crh::pedersen::Window, Vec, }; use algebra_core::{ fields::{Field, PrimeField}, to_bytes, ProjectiveCurve, Zero, }; use r1cs_core::{Namespace, SynthesisError}; use core::{borrow::Borrow, marker::PhantomData}; use r1cs_std::prelude::*; type ConstraintF = <::BaseField as Field>::BasePrimeField; #[derive(Derivative)] #[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar>"))] pub struct ParametersVar>> where for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, { params: Parameters, #[doc(hidden)] _group_var: PhantomData, } #[derive(Clone, Debug)] pub struct RandomnessVar(Vec>); pub struct CommGadget>, W: Window> where for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, { #[doc(hidden)] _curve: PhantomData<*const C>, #[doc(hidden)] _group_var: PhantomData<*const GG>, #[doc(hidden)] _window: PhantomData<*const W>, } impl crate::commitment::CommitmentGadget, ConstraintF> for CommGadget where C: ProjectiveCurve, GG: CurveVar>, W: Window, for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, ConstraintF: PrimeField, { type OutputVar = GG; type ParametersVar = ParametersVar; type RandomnessVar = RandomnessVar>; fn commit( parameters: &Self::ParametersVar, input: &[UInt8>], r: &Self::RandomnessVar, ) -> 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.to_bits_le().unwrap()) .collect(); let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE); let mut result = GG::precomputed_base_multiscalar_mul_le(¶meters.params.generators, input_in_bits)?; // Compute h^r let rand_bits: Vec<_> = r.0.iter() .flat_map(|byte| byte.to_bits_le().unwrap()) .collect(); result.precomputed_base_scalar_mul_le( rand_bits .iter() .zip(¶meters.params.randomness_generator), )?; Ok(result) } } impl AllocVar, ConstraintF> for ParametersVar where C: ProjectiveCurve, GG: CurveVar>, for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, { fn new_variable>>( _cs: impl Into>>, f: impl FnOnce() -> Result, _mode: AllocationMode, ) -> Result { let params = f()?.borrow().clone(); Ok(ParametersVar { params, _group_var: PhantomData, }) } } impl AllocVar, F> for RandomnessVar where C: ProjectiveCurve, F: PrimeField, { fn new_variable>>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { let r = to_bytes![&f().map(|b| b.borrow().0).unwrap_or(C::ScalarField::zero())].unwrap(); match mode { AllocationMode::Constant => Ok(Self(UInt8::constant_vec(&r))), AllocationMode::Input => UInt8::new_input_vec(cs, &r).map(Self), AllocationMode::Witness => UInt8::new_witness_vec(cs, &r).map(Self), } } } #[cfg(test)] mod test { use algebra::{ ed_on_bls12_381::{EdwardsProjective as JubJub, Fq, Fr}, test_rng, UniformRand, }; use crate::{ commitment::{ pedersen::{constraints::CommGadget, Commitment, Randomness}, CommitmentGadget, CommitmentScheme, }, crh::pedersen, }; use r1cs_core::ConstraintSystem; use r1cs_std::{ed_on_bls12_381::EdwardsVar, prelude::*}; #[test] fn commitment_gadget_test() { let cs = ConstraintSystem::::new_ref(); #[derive(Clone, PartialEq, Eq, Hash)] pub(super) struct Window; impl pedersen::Window for Window { const WINDOW_SIZE: usize = 4; const NUM_WINDOWS: usize = 8; } let input = [1u8; 4]; let rng = &mut test_rng(); type TestCOMM = Commitment; type TestCOMMGadget = CommGadget; let randomness = Randomness(Fr::rand(rng)); let parameters = Commitment::::setup(rng).unwrap(); let primitive_result = Commitment::::commit(¶meters, &input, &randomness).unwrap(); let mut input_var = vec![]; for input_byte in input.iter() { input_var.push(UInt8::new_witness(cs.clone(), || Ok(*input_byte)).unwrap()); } let randomness_var = >::RandomnessVar::new_witness( cs.ns("gadget_randomness"), || Ok(&randomness), ) .unwrap(); let parameters_var = >::ParametersVar::new_witness( cs.ns("gadget_parameters"), || Ok(¶meters), ) .unwrap(); let result_var = TestCOMMGadget::commit(¶meters_var, &input_var, &randomness_var).unwrap(); let primitive_result = primitive_result; assert_eq!(primitive_result, result_var.value().unwrap()); assert!(cs.is_satisfied().unwrap()); } }