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