fullcodec_plonk/constraint_system/ecc/scalar_mul/
fixed_base.rs1use crate::constraint_system::ecc::curve_addition::fixed_base_gate::WnafRound;
8use crate::constraint_system::{
9 Constraint, TurboComposer, Witness, WitnessPoint,
10};
11use dusk_bls12_381::BlsScalar;
12use dusk_bytes::Serializable;
13use dusk_jubjub::{JubJubAffine, JubJubExtended, JubJubScalar};
14use sp_std::vec;
15use sp_std::vec::Vec;
16
17fn compute_wnaf_point_multiples(
18 generator: JubJubExtended,
19 num_bits: usize,
20) -> Vec<JubJubAffine> {
21 assert_eq!(generator.is_prime_order().unwrap_u8(), 1);
22
23 let mut multiples = vec![JubJubExtended::default(); num_bits];
24 multiples[0] = generator;
25 for i in 1..num_bits {
26 multiples[i] = multiples[i - 1].double();
27 }
28
29 dusk_jubjub::batch_normalize(&mut multiples).collect()
30}
31
32impl TurboComposer {
33 pub fn component_mul_generator<P: Into<JubJubExtended>>(
37 &mut self,
38 jubjub: Witness,
39 generator: P,
40 ) -> WitnessPoint {
41 let generator = generator.into();
42
43 let num_bits = 256;
48
49 let mut point_multiples =
51 compute_wnaf_point_multiples(generator, num_bits);
52
53 point_multiples.reverse();
54
55 let raw_jubjub_scalar =
60 JubJubScalar::from_bytes(&self.witnesses[&jubjub].to_bytes())
61 .unwrap();
62
63 let wnaf_entries = raw_jubjub_scalar.compute_windowed_naf(2);
65 assert_eq!(wnaf_entries.len(), num_bits);
66
67 let mut scalar_acc = vec![BlsScalar::zero()];
69 let mut point_acc = vec![JubJubAffine::identity()];
70
71 let mut xy_alphas = Vec::new();
73
74 for (i, entry) in wnaf_entries.iter().rev().enumerate() {
76 let (scalar_to_add, point_to_add) = match entry {
78 0 => { (BlsScalar::zero(), JubJubAffine::identity()) }
79 -1 => { (BlsScalar::one().neg(), -point_multiples[i]) }
80 1 => { (BlsScalar::one(), point_multiples[i]) }
81 _ => unreachable!("Currently WNAF_2(k) is supported. The possible values are 1, -1 and 0. Current entry is {}", entry),
82 };
83
84 let prev_accumulator = BlsScalar::from(2u64) * scalar_acc[i];
85 scalar_acc.push(prev_accumulator + scalar_to_add);
86 point_acc.push(
87 (JubJubExtended::from(point_acc[i])
88 + JubJubExtended::from(point_to_add))
89 .into(),
90 );
91
92 let x_alpha = point_to_add.get_x();
93 let y_alpha = point_to_add.get_y();
94
95 xy_alphas.push(x_alpha * y_alpha);
96 }
97
98 for i in 0..num_bits {
99 let acc_x = self.append_witness(point_acc[i].get_x());
100 let acc_y = self.append_witness(point_acc[i].get_y());
101
102 let accumulated_bit = self.append_witness(scalar_acc[i]);
103
104 if i == 0 {
107 self.assert_equal_constant(acc_x, BlsScalar::zero(), None);
108 self.assert_equal_constant(acc_y, BlsScalar::one(), None);
109 self.assert_equal_constant(
110 accumulated_bit,
111 BlsScalar::zero(),
112 None,
113 );
114 }
115
116 let x_beta = point_multiples[i].get_x();
117 let y_beta = point_multiples[i].get_y();
118
119 let xy_alpha = self.append_witness(xy_alphas[i]);
120
121 let xy_beta = x_beta * y_beta;
122
123 let wnaf_round = WnafRound {
124 acc_x,
125 acc_y,
126 accumulated_bit,
127 xy_alpha,
128 x_beta,
129 y_beta,
130 xy_beta,
131 };
132
133 self.fixed_group_add(wnaf_round);
134 }
135
136 let acc_x = self.append_witness(point_acc[num_bits].get_x());
139 let acc_y = self.append_witness(point_acc[num_bits].get_y());
140 let last_accumulated_bit = self.append_witness(scalar_acc[num_bits]);
141
142 let constraint =
145 Constraint::new().a(acc_x).b(acc_y).d(last_accumulated_bit);
146 self.append_gate(constraint);
147
148 self.assert_equal(last_accumulated_bit, jubjub);
151
152 WitnessPoint { x: acc_x, y: acc_y }
153 }
154}
155
156#[cfg(feature = "std")]
157#[cfg(test)]
158mod tests {
159 use super::*;
160 use crate::constraint_system::helper::*;
161 use dusk_jubjub::GENERATOR_EXTENDED;
162
163 #[test]
164 fn test_ecc_constraint() {
165 let res = gadget_tester(
166 |composer| {
167 let scalar = JubJubScalar::from_bytes_wide(&[
168 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204,
169 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 103, 6, 169, 175,
170 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
172 0, 0,
173 ]);
174 let bls_scalar = BlsScalar::from(scalar);
175 let secret_scalar = composer.append_witness(bls_scalar);
176
177 let expected_point: JubJubAffine =
178 (GENERATOR_EXTENDED * scalar).into();
179
180 let point_scalar = composer
181 .component_mul_generator(secret_scalar, GENERATOR_EXTENDED);
182
183 composer
184 .assert_equal_public_point(point_scalar, expected_point);
185 },
186 600,
187 );
188 assert!(res.is_ok());
189 }
190
191 #[test]
192 fn test_ecc_constraint_zero() {
193 let res = gadget_tester(
194 |composer| {
195 let scalar = JubJubScalar::zero();
196 let bls_scalar = BlsScalar::from(scalar);
197 let secret_scalar = composer.append_witness(bls_scalar);
198
199 let expected_point: JubJubAffine =
200 (GENERATOR_EXTENDED * scalar).into();
201
202 let point_scalar = composer
203 .component_mul_generator(secret_scalar, GENERATOR_EXTENDED);
204
205 composer
206 .assert_equal_public_point(point_scalar, expected_point);
207 },
208 600,
209 );
210 assert!(res.is_ok());
211 }
212
213 #[test]
214 fn test_ecc_constraint_should_fail() {
215 let res = gadget_tester(
216 |composer| {
217 let scalar = JubJubScalar::from(100u64);
218 let bls_scalar = BlsScalar::from(scalar);
219 let secret_scalar = composer.append_witness(bls_scalar);
220 let double_gen = GENERATOR_EXTENDED.double();
224
225 let expected_point: JubJubAffine = (double_gen * scalar).into();
226
227 let point_scalar = composer
228 .component_mul_generator(secret_scalar, GENERATOR_EXTENDED);
229
230 composer
231 .assert_equal_public_point(point_scalar, expected_point);
232 },
233 600,
234 );
235
236 assert!(res.is_err());
237 }
238
239 #[test]
240 fn test_point_addition() {
241 let res = gadget_tester(
242 |composer| {
243 let point_a = GENERATOR_EXTENDED;
244 let point_b = point_a.double();
245 let expected_point = point_a + point_b;
246
247 let affine_point_a: JubJubAffine = point_a.into();
248 let affine_point_b: JubJubAffine = point_b.into();
249 let affine_expected_point: JubJubAffine = expected_point.into();
250
251 let var_point_a_x =
252 composer.append_witness(affine_point_a.get_x());
253 let var_point_a_y =
254 composer.append_witness(affine_point_a.get_y());
255 let point_a = WitnessPoint {
256 x: var_point_a_x,
257 y: var_point_a_y,
258 };
259 let var_point_b_x =
260 composer.append_witness(affine_point_b.get_x());
261 let var_point_b_y =
262 composer.append_witness(affine_point_b.get_y());
263 let point_b = WitnessPoint {
264 x: var_point_b_x,
265 y: var_point_b_y,
266 };
267 let new_point = composer.component_add_point(point_a, point_b);
268
269 composer.assert_equal_public_point(
270 new_point,
271 affine_expected_point,
272 );
273 },
274 600,
275 );
276
277 assert!(res.is_ok());
278 }
279
280 #[test]
281 #[allow(non_snake_case)]
282 fn test_pedersen_hash() {
283 let res = gadget_tester(
284 |composer| {
285 let scalar_a = JubJubScalar::from(112233u64);
287 let bls_scalar = BlsScalar::from(scalar_a);
288 let secret_scalar_a = composer.append_witness(bls_scalar);
289 let point_a = GENERATOR_EXTENDED;
290 let c_a: JubJubAffine = (point_a * scalar_a).into();
291
292 let scalar_b = JubJubScalar::from(445566u64);
294 let bls_scalar = BlsScalar::from(scalar_b);
295 let secret_scalar_b = composer.append_witness(bls_scalar);
296 let point_b = point_a.double() + point_a;
297 let c_b: JubJubAffine = (point_b * scalar_b).into();
298
299 let expected_point: JubJubAffine =
301 (point_a * scalar_a + point_b * scalar_b).into();
302
303 let aG =
309 composer.component_mul_generator(secret_scalar_a, point_a);
310 let bH =
311 composer.component_mul_generator(secret_scalar_b, point_b);
312
313 composer.assert_equal_public_point(aG, c_a);
317 composer.assert_equal_public_point(bH, c_b);
318
319 let commitment = composer.component_add_point(aG, bH);
321
322 composer.assert_equal_public_point(commitment, expected_point);
325 },
326 1024,
327 );
328 assert!(res.is_ok());
329 }
330
331 #[test]
332 #[allow(non_snake_case)]
333 fn test_pedersen_balance() {
334 let res = gadget_tester(
335 |composer| {
336 let scalar_a = JubJubScalar::from(25u64);
338 let bls_scalar_a = BlsScalar::from(scalar_a);
339 let secret_scalar_a = composer.append_witness(bls_scalar_a);
340 let scalar_b = JubJubScalar::from(30u64);
342 let bls_scalar_b = BlsScalar::from(scalar_b);
343 let secret_scalar_b = composer.append_witness(bls_scalar_b);
344 let scalar_c = JubJubScalar::from(10u64);
346 let bls_scalar_c = BlsScalar::from(scalar_c);
347 let secret_scalar_c = composer.append_witness(bls_scalar_c);
348 let scalar_d = JubJubScalar::from(45u64);
350 let bls_scalar_d = BlsScalar::from(scalar_d);
351 let secret_scalar_d = composer.append_witness(bls_scalar_d);
352
353 let gen = GENERATOR_EXTENDED;
354 let expected_lhs: JubJubAffine =
355 (gen * (scalar_a + scalar_b)).into();
356 let expected_rhs: JubJubAffine =
357 (gen * (scalar_c + scalar_d)).into();
358
359 let P1 = composer.component_mul_generator(secret_scalar_a, gen);
360 let P2 = composer.component_mul_generator(secret_scalar_b, gen);
361 let P3 = composer.component_mul_generator(secret_scalar_c, gen);
362 let P4 = composer.component_mul_generator(secret_scalar_d, gen);
363
364 let commitment_a = composer.component_add_point(P1, P2);
365 let commitment_b = composer.component_add_point(P3, P4);
366
367 composer.assert_equal_point(commitment_a, commitment_b);
368
369 composer.assert_equal_public_point(commitment_a, expected_lhs);
370 composer.assert_equal_public_point(commitment_b, expected_rhs);
371 },
372 2048,
373 );
374 assert!(res.is_ok());
375 }
376}