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 r1cs_std::prelude::*;
11use std::{borrow::Borrow, marker::PhantomData};
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 algebra::{
175 fields::jubjub::{fq::Fq, fr::Fr},
176 UniformRand,
177 };
178 use rand::thread_rng;
179
180 use crate::{
181 commitment::{
182 pedersen::{
183 constraints::PedersenCommitmentGadget, PedersenCommitment, PedersenRandomness,
184 },
185 CommitmentGadget, CommitmentScheme,
186 },
187 crh::pedersen::PedersenWindow,
188 };
189 use algebra::curves::{jubjub::JubJubProjective as JubJub, ProjectiveCurve};
190 use r1cs_core::ConstraintSystem;
191 use r1cs_std::{
192 groups::jubjub::JubJubGadget, prelude::*, test_constraint_system::TestConstraintSystem,
193 };
194
195 #[test]
196 fn commitment_gadget_test() {
197 let mut cs = TestConstraintSystem::<Fq>::new();
198
199 #[derive(Clone, PartialEq, Eq, Hash)]
200 pub(super) struct Window;
201
202 impl PedersenWindow for Window {
203 const WINDOW_SIZE: usize = 4;
204 const NUM_WINDOWS: usize = 8;
205 }
206
207 let input = [1u8; 4];
208
209 let rng = &mut thread_rng();
210
211 type TestCOMM = PedersenCommitment<JubJub, Window>;
212 type TestCOMMGadget = PedersenCommitmentGadget<JubJub, Fq, JubJubGadget>;
213
214 let randomness = PedersenRandomness(Fr::rand(rng));
215
216 let parameters = PedersenCommitment::<JubJub, Window>::setup(rng).unwrap();
217 let primitive_result =
218 PedersenCommitment::<JubJub, Window>::commit(¶meters, &input, &randomness).unwrap();
219
220 let mut input_bytes = vec![];
221 for (byte_i, input_byte) in input.into_iter().enumerate() {
222 let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
223 input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
224 }
225
226 let randomness =
227 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessGadget::alloc(
228 &mut cs.ns(|| "gadget_randomness"),
229 || Ok(&randomness),
230 )
231 .unwrap();
232 let gadget_parameters =
233 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersGadget::alloc(
234 &mut cs.ns(|| "gadget_parameters"),
235 || Ok(¶meters),
236 )
237 .unwrap();
238 let gadget_result =
239 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::check_commitment_gadget(
240 &mut cs.ns(|| "gadget_evaluation"),
241 &gadget_parameters,
242 &input_bytes,
243 &randomness,
244 )
245 .unwrap();
246
247 let primitive_result = primitive_result.into_affine();
248 assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
249 assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
250 assert!(cs.is_satisfied());
251 }
252}