ark-r1cs-std/crypto-primitives/src/commitment/pedersen/constraints.rs
crypto-primitives/src/commitment/pedersen/constraints.rsRaw
1use crate::{
2 commitment::pedersen::{Commitment, Parameters, Randomness},
3 crh::pedersen::Window,
4 Vec,
5};
6use algebra_core::{
7 fields::{Field, PrimeField},
8 to_bytes, ProjectiveCurve, Zero,
9};
10use r1cs_core::{Namespace, SynthesisError};
11
12use core::{borrow::Borrow, marker::PhantomData};
13use r1cs_std::prelude::*;
14
15type ConstraintF<C> = <<C as ProjectiveCurve>::BaseField as Field>::BasePrimeField;
16
17#[derive(Derivative)]
18#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>"))]
19pub struct ParametersVar<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>>
20where
21 for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
22{
23 params: Parameters<C>,
24 #[doc(hidden)]
25 _group_var: PhantomData<GG>,
26}
27
28#[derive(Clone, Debug)]
29pub struct RandomnessVar<F: Field>(Vec<UInt8<F>>);
30
31pub struct CommGadget<C: ProjectiveCurve, GG: CurveVar<C, ConstraintF<C>>, W: Window>
32where
33 for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
34{
35 #[doc(hidden)]
36 _curve: PhantomData<*const C>,
37 #[doc(hidden)]
38 _group_var: PhantomData<*const GG>,
39 #[doc(hidden)]
40 _window: PhantomData<*const W>,
41}
42
43impl<C, GG, W> crate::commitment::CommitmentGadget<Commitment<C, W>, ConstraintF<C>>
44 for CommGadget<C, GG, W>
45where
46 C: ProjectiveCurve,
47 GG: CurveVar<C, ConstraintF<C>>,
48 W: Window,
49 for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
50 ConstraintF<C>: PrimeField,
51{
52 type OutputVar = GG;
53 type ParametersVar = ParametersVar<C, GG>;
54 type RandomnessVar = RandomnessVar<ConstraintF<C>>;
55
56 #[tracing::instrument(target = "r1cs", skip(parameters, r))]
57 fn commit(
58 parameters: &Self::ParametersVar,
59 input: &[UInt8<ConstraintF<C>>],
60 r: &Self::RandomnessVar,
61 ) -> Result<Self::OutputVar, SynthesisError> {
62 assert!((input.len() * 8) <= (W::WINDOW_SIZE * W::NUM_WINDOWS));
63
64 let mut padded_input = input.to_vec();
65 // Pad if input length is less than `W::WINDOW_SIZE * W::NUM_WINDOWS`.
66 if (input.len() * 8) < W::WINDOW_SIZE * W::NUM_WINDOWS {
67 let current_length = input.len();
68 for _ in current_length..((W::WINDOW_SIZE * W::NUM_WINDOWS) / 8) {
69 padded_input.push(UInt8::constant(0u8));
70 }
71 }
72
73 assert_eq!(padded_input.len() * 8, W::WINDOW_SIZE * W::NUM_WINDOWS);
74 assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
75
76 // Allocate new variable for commitment output.
77 let input_in_bits: Vec<Boolean<_>> = padded_input
78 .iter()
79 .flat_map(|byte| byte.to_bits_le().unwrap())
80 .collect();
81 let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE);
82 let mut result =
83 GG::precomputed_base_multiscalar_mul_le(¶meters.params.generators, input_in_bits)?;
84
85 // Compute h^r
86 let rand_bits: Vec<_> =
87 r.0.iter()
88 .flat_map(|byte| byte.to_bits_le().unwrap())
89 .collect();
90 result.precomputed_base_scalar_mul_le(
91 rand_bits
92 .iter()
93 .zip(¶meters.params.randomness_generator),
94 )?;
95
96 Ok(result)
97 }
98}
99
100impl<C, GG> AllocVar<Parameters<C>, ConstraintF<C>> for ParametersVar<C, GG>
101where
102 C: ProjectiveCurve,
103 GG: CurveVar<C, ConstraintF<C>>,
104 for<'a> &'a GG: GroupOpsBounds<'a, C, GG>,
105{
106 fn new_variable<T: Borrow<Parameters<C>>>(
107 _cs: impl Into<Namespace<ConstraintF<C>>>,
108 f: impl FnOnce() -> Result<T, SynthesisError>,
109 _mode: AllocationMode,
110 ) -> Result<Self, SynthesisError> {
111 let params = f()?.borrow().clone();
112 Ok(ParametersVar {
113 params,
114 _group_var: PhantomData,
115 })
116 }
117}
118
119impl<C, F> AllocVar<Randomness<C>, F> for RandomnessVar<F>
120where
121 C: ProjectiveCurve,
122 F: PrimeField,
123{
124 fn new_variable<T: Borrow<Randomness<C>>>(
125 cs: impl Into<Namespace<F>>,
126 f: impl FnOnce() -> Result<T, SynthesisError>,
127 mode: AllocationMode,
128 ) -> Result<Self, SynthesisError> {
129 let r = to_bytes![&f().map(|b| b.borrow().0).unwrap_or(C::ScalarField::zero())].unwrap();
130 match mode {
131 AllocationMode::Constant => Ok(Self(UInt8::constant_vec(&r))),
132 AllocationMode::Input => UInt8::new_input_vec(cs, &r).map(Self),
133 AllocationMode::Witness => UInt8::new_witness_vec(cs, &r).map(Self),
134 }
135 }
136}
137
138#[cfg(test)]
139mod test {
140 use algebra::{
141 ed_on_bls12_381::{EdwardsProjective as JubJub, Fq, Fr},
142 test_rng, UniformRand,
143 };
144
145 use crate::{
146 commitment::{
147 pedersen::{constraints::CommGadget, Commitment, Randomness},
148 CommitmentGadget, CommitmentScheme,
149 },
150 crh::pedersen,
151 };
152 use r1cs_core::ConstraintSystem;
153 use r1cs_std::{ed_on_bls12_381::EdwardsVar, prelude::*};
154
155 #[test]
156 fn commitment_gadget_test() {
157 let cs = ConstraintSystem::<Fq>::new_ref();
158
159 #[derive(Clone, PartialEq, Eq, Hash)]
160 pub(super) struct Window;
161
162 impl pedersen::Window for Window {
163 const WINDOW_SIZE: usize = 4;
164 const NUM_WINDOWS: usize = 8;
165 }
166
167 let input = [1u8; 4];
168
169 let rng = &mut test_rng();
170
171 type TestCOMM = Commitment<JubJub, Window>;
172 type TestCOMMGadget = CommGadget<JubJub, EdwardsVar, Window>;
173
174 let randomness = Randomness(Fr::rand(rng));
175
176 let parameters = Commitment::<JubJub, Window>::setup(rng).unwrap();
177 let primitive_result =
178 Commitment::<JubJub, Window>::commit(¶meters, &input, &randomness).unwrap();
179
180 let mut input_var = vec![];
181 for input_byte in input.iter() {
182 input_var.push(UInt8::new_witness(cs.clone(), || Ok(*input_byte)).unwrap());
183 }
184
185 let randomness_var =
186 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::RandomnessVar::new_witness(
187 r1cs_core::ns!(cs, "gadget_randomness"),
188 || Ok(&randomness),
189 )
190 .unwrap();
191 let parameters_var =
192 <TestCOMMGadget as CommitmentGadget<TestCOMM, Fq>>::ParametersVar::new_witness(
193 r1cs_core::ns!(cs, "gadget_parameters"),
194 || Ok(¶meters),
195 )
196 .unwrap();
197 let result_var =
198 TestCOMMGadget::commit(¶meters_var, &input_var, &randomness_var).unwrap();
199
200 let primitive_result = primitive_result;
201 assert_eq!(primitive_result, result_var.value().unwrap());
202 assert!(cs.is_satisfied().unwrap());
203 }
204}