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