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<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect();
 60        if (input_in_bits.len()) % CHUNK_SIZE != 0 {
 61            let current_length = input_in_bits.len();
 62            for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) {
 63                input_in_bits.push(Boolean::constant(false));
 64            }
 65        }
 66        assert!(input_in_bits.len() % CHUNK_SIZE == 0);
 67        assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
 68        for generators in parameters.params.generators.iter() {
 69            assert_eq!(generators.len(), W::WINDOW_SIZE);
 70        }
 71
 72        // Allocate new variable for the result.
 73        let input_in_bits = input_in_bits
 74            .chunks(W::WINDOW_SIZE * CHUNK_SIZE)
 75            .map(|x| x.chunks(CHUNK_SIZE).collect::<Vec<_>>())
 76            .collect::<Vec<_>>();
 77        let result = AffineVar::precomputed_base_3_bit_signed_digit_scalar_mul(
 78            &parameters.params.generators,
 79            &input_in_bits,
 80        )?;
 81
 82        Ok(result)
 83    }
 84}
 85
 86impl<P, W> AllocVar<Parameters<P>, ConstraintF<P>> for ParametersVar<P, W>
 87where
 88    P: TEModelParameters,
 89    W: Window,
 90{
 91    fn new_variable<T: Borrow<Parameters<P>>>(
 92        _cs: impl Into<Namespace<ConstraintF<P>>>,
 93        f: impl FnOnce() -> Result<T, SynthesisError>,
 94        _mode: AllocationMode,
 95    ) -> Result<Self, SynthesisError> {
 96        let params = f()?.borrow().clone();
 97        Ok(ParametersVar {
 98            params,
 99            _window: PhantomData,
100        })
101    }
102}
103
104#[cfg(test)]
105mod test {
106    use rand::Rng;
107
108    use crate::crh::{
109        bowe_hopwood::{constraints::CRHGadget, CRH},
110        pedersen::Window as PedersenWindow,
111        FixedLengthCRH, FixedLengthCRHGadget,
112    };
113    use algebra::{
114        ed_on_bls12_381::{EdwardsParameters, Fq as Fr},
115        test_rng, ProjectiveCurve,
116    };
117    use r1cs_core::{ConstraintSystem, ConstraintSystemRef};
118    use r1cs_std::{alloc::AllocVar, ed_on_bls12_381::FqVar, uint8::UInt8, R1CSVar};
119
120    type TestCRH = CRH<EdwardsParameters, Window>;
121    type TestCRHGadget = CRHGadget<EdwardsParameters, FqVar>;
122
123    #[derive(Clone, PartialEq, Eq, Hash)]
124    pub(super) struct Window;
125
126    impl PedersenWindow for Window {
127        const WINDOW_SIZE: usize = 63;
128        const NUM_WINDOWS: usize = 8;
129    }
130
131    fn generate_input<R: Rng>(
132        cs: ConstraintSystemRef<Fr>,
133        rng: &mut R,
134    ) -> ([u8; 189], Vec<UInt8<Fr>>) {
135        let mut input = [1u8; 189];
136        rng.fill_bytes(&mut input);
137
138        let mut input_bytes = vec![];
139        for byte in input.iter() {
140            input_bytes.push(UInt8::new_witness(cs.clone(), || Ok(byte)).unwrap());
141        }
142        (input, input_bytes)
143    }
144
145    #[test]
146    fn test_native_equality() {
147        let rng = &mut test_rng();
148        let cs = ConstraintSystem::<Fr>::new_ref();
149
150        let (input, input_var) = generate_input(cs.clone(), rng);
151        println!("number of constraints for input: {}", cs.num_constraints());
152
153        let parameters = TestCRH::setup(rng).unwrap();
154        let primitive_result = TestCRH::evaluate(&parameters, &input).unwrap();
155
156        let parameters_var =
157            <TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersVar::new_witness(
158                cs.ns("parameters_var"),
159                || Ok(&parameters),
160            )
161            .unwrap();
162        println!(
163            "number of constraints for input + params: {}",
164            cs.num_constraints()
165        );
166
167        let result_var = TestCRHGadget::evaluate(&parameters_var, &input_var).unwrap();
168
169        println!("number of constraints total: {}", cs.num_constraints());
170
171        let primitive_result = primitive_result.into_affine();
172        assert_eq!(primitive_result, result_var.value().unwrap().into_affine());
173        assert!(cs.is_satisfied().unwrap());
174    }
175}