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