ark-r1cs-std/crypto-primitives/src/crh/bowe_hopwood/constraints.rs
crypto-primitives/src/crh/bowe_hopwood/constraints.rsRaw
1use core::{borrow::Borrow, hash::Hash, marker::PhantomData};
2
3use crate::{
4 crh::{
5 bowe_hopwood::{BoweHopwoodPedersenCRH, BoweHopwoodPedersenParameters, CHUNK_SIZE},
6 pedersen::PedersenWindow,
7 FixedLengthCRHGadget,
8 },
9 Vec,
10};
11use algebra_core::{groups::Group, Field};
12use r1cs_core::{ConstraintSystem, SynthesisError};
13use r1cs_std::{alloc::AllocGadget, groups::GroupGadget, uint8::UInt8};
14
15use r1cs_std::bits::boolean::Boolean;
16
17#[derive(Derivative)]
18#[derivative(Clone(
19 bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"
20))]
21pub struct BoweHopwoodPedersenCRHGadgetParameters<
22 G: Group,
23 W: PedersenWindow,
24 ConstraintF: Field,
25 GG: GroupGadget<G, ConstraintF>,
26> {
27 params: BoweHopwoodPedersenParameters<G>,
28 _group_g: PhantomData<GG>,
29 _engine: PhantomData<ConstraintF>,
30 _window: PhantomData<W>,
31}
32
33pub struct BoweHopwoodPedersenCRHGadget<
34 G: Group,
35 ConstraintF: Field,
36 GG: GroupGadget<G, ConstraintF>,
37> {
38 _group: PhantomData<*const G>,
39 _group_gadget: PhantomData<*const GG>,
40 _engine: PhantomData<ConstraintF>,
41}
42
43impl<ConstraintF, G, GG, W> FixedLengthCRHGadget<BoweHopwoodPedersenCRH<G, W>, ConstraintF>
44 for BoweHopwoodPedersenCRHGadget<G, ConstraintF, GG>
45where
46 ConstraintF: Field,
47 G: Group + Hash,
48 GG: GroupGadget<G, ConstraintF>,
49 W: PedersenWindow,
50{
51 type OutputGadget = GG;
52 type ParametersGadget = BoweHopwoodPedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
53
54 fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
55 cs: CS,
56 parameters: &Self::ParametersGadget,
57 input: &[UInt8],
58 ) -> Result<Self::OutputGadget, SynthesisError> {
59 // Pad the input if it is not the current length.
60 let mut input_in_bits: Vec<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect();
61 if (input_in_bits.len()) % CHUNK_SIZE != 0 {
62 let current_length = input_in_bits.len();
63 for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) {
64 input_in_bits.push(Boolean::constant(false));
65 }
66 }
67 assert!(input_in_bits.len() % CHUNK_SIZE == 0);
68 assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
69 for generators in parameters.params.generators.iter() {
70 assert_eq!(generators.len(), W::WINDOW_SIZE);
71 }
72
73 // Allocate new variable for the result.
74 let input_in_bits = input_in_bits
75 .chunks(W::WINDOW_SIZE * CHUNK_SIZE)
76 .map(|x| x.chunks(CHUNK_SIZE).collect::<Vec<_>>())
77 .collect::<Vec<_>>();
78 let result = GG::precomputed_base_3_bit_signed_digit_scalar_mul(
79 cs,
80 ¶meters.params.generators,
81 &input_in_bits,
82 )?;
83
84 Ok(result)
85 }
86}
87
88impl<G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
89 AllocGadget<BoweHopwoodPedersenParameters<G>, ConstraintF>
90 for BoweHopwoodPedersenCRHGadgetParameters<G, W, ConstraintF, GG>
91{
92 fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
93 _cs: CS,
94 val: T,
95 ) -> Result<Self, SynthesisError>
96 where
97 T: Borrow<BoweHopwoodPedersenParameters<G>>,
98 {
99 let params = val.borrow().clone();
100 Ok(BoweHopwoodPedersenCRHGadgetParameters {
101 params,
102 _group_g: PhantomData,
103 _engine: PhantomData,
104 _window: PhantomData,
105 })
106 }
107
108 fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
109 cs: CS,
110 value_gen: F,
111 ) -> Result<Self, SynthesisError>
112 where
113 F: FnOnce() -> Result<T, SynthesisError>,
114 T: Borrow<BoweHopwoodPedersenParameters<G>>,
115 {
116 let params = value_gen()?.borrow().clone();
117 Self::alloc_constant(cs, params)
118 }
119
120 fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
121 _cs: CS,
122 value_gen: F,
123 ) -> Result<Self, SynthesisError>
124 where
125 F: FnOnce() -> Result<T, SynthesisError>,
126 T: Borrow<BoweHopwoodPedersenParameters<G>>,
127 {
128 let params = value_gen()?.borrow().clone();
129 Ok(BoweHopwoodPedersenCRHGadgetParameters {
130 params,
131 _group_g: PhantomData,
132 _engine: PhantomData,
133 _window: PhantomData,
134 })
135 }
136}
137
138#[cfg(test)]
139mod test {
140 use rand::Rng;
141
142 use crate::crh::{
143 bowe_hopwood::{constraints::BoweHopwoodPedersenCRHGadget, BoweHopwoodPedersenCRH},
144 pedersen::PedersenWindow,
145 FixedLengthCRH, FixedLengthCRHGadget,
146 };
147 use algebra::{
148 edwards_on_bls12_381::{EdwardsProjective as JubJub, Fq as Fr},
149 test_rng, ProjectiveCurve,
150 };
151 use r1cs_core::ConstraintSystem;
152 use r1cs_std::{
153 alloc::AllocGadget, edwards_on_bls12_381::EdwardsGadget,
154 test_constraint_system::TestConstraintSystem, uint8::UInt8,
155 };
156
157 type TestCRH = BoweHopwoodPedersenCRH<JubJub, Window>;
158 type TestCRHGadget = BoweHopwoodPedersenCRHGadget<JubJub, Fr, EdwardsGadget>;
159
160 #[derive(Clone, PartialEq, Eq, Hash)]
161 pub(super) struct Window;
162
163 impl PedersenWindow for Window {
164 const WINDOW_SIZE: usize = 63;
165 const NUM_WINDOWS: usize = 8;
166 }
167
168 fn generate_input<CS: ConstraintSystem<Fr>, R: Rng>(
169 mut cs: CS,
170 rng: &mut R,
171 ) -> ([u8; 189], Vec<UInt8>) {
172 let mut input = [1u8; 189];
173 rng.fill_bytes(&mut input);
174
175 let mut input_bytes = vec![];
176 for (byte_i, input_byte) in input.iter().enumerate() {
177 let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
178 input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
179 }
180 (input, input_bytes)
181 }
182
183 #[test]
184 fn crh_primitive_gadget_test() {
185 let rng = &mut test_rng();
186 let mut cs = TestConstraintSystem::<Fr>::new();
187
188 let (input, input_bytes) = generate_input(&mut cs, rng);
189 println!("number of constraints for input: {}", cs.num_constraints());
190
191 let parameters = TestCRH::setup(rng).unwrap();
192 let primitive_result = TestCRH::evaluate(¶meters, &input).unwrap();
193
194 let gadget_parameters =
195 <TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersGadget::alloc(
196 &mut cs.ns(|| "gadget_parameters"),
197 || Ok(¶meters),
198 )
199 .unwrap();
200 println!(
201 "number of constraints for input + params: {}",
202 cs.num_constraints()
203 );
204
205 let gadget_result =
206 <TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::check_evaluation_gadget(
207 &mut cs.ns(|| "gadget_evaluation"),
208 &gadget_parameters,
209 &input_bytes,
210 )
211 .unwrap();
212
213 println!("number of constraints total: {}", cs.num_constraints());
214
215 let primitive_result = primitive_result.into_affine();
216 assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
217 assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
218 assert!(cs.is_satisfied());
219 }
220}