arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
ark-r1cs-std/crypto-primitives/src/crh/bowe_hopwood/constraints.rs
crypto-primitives/src/crh/bowe_hopwood/constraints.rsRaw
  1use core::{borrow::Borrow, marker::PhantomData};
  2
  3use crate::{
  4    crh::{
  5        bowe_hopwood::{Parameters, CHUNK_SIZE, CRH},
  6        pedersen::Window,
  7        FixedLengthCRHGadget,
  8    },
  9    Vec,
 10};
 11use algebra_core::{
 12    curves::{ModelParameters, TEModelParameters},
 13    Field,
 14};
 15use r1cs_core::{Namespace, SynthesisError};
 16use r1cs_std::{
 17    alloc::AllocVar, groups::curves::twisted_edwards::AffineVar, prelude::*, uint8::UInt8,
 18};
 19
 20use r1cs_std::bits::boolean::Boolean;
 21
 22type ConstraintF<P> = <<P as ModelParameters>::BaseField as Field>::BasePrimeField;
 23
 24#[derive(Derivative)]
 25#[derivative(Clone(bound = "P: TEModelParameters, W: Window"))]
 26pub struct ParametersVar<P: TEModelParameters, W: Window> {
 27    params: Parameters<P>,
 28    #[doc(hidden)]
 29    _window: PhantomData<W>,
 30}
 31
 32pub struct CRHGadget<P: TEModelParameters, F: FieldVar<P::BaseField, ConstraintF<P>>>
 33where
 34    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
 35{
 36    #[doc(hidden)]
 37    _params: PhantomData<P>,
 38    #[doc(hidden)]
 39    _base_field: PhantomData<F>,
 40}
 41
 42impl<P, F, W> FixedLengthCRHGadget<CRH<P, W>, ConstraintF<P>> for CRHGadget<P, F>
 43where
 44    for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
 45    F: FieldVar<P::BaseField, ConstraintF<P>>,
 46    F: TwoBitLookupGadget<ConstraintF<P>, TableConstant = P::BaseField>
 47        + ThreeBitCondNegLookupGadget<ConstraintF<P>, TableConstant = P::BaseField>,
 48    P: TEModelParameters,
 49    W: Window,
 50{
 51    type OutputVar = AffineVar<P, F>;
 52    type ParametersVar = ParametersVar<P, W>;
 53
 54    fn evaluate(
 55        parameters: &Self::ParametersVar,
 56        input: &[UInt8<ConstraintF<P>>],
 57    ) -> Result<Self::OutputVar, SynthesisError> {
 58        // Pad the input if it is not the current length.
 59        let mut input_in_bits: Vec<Boolean<_>> = input
 60            .iter()
 61            .flat_map(|byte| byte.to_bits_le().unwrap())
 62            .collect();
 63        if (input_in_bits.len()) % CHUNK_SIZE != 0 {
 64            let current_length = input_in_bits.len();
 65            for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) {
 66                input_in_bits.push(Boolean::constant(false));
 67            }
 68        }
 69        assert!(input_in_bits.len() % CHUNK_SIZE == 0);
 70        assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
 71        for generators in parameters.params.generators.iter() {
 72            assert_eq!(generators.len(), W::WINDOW_SIZE);
 73        }
 74
 75        // Allocate new variable for the result.
 76        let input_in_bits = input_in_bits
 77            .chunks(W::WINDOW_SIZE * CHUNK_SIZE)
 78            .map(|x| x.chunks(CHUNK_SIZE).collect::<Vec<_>>())
 79            .collect::<Vec<_>>();
 80        let result = AffineVar::precomputed_base_3_bit_signed_digit_scalar_mul(
 81            &parameters.params.generators,
 82            &input_in_bits,
 83        )?;
 84
 85        Ok(result)
 86    }
 87}
 88
 89impl<P, W> AllocVar<Parameters<P>, ConstraintF<P>> for ParametersVar<P, W>
 90where
 91    P: TEModelParameters,
 92    W: Window,
 93{
 94    fn new_variable<T: Borrow<Parameters<P>>>(
 95        _cs: impl Into<Namespace<ConstraintF<P>>>,
 96        f: impl FnOnce() -> Result<T, SynthesisError>,
 97        _mode: AllocationMode,
 98    ) -> Result<Self, SynthesisError> {
 99        let params = f()?.borrow().clone();
100        Ok(ParametersVar {
101            params,
102            _window: PhantomData,
103        })
104    }
105}
106
107#[cfg(test)]
108mod test {
109    use rand::Rng;
110
111    use crate::crh::{
112        bowe_hopwood::{constraints::CRHGadget, CRH},
113        pedersen::Window as PedersenWindow,
114        FixedLengthCRH, FixedLengthCRHGadget,
115    };
116    use algebra::{
117        ed_on_bls12_381::{EdwardsParameters, Fq as Fr},
118        test_rng, ProjectiveCurve,
119    };
120    use r1cs_core::{ConstraintSystem, ConstraintSystemRef};
121    use r1cs_std::{alloc::AllocVar, ed_on_bls12_381::FqVar, uint8::UInt8, R1CSVar};
122
123    type TestCRH = CRH<EdwardsParameters, Window>;
124    type TestCRHGadget = CRHGadget<EdwardsParameters, FqVar>;
125
126    #[derive(Clone, PartialEq, Eq, Hash)]
127    pub(super) struct Window;
128
129    impl PedersenWindow for Window {
130        const WINDOW_SIZE: usize = 63;
131        const NUM_WINDOWS: usize = 8;
132    }
133
134    fn generate_input<R: Rng>(
135        cs: ConstraintSystemRef<Fr>,
136        rng: &mut R,
137    ) -> ([u8; 189], Vec<UInt8<Fr>>) {
138        let mut input = [1u8; 189];
139        rng.fill_bytes(&mut input);
140
141        let mut input_bytes = vec![];
142        for byte in input.iter() {
143            input_bytes.push(UInt8::new_witness(cs.clone(), || Ok(byte)).unwrap());
144        }
145        (input, input_bytes)
146    }
147
148    #[test]
149    fn test_native_equality() {
150        let rng = &mut test_rng();
151        let cs = ConstraintSystem::<Fr>::new_ref();
152
153        let (input, input_var) = generate_input(cs.clone(), rng);
154        println!("number of constraints for input: {}", cs.num_constraints());
155
156        let parameters = TestCRH::setup(rng).unwrap();
157        let primitive_result = TestCRH::evaluate(&parameters, &input).unwrap();
158
159        let parameters_var =
160            <TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersVar::new_witness(
161                cs.ns("parameters_var"),
162                || Ok(&parameters),
163            )
164            .unwrap();
165        println!(
166            "number of constraints for input + params: {}",
167            cs.num_constraints()
168        );
169
170        let result_var = TestCRHGadget::evaluate(&parameters_var, &input_var).unwrap();
171
172        println!("number of constraints total: {}", cs.num_constraints());
173
174        let primitive_result = primitive_result.into_affine();
175        assert_eq!(primitive_result, result_var.value().unwrap().into_affine());
176        assert!(cs.is_satisfied().unwrap());
177    }
178}