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 ¶meters.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(¶meters, &input).unwrap();
155
156 let parameters_var =
157 <TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersVar::new_witness(
158 cs.ns("parameters_var"),
159 || Ok(¶meters),
160 )
161 .unwrap();
162 println!(
163 "number of constraints for input + params: {}",
164 cs.num_constraints()
165 );
166
167 let result_var = TestCRHGadget::evaluate(¶meters_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}