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