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