fullcodec_plonk/constraint_system/ecc/scalar_mul/
fixed_base.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use 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    /// Evaluate `jubjub ยท Generator` as a [`WitnessPoint`]
34    ///
35    /// `generator` will be appended to the circuit description as constant
36    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        // XXX: we can slice off 3 bits from the top of wnaf, since F_r prime
44        // has 252 bits. XXX :We can also move to base4 and have half
45        // the number of gates since wnaf adjacent entries product is
46        // zero, we will not go over the specified amount
47        let num_bits = 256;
48
49        // compute 2^iG
50        let mut point_multiples =
51            compute_wnaf_point_multiples(generator, num_bits);
52
53        point_multiples.reverse();
54
55        // Fetch the raw scalar value as bls scalar, then convert to a jubjub
56        // scalar XXX: Not very Tidy, impl From function in JubJub
57        // This will panic if the JubJub scalar is not a jubjub scalar indeed
58        // and was introduced as a BlsScalar.
59        let raw_jubjub_scalar =
60            JubJubScalar::from_bytes(&self.witnesses[&jubjub].to_bytes())
61                .unwrap();
62
63        // Convert scalar to wnaf_2(k)
64        let wnaf_entries = raw_jubjub_scalar.compute_windowed_naf(2);
65        assert_eq!(wnaf_entries.len(), num_bits);
66
67        // Initialise the accumulators
68        let mut scalar_acc = vec![BlsScalar::zero()];
69        let mut point_acc = vec![JubJubAffine::identity()];
70
71        // Auxillary point to help with checks on the backend
72        let mut xy_alphas = Vec::new();
73
74        // Load values into accumulators based on wnaf entries
75        for (i, entry) in wnaf_entries.iter().rev().enumerate() {
76            // Based on the WNAF, we decide what scalar and point to add
77            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            // We constrain the point accumulator to start from the Identity
105            // point and the Scalar accumulator to start from zero
106            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        // Add last gate, but do not activate it for ECC
137        // It is for use with the previous gate
138        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        // FIXME this gate isn't verifying anything because all the selectors
143        // are zeroed. Validate what was the intent
144        let constraint =
145            Constraint::new().a(acc_x).b(acc_y).d(last_accumulated_bit);
146        self.append_gate(constraint);
147
148        // Constrain the last element in the accumulator to be equal to the
149        // input jubjub scalar
150        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                // Fails because we are not multiplying by the GENERATOR, it is
221                // double
222
223                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                // First component
286                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                // Second component
293                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                // Expected pedersen hash
300                let expected_point: JubJubAffine =
301                    (point_a * scalar_a + point_b * scalar_b).into();
302
303                // To check this pedersen commitment, we will need to do:
304                // - Two scalar multiplications
305                // - One curve addition
306                //
307                // Scalar multiplications
308                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                // Depending on the context, one can check if the resulting aG
314                // and bH are as expected
315                //
316                composer.assert_equal_public_point(aG, c_a);
317                composer.assert_equal_public_point(bH, c_b);
318
319                // Curve addition
320                let commitment = composer.component_add_point(aG, bH);
321
322                // Add final gates to ensure that the commitment that we
323                // computed is equal to the public point
324                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                // First component
337                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                // Second component
341                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                // Third component
345                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                // Fourth component
349                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}