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<F, T, CS: ConstraintSystem<ConstraintF>>(
102 _cs: CS,
103 value_gen: F,
104 ) -> Result<Self, SynthesisError>
105 where
106 F: FnOnce() -> Result<T, SynthesisError>,
107 T: Borrow<PedersenParameters<G>>,
108 {
109 let temp = value_gen()?;
110 let parameters = temp.borrow().clone();
111
112 Ok(PedersenCommitmentGadgetParameters {
113 params: parameters,
114 _group: PhantomData,
115 _engine: PhantomData,
116 _window: PhantomData,
117 })
118 }
119
120 fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
121 _cs: CS,
122 value_gen: F,
123 ) -> Result<Self, SynthesisError>
124 where
125 F: FnOnce() -> Result<T, SynthesisError>,
126 T: Borrow<PedersenParameters<G>>,
127 {
128 let temp = value_gen()?;
129 let parameters = temp.borrow().clone();
130
131 Ok(PedersenCommitmentGadgetParameters {
132 params: parameters,
133 _group: PhantomData,
134 _engine: PhantomData,
135 _window: PhantomData,
136 })
137 }
138}
139
140impl<G, ConstraintF> AllocGadget<PedersenRandomness<G>, ConstraintF> for PedersenRandomnessGadget
141where
142 G: Group,
143 ConstraintF: PrimeField,
144{
145 fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
146 cs: CS,
147 value_gen: F,
148 ) -> Result<Self, SynthesisError>
149 where
150 F: FnOnce() -> Result<T, SynthesisError>,
151 T: Borrow<PedersenRandomness<G>>,
152 {
153 let temp = value_gen()?;
154 let randomness = to_bytes![temp.borrow().0].unwrap();
155 Ok(PedersenRandomnessGadget(UInt8::alloc_vec(cs, &randomness)?))
156 }
157
158 fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
159 cs: CS,
160 value_gen: F,
161 ) -> Result<Self, SynthesisError>
162 where
163 F: FnOnce() -> Result<T, SynthesisError>,
164 T: Borrow<PedersenRandomness<G>>,
165 {
166 let temp = value_gen()?;
167 let randomness = to_bytes![temp.borrow().0].unwrap();
168 Ok(PedersenRandomnessGadget(UInt8::alloc_input_vec(
169 cs,
170 &randomness,
171 )?))
172 }
173}
174
175#[cfg(test)]
176mod test {
177 use algebra::{
178 jubjub::{Fq, Fr, JubJubProjective as JubJub},
179 test_rng, ProjectiveCurve, UniformRand,
180 };
181
182 use crate::{
183 commitment::{
184 pedersen::{
185 constraints::PedersenCommitmentGadget, PedersenCommitment, PedersenRandomness,
186 },
187 CommitmentGadget, CommitmentScheme,
188 },
189 crh::pedersen::PedersenWindow,
190 };
191 use r1cs_core::ConstraintSystem;
192 use r1cs_std::{
193 jubjub::JubJubGadget, prelude::*, test_constraint_system::TestConstraintSystem,
194 };
195
196 #[test]
197 fn commitment_gadget_test() {
198 let mut cs = TestConstraintSystem::<Fq>::new();
199
200 #[derive(Clone, PartialEq, Eq, Hash)]
201 pub(super) struct Window;
202
203 impl PedersenWindow for Window {
204 const WINDOW_SIZE: usize = 4;
205 const NUM_WINDOWS: usize = 8;
206 }
207
208 let input = [1u8; 4];
209
210 let rng = &mut test_rng();
211
212 type TestCOMM = PedersenCommitment<JubJub, Window>;
213 type TestCOMMGadget = PedersenCommitmentGadget<JubJub, Fq, JubJubGadget>;
214
215 let randomness = PedersenRandomness(Fr::rand(rng));
216
217 let parameters = PedersenCommitment::<JubJub, Window>::setup(rng).unwrap();
218 let primitive_result =
219 PedersenCommitment::<JubJub, Window>::commit(¶meters, &input, &randomness).unwrap();
220
221 let mut input_bytes = vec![];
222 for (byte_i, input_byte) in input.iter().enumerate() {
223 let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
224 input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
225 }
226
227 let randomness =
228 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessGadget::alloc(
229 &mut cs.ns(|| "gadget_randomness"),
230 || Ok(&randomness),
231 )
232 .unwrap();
233 let gadget_parameters =
234 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersGadget::alloc(
235 &mut cs.ns(|| "gadget_parameters"),
236 || Ok(¶meters),
237 )
238 .unwrap();
239 let gadget_result =
240 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::check_commitment_gadget(
241 &mut cs.ns(|| "gadget_evaluation"),
242 &gadget_parameters,
243 &input_bytes,
244 &randomness,
245 )
246 .unwrap();
247
248 let primitive_result = primitive_result.into_affine();
249 assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
250 assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
251 assert!(cs.is_satisfied());
252 }
253}