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