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