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::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::{
40 util::{fe_to_le_bits, le_bits_to_field_elem},
41 ComposableChip,
42 },
43};
44
45pub const NB_EDWARDS_COLS: usize = 9;
47
48#[derive(Clone, Debug)]
51pub struct AssignedNativePoint<C: CircuitCurve> {
52 x: AssignedNative<C::Base>,
53 y: AssignedNative<C::Base>,
54}
55
56impl<C: CircuitCurve> InnerValue for AssignedNativePoint<C> {
57 type Element = C::CryptographicGroup;
58
59 fn value(&self) -> Value<Self::Element> {
60 self.x
61 .value()
62 .zip(self.y.value())
63 .map(|(x, y)| C::from_xy(*x, *y).expect("non-id").into_subgroup())
64 }
65}
66
67impl<C: CircuitCurve> AssignedNativePoint<C> {
68 fn curve_value(&self) -> Value<C> {
82 self.x
83 .value()
84 .zip(self.y.value())
85 .map(|(x, y)| C::from_xy(*x, *y).expect("Valid coordinates."))
86 }
87}
88
89impl<C: CircuitCurve> Instantiable<C::Base> for AssignedNativePoint<C> {
90 fn as_public_input(p: &C::CryptographicGroup) -> Vec<C::Base> {
91 let point: C = (*p).into();
92 let coordinates = point.coordinates().expect("non-id");
93 vec![coordinates.0, coordinates.1]
94 }
95
96 fn from_public_input(fields: &[C::Base]) -> Option<C::CryptographicGroup> {
97 if fields.len() != 2 {
98 return None;
99 }
100 C::from_xy(fields[0], fields[1]).map(|p| p.into_subgroup())
101 }
102}
103
104impl<C: EdwardsCurve> InnerConstants for AssignedNativePoint<C> {
105 fn inner_zero() -> C::CryptographicGroup {
106 C::CryptographicGroup::identity()
107 }
108
109 fn inner_one() -> Self::Element {
110 C::CryptographicGroup::generator()
111 }
112}
113
114#[derive(Clone, Debug)]
116pub struct AssignedScalarOfNativeCurve<C: CircuitCurve>(Vec<AssignedBit<C::Base>>);
117
118impl<C: CircuitCurve> InnerValue for AssignedScalarOfNativeCurve<C> {
119 type Element = C::Scalar;
120
121 fn value(&self) -> Value<Self::Element> {
122 let bools = self.0.iter().map(|b| b.value());
123 let value_bools: Value<Vec<bool>> = Value::from_iter(bools);
124 value_bools.map(|le_bits| le_bits_to_field_elem::<C::Scalar>(&le_bits))
125 }
126}
127
128impl<C: EdwardsCurve> Instantiable<C::Base> for AssignedScalarOfNativeCurve<C> {
129 fn as_public_input(element: &C::Scalar) -> Vec<C::Base> {
130 let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
132 fe_to_le_bits(element, Some(C::NUM_BITS_SUBGROUP as usize))
133 .chunks(nb_bits_per_batch)
134 .map(le_bits_to_field_elem)
135 .collect()
136 }
137
138 fn from_public_input(fields: &[C::Base]) -> Option<C::Scalar> {
139 let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
140
141 let bits: Vec<bool> =
142 fields.iter().flat_map(|f| fe_to_le_bits(f, Some(nb_bits_per_batch))).collect();
143
144 let (head, tail) = bits.split_at(C::NUM_BITS_SUBGROUP as usize);
145 if tail.iter().any(|b| *b) {
146 return None;
147 }
148
149 Some(le_bits_to_field_elem(head))
150 }
151}
152
153impl<C: EdwardsCurve> InnerConstants for AssignedScalarOfNativeCurve<C> {
154 fn inner_zero() -> C::Scalar {
155 C::Scalar::ZERO
156 }
157 fn inner_one() -> C::Scalar {
158 C::Scalar::ONE
159 }
160}
161
162#[cfg(any(test, feature = "testing"))]
163impl<C: EdwardsCurve> Sampleable for AssignedScalarOfNativeCurve<C> {
164 fn sample_inner(rng: impl RngCore) -> C::Scalar {
165 C::Scalar::random(rng)
166 }
167}
168
169#[derive(Clone, Debug)]
171pub struct EccConfig {
172 pub(crate) q_double: Selector,
173 pub(crate) q_cond_add: Selector,
174 pub(crate) q_mem: Selector,
175 pub(crate) advice_cols: [Column<Advice>; NB_EDWARDS_COLS],
176}
177
178impl EccConfig {
179 fn create_double_gate<C: EdwardsCurve>(
208 &self,
209 meta: &mut ConstraintSystem<C::Base>,
210 q_double: &Selector,
211 ) {
212 meta.create_gate("double", |meta| {
213 let xp = meta.query_advice(self.advice_cols[5], Rotation::cur());
214 let yp = meta.query_advice(self.advice_cols[6], Rotation::cur());
215 let xq = meta.query_advice(self.advice_cols[0], Rotation::next());
216 let yq = meta.query_advice(self.advice_cols[1], Rotation::next());
217
218 let xp_xp = meta.query_advice(self.advice_cols[7], Rotation::cur());
219
220 let one = Expression::from(1);
221 let edwards_d = Expression::Constant(C::D);
222 let xp_yp = &xp * &yp;
223 let yp_yp = yp.square();
224 let d_xp_xp_yp_yp = edwards_d * &xp_xp * &yp_yp;
225
226 let id1 = xq * (&one + &d_xp_xp_yp_yp) - (xp_yp.clone() + xp_yp);
227 let id2 = yq * (one - d_xp_xp_yp_yp) - (yp_yp + &xp_xp);
228 let id3 = xp.clone() * xp - xp_xp;
229
230 Constraints::with_selector(
231 *q_double,
232 vec![
233 ("qx constraint for q = 2 * p", id1),
234 ("qy constraint for q = 2 * p", id2),
235 ("constraint for xp_xp = xp * xp", id3),
236 ],
237 )
238 })
239 }
240
241 fn create_cond_add_gate<C: EdwardsCurve>(
269 &self,
270 meta: &mut ConstraintSystem<C::Base>,
271 q_cond_add: &Selector,
272 ) {
273 meta.create_gate("conditional add", |meta| {
274 let xq = meta.query_advice(self.advice_cols[0], Rotation::cur());
275 let yq = meta.query_advice(self.advice_cols[1], Rotation::cur());
276 let xs = meta.query_advice(self.advice_cols[2], Rotation::cur());
277 let ys = meta.query_advice(self.advice_cols[3], Rotation::cur());
278 let xr = meta.query_advice(self.advice_cols[5], Rotation::cur());
279 let yr = meta.query_advice(self.advice_cols[6], Rotation::cur());
280 let b = meta.query_advice(self.advice_cols[4], Rotation::cur());
281
282 let one = Expression::from(1);
283 let edwards_d = Expression::Constant(C::D);
284
285 let xq_yq_xs_ys = meta.query_advice(self.advice_cols[8], Rotation::cur());
286
287 let xq_xs = &xq * &xs;
288 let yq_ys = &yq * &ys;
289 let xq_ys = &xq * &ys;
290 let xs_yq = &xs * &yq;
291 let b_d_xq_xs_yq_ys = &b * edwards_d * &xq_yq_xs_ys;
292
293 let id1 = xr * (&one + &b_d_xq_xs_yq_ys) - (&xq + &b * (xq_ys + xs_yq - &xq));
294 let id2 = yr * (one - b_d_xq_xs_yq_ys) - (&yq + b * (yq_ys + xq_xs - &yq));
295 let id3 = xq_yq_xs_ys - xq * yq * xs * ys;
296
297 Constraints::with_selector(
298 *q_cond_add,
299 vec![
300 ("rx constraint for r = q + b * s", id1),
301 ("ry constraint for r = q + b * s", id2),
302 ("constraint for xq_yq_xs_ys = xq * yq * xs * ys", id3),
303 ],
304 )
305 })
306 }
307
308 fn create_membership_gate<C: EdwardsCurve>(
319 &self,
320 meta: &mut ConstraintSystem<C::Base>,
321 q_point: &Selector,
322 ) {
323 meta.create_gate("witness point", |meta| {
324 let x = meta.query_advice(self.advice_cols[0], Rotation::cur());
325 let y = meta.query_advice(self.advice_cols[1], Rotation::cur());
326
327 let one = Expression::from(1);
328 let edwards_d = Expression::Constant(C::D);
329
330 let x2 = x.square();
331 let y2 = y.square();
332
333 let id = y2.clone() - x2.clone() - (one + edwards_d * x2 * y2);
334
335 Constraints::with_selector(*q_point, vec![("curve equation", id)])
336 })
337 }
338}
339
340type NG<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
341
342#[derive(Clone, Debug)]
345pub struct EccChip<C: EdwardsCurve> {
346 config: EccConfig,
347 native_gadget: NG<C::Base>,
348}
349
350impl<C: EdwardsCurve> Chip<C::Base> for EccChip<C> {
351 type Config = EccConfig;
352 type Loaded = ();
353
354 fn config(&self) -> &Self::Config {
355 &self.config
356 }
357
358 fn loaded(&self) -> &Self::Loaded {
359 &()
360 }
361}
362
363impl<C: EdwardsCurve> ComposableChip<C::Base> for EccChip<C> {
364 type SharedResources = [Column<Advice>; NB_EDWARDS_COLS];
365 type InstructionDeps = NG<C::Base>;
366
367 fn new(config: &Self::Config, sub_chips: &Self::InstructionDeps) -> Self {
368 Self {
369 config: config.clone(),
370 native_gadget: sub_chips.clone(),
371 }
372 }
373
374 fn configure(
375 meta: &mut ConstraintSystem<C::Base>,
376 advice_cols: &Self::SharedResources,
377 ) -> Self::Config {
378 assert_eq!(C::A, -C::Base::ONE);
379
380 for col in advice_cols.iter().take(7) {
382 meta.enable_equality(*col)
383 }
384
385 let q_double = meta.selector();
386 let q_cond_add = meta.selector();
387 let q_mem = meta.selector();
388
389 let config = EccConfig {
390 q_double,
391 q_cond_add,
392 q_mem,
393 advice_cols: *advice_cols,
394 };
395
396 config.create_double_gate::<C>(meta, &q_double);
397 config.create_cond_add_gate::<C>(meta, &q_cond_add);
398 config.create_membership_gate::<C>(meta, &q_mem);
399
400 config
401 }
402
403 fn load(&self, _layouter: &mut impl Layouter<C::Base>) -> Result<(), Error> {
404 Ok(())
405 }
406}
407
408impl<C: EdwardsCurve> EccChip<C> {
409 fn assign_cond_add(
422 &self,
423 region: &mut Region<C::Base>,
424 offset: usize,
425 q: Value<C>,
426 s: Value<C>,
427 b: Value<bool>,
428 ) -> Result<AssignedNativePoint<C>, Error> {
429 let config = self.config();
430 config.q_cond_add.enable(region, offset)?;
431
432 let (xr_val, yr_val) = Self::p_plus_b_q(q, s, b);
433 let xr = region.assign_advice(|| "xr", config.advice_cols[5], offset, || xr_val)?;
434 let yr = region.assign_advice(|| "yr", config.advice_cols[6], offset, || yr_val)?;
435
436 let (xq, yq) = q.map(|q| q.coordinates().expect("non-id")).unzip();
437 let (xs, ys) = s.map(|s| s.coordinates().expect("non-id")).unzip();
438 let prod_val = xq * yq * xs * ys;
439 region.assign_advice(|| "xq_yq_xs_ys", config.advice_cols[8], offset, || prod_val)?;
440
441 Ok(AssignedNativePoint { x: xr, y: yr })
442 }
443
444 fn assign_add_then_double(
457 &self,
458 region: &mut Region<C::Base>,
459 offset: usize,
460 p_val: Value<C>,
461 q_val: Value<C>,
462 b_val: Value<bool>,
463 ) -> Result<AssignedNativePoint<C>, Error> {
464 let config = self.config();
465
466 config.q_cond_add.enable(region, offset)?;
467 config.q_double.enable(region, offset)?;
468
469 let (xs_val, ys_val) = Self::p_plus_b_q(p_val, q_val, b_val);
470
471 region.assign_advice(|| "xs", config.advice_cols[5], offset, || xs_val)?;
472 region.assign_advice(|| "ys", config.advice_cols[6], offset, || ys_val)?;
473
474 let s_val = xs_val.zip(ys_val).map(|(xs, ys)| C::from_xy(xs, ys).unwrap());
475 let r_val = s_val.map(|s| s + s);
476
477 let xr_val = r_val.map(|r: C| r.coordinates().expect("non-id").0);
478 let yr_val = r_val.map(|r: C| r.coordinates().expect("non-id").1);
479
480 let xr = region.assign_advice(|| "xr", config.advice_cols[0], offset + 1, || xr_val)?;
481 let yr = region.assign_advice(|| "yr", config.advice_cols[1], offset + 1, || yr_val)?;
482
483 region.assign_advice(
484 || "xs_xs",
485 config.advice_cols[7],
486 offset,
487 || xs_val * xs_val,
488 )?;
489
490 let (xp, yp) = p_val.map(|c| c.coordinates().expect("non-id")).unzip();
491 let (xq, yq) = q_val.map(|c| c.coordinates().expect("non-id")).unzip();
492 let prod_val = xp * yp * xq * yq;
493 region.assign_advice(|| "xp_yp_xq_yq", config.advice_cols[8], offset, || prod_val)?;
494
495 Ok(AssignedNativePoint { x: xr, y: yr })
496 }
497
498 pub fn mul(
500 &self,
501 layouter: &mut impl Layouter<C::Base>,
502 scalar: &AssignedScalarOfNativeCurve<C>,
503 base: &AssignedNativePoint<C>,
504 ) -> Result<AssignedNativePoint<C>, Error> {
505 let config = &self.config();
506
507 let scalar_be_bits = &mut scalar.0.clone();
509 scalar_be_bits.reverse();
510
511 let base_val = base.curve_value();
512 let id_point: AssignedNativePoint<C> =
513 self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
514
515 layouter.assign_region(
516 || "assign mul",
517 |mut region: Region<'_, C::Base>| {
518 id_point.x.copy_advice(|| "id.x", &mut region, config.advice_cols[0], 0)?;
519 id_point.y.copy_advice(|| "id.y", &mut region, config.advice_cols[1], 0)?;
520
521 let mut acc = id_point.clone();
522
523 for (i, bit) in scalar_be_bits.iter().enumerate() {
524 base.x.copy_advice(|| "base.x", &mut region, config.advice_cols[2], i)?;
525 base.y.copy_advice(|| "base.y", &mut region, config.advice_cols[3], i)?;
526 bit.0.copy_advice(|| "b cond_add", &mut region, config.advice_cols[4], i)?;
527
528 if i < scalar_be_bits.len() - 1 {
529 acc = self.assign_add_then_double(
530 &mut region,
531 i,
532 acc.curve_value(),
533 base_val,
534 bit.value(),
535 )?;
536 }
537 else {
539 acc = self.assign_cond_add(
540 &mut region,
541 i,
542 acc.curve_value(),
543 base_val,
544 bit.value(),
545 )?;
546 }
547 }
548
549 Ok(acc)
550 },
551 )
552 }
553
554 fn p_plus_b_q(p: Value<C>, q: Value<C>, b: Value<bool>) -> (Value<C::Base>, Value<C::Base>) {
556 p.zip(q)
557 .zip(b)
558 .map(|((p, q), b)| if b { p + q } else { p })
559 .map(|r| r.coordinates().expect("non-id"))
560 .unzip()
561 }
562
563 pub fn native_gadget(&self) -> &impl NativeInstructions<C::Base> {
565 &self.native_gadget
566 }
567}
568
569impl<C: EdwardsCurve> EccInstructions<C::Base, C> for EccChip<C> {
570 type Point = AssignedNativePoint<C>;
571 type Coordinate = AssignedNative<C::Base>;
572 type Scalar = AssignedScalarOfNativeCurve<C>;
573
574 fn add(
575 &self,
576 layouter: &mut impl Layouter<C::Base>,
577 p: &Self::Point,
578 q: &Self::Point,
579 ) -> Result<Self::Point, Error> {
580 let config = self.config();
581 let b: AssignedBit<C::Base> = self.native_gadget.assign_fixed(layouter, true)?;
582
583 layouter.assign_region(
584 || "assign add",
585 |mut region: Region<'_, C::Base>| {
586 p.x.copy_advice(|| "px", &mut region, config.advice_cols[0], 0)?;
587 p.y.copy_advice(|| "py", &mut region, config.advice_cols[1], 0)?;
588 q.x.copy_advice(|| "qx", &mut region, config.advice_cols[2], 0)?;
589 q.y.copy_advice(|| "qy", &mut region, config.advice_cols[3], 0)?;
590 b.0.copy_advice(|| "b", &mut region, config.advice_cols[4], 0)?;
591
592 self.assign_cond_add(&mut region, 0, p.curve_value(), q.curve_value(), b.value())
593 },
594 )
595 }
596
597 fn double(
598 &self,
599 layouter: &mut impl Layouter<C::Base>,
600 p: &Self::Point,
601 ) -> Result<Self::Point, Error> {
602 self.add(layouter, p, p)
603 }
604
605 fn negate(
606 &self,
607 layouter: &mut impl Layouter<C::Base>,
608 p: &Self::Point,
609 ) -> Result<Self::Point, Error> {
610 Ok(AssignedNativePoint {
611 x: self.native_gadget.neg(layouter, &p.x)?,
612 y: p.y.clone(),
613 })
614 }
615
616 fn msm(
617 &self,
618 layouter: &mut impl Layouter<C::Base>,
619 scalars: &[Self::Scalar],
620 bases: &[Self::Point],
621 ) -> Result<Self::Point, Error> {
622 let scaled_points = scalars
623 .iter()
624 .zip(bases.iter())
625 .map(|(scalar, point)| self.mul(layouter, scalar, point))
626 .collect::<Result<Vec<Self::Point>, Error>>()?;
627
628 scaled_points[1..].iter().try_fold(scaled_points[0].clone(), |acc, e| {
629 self.add(layouter, &acc, e)
630 })
631 }
632
633 fn mul_by_constant(
634 &self,
635 layouter: &mut impl Layouter<C::Base>,
636 scalar: C::Scalar,
637 base: &Self::Point,
638 ) -> Result<Self::Point, Error> {
639 if scalar == C::Scalar::ZERO {
640 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
641 }
642
643 if scalar == C::Scalar::ONE {
644 return Ok(base.clone());
645 }
646
647 let s = self.assign_fixed(layouter, scalar)?;
648 self.msm(layouter, &[s], &[base.clone()])
649 }
650
651 fn point_from_coordinates(
652 &self,
653 layouter: &mut impl Layouter<C::Base>,
654 x: &Self::Coordinate,
655 y: &Self::Coordinate,
656 ) -> Result<Self::Point, Error> {
657 layouter.assign_region(
658 || "assign new point",
659 |mut region: Region<'_, C::Base>| {
660 x.copy_advice(|| "x", &mut region, self.config.advice_cols[0], 0)?;
661 y.copy_advice(|| "y", &mut region, self.config.advice_cols[1], 0)?;
662 self.config.q_mem.enable(&mut region, 0)
663 },
664 )?;
665 Ok(AssignedNativePoint {
666 x: x.clone(),
667 y: y.clone(),
668 })
669 }
670
671 fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
672 point.x.clone()
673 }
674
675 fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
676 point.y.clone()
677 }
678
679 fn base_field(&self) -> &impl DecompositionInstructions<C::Base, Self::Coordinate> {
680 &self.native_gadget
681 }
682}
683
684impl<C: EdwardsCurve> AssignmentInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
685 fn assign(
686 &self,
687 layouter: &mut impl Layouter<C::Base>,
688 value: Value<C::CryptographicGroup>,
689 ) -> Result<AssignedNativePoint<C>, Error> {
690 let config = self.config();
691
692 let cofactor = C::Scalar::from_u128(C::COFACTOR);
697 let (x_val, y_val) = value
698 .map(|p| {
699 let p = p * cofactor.invert().expect("cofactor should not be 0");
700 p.into().coordinates().expect("non-id")
701 })
702 .unzip();
703
704 let cf_root = layouter.assign_region(
705 || "assign point",
706 |mut region: Region<'_, C::Base>| {
707 config.q_mem.enable(&mut region, 0)?;
708 let x = region.assign_advice(|| "x", config.advice_cols[0], 0, || x_val)?;
709 let y = region.assign_advice(|| "y", config.advice_cols[1], 0, || y_val)?;
710 Ok(AssignedNativePoint { x, y })
711 },
712 )?;
713
714 self.mul_by_constant(layouter, cofactor, &cf_root)
715 }
716
717 fn assign_fixed(
718 &self,
719 layouter: &mut impl Layouter<C::Base>,
720 constant: C::CryptographicGroup,
721 ) -> Result<AssignedNativePoint<C>, Error> {
722 let coords = constant.into().coordinates().expect("non-id");
723 let x = self.native_gadget.assign_fixed(layouter, coords.0)?;
724 let y = self.native_gadget.assign_fixed(layouter, coords.1)?;
725 Ok(AssignedNativePoint { x, y })
726 }
727}
728
729impl<C: EdwardsCurve> AssignmentInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
730 for EccChip<C>
731{
732 fn assign(
733 &self,
734 layouter: &mut impl Layouter<C::Base>,
735 value: Value<C::Scalar>,
736 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
737 let bits = value
738 .map(|s| fe_to_le_bits(&s, Some(C::Scalar::NUM_BITS as usize)))
739 .transpose_vec(<C::Scalar as PrimeField>::NUM_BITS as usize);
740 self.native_gadget.assign_many(layouter, &bits).map(AssignedScalarOfNativeCurve)
741 }
742
743 fn assign_fixed(
744 &self,
745 layouter: &mut impl Layouter<C::Base>,
746 constant: C::Scalar,
747 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
748 self.native_gadget
749 .assign_many_fixed(layouter, &fe_to_le_bits(&constant, None))
750 .map(AssignedScalarOfNativeCurve)
751 }
752}
753
754impl<C: EdwardsCurve> AssertionInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
755 fn assert_equal(
756 &self,
757 layouter: &mut impl Layouter<C::Base>,
758 p: &AssignedNativePoint<C>,
759 q: &AssignedNativePoint<C>,
760 ) -> Result<(), Error> {
761 self.native_gadget.assert_equal(layouter, &p.x, &q.x)?;
762 self.native_gadget.assert_equal(layouter, &p.y, &q.y)
763 }
764
765 fn assert_not_equal(
766 &self,
767 layouter: &mut impl Layouter<C::Base>,
768 p: &AssignedNativePoint<C>,
769 q: &AssignedNativePoint<C>,
770 ) -> Result<(), Error> {
771 let is_eq = self.is_equal(layouter, p, q)?;
772 self.native_gadget.assert_equal_to_fixed(layouter, &is_eq, false)
773 }
774
775 fn assert_equal_to_fixed(
776 &self,
777 layouter: &mut impl Layouter<C::Base>,
778 p: &AssignedNativePoint<C>,
779 constant: C::CryptographicGroup,
780 ) -> Result<(), Error> {
781 let (cx, cy) = constant.into().coordinates().expect("non-id");
782 self.native_gadget.assert_equal_to_fixed(layouter, &p.x, cx)?;
783 self.native_gadget.assert_equal_to_fixed(layouter, &p.y, cy)
784 }
785
786 fn assert_not_equal_to_fixed(
787 &self,
788 layouter: &mut impl Layouter<C::Base>,
789 p: &AssignedNativePoint<C>,
790 constant: C::CryptographicGroup,
791 ) -> Result<(), Error> {
792 let is_eq = self.is_equal_to_fixed(layouter, p, constant)?;
793 self.native_gadget.assert_equal_to_fixed(layouter, &is_eq, false)
794 }
795}
796
797impl<C: EdwardsCurve> PublicInputInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
798 fn as_public_input(
799 &self,
800 _layouter: &mut impl Layouter<C::Base>,
801 p: &AssignedNativePoint<C>,
802 ) -> Result<Vec<AssignedNative<C::Base>>, Error> {
803 Ok(vec![p.x.clone(), p.y.clone()])
804 }
805
806 fn constrain_as_public_input(
807 &self,
808 layouter: &mut impl Layouter<C::Base>,
809 p: &AssignedNativePoint<C>,
810 ) -> Result<(), Error> {
811 self.as_public_input(layouter, p)?
812 .iter()
813 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
814 }
815
816 fn assign_as_public_input(
817 &self,
818 layouter: &mut impl Layouter<C::Base>,
819 p: Value<C::CryptographicGroup>,
820 ) -> Result<AssignedNativePoint<C>, Error> {
821 let (x, y) = p.map(|p| p.into().coordinates().expect("non-id")).unzip();
823 let x = self.native_gadget.assign_as_public_input(layouter, x)?;
824 let y = self.native_gadget.assign_as_public_input(layouter, y)?;
825 Ok(AssignedNativePoint { x, y })
826 }
827}
828
829impl<C: EdwardsCurve> PublicInputInstructions<C::Base, AssignedScalarOfNativeCurve<C>>
830 for EccChip<C>
831{
832 fn as_public_input(
833 &self,
834 layouter: &mut impl Layouter<C::Base>,
835 assigned: &AssignedScalarOfNativeCurve<C>,
836 ) -> Result<Vec<AssignedNative<C::Base>>, Error> {
837 let nb_bits_per_batch = C::Base::NUM_BITS as usize - 1;
839 assigned
840 .0
841 .chunks(nb_bits_per_batch)
842 .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
843 .collect()
844 }
845
846 fn constrain_as_public_input(
847 &self,
848 layouter: &mut impl Layouter<C::Base>,
849 assigned: &AssignedScalarOfNativeCurve<C>,
850 ) -> Result<(), Error> {
851 self.as_public_input(layouter, assigned)?
852 .iter()
853 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
854 }
855
856 fn assign_as_public_input(
857 &self,
858 layouter: &mut impl Layouter<C::Base>,
859 value: Value<C::Scalar>,
860 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
861 let assigned: AssignedScalarOfNativeCurve<C> = self.assign(layouter, value)?;
862 self.constrain_as_public_input(layouter, &assigned)?;
863 Ok(assigned)
864 }
865}
866
867impl<C: EdwardsCurve> EqualityInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
868 fn is_equal(
869 &self,
870 layouter: &mut impl Layouter<C::Base>,
871 p: &AssignedNativePoint<C>,
872 q: &AssignedNativePoint<C>,
873 ) -> Result<AssignedBit<C::Base>, Error> {
874 let eq_x = self.native_gadget.is_equal(layouter, &p.x, &q.x)?;
875 let eq_y = self.native_gadget.is_equal(layouter, &p.y, &q.y)?;
876 self.native_gadget.and(layouter, &[eq_x, eq_y])
877 }
878
879 fn is_not_equal(
880 &self,
881 layouter: &mut impl Layouter<C::Base>,
882 p: &AssignedNativePoint<C>,
883 q: &AssignedNativePoint<C>,
884 ) -> Result<AssignedBit<C::Base>, Error> {
885 let not_eq_x = self.native_gadget.is_not_equal(layouter, &p.x, &q.x)?;
886 let not_eq_y = self.native_gadget.is_not_equal(layouter, &p.y, &q.y)?;
887 self.native_gadget.or(layouter, &[not_eq_x, not_eq_y])
888 }
889
890 fn is_equal_to_fixed(
891 &self,
892 layouter: &mut impl Layouter<C::Base>,
893 p: &AssignedNativePoint<C>,
894 constant: C::CryptographicGroup,
895 ) -> Result<AssignedBit<C::Base>, Error> {
896 let (cx, cy) = constant.into().coordinates().expect("non-id");
897 let eq_x = self.native_gadget.is_equal_to_fixed(layouter, &p.x, cx)?;
898 let eq_y = self.native_gadget.is_equal_to_fixed(layouter, &p.y, cy)?;
899 self.native_gadget.and(layouter, &[eq_x, eq_y])
900 }
901
902 fn is_not_equal_to_fixed(
903 &self,
904 layouter: &mut impl Layouter<C::Base>,
905 p: &AssignedNativePoint<C>,
906 constant: C::CryptographicGroup,
907 ) -> Result<AssignedBit<C::Base>, Error> {
908 let (cx, cy) = constant.into().coordinates().expect("non-id");
909 let not_eq_x = self.native_gadget.is_not_equal_to_fixed(layouter, &p.x, cx)?;
910 let not_eq_y = self.native_gadget.is_not_equal_to_fixed(layouter, &p.y, cy)?;
911 self.native_gadget.or(layouter, &[not_eq_x, not_eq_y])
912 }
913}
914
915impl<C: EdwardsCurve> ZeroInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {}
916
917impl<C: EdwardsCurve> ControlFlowInstructions<C::Base, AssignedNativePoint<C>> for EccChip<C> {
918 fn select(
919 &self,
920 layouter: &mut impl Layouter<C::Base>,
921 cond: &AssignedBit<C::Base>,
922 a: &AssignedNativePoint<C>,
923 b: &AssignedNativePoint<C>,
924 ) -> Result<AssignedNativePoint<C>, Error> {
925 let x = self.native_gadget.select(layouter, cond, &a.x, &b.x)?;
926 let y = self.native_gadget.select(layouter, cond, &a.y, &b.y)?;
927 Ok(AssignedNativePoint { x, y })
928 }
929}
930
931#[cfg(any(test, feature = "testing"))]
932impl<C: EdwardsCurve> FromScratch<C::Base> for EccChip<C> {
933 type Config = (EccConfig, P2RDecompositionConfig);
934
935 fn new_from_scratch(config: &Self::Config) -> Self {
936 let p2r_decomp_config = &config.1;
937 let max_bit_len = 8;
938 let native_chip = NativeChip::new_from_scratch(&p2r_decomp_config.native_config);
939 let core_decomposition_chip = P2RDecompositionChip::new(p2r_decomp_config, &max_bit_len);
940 let native_gadget = NativeGadget::new(core_decomposition_chip, native_chip);
941 Self {
942 native_gadget,
943 config: config.0.clone(),
944 }
945 }
946
947 fn configure_from_scratch(
948 meta: &mut ConstraintSystem<C::Base>,
949 instance_columns: &[Column<Instance>; 2],
950 ) -> Self::Config {
951 let native_gadget_config =
952 <NG<C::Base> as FromScratch<C::Base>>::configure_from_scratch(meta, instance_columns);
953 let advice_cols: [Column<Advice>; NB_EDWARDS_COLS] =
954 core::array::from_fn(|_| meta.advice_column());
955 let ecc_config = EccChip::<C>::configure(meta, &advice_cols);
956
957 (ecc_config, native_gadget_config)
958 }
959
960 fn load_from_scratch(&self, layouter: &mut impl Layouter<C::Base>) -> Result<(), Error> {
961 self.native_gadget.load_from_scratch(layouter)
962 }
963}
964
965#[cfg(any(test, feature = "testing"))]
966impl<C: EdwardsCurve> Sampleable for AssignedNativePoint<C> {
967 fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
968 C::CryptographicGroup::random(rng)
969 }
970}
971
972impl<C: EdwardsCurve> EccChip<C> {
973 pub fn scalar_from_le_bytes(
976 &self,
977 layouter: &mut impl Layouter<C::Base>,
978 bytes: &[AssignedByte<C::Base>],
979 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
980 let mut bits = Vec::with_capacity(bytes.len() * 8);
981 for byte in bytes {
982 let byte_as_f: AssignedNative<C::Base> = self.native_gadget.convert(layouter, byte)?;
983 bits.extend(self.native_gadget.assigned_to_le_bits(
984 layouter,
985 &byte_as_f,
986 Some(8),
987 true,
988 )?)
989 }
990 Ok(AssignedScalarOfNativeCurve(bits))
991 }
992
993 pub fn scalar_from_reduced_le_bytes(
1003 &self,
1004 layouter: &mut impl Layouter<C::Base>,
1005 bytes: &[AssignedByte<C::Base>],
1006 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1007 let n = C::NUM_BITS_SUBGROUP as usize;
1008 let s = self.scalar_from_le_bytes(layouter, bytes)?;
1009 for b in s.0[n..].iter() {
1010 self.native_gadget.assert_equal_to_fixed(layouter, b, false)?;
1011 }
1012 Ok(AssignedScalarOfNativeCurve(s.0[..n].to_vec()))
1013 }
1014}
1015
1016impl<C: EdwardsCurve>
1020 ConversionInstructions<C::Base, AssignedNative<C::Base>, AssignedScalarOfNativeCurve<C>>
1021 for EccChip<C>
1022{
1023 fn convert_value(&self, _x: &C::Base) -> Option<C::Scalar> {
1024 unimplemented!("The caller should decide how to convert the value off-circuit, i.e., what to do with overflows.");
1025 }
1026
1027 fn convert(
1028 &self,
1029 layouter: &mut impl Layouter<C::Base>,
1030 x: &AssignedNative<C::Base>,
1031 ) -> Result<AssignedScalarOfNativeCurve<C>, Error> {
1032 Ok(AssignedScalarOfNativeCurve(
1033 self.native_gadget.assigned_to_le_bits(layouter, x, None, true)?,
1034 ))
1035 }
1036}
1037
1038#[cfg(test)]
1039mod tests {
1040 use midnight_curves::{Fq as JubjubBase, JubjubExtended};
1041
1042 use super::*;
1043 use crate::{
1044 ecc::hash_to_curve::HashToCurveGadget,
1045 hash::poseidon::PoseidonChip,
1046 instructions::{ecc, hash_to_curve::tests::test_hash_to_curve},
1047 };
1048
1049 macro_rules! test_generic {
1050 ($mod:ident, $op:ident, $native:ty, $curve:ty, $name:expr) => {
1051 $mod::tests::$op::<$native, AssignedNativePoint<$curve>, EccChip<$curve>>($name);
1052 };
1053 }
1054
1055 macro_rules! test {
1056 ($mod:ident, $op:ident) => {
1057 #[test]
1058 fn $op() {
1059 test_generic!($mod, $op, JubjubBase, JubjubExtended, "native_ecc");
1060 }
1061 };
1062 }
1063
1064 test!(assertions, test_assertions);
1065
1066 test!(public_input, test_public_inputs);
1067
1068 #[test]
1069 fn test_scalarvar_public_inputs() {
1070 public_input::tests::test_public_inputs::<
1071 JubjubBase,
1072 AssignedScalarOfNativeCurve<JubjubExtended>,
1073 EccChip<JubjubExtended>,
1074 >("public_inputs_scalar_var");
1075 }
1076
1077 test!(equality, test_is_equal);
1078
1079 test!(zero, test_zero_assertions);
1080 test!(zero, test_is_zero);
1081
1082 test!(control_flow, test_select);
1083 test!(control_flow, test_cond_assert_equal);
1084 test!(control_flow, test_cond_swap);
1085
1086 macro_rules! ecc_tests {
1087 ($op:ident) => {
1088 #[test]
1089 fn $op() {
1090 ecc::tests::$op::<JubjubBase, JubjubExtended, EccChip<JubjubExtended>>(
1091 "native_ecc",
1092 );
1093 }
1094 };
1095 }
1096
1097 ecc_tests!(test_add);
1098 ecc_tests!(test_double);
1099 ecc_tests!(test_negate);
1100 ecc_tests!(test_msm);
1101 ecc_tests!(test_msm_by_bounded_scalars);
1102 ecc_tests!(test_mul_by_constant);
1103 ecc_tests!(test_coordinates_edwards);
1104
1105 #[test]
1106 fn test_htc() {
1107 test_hash_to_curve::<
1108 JubjubBase,
1109 JubjubExtended,
1110 AssignedNative<JubjubBase>,
1111 EccChip<JubjubExtended>,
1112 NativeChip<JubjubBase>,
1113 HashToCurveGadget<_, _, _, PoseidonChip<JubjubBase>, _>,
1114 >("native_ecc")
1115 }
1116}