ark-r1cs-std/crypto-primitives/src/commitment/pedersen/constraints.rs
crypto-primitives/src/commitment/pedersen/constraints.rsRaw
1use crate::{
2 commitment::pedersen::{PedersenCommitment, PedersenParameters, PedersenRandomness},
3 crh::pedersen::PedersenWindow,
4 Vec,
5};
6use algebra_core::{
7 fields::{Field, PrimeField},
8 to_bytes, Group, ToBytes,
9};
10use r1cs_core::{ConstraintSystem, SynthesisError};
11
12use crate::commitment::CommitmentGadget;
13use core::{borrow::Borrow, marker::PhantomData};
14use r1cs_std::prelude::*;
15
16#[derive(Derivative)]
17#[derivative(Clone(bound = "G: Group, W: PedersenWindow, ConstraintF: Field"))]
18pub struct PedersenCommitmentGadgetParameters<G: Group, W: PedersenWindow, ConstraintF: Field> {
19 params: PedersenParameters<G>,
20 #[doc(hidden)]
21 _group: PhantomData<G>,
22 #[doc(hidden)]
23 _engine: PhantomData<ConstraintF>,
24 #[doc(hidden)]
25 _window: PhantomData<W>,
26}
27
28#[derive(Clone, Debug)]
29pub struct PedersenRandomnessGadget(Vec<UInt8>);
30
31pub struct PedersenCommitmentGadget<G: Group, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>(
32 #[doc(hidden)] PhantomData<*const G>,
33 #[doc(hidden)] PhantomData<*const GG>,
34 PhantomData<ConstraintF>,
35);
36
37impl<ConstraintF, G, GG, W> CommitmentGadget<PedersenCommitment<G, W>, ConstraintF>
38 for PedersenCommitmentGadget<G, ConstraintF, GG>
39where
40 ConstraintF: PrimeField,
41 G: Group,
42 GG: GroupGadget<G, ConstraintF>,
43 W: PedersenWindow,
44{
45 type OutputGadget = GG;
46 type ParametersGadget = PedersenCommitmentGadgetParameters<G, W, ConstraintF>;
47 type RandomnessGadget = PedersenRandomnessGadget;
48
49 fn check_commitment_gadget<CS: ConstraintSystem<ConstraintF>>(
50 mut cs: CS,
51 parameters: &Self::ParametersGadget,
52 input: &[UInt8],
53 r: &Self::RandomnessGadget,
54 ) -> Result<Self::OutputGadget, SynthesisError> {
55 assert!((input.len() * 8) <= (W::WINDOW_SIZE * W::NUM_WINDOWS));
56
57 let mut padded_input = input.to_vec();
58 // Pad if input length is less than `W::WINDOW_SIZE * W::NUM_WINDOWS`.
59 if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
60 let current_length = input.len();
61 for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
62 padded_input.push(UInt8::constant(0u8));
63 }
64 }
65
66 assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS);
67 assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
68
69 // Allocate new variable for commitment output.
70 let input_in_bits: Vec<_> = padded_input
71 .iter()
72 .flat_map(|byte| byte.into_bits_le())
73 .collect();
74 let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE);
75 let mut result = GG::precomputed_base_multiscalar_mul(
76 cs.ns(|| "multiexp"),
77 ¶meters.params.generators,
78 input_in_bits,
79 )?;
80
81 // Compute h^r
82 let rand_bits: Vec<_> = r.0.iter().flat_map(|byte| byte.into_bits_le()).collect();
83 result.precomputed_base_scalar_mul(
84 cs.ns(|| "Randomizer"),
85 rand_bits
86 .iter()
87 .zip(¶meters.params.randomness_generator),
88 )?;
89
90 Ok(result)
91 }
92}
93
94impl<G, W, ConstraintF> AllocGadget<PedersenParameters<G>, ConstraintF>
95 for PedersenCommitmentGadgetParameters<G, W, ConstraintF>
96where
97 G: Group,
98 W: PedersenWindow,
99 ConstraintF: PrimeField,
100{
101 fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
102 _cs: CS,
103 val: T,
104 ) -> Result<Self, SynthesisError>
105 where
106 T: Borrow<PedersenParameters<G>>,
107 {
108 let parameters = val.borrow().clone();
109
110 Ok(PedersenCommitmentGadgetParameters {
111 params: parameters,
112 _group: PhantomData,
113 _engine: PhantomData,
114 _window: PhantomData,
115 })
116 }
117
118 fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
119 cs: CS,
120 value_gen: F,
121 ) -> Result<Self, SynthesisError>
122 where
123 F: FnOnce() -> Result<T, SynthesisError>,
124 T: Borrow<PedersenParameters<G>>,
125 {
126 let temp = value_gen()?;
127 Self::alloc_constant(cs, temp)
128 }
129
130 fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
131 _cs: CS,
132 value_gen: F,
133 ) -> Result<Self, SynthesisError>
134 where
135 F: FnOnce() -> Result<T, SynthesisError>,
136 T: Borrow<PedersenParameters<G>>,
137 {
138 let temp = value_gen()?;
139 let parameters = temp.borrow().clone();
140
141 Ok(PedersenCommitmentGadgetParameters {
142 params: parameters,
143 _group: PhantomData,
144 _engine: PhantomData,
145 _window: PhantomData,
146 })
147 }
148}
149
150impl<G, ConstraintF> AllocGadget<PedersenRandomness<G>, ConstraintF> for PedersenRandomnessGadget
151where
152 G: Group,
153 ConstraintF: PrimeField,
154{
155 fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
156 mut cs: CS,
157 val: T,
158 ) -> Result<Self, SynthesisError>
159 where
160 T: Borrow<PedersenRandomness<G>>,
161 {
162 let mut result_bytes = vec![];
163 for (i, byte) in to_bytes![val.borrow().0].unwrap().into_iter().enumerate() {
164 let cur = UInt8::alloc_constant(cs.ns(|| format!("byte {}", i)), byte)?;
165 result_bytes.push(cur);
166 }
167 Ok(PedersenRandomnessGadget(result_bytes))
168 }
169
170 fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
171 cs: CS,
172 value_gen: F,
173 ) -> Result<Self, SynthesisError>
174 where
175 F: FnOnce() -> Result<T, SynthesisError>,
176 T: Borrow<PedersenRandomness<G>>,
177 {
178 let temp = value_gen()?;
179 let randomness = to_bytes![temp.borrow().0].unwrap();
180 Ok(PedersenRandomnessGadget(UInt8::alloc_vec(cs, &randomness)?))
181 }
182
183 fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
184 cs: CS,
185 value_gen: F,
186 ) -> Result<Self, SynthesisError>
187 where
188 F: FnOnce() -> Result<T, SynthesisError>,
189 T: Borrow<PedersenRandomness<G>>,
190 {
191 let temp = value_gen()?;
192 let randomness = to_bytes![temp.borrow().0].unwrap();
193 Ok(PedersenRandomnessGadget(UInt8::alloc_input_vec(
194 cs,
195 &randomness,
196 )?))
197 }
198}
199
200#[cfg(test)]
201mod test {
202 use algebra::{
203 jubjub::{Fq, Fr, JubJubProjective as JubJub},
204 test_rng, ProjectiveCurve, UniformRand,
205 };
206
207 use crate::{
208 commitment::{
209 pedersen::{
210 constraints::PedersenCommitmentGadget, PedersenCommitment, PedersenRandomness,
211 },
212 CommitmentGadget, CommitmentScheme,
213 },
214 crh::pedersen::PedersenWindow,
215 };
216 use r1cs_core::ConstraintSystem;
217 use r1cs_std::{
218 jubjub::JubJubGadget, prelude::*, test_constraint_system::TestConstraintSystem,
219 };
220
221 #[test]
222 fn commitment_gadget_test() {
223 let mut cs = TestConstraintSystem::<Fq>::new();
224
225 #[derive(Clone, PartialEq, Eq, Hash)]
226 pub(super) struct Window;
227
228 impl PedersenWindow for Window {
229 const WINDOW_SIZE: usize = 4;
230 const NUM_WINDOWS: usize = 8;
231 }
232
233 let input = [1u8; 4];
234
235 let rng = &mut test_rng();
236
237 type TestCOMM = PedersenCommitment<JubJub, Window>;
238 type TestCOMMGadget = PedersenCommitmentGadget<JubJub, Fq, JubJubGadget>;
239
240 let randomness = PedersenRandomness(Fr::rand(rng));
241
242 let parameters = PedersenCommitment::<JubJub, Window>::setup(rng).unwrap();
243 let primitive_result =
244 PedersenCommitment::<JubJub, Window>::commit(¶meters, &input, &randomness).unwrap();
245
246 let mut input_bytes = vec![];
247 for (byte_i, input_byte) in input.iter().enumerate() {
248 let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
249 input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
250 }
251
252 let randomness =
253 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessGadget::alloc(
254 &mut cs.ns(|| "gadget_randomness"),
255 || Ok(&randomness),
256 )
257 .unwrap();
258 let gadget_parameters =
259 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersGadget::alloc(
260 &mut cs.ns(|| "gadget_parameters"),
261 || Ok(¶meters),
262 )
263 .unwrap();
264 let gadget_result =
265 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::check_commitment_gadget(
266 &mut cs.ns(|| "gadget_evaluation"),
267 &gadget_parameters,
268 &input_bytes,
269 &randomness,
270 )
271 .unwrap();
272
273 let primitive_result = primitive_result.into_affine();
274 assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
275 assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
276 assert!(cs.is_satisfied());
277 }
278}