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