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