Skip to main content

midnight_circuits/ecc/native/
edwards_chip.rs

1// This file is part of MIDNIGHT-ZK.
2// Copyright (C) Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//! Generic Chip implementation for the ECC Instructions over twisted Edwards
15//! curves. Indeed, this chip only implements partially generic twisted Edwards
16//! curve, i.e. with a = -1, which is the case of Jubjub.
17
18use ecc::EccInstructions;
19use ff::{Field, PrimeField};
20use group::Group;
21use midnight_proofs::{
22    circuit::{Chip, Layouter, Region, Value},
23    plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector},
24    poly::Rotation,
25};
26use num_bigint::{BigUint, ToBigUint};
27#[cfg(any(test, feature = "testing"))]
28use {
29    crate::field::decomposition::chip::P2RDecompositionConfig,
30    crate::testing_utils::{FromScratch, Sampleable},
31    midnight_proofs::plonk::{Fixed, Instance},
32    rand::RngCore,
33};
34
35use crate::{
36    biguint::BigUintGadget,
37    ecc::curves::{CircuitCurve, EdwardsCurve},
38    field::{decomposition::chip::P2RDecompositionChip, NativeChip, NativeGadget},
39    instructions::*,
40    types::{
41        AssignedBigUint, AssignedBit, AssignedByte, AssignedNative, InnerConstants, InnerValue,
42        Instantiable,
43    },
44    utils::ComposableChip,
45    CircuitField,
46};
47
48/// The number of advice columns used by the EccChip.
49pub const NB_EDWARDS_COLS: usize = 9;
50
51/// A twisted Edwards curve point represented in affine (x, y) coordinates, the
52/// identity represented as (0, 1).
53/// The represented point may or may not lie in the prime order subgroup. If
54/// `in_subgroup` is true, then the point has been constrained to be in the
55/// prime order subgroup.
56/// Since in most use cases we want to ensure the point is in the subgroup,
57/// the implementation of `InnerValue` and `AssignmentInstructions` require
58/// subgroup membership. To use arbitrary points of the curve there are
59/// equivalent functions that explicitly state the absence of this subgroup
60/// membership check.
61#[derive(Clone, Debug)]
62pub struct AssignedNativePoint<C: CircuitCurve> {
63    x: AssignedNative<C::Base>,
64    y: AssignedNative<C::Base>,
65    checked_in_subgroup: bool,
66}
67
68impl<C: CircuitCurve> InnerValue for AssignedNativePoint<C> {
69    type Element = C::CryptographicGroup;
70
71    fn value(&self) -> Value<Self::Element> {
72        assert!(self.checked_in_subgroup);
73        self.x
74            .value()
75            .zip(self.y.value())
76            .map(|(x, y)| C::from_xy(*x, *y).expect("Valid coordinates.").into_subgroup())
77    }
78}
79
80impl<C: CircuitCurve> AssignedNativePoint<C> {
81    /// Return the value of the assigned point, possibly not in the subgroup.
82    /// If the point is known to be in the subgroup, consider using `value()`
83    /// instead.
84    fn curve_value(&self) -> Value<C> {
85        self.x
86            .value()
87            .zip(self.y.value())
88            .map(|(x, y)| C::from_xy(*x, *y).expect("Valid coordinates."))
89    }
90}
91
92impl<C: CircuitCurve> Instantiable<C::Base> for AssignedNativePoint<C> {
93    fn as_public_input(p: &C::CryptographicGroup) -> Vec<C::Base> {
94        let point: C = (*p).into();
95        let coordinates = point.coordinates().unwrap();
96        vec![coordinates.0, coordinates.1]
97    }
98
99    fn from_public_input(fields: &[C::Base]) -> Option<C::CryptographicGroup> {
100        if fields.len() != 2 {
101            return None;
102        }
103        C::from_xy(fields[0], fields[1]).map(|p| p.into_subgroup())
104    }
105}
106
107impl<C: EdwardsCurve> InnerConstants for AssignedNativePoint<C> {
108    fn inner_zero() -> C::CryptographicGroup {
109        C::CryptographicGroup::identity()
110    }
111
112    fn inner_one() -> Self::Element {
113        C::CryptographicGroup::generator()
114    }
115}
116
117/// Scalars are represented as a vector of assigned bits in little endian.
118#[derive(Clone, Debug)]
119pub struct AssignedScalarOfNativeCurve<C: CircuitCurve> {
120    bits: Vec<AssignedBit<C::Base>>,
121    enforced_canonical: bool,
122}
123
124impl<C: CircuitCurve> InnerValue for AssignedScalarOfNativeCurve<C> {
125    type Element = C::ScalarField;
126
127    fn value(&self) -> Value<Self::Element> {
128        let bools = self.bits.iter().map(|b| b.value());
129        let value_bools: Value<Vec<bool>> = Value::from_iter(bools);
130        value_bools.map(|le_bits| {
131            let bytes: Vec<u8> = le_bits
132                .chunks(8)
133                .map(|chunk| chunk.iter().rev().fold(0u8, |acc, &b| (acc << 1) | (b as u8)))
134                .collect();
135            let n = BigUint::from_bytes_le(&bytes);
136            let modulus = C::ScalarField::modulus();
137            C::ScalarField::from_biguint(&(n % modulus)).unwrap()
138        })
139    }
140}
141
142impl<C: EdwardsCurve> Instantiable<C::Base> for AssignedScalarOfNativeCurve<C> {
143    fn as_public_input(element: &C::ScalarField) -> Vec<C::Base> {
144        // We aggregate the bits while they fit in a single `C::Base` value.
145        let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
146        element
147            .to_bits_le(Some(C::NUM_BITS_SUBGROUP as usize))
148            .chunks(nb_bits_per_batch)
149            .map(C::Base::from_bits_le)
150            .collect()
151    }
152
153    fn from_public_input(fields: &[C::Base]) -> Option<C::ScalarField> {
154        // A scalar needs at most two elements to be represented.
155        if fields.len() > 2 {
156            return None;
157        }
158        let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
159        let bits: Vec<bool> = fields
160            .iter()
161            .flat_map(|f| f.to_bits_le(Some(nb_bits_per_batch)))
162            .take(C::NUM_BITS_SUBGROUP as usize)
163            .collect();
164        Some(C::ScalarField::from_bits_le(&bits))
165    }
166}
167
168impl<C: EdwardsCurve> InnerConstants for AssignedScalarOfNativeCurve<C> {
169    fn inner_zero() -> C::ScalarField {
170        C::ScalarField::ZERO
171    }
172    fn inner_one() -> C::ScalarField {
173        C::ScalarField::ONE
174    }
175}
176
177#[cfg(any(test, feature = "testing"))]
178impl<C: EdwardsCurve> Sampleable for AssignedScalarOfNativeCurve<C> {
179    fn sample_inner(rng: impl RngCore) -> C::ScalarField {
180        C::ScalarField::random(rng)
181    }
182}
183
184/// Reduces the given [`AssignedBigUint`] modulo the scalar field order of `C`,
185/// in-circuit. If the input is known to have fewer bits than the scalar field
186/// modulus, it is already in reduced form and is returned as-is.
187fn reduce_biguint_mod_scalar_order<C: CircuitCurve>(
188    layouter: &mut impl Layouter<C::Base>,
189    biguint_gadget: &BigUintGadget<C::Base, NG<C::Base>>,
190    input: &AssignedBigUint<C::Base>,
191) -> Result<AssignedBigUint<C::Base>, Error> {
192    if input.nb_bits() < C::ScalarField::NUM_BITS {
193        return Ok(input.clone());
194    }
195
196    let modulus = C::ScalarField::modulus().to_biguint().unwrap();
197    let m = biguint_gadget.assign_fixed_biguint(layouter, modulus)?;
198    let (_q, r) = biguint_gadget.div_rem(layouter, input, &m)?;
199    Ok(r)
200}
201
202impl<C: CircuitCurve> AssignedScalarOfNativeCurve<C> {
203    /// Converts the scalar into an [`AssignedBigUint`].
204    /// The result is not guaranteed to be in canonical form (i.e. it may
205    /// represent a value greater than or equal to the scalar field order).
206    pub fn to_biguint(
207        &self,
208        layouter: &mut impl Layouter<C::Base>,
209        biguint_gadget: &BigUintGadget<C::Base, NG<C::Base>>,
210    ) -> Result<AssignedBigUint<C::Base>, Error> {
211        biguint_gadget.from_le_bits(layouter, &self.bits)
212    }
213
214    /// Converts the scalar into an [`AssignedBigUint`].
215    /// The result is guaranteed to be in canonical form.
216    pub fn to_canonical_biguint(
217        &self,
218        layouter: &mut impl Layouter<C::Base>,
219        biguint_gadget: &BigUintGadget<C::Base, NG<C::Base>>,
220    ) -> Result<AssignedBigUint<C::Base>, Error> {
221        let s = biguint_gadget.from_le_bits(layouter, &self.bits)?;
222        if self.enforced_canonical {
223            return Ok(s);
224        }
225        reduce_biguint_mod_scalar_order::<C>(layouter, biguint_gadget, &s)
226    }
227
228    /// Constructs an [`AssignedScalarOfNativeCurve`] from an
229    /// [`AssignedBigUint`]. The result is not guaranteed to be canonical but is
230    /// guaranteed to have at most `C::ScalarField::NUM_BITS` bits.
231    pub fn from_biguint(
232        layouter: &mut impl Layouter<C::Base>,
233        biguint_gadget: &BigUintGadget<C::Base, NG<C::Base>>,
234        s: &AssignedBigUint<C::Base>,
235    ) -> Result<Self, Error> {
236        let mut s = s.clone();
237        let mut enforced_canonical = false;
238        if s.nb_bits() > C::ScalarField::NUM_BITS {
239            s = reduce_biguint_mod_scalar_order::<C>(layouter, biguint_gadget, &s)?;
240            enforced_canonical = true;
241        }
242        Ok(AssignedScalarOfNativeCurve {
243            bits: biguint_gadget.to_le_bits(layouter, &s)?,
244            enforced_canonical,
245        })
246    }
247}
248
249/// [`EccConfig`], which uses [`NB_EDWARDS_COLS`] advice columns.
250#[derive(Clone, Debug)]
251pub struct EccConfig {
252    pub(crate) q_double: Selector,
253    pub(crate) q_cond_add: Selector,
254    pub(crate) q_mem: Selector,
255    pub(crate) advice_cols: [Column<Advice>; NB_EDWARDS_COLS],
256}
257
258impl EccConfig {
259    /// Enforce `Q = 2 * P`, using columns:
260    ///
261    /// ```text
262    ///    0      1      2      3       4      5      6       7      8
263    /// ------------------------------------------------------------------
264    /// |      |      |      |      |      |  xp  |  yp  | xp_xp |       |
265    /// |  xq  |  yq  |      |      |      |      |      |       |       |
266    /// ------------------------------------------------------------------
267    /// ```
268    ///
269    /// The curve equation is `-x^2 + y^2 = 1 + d * x^2 * y^2`.
270    /// The result of doubling, the point `Q = (xq, yq)`, can be computed as:
271    /// * `xq = (2 * xp * yp) / (1 + d * xp * xp * yp * yp)`
272    /// * `yq = (yp * yp + xp * xp) / (1 - d * xp * xp * yp * yp)`
273    ///
274    /// Equivalently, the above can be computed as:
275    /// * `xq * (1 + d * xp * xp * yp * yp) = 2 * xp * yp`
276    /// * `yq * (1 - d * xp * xp * yp * yp) = yp * yp + xp * xp`
277    ///
278    /// Note, that `d * xp * xp * yp * yp != 1,-1` if `P` satisfies the
279    /// curve equation (since `-1` is a square and `d` is not a square
280    /// in the base field).
281    /// See <https://eprint.iacr.org/2008/013.pdf>.
282    ///
283    /// Enforce the constraints:
284    /// * `xq * (1 + d * xp_xp * yp * yp) = 2 * xp * yp`
285    /// * `yq * (1 - d * xp_xp * yp * yp) = yp * yp + xp * xp`
286    /// * `xp_xp = xp * xp`
287    fn create_double_gate<C: EdwardsCurve>(
288        &self,
289        meta: &mut ConstraintSystem<C::Base>,
290        q_double: &Selector,
291    ) {
292        meta.create_gate("double", |meta| {
293            let xp = meta.query_advice(self.advice_cols[5], Rotation::cur());
294            let yp = meta.query_advice(self.advice_cols[6], Rotation::cur());
295            let xq = meta.query_advice(self.advice_cols[0], Rotation::next());
296            let yq = meta.query_advice(self.advice_cols[1], Rotation::next());
297
298            let xp_xp = meta.query_advice(self.advice_cols[7], Rotation::cur());
299
300            let one = Expression::from(1);
301            let edwards_d = Expression::Constant(C::D);
302            let xp_yp = &xp * &yp;
303            let yp_yp = yp.square();
304            let d_xp_xp_yp_yp = edwards_d * &xp_xp * &yp_yp;
305
306            let id1 = xq * (&one + &d_xp_xp_yp_yp) - (xp_yp.clone() + xp_yp);
307            let id2 = yq * (one - d_xp_xp_yp_yp) - (yp_yp + &xp_xp);
308            let id3 = xp.clone() * xp - xp_xp;
309
310            Constraints::with_selector(
311                *q_double,
312                vec![
313                    ("qx constraint for q = 2 * p", id1),
314                    ("qy constraint for q = 2 * p", id2),
315                    ("constraint for xp_xp = xp * xp", id3),
316                ],
317            )
318        })
319    }
320
321    /// Enforce `R = Q + b * S`, using columns:
322    ///
323    /// ```text
324    ///    0      1      2      3       4      5      6      7         8
325    /// -----------------------------------------------------------------------
326    /// |  xq  |  yq  |  xs  |  ys  |   b   |  xr  |  yr  |     | xq_yq_xs_ys |
327    /// -----------------------------------------------------------------------
328    /// ```
329    ///
330    /// The curve equation is `-x^2 + y^2 = 1 + d * x^2 * y^2`.
331    /// The result, `R = (xr, yr)`, can be computed as:
332    /// * `xr = (xq + b * (xq*ys + xs*yq - xq)) / (1 + b*d * xq*xs*yq*ys)`
333    /// * `yr = (yq + b * (yq*ys + xq*xs - yq)) / (1 - b*d * xq*xs*yq*ys)`
334    ///
335    /// Equivalently, the above can be computed as:
336    /// * `xr * (1 + b * d * xq * xs * yq * ys) = xq + b * (xq*ys + xs*yq - xq)`
337    /// * `yr * (1 - b * d * xq * xs * yq * ys) = yq + b * (yq*ys + xq*xs - yq)`
338    ///
339    /// Note, that `b * d * xq * xs * yq * ys != 1,-1` if `Q`, `S` satisfy the
340    /// curve equation (since `-1` is a square and `d` is not a square
341    /// in the base field).
342    /// See <https://eprint.iacr.org/2008/013.pdf>.
343    ///
344    /// Enforce the constraints:
345    /// * `xr * (1 + b * d * xq_yq_xs_ys) = xq + b * (xq*ys + xs*yq - xq)`
346    /// * `yr * (1 - b * d * xq_yq_xs_ys) = yq + b * (yq*ys + xq*xs - yq)`
347    /// * `xq_yq_xs_ys = xq * yq * xs * ys`
348    fn create_cond_add_gate<C: EdwardsCurve>(
349        &self,
350        meta: &mut ConstraintSystem<C::Base>,
351        q_cond_add: &Selector,
352    ) {
353        meta.create_gate("conditional add", |meta| {
354            let xq = meta.query_advice(self.advice_cols[0], Rotation::cur());
355            let yq = meta.query_advice(self.advice_cols[1], Rotation::cur());
356            let xs = meta.query_advice(self.advice_cols[2], Rotation::cur());
357            let ys = meta.query_advice(self.advice_cols[3], Rotation::cur());
358            let xr = meta.query_advice(self.advice_cols[5], Rotation::cur());
359            let yr = meta.query_advice(self.advice_cols[6], Rotation::cur());
360            let b = meta.query_advice(self.advice_cols[4], Rotation::cur());
361
362            let one = Expression::from(1);
363            let edwards_d = Expression::Constant(C::D);
364
365            let xq_yq_xs_ys = meta.query_advice(self.advice_cols[8], Rotation::cur());
366
367            let xq_xs = &xq * &xs;
368            let yq_ys = &yq * &ys;
369            let xq_ys = &xq * &ys;
370            let xs_yq = &xs * &yq;
371            let b_d_xq_xs_yq_ys = &b * edwards_d * &xq_yq_xs_ys;
372
373            let id1 = xr * (&one + &b_d_xq_xs_yq_ys) - (&xq + &b * (xq_ys + xs_yq - &xq));
374            let id2 = yr * (one - b_d_xq_xs_yq_ys) - (&yq + b * (yq_ys + xq_xs - &yq));
375            let id3 = xq_yq_xs_ys - xq * yq * xs * ys;
376
377            Constraints::with_selector(
378                *q_cond_add,
379                vec![
380                    ("rx constraint for r = q + b * s", id1),
381                    ("ry constraint for r = q + b * s", id2),
382                    ("constraint for xq_yq_xs_ys = xq * yq * xs * ys", id3),
383                ],
384            )
385        })
386    }
387
388    /// Enforce `P = (x, y)` is on the curve, using columns:
389    ///
390    /// ```text
391    /// -------------
392    /// |  x  |  y  |
393    /// -------------
394    /// ```
395    ///
396    /// Enforce the constraint:
397    /// * `-x^2 + y^2 = 1 + d * x^2 * y^2`
398    fn create_membership_gate<C: EdwardsCurve>(
399        &self,
400        meta: &mut ConstraintSystem<C::Base>,
401        q_point: &Selector,
402    ) {
403        meta.create_gate("witness point", |meta| {
404            let x = meta.query_advice(self.advice_cols[0], Rotation::cur());
405            let y = meta.query_advice(self.advice_cols[1], Rotation::cur());
406
407            let one = Expression::from(1);
408            let edwards_d = Expression::Constant(C::D);
409
410            let x_sq = x.square();
411            let y_sq = y.square();
412
413            let id = y_sq.clone() - x_sq.clone() - (one + edwards_d * x_sq * y_sq);
414
415            Constraints::with_selector(*q_point, vec![("curve equation", id)])
416        })
417    }
418}
419
420type NG<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
421
422/// A native  [`EccInstructions`] chip.
423/// Since the chip is native, it only supports the embedded curve Jubjub.
424#[derive(Clone, Debug)]
425pub struct EccChip<C: EdwardsCurve> {
426    config: EccConfig,
427    native_gadget: NG<C::Base>,
428    biguint_gadget: BigUintGadget<C::Base, NG<C::Base>>,
429}
430
431impl<C: EdwardsCurve> Chip<C::Base> for EccChip<C> {
432    type Config = EccConfig;
433    type Loaded = ();
434
435    fn config(&self) -> &Self::Config {
436        &self.config
437    }
438
439    fn loaded(&self) -> &Self::Loaded {
440        &()
441    }
442}
443
444impl<C: EdwardsCurve> ComposableChip<C::Base> for EccChip<C> {
445    type SharedResources = [Column<Advice>; NB_EDWARDS_COLS];
446    type InstructionDeps = NG<C::Base>;
447
448    fn new(config: &Self::Config, native_gadget: &Self::InstructionDeps) -> Self {
449        Self {
450            config: config.clone(),
451            native_gadget: native_gadget.clone(),
452            biguint_gadget: BigUintGadget::new(native_gadget),
453        }
454    }
455
456    fn configure(
457        meta: &mut ConstraintSystem<C::Base>,
458        advice_cols: &Self::SharedResources,
459    ) -> Self::Config {
460        assert_eq!(C::A, -C::Base::ONE);
461
462        // Only the first 7 need to be copy-enabled.
463        for col in advice_cols.iter().take(7) {
464            meta.enable_equality(*col)
465        }
466
467        let q_double = meta.selector();
468        let q_cond_add = meta.selector();
469        let q_mem = meta.selector();
470
471        let config = EccConfig {
472            q_double,
473            q_cond_add,
474            q_mem,
475            advice_cols: *advice_cols,
476        };
477
478        config.create_double_gate::<C>(meta, &q_double);
479        config.create_cond_add_gate::<C>(meta, &q_cond_add);
480        config.create_membership_gate::<C>(meta, &q_mem);
481
482        config
483    }
484
485    fn load(&self, _layouter: &mut impl Layouter<C::Base>) -> Result<(), Error> {
486        Ok(())
487    }
488}
489
490impl<C: EdwardsCurve> EccChip<C> {
491    /// Given points Q, S and bit `b`, computes R = Q + b * S.
492    /// This function requires the inputs to be already assigned in the current
493    /// row. The result R will be asigned by this function in the same row,
494    /// following the layout:
495    ///
496    ///
497    /// ```text
498    ///    0      1      2      3       4     5      6      7         8
499    /// ----------------------------------------------------------------------
500    /// |  xq  |  yq  |  xs  |  ys  |   b   | xr  |  yr  |     | xq_yq_xs_ys |
501    /// ----------------------------------------------------------------------
502    /// ```
503    fn cond_add(
504        &self,
505        region: &mut Region<C::Base>,
506        offset: usize,
507        q: &AssignedNativePoint<C>,
508        s: &AssignedNativePoint<C>,
509        b: &AssignedBit<C::Base>,
510    ) -> Result<AssignedNativePoint<C>, Error> {
511        let config = self.config();
512        config.q_cond_add.enable(region, offset)?;
513
514        let (q_val, s_val) = (q.curve_value(), s.curve_value());
515        let in_subgroup = q.checked_in_subgroup && s.checked_in_subgroup;
516
517        let (xr_val, yr_val) = Self::p_plus_b_q(q_val, s_val, b.value());
518        let xr = region.assign_advice(|| "xr", config.advice_cols[5], offset, || xr_val)?;
519        let yr = region.assign_advice(|| "yr", config.advice_cols[6], offset, || yr_val)?;
520
521        let (xq, yq) = q_val.map(|q| q.coordinates().unwrap()).unzip();
522        let (xs, ys) = s_val.map(|s| s.coordinates().unwrap()).unzip();
523        let prod_val = xq * yq * xs * ys;
524        region.assign_advice(|| "xq_yq_xs_ys", config.advice_cols[8], offset, || prod_val)?;
525
526        Ok(AssignedNativePoint {
527            x: xr,
528            y: yr,
529            checked_in_subgroup: in_subgroup,
530        })
531    }
532
533    /// Given points P, Q and bit `b`, computes R = 2 * (P + b * Q).
534    /// This function requires the inputs to be already assigned in the current
535    /// row. The result R will be asigned by this function in the next row,
536    /// following the layout:
537    ///
538    ///
539    /// ```text
540    /// ------------------------------------------------------------------------
541    /// |  xp  |  yp  |  xq  |  yq  |  b   |  xs  |  ys  | xs_xs | xp_yp_xq_yq |
542    /// |  xr  |  yr  |      |      |      |      |      |       |             |
543    /// ------------------------------------------------------------------------
544    /// ```
545    fn add_then_double(
546        &self,
547        region: &mut Region<C::Base>,
548        offset: usize,
549        p: &AssignedNativePoint<C>,
550        q: &AssignedNativePoint<C>,
551        b: &AssignedBit<C::Base>,
552    ) -> Result<AssignedNativePoint<C>, Error> {
553        let config = self.config();
554
555        config.q_cond_add.enable(region, offset)?;
556        config.q_double.enable(region, offset)?;
557
558        let (q_val, p_val) = (q.curve_value(), p.curve_value());
559        let in_subgroup = q.checked_in_subgroup && p.checked_in_subgroup;
560
561        let (xs_val, ys_val) = Self::p_plus_b_q(p_val, q_val, b.value());
562
563        region.assign_advice(|| "xs", config.advice_cols[5], offset, || xs_val)?;
564        region.assign_advice(|| "ys", config.advice_cols[6], offset, || ys_val)?;
565
566        let s_val = xs_val.zip(ys_val).map(|(xs, ys)| C::from_xy(xs, ys).unwrap());
567        let r_val = s_val.map(|s| s + s);
568
569        let xr_val = r_val.map(|r: C| r.coordinates().unwrap().0);
570        let yr_val = r_val.map(|r: C| r.coordinates().unwrap().1);
571
572        let xr = region.assign_advice(|| "xr", config.advice_cols[0], offset + 1, || xr_val)?;
573        let yr = region.assign_advice(|| "yr", config.advice_cols[1], offset + 1, || yr_val)?;
574
575        region.assign_advice(
576            || "xs_xs",
577            config.advice_cols[7],
578            offset,
579            || xs_val * xs_val,
580        )?;
581
582        let (xp, yp) = p_val.map(|c| c.coordinates().unwrap()).unzip();
583        let (xq, yq) = q_val.map(|c| c.coordinates().unwrap()).unzip();
584        let prod_val = xp * yp * xq * yq;
585        region.assign_advice(|| "xp_yp_xq_yq", config.advice_cols[8], offset, || prod_val)?;
586
587        Ok(AssignedNativePoint {
588            x: xr,
589            y: yr,
590            checked_in_subgroup: in_subgroup,
591        })
592    }
593
594    /// Given the scalar in little-endian, double and add for each bit.
595    pub fn mul(
596        &self,
597        layouter: &mut impl Layouter<C::Base>,
598        scalar: &AssignedScalarOfNativeCurve<C>,
599        base: &AssignedNativePoint<C>,
600    ) -> Result<AssignedNativePoint<C>, Error> {
601        let config = &self.config();
602
603        // Convert to big-endian.
604        let scalar_be_bits = &mut scalar.bits.clone();
605        scalar_be_bits.reverse();
606
607        let id_point: AssignedNativePoint<C> =
608            self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
609
610        layouter.assign_region(
611            || "assign mul",
612            |mut region: Region<'_, C::Base>| {
613                id_point.x.copy_advice(|| "id.x", &mut region, config.advice_cols[0], 0)?;
614                id_point.y.copy_advice(|| "id.y", &mut region, config.advice_cols[1], 0)?;
615
616                let mut acc = id_point.clone();
617
618                for (i, bit) in scalar_be_bits.iter().enumerate() {
619                    base.x.copy_advice(|| "base.x", &mut region, config.advice_cols[2], i)?;
620                    base.y.copy_advice(|| "base.y", &mut region, config.advice_cols[3], i)?;
621                    bit.0.copy_advice(|| "b cond_add", &mut region, config.advice_cols[4], i)?;
622
623                    if i < scalar_be_bits.len() - 1 {
624                        acc = self.add_then_double(&mut region, i, &acc, base, bit)?;
625                    }
626                    // In the last iteration, add but do not double.
627                    else {
628                        acc = self.cond_add(&mut region, i, &acc, base, bit)?;
629                    }
630                }
631
632                Ok(acc)
633            },
634        )
635    }
636
637    /// Given values of P, Q and b, computes the value of P + b * Q.
638    fn p_plus_b_q(p: Value<C>, q: Value<C>, b: Value<bool>) -> (Value<C::Base>, Value<C::Base>) {
639        p.zip(q)
640            .zip(b)
641            .map(|((p, q), b)| if b { p + q } else { p })
642            .map(|r| r.coordinates().unwrap())
643            .unzip()
644    }
645
646    /// The native gadget carried by this chip.
647    pub fn native_gadget(&self) -> &impl NativeInstructions<C::Base> {
648        &self.native_gadget
649    }
650}
651
652impl<C: EdwardsCurve> EccChip<C> {
653    /// Multiplies by the cofactor, ensuring the result lies in the prime order
654    /// subgroup.
655    pub(crate) fn clear_cofactor(
656        &self,
657        layouter: &mut impl Layouter<C::Base>,
658        point: &AssignedNativePoint<C>,
659    ) -> Result<AssignedNativePoint<C>, Error> {
660        if point.checked_in_subgroup {
661            return Err(Error::Synthesis("clear_cofactor() should not be called in a point that is already guaranteed to be in the prime-order subgroup.".to_owned()));
662        }
663        let r = EccInstructions::mul_by_constant(
664            self,
665            layouter,
666            C::ScalarField::from_u128(C::COFACTOR),
667            point,
668        )?;
669        Ok(AssignedNativePoint {
670            x: r.x,
671            y: r.y,
672            checked_in_subgroup: true,
673        })
674    }
675
676    /// Assigns a point given its affine coordinates, checking the curve
677    /// equation and *not* checking subgroup membership.
678    pub(crate) fn point_from_coordinates_unsafe(
679        &self,
680        layouter: &mut impl Layouter<C::Base>,
681        x: &AssignedNative<C::Base>,
682        y: &AssignedNative<C::Base>,
683    ) -> Result<AssignedNativePoint<C>, Error> {
684        layouter.assign_region(
685            || "assign new point",
686            |mut region: Region<'_, C::Base>| {
687                x.copy_advice(|| "x", &mut region, self.config.advice_cols[0], 0)?;
688                y.copy_advice(|| "y", &mut region, self.config.advice_cols[1], 0)?;
689                self.config.q_mem.enable(&mut region, 0)
690            },
691        )?;
692        Ok(AssignedNativePoint {
693            x: x.clone(),
694            y: y.clone(),
695            checked_in_subgroup: false,
696        })
697    }
698}
699
700impl<C: EdwardsCurve> EccInstructions<C::Base, C> for EccChip<C> {
701    type Point = AssignedNativePoint<C>;
702    type Coordinate = AssignedNative<C::Base>;
703    type Scalar = AssignedScalarOfNativeCurve<C>;
704
705    fn add(
706        &self,
707        layouter: &mut impl Layouter<C::Base>,
708        p: &Self::Point,
709        q: &Self::Point,
710    ) -> Result<Self::Point, Error> {
711        let config = self.config();
712        let b: AssignedBit<C::Base> = self.native_gadget.assign_fixed(layouter, true)?;
713
714        layouter.assign_region(
715            || "assign add",
716            |mut region: Region<'_, C::Base>| {
717                p.x.copy_advice(|| "px", &mut region, config.advice_cols[0], 0)?;
718                p.y.copy_advice(|| "py", &mut region, config.advice_cols[1], 0)?;
719                q.x.copy_advice(|| "qx", &mut region, config.advice_cols[2], 0)?;
720                q.y.copy_advice(|| "qy", &mut region, config.advice_cols[3], 0)?;
721                b.0.copy_advice(|| "b", &mut region, config.advice_cols[4], 0)?;
722
723                self.cond_add(&mut region, 0, p, q, &b)
724            },
725        )
726    }
727
728    fn double(
729        &self,
730        layouter: &mut impl Layouter<C::Base>,
731        p: &Self::Point,
732    ) -> Result<Self::Point, Error> {
733        EccInstructions::add(self, layouter, p, p)
734    }
735
736    fn negate(
737        &self,
738        layouter: &mut impl Layouter<C::Base>,
739        p: &Self::Point,
740    ) -> Result<Self::Point, Error> {
741        Ok(AssignedNativePoint {
742            x: self.native_gadget.neg(layouter, &p.x)?,
743            y: p.y.clone(),
744            checked_in_subgroup: p.checked_in_subgroup,
745        })
746    }
747
748    fn msm(
749        &self,
750        layouter: &mut impl Layouter<C::Base>,
751        scalars: &[Self::Scalar],
752        bases: &[Self::Point],
753    ) -> Result<Self::Point, Error> {
754        let scaled_points = scalars
755            .iter()
756            .zip(bases.iter())
757            .map(|(scalar, point)| self.mul(layouter, scalar, point))
758            .collect::<Result<Vec<Self::Point>, Error>>()?;
759
760        scaled_points[1..].iter().try_fold(scaled_points[0].clone(), |acc, e| {
761            EccInstructions::add(self, layouter, &acc, e)
762        })
763    }
764
765    fn mul_by_constant(
766        &self,
767        layouter: &mut impl Layouter<C::Base>,
768        scalar: C::ScalarField,
769        base: &Self::Point,
770    ) -> Result<Self::Point, Error> {
771        if scalar == C::ScalarField::ZERO {
772            return self.assign_fixed(layouter, C::CryptographicGroup::identity());
773        }
774
775        if scalar == C::ScalarField::ONE {
776            return Ok(base.clone());
777        }
778
779        let s = self.assign_fixed(layouter, scalar)?;
780        self.msm(layouter, &[s], std::slice::from_ref(base))
781    }
782
783    fn point_from_coordinates(
784        &self,
785        layouter: &mut impl Layouter<C::Base>,
786        x: &Self::Coordinate,
787        y: &Self::Coordinate,
788    ) -> Result<Self::Point, Error> {
789        let point_val = x.value().zip(y.value()).map(|(x, y)| {
790            C::from_xy(*x, *y)
791                .expect("Affine coordinates must satisfy Jubjub equation.")
792                .into_subgroup()
793        });
794        let point: Self::Point = self.assign(layouter, point_val)?;
795        self.native_gadget.assert_equal(layouter, x, &point.x)?;
796        self.native_gadget.assert_equal(layouter, y, &point.y)?;
797        Ok(point)
798    }
799
800    fn assign_without_subgroup_check(
801        &self,
802        layouter: &mut impl Layouter<C::Base>,
803        value: Value<C::CryptographicGroup>,
804    ) -> Result<Self::Point, Error> {
805        let config = self.config();
806        let (x_val, y_val) = value
807            .map(|p| p.into().coordinates().expect("assign_without_subgroup_check: invalid point"))
808            .unzip();
809
810        layouter.assign_region(
811            || "assign point without subgroup check",
812            |mut region: Region<'_, C::Base>| {
813                config.q_mem.enable(&mut region, 0)?;
814                let x = region.assign_advice(|| "x", config.advice_cols[0], 0, || x_val)?;
815                let y = region.assign_advice(|| "y", config.advice_cols[1], 0, || y_val)?;
816                Ok(AssignedNativePoint {
817                    x,
818                    y,
819                    checked_in_subgroup: false,
820                })
821            },
822        )
823    }
824
825    fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
826        point.x.clone()
827    }
828
829    fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
830        point.y.clone()
831    }
832
833    fn base_field(&self) -> &impl DecompositionInstructions<C::Base, Self::Coordinate> {
834        &self.native_gadget
835    }
836}
837
838impl<C: EdwardsCurve> AssignmentInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
839    fn assign(
840        &self,
841        layouter: &mut impl Layouter<C::Base>,
842        value: Value<C::CryptographicGroup>,
843    ) -> Result<AssignedNativePoint<C>, Error> {
844        // Ensure the point lies in the correct subgroup.
845        // To achieve this, we first assign the point multiplied by the inverse of the
846        // cofactor. Then, we return the assigned point after multiplying it by
847        // the cofactor.
848        let cofactor = C::ScalarField::from_u128(C::COFACTOR);
849        let cf_root_val = value.map(|p| p * cofactor.invert().expect("Cofactor must not be 0"));
850        let cf_root = self.assign_without_subgroup_check(layouter, cf_root_val)?;
851
852        self.clear_cofactor(layouter, &cf_root)
853    }
854
855    fn assign_fixed(
856        &self,
857        layouter: &mut impl Layouter<C::Base>,
858        constant: C::CryptographicGroup,
859    ) -> Result<AssignedNativePoint<C>, Error> {
860        let coords = constant.into().coordinates().unwrap();
861        let x = self.native_gadget.assign_fixed(layouter, coords.0)?;
862        let y = self.native_gadget.assign_fixed(layouter, coords.1)?;
863        Ok(AssignedNativePoint {
864            x,
865            y,
866            checked_in_subgroup: true,
867        })
868    }
869}
870
871impl<C: EdwardsCurve> AssignmentInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
872    for EccChip<C>
873{
874    fn assign(
875        &self,
876        layouter: &mut impl Layouter<C::Base>,
877        value: Value<C::ScalarField>,
878    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
879        let bits = value
880            .map(|s| s.to_bits_le(Some(C::ScalarField::NUM_BITS as usize)))
881            .transpose_vec(<C::ScalarField as PrimeField>::NUM_BITS as usize);
882        Ok(AssignedScalarOfNativeCurve {
883            bits: self.native_gadget.assign_many(layouter, &bits)?,
884            enforced_canonical: false,
885        })
886    }
887
888    fn assign_fixed(
889        &self,
890        layouter: &mut impl Layouter<C::Base>,
891        constant: C::ScalarField,
892    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
893        Ok(AssignedScalarOfNativeCurve {
894            bits: self.native_gadget.assign_many_fixed(layouter, &constant.to_bits_le(None))?,
895            enforced_canonical: true,
896        })
897    }
898}
899
900impl<C: EdwardsCurve> AssertionInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
901    fn assert_equal(
902        &self,
903        layouter: &mut impl Layouter<C::Base>,
904        p: &AssignedNativePoint<C>,
905        q: &AssignedNativePoint<C>,
906    ) -> Result<(), Error> {
907        self.native_gadget.assert_equal(layouter, &p.x, &q.x)?;
908        self.native_gadget.assert_equal(layouter, &p.y, &q.y)
909    }
910
911    fn assert_not_equal(
912        &self,
913        layouter: &mut impl Layouter<C::Base>,
914        p: &AssignedNativePoint<C>,
915        q: &AssignedNativePoint<C>,
916    ) -> Result<(), Error> {
917        let is_eq = self.is_equal(layouter, p, q)?;
918        self.native_gadget.assert_equal_to_fixed(layouter, &is_eq, false)
919    }
920
921    fn assert_equal_to_fixed(
922        &self,
923        layouter: &mut impl Layouter<C::Base>,
924        p: &AssignedNativePoint<C>,
925        constant: C::CryptographicGroup,
926    ) -> Result<(), Error> {
927        let (cx, cy) = constant.into().coordinates().unwrap();
928        self.native_gadget.assert_equal_to_fixed(layouter, &p.x, cx)?;
929        self.native_gadget.assert_equal_to_fixed(layouter, &p.y, cy)
930    }
931
932    fn assert_not_equal_to_fixed(
933        &self,
934        layouter: &mut impl Layouter<C::Base>,
935        p: &AssignedNativePoint<C>,
936        constant: C::CryptographicGroup,
937    ) -> Result<(), Error> {
938        let is_eq = self.is_equal_to_fixed(layouter, p, constant)?;
939        self.native_gadget.assert_equal_to_fixed(layouter, &is_eq, false)
940    }
941}
942
943impl<C: EdwardsCurve> AssertionInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
944    for EccChip<C>
945{
946    fn assert_equal(
947        &self,
948        layouter: &mut impl Layouter<C::Base>,
949        r: &AssignedScalarOfNativeCurve<C>,
950        s: &AssignedScalarOfNativeCurve<C>,
951    ) -> Result<(), Error> {
952        let r = r.to_canonical_biguint(layouter, &self.biguint_gadget)?;
953        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
954        self.biguint_gadget.assert_equal(layouter, &r, &s)
955    }
956
957    fn assert_not_equal(
958        &self,
959        layouter: &mut impl Layouter<C::Base>,
960        r: &AssignedScalarOfNativeCurve<C>,
961        s: &AssignedScalarOfNativeCurve<C>,
962    ) -> Result<(), Error> {
963        let r = r.to_canonical_biguint(layouter, &self.biguint_gadget)?;
964        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
965        self.biguint_gadget.assert_not_equal(layouter, &r, &s)
966    }
967
968    fn assert_equal_to_fixed(
969        &self,
970        layouter: &mut impl Layouter<C::Base>,
971        s: &AssignedScalarOfNativeCurve<C>,
972        constant: C::ScalarField,
973    ) -> Result<(), Error> {
974        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
975        self.biguint_gadget.assert_equal_to_fixed(layouter, &s, constant.to_biguint())
976    }
977
978    fn assert_not_equal_to_fixed(
979        &self,
980        layouter: &mut impl Layouter<C::Base>,
981        s: &AssignedScalarOfNativeCurve<C>,
982        constant: C::ScalarField,
983    ) -> Result<(), Error> {
984        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
985        self.biguint_gadget
986            .assert_not_equal_to_fixed(layouter, &s, constant.to_biguint())
987    }
988}
989
990impl<C: EdwardsCurve> PublicInputInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
991    fn as_public_input(
992        &self,
993        _layouter: &mut impl Layouter<C::Base>,
994        p: &AssignedNativePoint<C>,
995    ) -> Result<Vec<AssignedNative<C::Base>>, Error> {
996        Ok(vec![p.x.clone(), p.y.clone()])
997    }
998
999    fn constrain_as_public_input(
1000        &self,
1001        layouter: &mut impl Layouter<C::Base>,
1002        p: &AssignedNativePoint<C>,
1003    ) -> Result<(), Error> {
1004        self.as_public_input(layouter, p)?
1005            .iter()
1006            .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
1007    }
1008
1009    fn assign_as_public_input(
1010        &self,
1011        layouter: &mut impl Layouter<C::Base>,
1012        p: Value<C::CryptographicGroup>,
1013    ) -> Result<AssignedNativePoint<C>, Error> {
1014        let (x, y) = p.map(|p| p.into().coordinates().unwrap()).unzip();
1015        let x = self.native_gadget.assign_as_public_input(layouter, x)?;
1016        let y = self.native_gadget.assign_as_public_input(layouter, y)?;
1017
1018        // Since the input value will be constrained to be equal to a PI,
1019        // the verifier will check the validity of the point out-of-circuit,
1020        // so the checks can be skipped.
1021        Ok(AssignedNativePoint {
1022            x,
1023            y,
1024            checked_in_subgroup: true,
1025        })
1026    }
1027}
1028
1029impl<C: EdwardsCurve> PublicInputInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
1030    for EccChip<C>
1031{
1032    fn as_public_input(
1033        &self,
1034        layouter: &mut impl Layouter<C::Base>,
1035        assigned: &AssignedScalarOfNativeCurve<C>,
1036    ) -> Result<Vec<AssignedNative<C::Base>>, Error> {
1037        // We aggregate the bits while they fit in a single `AssignedNative`.
1038        let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
1039        assigned
1040            .bits
1041            .chunks(nb_bits_per_batch)
1042            .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
1043            .collect()
1044    }
1045
1046    fn constrain_as_public_input(
1047        &self,
1048        layouter: &mut impl Layouter<C::Base>,
1049        assigned: &AssignedScalarOfNativeCurve<C>,
1050    ) -> Result<(), Error> {
1051        self.as_public_input(layouter, assigned)?
1052            .iter()
1053            .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
1054    }
1055
1056    fn assign_as_public_input(
1057        &self,
1058        layouter: &mut impl Layouter<C::Base>,
1059        value: Value<C::ScalarField>,
1060    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1061        let assigned: AssignedScalarOfNativeCurve<C> = self.assign(layouter, value)?;
1062        self.constrain_as_public_input(layouter, &assigned)?;
1063        Ok(assigned)
1064    }
1065}
1066
1067impl<C: EdwardsCurve> EqualityInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
1068    fn is_equal(
1069        &self,
1070        layouter: &mut impl Layouter<C::Base>,
1071        p: &AssignedNativePoint<C>,
1072        q: &AssignedNativePoint<C>,
1073    ) -> Result<AssignedBit<C::Base>, Error> {
1074        let eq_x = self.native_gadget.is_equal(layouter, &p.x, &q.x)?;
1075        let eq_y = self.native_gadget.is_equal(layouter, &p.y, &q.y)?;
1076        self.native_gadget.and(layouter, &[eq_x, eq_y])
1077    }
1078
1079    fn is_not_equal(
1080        &self,
1081        layouter: &mut impl Layouter<C::Base>,
1082        p: &AssignedNativePoint<C>,
1083        q: &AssignedNativePoint<C>,
1084    ) -> Result<AssignedBit<C::Base>, Error> {
1085        let not_eq_x = self.native_gadget.is_not_equal(layouter, &p.x, &q.x)?;
1086        let not_eq_y = self.native_gadget.is_not_equal(layouter, &p.y, &q.y)?;
1087        self.native_gadget.or(layouter, &[not_eq_x, not_eq_y])
1088    }
1089
1090    fn is_equal_to_fixed(
1091        &self,
1092        layouter: &mut impl Layouter<C::Base>,
1093        p: &AssignedNativePoint<C>,
1094        constant: C::CryptographicGroup,
1095    ) -> Result<AssignedBit<C::Base>, Error> {
1096        let (cx, cy) = constant.into().coordinates().unwrap();
1097        let eq_x = self.native_gadget.is_equal_to_fixed(layouter, &p.x, cx)?;
1098        let eq_y = self.native_gadget.is_equal_to_fixed(layouter, &p.y, cy)?;
1099        self.native_gadget.and(layouter, &[eq_x, eq_y])
1100    }
1101
1102    fn is_not_equal_to_fixed(
1103        &self,
1104        layouter: &mut impl Layouter<C::Base>,
1105        p: &AssignedNativePoint<C>,
1106        constant: C::CryptographicGroup,
1107    ) -> Result<AssignedBit<C::Base>, Error> {
1108        let (cx, cy) = constant.into().coordinates().unwrap();
1109        let not_eq_x = self.native_gadget.is_not_equal_to_fixed(layouter, &p.x, cx)?;
1110        let not_eq_y = self.native_gadget.is_not_equal_to_fixed(layouter, &p.y, cy)?;
1111        self.native_gadget.or(layouter, &[not_eq_x, not_eq_y])
1112    }
1113}
1114
1115impl<C: EdwardsCurve> EqualityInstructions<C::Base, AssignedScalarOfNativeCurve<C>> for EccChip<C> {
1116    fn is_equal(
1117        &self,
1118        layouter: &mut impl Layouter<C::Base>,
1119        r: &AssignedScalarOfNativeCurve<C>,
1120        s: &AssignedScalarOfNativeCurve<C>,
1121    ) -> Result<AssignedBit<C::Base>, Error> {
1122        let r = r.to_canonical_biguint(layouter, &self.biguint_gadget)?;
1123        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
1124        self.biguint_gadget.is_equal(layouter, &r, &s)
1125    }
1126
1127    fn is_not_equal(
1128        &self,
1129        layouter: &mut impl Layouter<C::Base>,
1130        r: &AssignedScalarOfNativeCurve<C>,
1131        s: &AssignedScalarOfNativeCurve<C>,
1132    ) -> Result<AssignedBit<C::Base>, Error> {
1133        let r = r.to_canonical_biguint(layouter, &self.biguint_gadget)?;
1134        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
1135        self.biguint_gadget.is_not_equal(layouter, &s, &r)
1136    }
1137
1138    fn is_equal_to_fixed(
1139        &self,
1140        layouter: &mut impl Layouter<C::Base>,
1141        s: &AssignedScalarOfNativeCurve<C>,
1142        constant: C::ScalarField,
1143    ) -> Result<AssignedBit<C::Base>, Error> {
1144        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
1145        self.biguint_gadget.is_equal_to_fixed(layouter, &s, constant.to_biguint())
1146    }
1147
1148    fn is_not_equal_to_fixed(
1149        &self,
1150        layouter: &mut impl Layouter<C::Base>,
1151        s: &AssignedScalarOfNativeCurve<C>,
1152        constant: C::ScalarField,
1153    ) -> Result<AssignedBit<C::Base>, Error> {
1154        let s = s.to_canonical_biguint(layouter, &self.biguint_gadget)?;
1155        self.biguint_gadget.is_not_equal_to_fixed(layouter, &s, constant.to_biguint())
1156    }
1157}
1158
1159impl<C: EdwardsCurve> ArithInstructions<C::Base, AssignedScalarOfNativeCurve<C>> for EccChip<C> {
1160    fn linear_combination(
1161        &self,
1162        layouter: &mut impl Layouter<C::Base>,
1163        terms: &[(
1164            <AssignedScalarOfNativeCurve<C> as InnerValue>::Element,
1165            AssignedScalarOfNativeCurve<C>,
1166        )],
1167        constant: <AssignedScalarOfNativeCurve<C> as InnerValue>::Element,
1168    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1169        let mut acc = self.biguint_gadget.assign_fixed_biguint(layouter, constant.to_biguint())?;
1170
1171        for (c, s) in terms {
1172            let s = s.to_biguint(layouter, &self.biguint_gadget)?;
1173            let c = self.biguint_gadget.assign_fixed_biguint(layouter, c.to_biguint())?;
1174            let c_times_s = self.biguint_gadget.mul(layouter, &c, &s)?;
1175            acc = self.biguint_gadget.add(layouter, &acc, &c_times_s)?;
1176        }
1177
1178        AssignedScalarOfNativeCurve::from_biguint(layouter, &self.biguint_gadget, &acc)
1179    }
1180
1181    fn add(
1182        &self,
1183        layouter: &mut impl Layouter<C::Base>,
1184        r: &AssignedScalarOfNativeCurve<C>,
1185        s: &AssignedScalarOfNativeCurve<C>,
1186    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1187        let r = r.to_biguint(layouter, &self.biguint_gadget)?;
1188        let s = s.to_biguint(layouter, &self.biguint_gadget)?;
1189        let res = self.biguint_gadget.add(layouter, &r, &s)?;
1190        AssignedScalarOfNativeCurve::from_biguint(layouter, &self.biguint_gadget, &res)
1191    }
1192
1193    fn mul(
1194        &self,
1195        layouter: &mut impl Layouter<C::Base>,
1196        r: &AssignedScalarOfNativeCurve<C>,
1197        s: &AssignedScalarOfNativeCurve<C>,
1198        multiplying_constant: Option<C::ScalarField>,
1199    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1200        let r = r.to_biguint(layouter, &self.biguint_gadget)?;
1201        let s = s.to_biguint(layouter, &self.biguint_gadget)?;
1202        let mut res = self.biguint_gadget.mul(layouter, &r, &s)?;
1203
1204        if let Some(c) = multiplying_constant {
1205            let c = self.biguint_gadget.assign_fixed_biguint(layouter, c.to_biguint())?;
1206            res = self.biguint_gadget.mul(layouter, &res, &c)?;
1207        }
1208
1209        AssignedScalarOfNativeCurve::from_biguint(layouter, &self.biguint_gadget, &res)
1210    }
1211
1212    fn div(
1213        &self,
1214        layouter: &mut impl Layouter<C::Base>,
1215        r: &AssignedScalarOfNativeCurve<C>,
1216        s: &AssignedScalarOfNativeCurve<C>,
1217    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1218        s.value().error_if_known_and(|s| s.is_zero_vartime())?;
1219
1220        let res_val = r.value().zip(s.value()).map(|(r, s)| r * s.invert().unwrap());
1221        let res = self.biguint_gadget.assign_biguint(
1222            layouter,
1223            res_val.map(|a| a.to_biguint()),
1224            C::ScalarField::NUM_BITS,
1225        )?;
1226
1227        let r = r.to_biguint(layouter, &self.biguint_gadget)?;
1228        let s = s.to_biguint(layouter, &self.biguint_gadget)?;
1229        let res_times_s = self.biguint_gadget.mul(layouter, &res, &s)?;
1230        let reduced_res_times_s =
1231            reduce_biguint_mod_scalar_order::<C>(layouter, &self.biguint_gadget, &res_times_s)?;
1232
1233        self.biguint_gadget.assert_equal(layouter, &r, &reduced_res_times_s)?;
1234
1235        AssignedScalarOfNativeCurve::from_biguint(layouter, &self.biguint_gadget, &res)
1236    }
1237
1238    fn inv(
1239        &self,
1240        layouter: &mut impl Layouter<C::Base>,
1241        s: &AssignedScalarOfNativeCurve<C>,
1242    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1243        let one = self.assign_fixed(layouter, C::ScalarField::ONE)?;
1244        self.div(layouter, &one, s)
1245    }
1246
1247    fn inv0(
1248        &self,
1249        layouter: &mut impl Layouter<C::Base>,
1250        x: &AssignedScalarOfNativeCurve<C>,
1251    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1252        let is_zero = self.is_zero(layouter, x)?;
1253        let zero = self.assign_fixed(layouter, C::ScalarField::ZERO)?;
1254        let one = self.assign_fixed(layouter, C::ScalarField::ONE)?;
1255        let invertible = self.select(layouter, &is_zero, &one, x)?;
1256        let inverse = self.inv(layouter, &invertible)?;
1257        self.select(layouter, &is_zero, &zero, &inverse)
1258    }
1259}
1260
1261impl<C: EdwardsCurve> ZeroInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {}
1262
1263impl<C: EdwardsCurve> ZeroInstructions<C::Base, AssignedScalarOfNativeCurve<C>> for EccChip<C> {}
1264
1265impl<C: EdwardsCurve> ControlFlowInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
1266    fn select(
1267        &self,
1268        layouter: &mut impl Layouter<C::Base>,
1269        cond: &AssignedBit<C::Base>,
1270        a: &AssignedNativePoint<C>,
1271        b: &AssignedNativePoint<C>,
1272    ) -> Result<AssignedNativePoint<C>, Error> {
1273        let x = self.native_gadget.select(layouter, cond, &a.x, &b.x)?;
1274        let y = self.native_gadget.select(layouter, cond, &a.y, &b.y)?;
1275        let in_subgroup = a.checked_in_subgroup && b.checked_in_subgroup;
1276        Ok(AssignedNativePoint {
1277            x,
1278            y,
1279            checked_in_subgroup: in_subgroup,
1280        })
1281    }
1282}
1283
1284impl<C: EdwardsCurve> ControlFlowInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
1285    for EccChip<C>
1286{
1287    fn select(
1288        &self,
1289        layouter: &mut impl Layouter<C::Base>,
1290        cond: &AssignedBit<C::Base>,
1291        a: &AssignedScalarOfNativeCurve<C>,
1292        b: &AssignedScalarOfNativeCurve<C>,
1293    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1294        let a_as_big = a.to_biguint(layouter, &self.biguint_gadget)?;
1295        let b_as_big = b.to_biguint(layouter, &self.biguint_gadget)?;
1296        let selected = self.biguint_gadget.select(layouter, cond, &a_as_big, &b_as_big)?;
1297        Ok(AssignedScalarOfNativeCurve {
1298            bits: self.biguint_gadget.to_le_bits(layouter, &selected)?,
1299            enforced_canonical: a.enforced_canonical && b.enforced_canonical,
1300        })
1301    }
1302}
1303
1304#[cfg(any(test, feature = "testing"))]
1305impl<C: EdwardsCurve> FromScratch<C::Base> for EccChip<C> {
1306    type Config = (EccConfig, P2RDecompositionConfig);
1307
1308    fn new_from_scratch(config: &Self::Config) -> Self {
1309        let p2r_decomp_config = &config.1;
1310        let max_bit_len = 8;
1311        let native_chip = NativeChip::new_from_scratch(&p2r_decomp_config.native_config);
1312        let core_decomposition_chip = P2RDecompositionChip::new(p2r_decomp_config, &max_bit_len);
1313        let native_gadget = NativeGadget::new(core_decomposition_chip, native_chip);
1314        let biguint_gadget = BigUintGadget::new(&native_gadget);
1315        Self {
1316            config: config.0.clone(),
1317            native_gadget,
1318            biguint_gadget,
1319        }
1320    }
1321
1322    fn configure_from_scratch(
1323        meta: &mut ConstraintSystem<C::Base>,
1324        advice_columns: &mut Vec<Column<Advice>>,
1325        fixed_columns: &mut Vec<Column<Fixed>>,
1326        instance_columns: &[Column<Instance>; 2],
1327    ) -> Self::Config {
1328        let native_gadget_config = <NG<C::Base> as FromScratch<C::Base>>::configure_from_scratch(
1329            meta,
1330            advice_columns,
1331            fixed_columns,
1332            instance_columns,
1333        );
1334        while advice_columns.len() < NB_EDWARDS_COLS {
1335            advice_columns.push(meta.advice_column());
1336        }
1337        let advice_cols: [_; NB_EDWARDS_COLS] =
1338            advice_columns[..NB_EDWARDS_COLS].try_into().unwrap();
1339        let ecc_config = EccChip::<C>::configure(meta, &advice_cols);
1340
1341        (ecc_config, native_gadget_config)
1342    }
1343
1344    fn load_from_scratch(&self, layouter: &mut impl Layouter<C::Base>) -> Result<(), Error> {
1345        self.native_gadget.load_from_scratch(layouter)
1346    }
1347}
1348
1349#[cfg(any(test, feature = "testing"))]
1350impl<C: EdwardsCurve> Sampleable for AssignedNativePoint<C> {
1351    fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
1352        C::CryptographicGroup::random(rng)
1353    }
1354}
1355
1356impl<C: EdwardsCurve> EccChip<C> {
1357    /// Creates an assigned Jubjub scalar from an integer represented as a
1358    /// sequence of little-endian bytes.
1359    pub fn scalar_from_le_bytes(
1360        &self,
1361        layouter: &mut impl Layouter<C::Base>,
1362        bytes: &[AssignedByte<C::Base>],
1363    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1364        let mut bits = Vec::with_capacity(bytes.len() * 8);
1365        for byte in bytes {
1366            let byte_as_f: AssignedNative<C::Base> = self.native_gadget.convert(layouter, byte)?;
1367            bits.extend(self.native_gadget.assigned_to_le_bits(
1368                layouter,
1369                &byte_as_f,
1370                Some(8),
1371                true,
1372            )?)
1373        }
1374        Ok(AssignedScalarOfNativeCurve {
1375            bits,
1376            enforced_canonical: false,
1377        })
1378    }
1379}
1380
1381/// This conversion should not exist for Base -> Scalar. It is a tech debt. We
1382/// should fix this as soon as compact supports types (other than assigned
1383/// native) <https://github.com/midnightntwrk/midnight-circuits/issues/433>
1384impl<C: EdwardsCurve>
1385    ConversionInstructions<C::Base, AssignedNative<C::Base>, AssignedScalarOfNativeCurve<C>>
1386    for EccChip<C>
1387{
1388    fn convert_value(&self, _x: &C::Base) -> Option<C::ScalarField> {
1389        unimplemented!("The caller should decide how to convert the value off-circuit, i.e., what to do with overflows.");
1390    }
1391
1392    fn convert(
1393        &self,
1394        layouter: &mut impl Layouter<C::Base>,
1395        x: &AssignedNative<C::Base>,
1396    ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1397        Ok(AssignedScalarOfNativeCurve {
1398            bits: self.native_gadget.assigned_to_le_bits(layouter, x, None, true)?,
1399            enforced_canonical: false,
1400        })
1401    }
1402}
1403
1404#[cfg(test)]
1405mod tests {
1406    use midnight_curves::{Fq as JubjubBase, JubjubExtended};
1407
1408    use super::*;
1409    use crate::{
1410        ecc::hash_to_curve::HashToCurveGadget,
1411        hash::poseidon::PoseidonChip,
1412        instructions::{ecc, hash_to_curve::tests::test_hash_to_curve},
1413    };
1414
1415    macro_rules! test_generic {
1416        ($mod:ident, $op:ident, $native:ty, $curve:ty, $name:expr) => {
1417            $mod::tests::$op::<$native, AssignedNativePoint<$curve>, EccChip<$curve>>($name);
1418        };
1419    }
1420
1421    macro_rules! test {
1422        ($mod:ident, $op:ident) => {
1423            #[test]
1424            fn $op() {
1425                test_generic!($mod, $op, JubjubBase, JubjubExtended, "native_ecc");
1426            }
1427        };
1428    }
1429
1430    test!(assertions, test_assertions);
1431
1432    test!(public_input, test_public_inputs);
1433
1434    test!(equality, test_is_equal);
1435
1436    test!(zero, test_zero_assertions);
1437    test!(zero, test_is_zero);
1438
1439    test!(control_flow, test_select);
1440    test!(control_flow, test_cond_assert_equal);
1441    test!(control_flow, test_cond_swap);
1442
1443    macro_rules! test_scalar {
1444        ($mod:ident, $op:ident, $name:ident) => {
1445            #[test]
1446            fn $name() {
1447                $mod::tests::$op::<
1448                    JubjubBase,
1449                    AssignedScalarOfNativeCurve<JubjubExtended>,
1450                    EccChip<JubjubExtended>,
1451                >("native_ecc_scalar");
1452            }
1453        };
1454    }
1455
1456    test_scalar!(assertions, test_assertions, scalar_assertions);
1457
1458    test_scalar!(public_input, test_public_inputs, scalar_public_inputs);
1459
1460    test_scalar!(equality, test_is_equal, scalar_is_equal);
1461
1462    test_scalar!(zero, test_zero_assertions, scalar_zero_assertions);
1463    test_scalar!(zero, test_is_zero, scalar_is_zero);
1464
1465    test_scalar!(control_flow, test_select, scalar_select);
1466    test_scalar!(control_flow, test_cond_assert_equal, scalar_cond_assert_eq);
1467    test_scalar!(control_flow, test_cond_swap, scalar_test_cond_swap);
1468
1469    test_scalar!(arithmetic, test_add, scalar_add);
1470    test_scalar!(arithmetic, test_sub, scalar_sub);
1471    test_scalar!(arithmetic, test_mul, scalar_mul);
1472    test_scalar!(arithmetic, test_div, scalar_div);
1473    test_scalar!(arithmetic, test_neg, scalar_neg);
1474    test_scalar!(arithmetic, test_inv, scalar_inv);
1475    test_scalar!(arithmetic, test_pow, scalar_pow);
1476    test_scalar!(
1477        arithmetic,
1478        test_linear_combination,
1479        scalar_linear_combination
1480    );
1481    test_scalar!(arithmetic, test_add_and_mul, scalar_add_and_mul);
1482
1483    macro_rules! ecc_tests {
1484        ($op:ident) => {
1485            #[test]
1486            fn $op() {
1487                ecc::tests::$op::<JubjubBase, JubjubExtended, EccChip<JubjubExtended>>(
1488                    "native_ecc",
1489                );
1490            }
1491        };
1492    }
1493
1494    ecc_tests!(test_assign);
1495    ecc_tests!(test_assign_without_subgroup_check);
1496    ecc_tests!(test_add);
1497    ecc_tests!(test_double);
1498    ecc_tests!(test_negate);
1499    ecc_tests!(test_msm);
1500    ecc_tests!(test_msm_by_bounded_scalars);
1501    ecc_tests!(test_mul_by_constant);
1502    ecc_tests!(test_coordinates);
1503
1504    #[test]
1505    fn test_htc() {
1506        test_hash_to_curve::<
1507            JubjubBase,
1508            JubjubExtended,
1509            AssignedNative<JubjubBase>,
1510            EccChip<JubjubExtended>,
1511            NativeChip<JubjubBase>,
1512            HashToCurveGadget<_, _, _, PoseidonChip<JubjubBase>, _>,
1513        >("native_ecc")
1514    }
1515}