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::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| 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 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 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
176fn 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 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 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 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#[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 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 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 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#[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 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 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 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 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 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 else {
620 acc = self.cond_add(&mut region, i, &acc, base, bit)?;
621 }
622 }
623
624 Ok(acc)
625 },
626 )
627 }
628
629 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 pub fn native_gadget(&self) -> &impl NativeInstructions<C::Base> {
640 &self.native_gadget
641 }
642}
643
644impl<C: EdwardsCurve> EccChip<C> {
645 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 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 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 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 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 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
1373impl<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}