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};
26#[cfg(any(test, feature = "testing"))]
27use {
28 crate::field::decomposition::chip::P2RDecompositionConfig,
29 crate::testing_utils::{FromScratch, Sampleable},
30 midnight_proofs::plonk::{Fixed, Instance},
31 rand::RngCore,
32};
33
34use crate::{
35 ecc::curves::{CircuitCurve, EdwardsCurve},
36 field::{decomposition::chip::P2RDecompositionChip, NativeChip, NativeGadget},
37 instructions::*,
38 types::{AssignedBit, AssignedByte, AssignedNative, InnerConstants, InnerValue, Instantiable},
39 utils::ComposableChip,
40 CircuitField,
41};
42
43pub const NB_EDWARDS_COLS: usize = 9;
45
46#[derive(Clone, Debug)]
57pub struct AssignedNativePoint<C: CircuitCurve> {
58 x: AssignedNative<C::Base>,
59 y: AssignedNative<C::Base>,
60 checked_in_subgroup: bool,
61}
62
63impl<C: CircuitCurve> InnerValue for AssignedNativePoint<C> {
64 type Element = C::CryptographicGroup;
65
66 fn value(&self) -> Value<Self::Element> {
67 assert!(self.checked_in_subgroup);
68 self.x
69 .value()
70 .zip(self.y.value())
71 .map(|(x, y)| C::from_xy(*x, *y).expect("Valid coordinates.").into_subgroup())
72 }
73}
74
75impl<C: CircuitCurve> AssignedNativePoint<C> {
76 fn curve_value(&self) -> Value<C> {
80 self.x
81 .value()
82 .zip(self.y.value())
83 .map(|(x, y)| C::from_xy(*x, *y).expect("Valid coordinates."))
84 }
85}
86
87impl<C: CircuitCurve> Instantiable<C::Base> for AssignedNativePoint<C> {
88 fn as_public_input(p: &C::CryptographicGroup) -> Vec<C::Base> {
89 let point: C = (*p).into();
90 let coordinates = point.coordinates().unwrap();
91 vec![coordinates.0, coordinates.1]
92 }
93}
94
95impl<C: EdwardsCurve> InnerConstants for AssignedNativePoint<C> {
96 fn inner_zero() -> C::CryptographicGroup {
97 C::CryptographicGroup::identity()
98 }
99
100 fn inner_one() -> Self::Element {
101 C::CryptographicGroup::generator()
102 }
103}
104
105#[derive(Clone, Debug)]
107pub struct AssignedScalarOfNativeCurve<C: CircuitCurve>(Vec<AssignedBit<C::Base>>);
108
109impl<C: CircuitCurve> InnerValue for AssignedScalarOfNativeCurve<C> {
110 type Element = C::ScalarField;
111
112 fn value(&self) -> Value<Self::Element> {
113 let bools = self.0.iter().map(|b| b.value());
114 let value_bools: Value<Vec<bool>> = Value::from_iter(bools);
115 value_bools.map(|le_bits| C::ScalarField::from_bits_le(&le_bits))
116 }
117}
118
119impl<C: EdwardsCurve> Instantiable<C::Base> for AssignedScalarOfNativeCurve<C> {
120 fn as_public_input(element: &C::ScalarField) -> Vec<C::Base> {
121 let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
123 element
124 .to_bits_le(Some(C::NUM_BITS_SUBGROUP as usize))
125 .chunks(nb_bits_per_batch)
126 .map(C::Base::from_bits_le)
127 .collect()
128 }
129}
130
131impl<C: EdwardsCurve> InnerConstants for AssignedScalarOfNativeCurve<C> {
132 fn inner_zero() -> C::ScalarField {
133 C::ScalarField::ZERO
134 }
135 fn inner_one() -> C::ScalarField {
136 C::ScalarField::ONE
137 }
138}
139
140#[cfg(any(test, feature = "testing"))]
141impl<C: EdwardsCurve> Sampleable for AssignedScalarOfNativeCurve<C> {
142 fn sample_inner(rng: impl RngCore) -> C::ScalarField {
143 C::ScalarField::random(rng)
144 }
145}
146
147#[derive(Clone, Debug)]
149pub struct EccConfig {
150 pub(crate) q_double: Selector,
151 pub(crate) q_cond_add: Selector,
152 pub(crate) q_mem: Selector,
153 pub(crate) advice_cols: [Column<Advice>; NB_EDWARDS_COLS],
154}
155
156impl EccConfig {
157 fn create_double_gate<C: EdwardsCurve>(
186 &self,
187 meta: &mut ConstraintSystem<C::Base>,
188 q_double: &Selector,
189 ) {
190 meta.create_gate("double", |meta| {
191 let xp = meta.query_advice(self.advice_cols[5], Rotation::cur());
192 let yp = meta.query_advice(self.advice_cols[6], Rotation::cur());
193 let xq = meta.query_advice(self.advice_cols[0], Rotation::next());
194 let yq = meta.query_advice(self.advice_cols[1], Rotation::next());
195
196 let xp_xp = meta.query_advice(self.advice_cols[7], Rotation::cur());
197
198 let one = Expression::from(1);
199 let edwards_d = Expression::Constant(C::D);
200 let xp_yp = &xp * &yp;
201 let yp_yp = yp.square();
202 let d_xp_xp_yp_yp = edwards_d * &xp_xp * &yp_yp;
203
204 let id1 = xq * (&one + &d_xp_xp_yp_yp) - (xp_yp.clone() + xp_yp);
205 let id2 = yq * (one - d_xp_xp_yp_yp) - (yp_yp + &xp_xp);
206 let id3 = xp.clone() * xp - xp_xp;
207
208 Constraints::with_selector(
209 *q_double,
210 vec![
211 ("qx constraint for q = 2 * p", id1),
212 ("qy constraint for q = 2 * p", id2),
213 ("constraint for xp_xp = xp * xp", id3),
214 ],
215 )
216 })
217 }
218
219 fn create_cond_add_gate<C: EdwardsCurve>(
247 &self,
248 meta: &mut ConstraintSystem<C::Base>,
249 q_cond_add: &Selector,
250 ) {
251 meta.create_gate("conditional add", |meta| {
252 let xq = meta.query_advice(self.advice_cols[0], Rotation::cur());
253 let yq = meta.query_advice(self.advice_cols[1], Rotation::cur());
254 let xs = meta.query_advice(self.advice_cols[2], Rotation::cur());
255 let ys = meta.query_advice(self.advice_cols[3], Rotation::cur());
256 let xr = meta.query_advice(self.advice_cols[5], Rotation::cur());
257 let yr = meta.query_advice(self.advice_cols[6], Rotation::cur());
258 let b = meta.query_advice(self.advice_cols[4], Rotation::cur());
259
260 let one = Expression::from(1);
261 let edwards_d = Expression::Constant(C::D);
262
263 let xq_yq_xs_ys = meta.query_advice(self.advice_cols[8], Rotation::cur());
264
265 let xq_xs = &xq * &xs;
266 let yq_ys = &yq * &ys;
267 let xq_ys = &xq * &ys;
268 let xs_yq = &xs * &yq;
269 let b_d_xq_xs_yq_ys = &b * edwards_d * &xq_yq_xs_ys;
270
271 let id1 = xr * (&one + &b_d_xq_xs_yq_ys) - (&xq + &b * (xq_ys + xs_yq - &xq));
272 let id2 = yr * (one - b_d_xq_xs_yq_ys) - (&yq + b * (yq_ys + xq_xs - &yq));
273 let id3 = xq_yq_xs_ys - xq * yq * xs * ys;
274
275 Constraints::with_selector(
276 *q_cond_add,
277 vec![
278 ("rx constraint for r = q + b * s", id1),
279 ("ry constraint for r = q + b * s", id2),
280 ("constraint for xq_yq_xs_ys = xq * yq * xs * ys", id3),
281 ],
282 )
283 })
284 }
285
286 fn create_membership_gate<C: EdwardsCurve>(
297 &self,
298 meta: &mut ConstraintSystem<C::Base>,
299 q_point: &Selector,
300 ) {
301 meta.create_gate("witness point", |meta| {
302 let x = meta.query_advice(self.advice_cols[0], Rotation::cur());
303 let y = meta.query_advice(self.advice_cols[1], Rotation::cur());
304
305 let one = Expression::from(1);
306 let edwards_d = Expression::Constant(C::D);
307
308 let x_sq = x.square();
309 let y_sq = y.square();
310
311 let id = y_sq.clone() - x_sq.clone() - (one + edwards_d * x_sq * y_sq);
312
313 Constraints::with_selector(*q_point, vec![("curve equation", id)])
314 })
315 }
316}
317
318type NG<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
319
320#[derive(Clone, Debug)]
323pub struct EccChip<C: EdwardsCurve> {
324 config: EccConfig,
325 native_gadget: NG<C::Base>,
326}
327
328impl<C: EdwardsCurve> Chip<C::Base> for EccChip<C> {
329 type Config = EccConfig;
330 type Loaded = ();
331
332 fn config(&self) -> &Self::Config {
333 &self.config
334 }
335
336 fn loaded(&self) -> &Self::Loaded {
337 &()
338 }
339}
340
341impl<C: EdwardsCurve> ComposableChip<C::Base> for EccChip<C> {
342 type SharedResources = [Column<Advice>; NB_EDWARDS_COLS];
343 type InstructionDeps = NG<C::Base>;
344
345 fn new(config: &Self::Config, sub_chips: &Self::InstructionDeps) -> Self {
346 Self {
347 config: config.clone(),
348 native_gadget: sub_chips.clone(),
349 }
350 }
351
352 fn configure(
353 meta: &mut ConstraintSystem<C::Base>,
354 advice_cols: &Self::SharedResources,
355 ) -> Self::Config {
356 assert_eq!(C::A, -C::Base::ONE);
357
358 for col in advice_cols.iter().take(7) {
360 meta.enable_equality(*col)
361 }
362
363 let q_double = meta.selector();
364 let q_cond_add = meta.selector();
365 let q_mem = meta.selector();
366
367 let config = EccConfig {
368 q_double,
369 q_cond_add,
370 q_mem,
371 advice_cols: *advice_cols,
372 };
373
374 config.create_double_gate::<C>(meta, &q_double);
375 config.create_cond_add_gate::<C>(meta, &q_cond_add);
376 config.create_membership_gate::<C>(meta, &q_mem);
377
378 config
379 }
380
381 fn load(&self, _layouter: &mut impl Layouter<C::Base>) -> Result<(), Error> {
382 Ok(())
383 }
384}
385
386impl<C: EdwardsCurve> EccChip<C> {
387 fn cond_add(
400 &self,
401 region: &mut Region<C::Base>,
402 offset: usize,
403 q: &AssignedNativePoint<C>,
404 s: &AssignedNativePoint<C>,
405 b: &AssignedBit<C::Base>,
406 ) -> Result<AssignedNativePoint<C>, Error> {
407 let config = self.config();
408 config.q_cond_add.enable(region, offset)?;
409
410 let (q_val, s_val) = (q.curve_value(), s.curve_value());
411 let in_subgroup = q.checked_in_subgroup && s.checked_in_subgroup;
412
413 let (xr_val, yr_val) = Self::p_plus_b_q(q_val, s_val, b.value());
414 let xr = region.assign_advice(|| "xr", config.advice_cols[5], offset, || xr_val)?;
415 let yr = region.assign_advice(|| "yr", config.advice_cols[6], offset, || yr_val)?;
416
417 let (xq, yq) = q_val.map(|q| q.coordinates().unwrap()).unzip();
418 let (xs, ys) = s_val.map(|s| s.coordinates().unwrap()).unzip();
419 let prod_val = xq * yq * xs * ys;
420 region.assign_advice(|| "xq_yq_xs_ys", config.advice_cols[8], offset, || prod_val)?;
421
422 Ok(AssignedNativePoint {
423 x: xr,
424 y: yr,
425 checked_in_subgroup: in_subgroup,
426 })
427 }
428
429 fn add_then_double(
442 &self,
443 region: &mut Region<C::Base>,
444 offset: usize,
445 p: &AssignedNativePoint<C>,
446 q: &AssignedNativePoint<C>,
447 b: &AssignedBit<C::Base>,
448 ) -> Result<AssignedNativePoint<C>, Error> {
449 let config = self.config();
450
451 config.q_cond_add.enable(region, offset)?;
452 config.q_double.enable(region, offset)?;
453
454 let (q_val, p_val) = (q.curve_value(), p.curve_value());
455 let in_subgroup = q.checked_in_subgroup && p.checked_in_subgroup;
456
457 let (xs_val, ys_val) = Self::p_plus_b_q(p_val, q_val, b.value());
458
459 region.assign_advice(|| "xs", config.advice_cols[5], offset, || xs_val)?;
460 region.assign_advice(|| "ys", config.advice_cols[6], offset, || ys_val)?;
461
462 let s_val = xs_val.zip(ys_val).map(|(xs, ys)| C::from_xy(xs, ys).unwrap());
463 let r_val = s_val.map(|s| s + s);
464
465 let xr_val = r_val.map(|r: C| r.coordinates().unwrap().0);
466 let yr_val = r_val.map(|r: C| r.coordinates().unwrap().1);
467
468 let xr = region.assign_advice(|| "xr", config.advice_cols[0], offset + 1, || xr_val)?;
469 let yr = region.assign_advice(|| "yr", config.advice_cols[1], offset + 1, || yr_val)?;
470
471 region.assign_advice(
472 || "xs_xs",
473 config.advice_cols[7],
474 offset,
475 || xs_val * xs_val,
476 )?;
477
478 let (xp, yp) = p_val.map(|c| c.coordinates().unwrap()).unzip();
479 let (xq, yq) = q_val.map(|c| c.coordinates().unwrap()).unzip();
480 let prod_val = xp * yp * xq * yq;
481 region.assign_advice(|| "xp_yp_xq_yq", config.advice_cols[8], offset, || prod_val)?;
482
483 Ok(AssignedNativePoint {
484 x: xr,
485 y: yr,
486 checked_in_subgroup: in_subgroup,
487 })
488 }
489
490 pub fn mul(
492 &self,
493 layouter: &mut impl Layouter<C::Base>,
494 scalar: &AssignedScalarOfNativeCurve<C>,
495 base: &AssignedNativePoint<C>,
496 ) -> Result<AssignedNativePoint<C>, Error> {
497 let config = &self.config();
498
499 let scalar_be_bits = &mut scalar.0.clone();
501 scalar_be_bits.reverse();
502
503 let id_point: AssignedNativePoint<C> =
504 self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
505
506 layouter.assign_region(
507 || "assign mul",
508 |mut region: Region<'_, C::Base>| {
509 id_point.x.copy_advice(|| "id.x", &mut region, config.advice_cols[0], 0)?;
510 id_point.y.copy_advice(|| "id.y", &mut region, config.advice_cols[1], 0)?;
511
512 let mut acc = id_point.clone();
513
514 for (i, bit) in scalar_be_bits.iter().enumerate() {
515 base.x.copy_advice(|| "base.x", &mut region, config.advice_cols[2], i)?;
516 base.y.copy_advice(|| "base.y", &mut region, config.advice_cols[3], i)?;
517 bit.0.copy_advice(|| "b cond_add", &mut region, config.advice_cols[4], i)?;
518
519 if i < scalar_be_bits.len() - 1 {
520 acc = self.add_then_double(&mut region, i, &acc, base, bit)?;
521 }
522 else {
524 acc = self.cond_add(&mut region, i, &acc, base, bit)?;
525 }
526 }
527
528 Ok(acc)
529 },
530 )
531 }
532
533 fn p_plus_b_q(p: Value<C>, q: Value<C>, b: Value<bool>) -> (Value<C::Base>, Value<C::Base>) {
535 p.zip(q)
536 .zip(b)
537 .map(|((p, q), b)| if b { p + q } else { p })
538 .map(|r| r.coordinates().unwrap())
539 .unzip()
540 }
541
542 pub fn native_gadget(&self) -> &impl NativeInstructions<C::Base> {
544 &self.native_gadget
545 }
546}
547
548impl<C: EdwardsCurve> EccChip<C> {
549 pub(crate) fn clear_cofactor(
552 &self,
553 layouter: &mut impl Layouter<C::Base>,
554 point: &AssignedNativePoint<C>,
555 ) -> Result<AssignedNativePoint<C>, Error> {
556 if point.checked_in_subgroup {
557 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()));
558 }
559 let r = self.mul_by_constant(layouter, C::ScalarField::from_u128(C::COFACTOR), point)?;
560 Ok(AssignedNativePoint {
561 x: r.x,
562 y: r.y,
563 checked_in_subgroup: true,
564 })
565 }
566
567 pub(crate) fn point_from_coordinates_unsafe(
570 &self,
571 layouter: &mut impl Layouter<C::Base>,
572 x: &AssignedNative<C::Base>,
573 y: &AssignedNative<C::Base>,
574 ) -> Result<AssignedNativePoint<C>, Error> {
575 layouter.assign_region(
576 || "assign new point",
577 |mut region: Region<'_, C::Base>| {
578 x.copy_advice(|| "x", &mut region, self.config.advice_cols[0], 0)?;
579 y.copy_advice(|| "y", &mut region, self.config.advice_cols[1], 0)?;
580 self.config.q_mem.enable(&mut region, 0)
581 },
582 )?;
583 Ok(AssignedNativePoint {
584 x: x.clone(),
585 y: y.clone(),
586 checked_in_subgroup: false,
587 })
588 }
589}
590
591impl<C: EdwardsCurve> EccInstructions<C::Base, C> for EccChip<C> {
592 type Point = AssignedNativePoint<C>;
593 type Coordinate = AssignedNative<C::Base>;
594 type Scalar = AssignedScalarOfNativeCurve<C>;
595
596 fn add(
597 &self,
598 layouter: &mut impl Layouter<C::Base>,
599 p: &Self::Point,
600 q: &Self::Point,
601 ) -> Result<Self::Point, Error> {
602 let config = self.config();
603 let b: AssignedBit<C::Base> = self.native_gadget.assign_fixed(layouter, true)?;
604
605 layouter.assign_region(
606 || "assign add",
607 |mut region: Region<'_, C::Base>| {
608 p.x.copy_advice(|| "px", &mut region, config.advice_cols[0], 0)?;
609 p.y.copy_advice(|| "py", &mut region, config.advice_cols[1], 0)?;
610 q.x.copy_advice(|| "qx", &mut region, config.advice_cols[2], 0)?;
611 q.y.copy_advice(|| "qy", &mut region, config.advice_cols[3], 0)?;
612 b.0.copy_advice(|| "b", &mut region, config.advice_cols[4], 0)?;
613
614 self.cond_add(&mut region, 0, p, q, &b)
615 },
616 )
617 }
618
619 fn double(
620 &self,
621 layouter: &mut impl Layouter<C::Base>,
622 p: &Self::Point,
623 ) -> Result<Self::Point, Error> {
624 self.add(layouter, p, p)
625 }
626
627 fn negate(
628 &self,
629 layouter: &mut impl Layouter<C::Base>,
630 p: &Self::Point,
631 ) -> Result<Self::Point, Error> {
632 Ok(AssignedNativePoint {
633 x: self.native_gadget.neg(layouter, &p.x)?,
634 y: p.y.clone(),
635 checked_in_subgroup: p.checked_in_subgroup,
636 })
637 }
638
639 fn msm(
640 &self,
641 layouter: &mut impl Layouter<C::Base>,
642 scalars: &[Self::Scalar],
643 bases: &[Self::Point],
644 ) -> Result<Self::Point, Error> {
645 let scaled_points = scalars
646 .iter()
647 .zip(bases.iter())
648 .map(|(scalar, point)| self.mul(layouter, scalar, point))
649 .collect::<Result<Vec<Self::Point>, Error>>()?;
650
651 scaled_points[1..].iter().try_fold(scaled_points[0].clone(), |acc, e| {
652 self.add(layouter, &acc, e)
653 })
654 }
655
656 fn mul_by_constant(
657 &self,
658 layouter: &mut impl Layouter<C::Base>,
659 scalar: C::ScalarField,
660 base: &Self::Point,
661 ) -> Result<Self::Point, Error> {
662 if scalar == C::ScalarField::ZERO {
663 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
664 }
665
666 if scalar == C::ScalarField::ONE {
667 return Ok(base.clone());
668 }
669
670 let s = self.assign_fixed(layouter, scalar)?;
671 self.msm(layouter, &[s], std::slice::from_ref(base))
672 }
673
674 fn point_from_coordinates(
675 &self,
676 layouter: &mut impl Layouter<C::Base>,
677 x: &Self::Coordinate,
678 y: &Self::Coordinate,
679 ) -> Result<Self::Point, Error> {
680 let point_val = x.value().zip(y.value()).map(|(x, y)| {
681 C::from_xy(*x, *y)
682 .expect("Affine coordinates must satisfy Jubjub equation.")
683 .into_subgroup()
684 });
685 let point: Self::Point = self.assign(layouter, point_val)?;
686 self.native_gadget.assert_equal(layouter, x, &point.x)?;
687 self.native_gadget.assert_equal(layouter, y, &point.y)?;
688 Ok(point)
689 }
690
691 fn assign_without_subgroup_check(
692 &self,
693 layouter: &mut impl Layouter<C::Base>,
694 value: Value<C::CryptographicGroup>,
695 ) -> Result<Self::Point, Error> {
696 let config = self.config();
697 let (x_val, y_val) = value
698 .map(|p| p.into().coordinates().expect("assign_without_subgroup_check: invalid point"))
699 .unzip();
700
701 layouter.assign_region(
702 || "assign point without subgroup check",
703 |mut region: Region<'_, C::Base>| {
704 config.q_mem.enable(&mut region, 0)?;
705 let x = region.assign_advice(|| "x", config.advice_cols[0], 0, || x_val)?;
706 let y = region.assign_advice(|| "y", config.advice_cols[1], 0, || y_val)?;
707 Ok(AssignedNativePoint {
708 x,
709 y,
710 checked_in_subgroup: false,
711 })
712 },
713 )
714 }
715
716 fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
717 point.x.clone()
718 }
719
720 fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
721 point.y.clone()
722 }
723
724 fn base_field(&self) -> &impl DecompositionInstructions<C::Base, Self::Coordinate> {
725 &self.native_gadget
726 }
727}
728
729impl<C: EdwardsCurve> AssignmentInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
730 fn assign(
731 &self,
732 layouter: &mut impl Layouter<C::Base>,
733 value: Value<C::CryptographicGroup>,
734 ) -> Result<AssignedNativePoint<C>, Error> {
735 let cofactor = C::ScalarField::from_u128(C::COFACTOR);
740 let cf_root_val = value.map(|p| p * cofactor.invert().expect("Cofactor must not be 0"));
741 let cf_root = self.assign_without_subgroup_check(layouter, cf_root_val)?;
742
743 self.clear_cofactor(layouter, &cf_root)
744 }
745
746 fn assign_fixed(
747 &self,
748 layouter: &mut impl Layouter<C::Base>,
749 constant: C::CryptographicGroup,
750 ) -> Result<AssignedNativePoint<C>, Error> {
751 let coords = constant.into().coordinates().unwrap();
752 let x = self.native_gadget.assign_fixed(layouter, coords.0)?;
753 let y = self.native_gadget.assign_fixed(layouter, coords.1)?;
754 Ok(AssignedNativePoint {
755 x,
756 y,
757 checked_in_subgroup: true,
758 })
759 }
760}
761
762impl<C: EdwardsCurve> AssignmentInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
763 for EccChip<C>
764{
765 fn assign(
766 &self,
767 layouter: &mut impl Layouter<C::Base>,
768 value: Value<C::ScalarField>,
769 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
770 let bits = value
771 .map(|s| s.to_bits_le(Some(C::ScalarField::NUM_BITS as usize)))
772 .transpose_vec(<C::ScalarField as PrimeField>::NUM_BITS as usize);
773 self.native_gadget.assign_many(layouter, &bits).map(AssignedScalarOfNativeCurve)
774 }
775
776 fn assign_fixed(
777 &self,
778 layouter: &mut impl Layouter<C::Base>,
779 constant: C::ScalarField,
780 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
781 self.native_gadget
782 .assign_many_fixed(layouter, &constant.to_bits_le(None))
783 .map(AssignedScalarOfNativeCurve)
784 }
785}
786
787impl<C: EdwardsCurve> AssertionInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
788 fn assert_equal(
789 &self,
790 layouter: &mut impl Layouter<C::Base>,
791 p: &AssignedNativePoint<C>,
792 q: &AssignedNativePoint<C>,
793 ) -> Result<(), Error> {
794 self.native_gadget.assert_equal(layouter, &p.x, &q.x)?;
795 self.native_gadget.assert_equal(layouter, &p.y, &q.y)
796 }
797
798 fn assert_not_equal(
799 &self,
800 layouter: &mut impl Layouter<C::Base>,
801 p: &AssignedNativePoint<C>,
802 q: &AssignedNativePoint<C>,
803 ) -> Result<(), Error> {
804 let is_eq = self.is_equal(layouter, p, q)?;
805 self.native_gadget.assert_equal_to_fixed(layouter, &is_eq, false)
806 }
807
808 fn assert_equal_to_fixed(
809 &self,
810 layouter: &mut impl Layouter<C::Base>,
811 p: &AssignedNativePoint<C>,
812 constant: C::CryptographicGroup,
813 ) -> Result<(), Error> {
814 let (cx, cy) = constant.into().coordinates().unwrap();
815 self.native_gadget.assert_equal_to_fixed(layouter, &p.x, cx)?;
816 self.native_gadget.assert_equal_to_fixed(layouter, &p.y, cy)
817 }
818
819 fn assert_not_equal_to_fixed(
820 &self,
821 layouter: &mut impl Layouter<C::Base>,
822 p: &AssignedNativePoint<C>,
823 constant: C::CryptographicGroup,
824 ) -> Result<(), Error> {
825 let is_eq = self.is_equal_to_fixed(layouter, p, constant)?;
826 self.native_gadget.assert_equal_to_fixed(layouter, &is_eq, false)
827 }
828}
829
830impl<C: EdwardsCurve> PublicInputInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
831 fn as_public_input(
832 &self,
833 _layouter: &mut impl Layouter<C::Base>,
834 p: &AssignedNativePoint<C>,
835 ) -> Result<Vec<AssignedNative<C::Base>>, Error> {
836 Ok(vec![p.x.clone(), p.y.clone()])
837 }
838
839 fn constrain_as_public_input(
840 &self,
841 layouter: &mut impl Layouter<C::Base>,
842 p: &AssignedNativePoint<C>,
843 ) -> Result<(), Error> {
844 self.as_public_input(layouter, p)?
845 .iter()
846 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
847 }
848
849 fn assign_as_public_input(
850 &self,
851 layouter: &mut impl Layouter<C::Base>,
852 p: Value<C::CryptographicGroup>,
853 ) -> Result<AssignedNativePoint<C>, Error> {
854 let (x, y) = p.map(|p| p.into().coordinates().unwrap()).unzip();
855 let x = self.native_gadget.assign_as_public_input(layouter, x)?;
856 let y = self.native_gadget.assign_as_public_input(layouter, y)?;
857
858 Ok(AssignedNativePoint {
862 x,
863 y,
864 checked_in_subgroup: true,
865 })
866 }
867}
868
869impl<C: EdwardsCurve> PublicInputInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
870 for EccChip<C>
871{
872 fn as_public_input(
873 &self,
874 layouter: &mut impl Layouter<C::Base>,
875 assigned: &AssignedScalarOfNativeCurve<C>,
876 ) -> Result<Vec<AssignedNative<C::Base>>, Error> {
877 let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
879 assigned
880 .0
881 .chunks(nb_bits_per_batch)
882 .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
883 .collect()
884 }
885
886 fn constrain_as_public_input(
887 &self,
888 layouter: &mut impl Layouter<C::Base>,
889 assigned: &AssignedScalarOfNativeCurve<C>,
890 ) -> Result<(), Error> {
891 self.as_public_input(layouter, assigned)?
892 .iter()
893 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
894 }
895
896 fn assign_as_public_input(
897 &self,
898 layouter: &mut impl Layouter<C::Base>,
899 value: Value<C::ScalarField>,
900 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
901 let assigned: AssignedScalarOfNativeCurve<C> = self.assign(layouter, value)?;
902 self.constrain_as_public_input(layouter, &assigned)?;
903 Ok(assigned)
904 }
905}
906
907impl<C: EdwardsCurve> EqualityInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
908 fn is_equal(
909 &self,
910 layouter: &mut impl Layouter<C::Base>,
911 p: &AssignedNativePoint<C>,
912 q: &AssignedNativePoint<C>,
913 ) -> Result<AssignedBit<C::Base>, Error> {
914 let eq_x = self.native_gadget.is_equal(layouter, &p.x, &q.x)?;
915 let eq_y = self.native_gadget.is_equal(layouter, &p.y, &q.y)?;
916 self.native_gadget.and(layouter, &[eq_x, eq_y])
917 }
918
919 fn is_not_equal(
920 &self,
921 layouter: &mut impl Layouter<C::Base>,
922 p: &AssignedNativePoint<C>,
923 q: &AssignedNativePoint<C>,
924 ) -> Result<AssignedBit<C::Base>, Error> {
925 let not_eq_x = self.native_gadget.is_not_equal(layouter, &p.x, &q.x)?;
926 let not_eq_y = self.native_gadget.is_not_equal(layouter, &p.y, &q.y)?;
927 self.native_gadget.or(layouter, &[not_eq_x, not_eq_y])
928 }
929
930 fn is_equal_to_fixed(
931 &self,
932 layouter: &mut impl Layouter<C::Base>,
933 p: &AssignedNativePoint<C>,
934 constant: C::CryptographicGroup,
935 ) -> Result<AssignedBit<C::Base>, Error> {
936 let (cx, cy) = constant.into().coordinates().unwrap();
937 let eq_x = self.native_gadget.is_equal_to_fixed(layouter, &p.x, cx)?;
938 let eq_y = self.native_gadget.is_equal_to_fixed(layouter, &p.y, cy)?;
939 self.native_gadget.and(layouter, &[eq_x, eq_y])
940 }
941
942 fn is_not_equal_to_fixed(
943 &self,
944 layouter: &mut impl Layouter<C::Base>,
945 p: &AssignedNativePoint<C>,
946 constant: C::CryptographicGroup,
947 ) -> Result<AssignedBit<C::Base>, Error> {
948 let (cx, cy) = constant.into().coordinates().unwrap();
949 let not_eq_x = self.native_gadget.is_not_equal_to_fixed(layouter, &p.x, cx)?;
950 let not_eq_y = self.native_gadget.is_not_equal_to_fixed(layouter, &p.y, cy)?;
951 self.native_gadget.or(layouter, &[not_eq_x, not_eq_y])
952 }
953}
954
955impl<C: EdwardsCurve> ZeroInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {}
956
957impl<C: EdwardsCurve> ControlFlowInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
958 fn select(
959 &self,
960 layouter: &mut impl Layouter<C::Base>,
961 cond: &AssignedBit<C::Base>,
962 a: &AssignedNativePoint<C>,
963 b: &AssignedNativePoint<C>,
964 ) -> Result<AssignedNativePoint<C>, Error> {
965 let x = self.native_gadget.select(layouter, cond, &a.x, &b.x)?;
966 let y = self.native_gadget.select(layouter, cond, &a.y, &b.y)?;
967 let in_subgroup = a.checked_in_subgroup && b.checked_in_subgroup;
968 Ok(AssignedNativePoint {
969 x,
970 y,
971 checked_in_subgroup: in_subgroup,
972 })
973 }
974}
975
976#[cfg(any(test, feature = "testing"))]
977impl<C: EdwardsCurve> FromScratch<C::Base> for EccChip<C> {
978 type Config = (EccConfig, P2RDecompositionConfig);
979
980 fn new_from_scratch(config: &Self::Config) -> Self {
981 let p2r_decomp_config = &config.1;
982 let max_bit_len = 8;
983 let native_chip = NativeChip::new_from_scratch(&p2r_decomp_config.native_config);
984 let core_decomposition_chip = P2RDecompositionChip::new(p2r_decomp_config, &max_bit_len);
985 let native_gadget = NativeGadget::new(core_decomposition_chip, native_chip);
986 Self {
987 native_gadget,
988 config: config.0.clone(),
989 }
990 }
991
992 fn configure_from_scratch(
993 meta: &mut ConstraintSystem<C::Base>,
994 advice_columns: &mut Vec<Column<Advice>>,
995 fixed_columns: &mut Vec<Column<Fixed>>,
996 instance_columns: &[Column<Instance>; 2],
997 ) -> Self::Config {
998 let native_gadget_config = <NG<C::Base> as FromScratch<C::Base>>::configure_from_scratch(
999 meta,
1000 advice_columns,
1001 fixed_columns,
1002 instance_columns,
1003 );
1004 while advice_columns.len() < NB_EDWARDS_COLS {
1005 advice_columns.push(meta.advice_column());
1006 }
1007 let advice_cols: [_; NB_EDWARDS_COLS] =
1008 advice_columns[..NB_EDWARDS_COLS].try_into().unwrap();
1009 let ecc_config = EccChip::<C>::configure(meta, &advice_cols);
1010
1011 (ecc_config, native_gadget_config)
1012 }
1013
1014 fn load_from_scratch(&self, layouter: &mut impl Layouter<C::Base>) -> Result<(), Error> {
1015 self.native_gadget.load_from_scratch(layouter)
1016 }
1017}
1018
1019#[cfg(any(test, feature = "testing"))]
1020impl<C: EdwardsCurve> Sampleable for AssignedNativePoint<C> {
1021 fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
1022 C::CryptographicGroup::random(rng)
1023 }
1024}
1025
1026impl<C: EdwardsCurve> EccChip<C> {
1027 pub fn scalar_from_le_bytes(
1030 &self,
1031 layouter: &mut impl Layouter<C::Base>,
1032 bytes: &[AssignedByte<C::Base>],
1033 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1034 let mut bits = Vec::with_capacity(bytes.len() * 8);
1035 for byte in bytes {
1036 let byte_as_f: AssignedNative<C::Base> = self.native_gadget.convert(layouter, byte)?;
1037 bits.extend(self.native_gadget.assigned_to_le_bits(
1038 layouter,
1039 &byte_as_f,
1040 Some(8),
1041 true,
1042 )?)
1043 }
1044 Ok(AssignedScalarOfNativeCurve(bits))
1045 }
1046}
1047
1048impl<C: EdwardsCurve>
1052 ConversionInstructions<C::Base, AssignedNative<C::Base>, AssignedScalarOfNativeCurve<C>>
1053 for EccChip<C>
1054{
1055 fn convert_value(&self, _x: &C::Base) -> Option<C::ScalarField> {
1056 unimplemented!("The caller should decide how to convert the value off-circuit, i.e., what to do with overflows.");
1057 }
1058
1059 fn convert(
1060 &self,
1061 layouter: &mut impl Layouter<C::Base>,
1062 x: &AssignedNative<C::Base>,
1063 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1064 Ok(AssignedScalarOfNativeCurve(
1065 self.native_gadget.assigned_to_le_bits(layouter, x, None, true)?,
1066 ))
1067 }
1068}
1069
1070#[cfg(test)]
1071mod tests {
1072 use midnight_curves::{Fq as JubjubBase, JubjubExtended};
1073
1074 use super::*;
1075 use crate::{
1076 ecc::hash_to_curve::HashToCurveGadget,
1077 hash::poseidon::PoseidonChip,
1078 instructions::{ecc, hash_to_curve::tests::test_hash_to_curve},
1079 };
1080
1081 macro_rules! test_generic {
1082 ($mod:ident, $op:ident, $native:ty, $curve:ty, $name:expr) => {
1083 $mod::tests::$op::<$native, AssignedNativePoint<$curve>, EccChip<$curve>>($name);
1084 };
1085 }
1086
1087 macro_rules! test {
1088 ($mod:ident, $op:ident) => {
1089 #[test]
1090 fn $op() {
1091 test_generic!($mod, $op, JubjubBase, JubjubExtended, "native_ecc");
1092 }
1093 };
1094 }
1095
1096 test!(assertions, test_assertions);
1097
1098 test!(public_input, test_public_inputs);
1099
1100 #[test]
1101 fn test_scalarvar_public_inputs() {
1102 public_input::tests::test_public_inputs::<
1103 JubjubBase,
1104 AssignedScalarOfNativeCurve<JubjubExtended>,
1105 EccChip<JubjubExtended>,
1106 >("public_inputs_scalar_var");
1107 }
1108
1109 test!(equality, test_is_equal);
1110
1111 test!(zero, test_zero_assertions);
1112 test!(zero, test_is_zero);
1113
1114 test!(control_flow, test_select);
1115 test!(control_flow, test_cond_assert_equal);
1116 test!(control_flow, test_cond_swap);
1117
1118 macro_rules! ecc_tests {
1119 ($op:ident) => {
1120 #[test]
1121 fn $op() {
1122 ecc::tests::$op::<JubjubBase, JubjubExtended, EccChip<JubjubExtended>>(
1123 "native_ecc",
1124 );
1125 }
1126 };
1127 }
1128
1129 ecc_tests!(test_assign);
1130 ecc_tests!(test_assign_without_subgroup_check);
1131 ecc_tests!(test_add);
1132 ecc_tests!(test_double);
1133 ecc_tests!(test_negate);
1134 ecc_tests!(test_msm);
1135 ecc_tests!(test_msm_by_bounded_scalars);
1136 ecc_tests!(test_mul_by_constant);
1137 ecc_tests!(test_coordinates);
1138
1139 #[test]
1140 fn test_htc() {
1141 test_hash_to_curve::<
1142 JubjubBase,
1143 JubjubExtended,
1144 AssignedNative<JubjubBase>,
1145 EccChip<JubjubExtended>,
1146 NativeChip<JubjubBase>,
1147 HashToCurveGadget<_, _, _, PoseidonChip<JubjubBase>, _>,
1148 >("native_ecc")
1149 }
1150}