1use 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
48pub const NB_EDWARDS_COLS: usize = 9;
50
51#[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 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#[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 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 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
184fn 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 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 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 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#[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 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 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 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#[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 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 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 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 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 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 else {
628 acc = self.cond_add(&mut region, i, &acc, base, bit)?;
629 }
630 }
631
632 Ok(acc)
633 },
634 )
635 }
636
637 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 pub fn native_gadget(&self) -> &impl NativeInstructions<C::Base> {
648 &self.native_gadget
649 }
650}
651
652impl<C: EdwardsCurve> EccChip<C> {
653 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 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 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 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 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 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
1381impl<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}