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