1use std::{
23 cell::RefCell,
24 collections::HashMap,
25 fmt::Debug,
26 hash::{Hash, Hasher},
27 rc::Rc,
28};
29
30use ff::{Field, PrimeField};
31use group::Group;
32use midnight_curves::{
33 curve25519::{Curve25519, Curve25519Subgroup},
34 ff_ext::Legendre,
35};
36#[cfg(any(test, feature = "testing"))]
37use midnight_proofs::plonk::Instance;
38use midnight_proofs::{
39 circuit::{Chip, Layouter, Value},
40 plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Selector},
41};
42#[cfg(any(test, feature = "testing"))]
43use {
44 crate::testing_utils::{FromScratch, Sampleable},
45 rand::RngCore,
46};
47
48use super::common::{
49 add_1bit_scalar_bases, configure_multi_select_lookup, fill_dynamic_lookup_row, msm_preprocess,
50};
51use crate::{
52 ecc::{
53 curves::{CircuitCurve, EdwardsCurve},
54 foreign::gates::edwards::addition::{self, AdditionConfig},
55 },
56 field::{
57 foreign::{
58 field_chip::{FieldChip, FieldChipConfig},
59 params::FieldEmulationParams,
60 },
61 AssignedNative,
62 },
63 instructions::{
64 ArithInstructions, AssertionInstructions, AssignmentInstructions, ControlFlowInstructions,
65 DecompositionInstructions, EccInstructions, EqualityInstructions, NativeInstructions,
66 PublicInputInstructions, ScalarFieldInstructions, ZeroInstructions,
67 },
68 types::{AssignedBit, AssignedByte, AssignedField, InnerConstants, InnerValue, Instantiable},
69 CircuitField,
70};
71
72pub fn nb_foreign_edwards_chip_columns<F, C, B>() -> usize
74where
75 F: CircuitField,
76 C: EdwardsCurve,
77 B: FieldEmulationParams<F, C::Base>,
78{
79 B::NB_LIMBS as usize + std::cmp::max(B::NB_LIMBS as usize, 1 + B::moduli().len()) + 1
84}
85
86#[derive(Clone, Debug)]
88pub struct ForeignEdwardsEccConfig<C>
89where
90 C: EdwardsCurve,
91{
92 base_field_config: FieldChipConfig,
93 addition_config: AdditionConfig<C>,
94 q_multi_select: Selector,
96 idx_col_multi_select: Column<Advice>,
97 tag_col_multi_select: Column<Fixed>,
98}
99
100type ConstantPointCache<F, C, B> = Rc<
102 RefCell<HashMap<AssignedForeignEdwardsPoint<F, C, B>, <C as CircuitCurve>::CryptographicGroup>>,
103>;
104
105#[derive(Clone, Debug)]
107pub struct ForeignEdwardsEccChip<F, C, B, S, N>
108where
109 F: CircuitField,
110 C: EdwardsCurve,
111 B: FieldEmulationParams<F, C::Base>,
112 S: ScalarFieldInstructions<F>,
113 S::Scalar: InnerValue<Element = C::ScalarField>,
114 N: NativeInstructions<F>,
115{
116 config: ForeignEdwardsEccConfig<C>,
117 native_gadget: N,
118 base_field_chip: FieldChip<F, C::Base, B, N>,
119 scalar_field_chip: S,
120 tag_cnt: Rc<RefCell<u64>>,
122 constant_cache: ConstantPointCache<F, C, B>,
124}
125
126impl<F, C, B, S, N> ForeignEdwardsEccChip<F, C, B, S, N>
127where
128 F: CircuitField,
129 C: EdwardsCurve,
130 C::Base: Legendre,
131 B: FieldEmulationParams<F, C::Base>,
132 S: ScalarFieldInstructions<F>,
133 S::Scalar: InnerValue<Element = C::ScalarField>,
134 N: NativeInstructions<F>,
135{
136 pub fn configure(
138 meta: &mut ConstraintSystem<F>,
139 base_field_config: &FieldChipConfig,
140 advice_columns: &[Column<Advice>],
141 fixed_columns: &[Column<Fixed>],
142 nb_parallel_range_checks: usize,
143 max_bit_len: u32,
144 ) -> ForeignEdwardsEccConfig<C> {
145 assert!(C::A.legendre() == 1);
146 assert!(C::D.legendre() == -1);
147
148 let addition_config = AdditionConfig::<C>::configure::<F, B>(
149 meta,
150 base_field_config,
151 fixed_columns[0],
152 nb_parallel_range_checks,
153 max_bit_len,
154 );
155
156 let (q_multi_select, idx_col_multi_select, tag_col_multi_select) =
157 configure_multi_select_lookup(meta, advice_columns, base_field_config);
158
159 ForeignEdwardsEccConfig {
160 base_field_config: base_field_config.clone(),
161 addition_config,
162 q_multi_select,
163 idx_col_multi_select,
164 tag_col_multi_select,
165 }
166 }
167}
168
169impl<F, C, B, S, N> ForeignEdwardsEccChip<F, C, B, S, N>
170where
171 F: CircuitField,
172 C: EdwardsCurve,
173 B: FieldEmulationParams<F, C::Base>,
174 S: ScalarFieldInstructions<F>,
175 S::Scalar: InnerValue<Element = C::ScalarField>,
176 N: NativeInstructions<F>,
177{
178 pub fn new(
180 config: &ForeignEdwardsEccConfig<C>,
181 native_gadget: &N,
182 scalar_field_chip: &S,
183 ) -> Self {
184 let base_field_chip = FieldChip::new(&config.base_field_config, native_gadget);
185 Self {
186 config: config.clone(),
187 native_gadget: native_gadget.clone(),
188 base_field_chip,
189 scalar_field_chip: scalar_field_chip.clone(),
190 tag_cnt: Rc::new(RefCell::new(1)),
191 constant_cache: Rc::new(RefCell::new(HashMap::new())),
192 }
193 }
194
195 pub fn base_field_chip(&self) -> &FieldChip<F, C::Base, B, N> {
197 &self.base_field_chip
198 }
199
200 pub fn as_known_constant(
203 &self,
204 point: &AssignedForeignEdwardsPoint<F, C, B>,
205 ) -> Option<C::CryptographicGroup> {
206 self.constant_cache.borrow().get(point).copied()
207 }
208
209 pub fn scalar_field_chip(&self) -> &S {
211 &self.scalar_field_chip
212 }
213}
214
215impl<F, B, S, N> ForeignEdwardsEccChip<F, Curve25519, B, S, N>
216where
217 F: CircuitField,
218 B: FieldEmulationParams<F, <Curve25519 as CircuitCurve>::Base>,
219 S: ScalarFieldInstructions<F>,
220 S::Scalar: InnerValue<Element = <Curve25519 as CircuitCurve>::ScalarField>,
221 N: NativeInstructions<F>,
222{
223 pub fn to_canonical_compressed_bytes(
239 &self,
240 layouter: &mut impl Layouter<F>,
241 point: &AssignedForeignEdwardsPoint<F, Curve25519, B>,
242 ) -> Result<[AssignedByte<F>; 32], Error> {
243 let mut y_bytes = self.base_field_chip().assigned_to_le_bytes(
245 layouter,
246 &self.y_coordinate(point),
247 None,
248 )?;
249
250 let x_bits = self.base_field_chip().assigned_to_le_bits(
251 layouter,
252 &self.x_coordinate(point),
253 Some(255),
254 true,
255 )?;
256
257 let last_byte: AssignedNative<F> = self.native_gadget.linear_combination(
263 layouter,
264 &[
265 (F::ONE, y_bytes[y_bytes.len() - 1].clone().into()),
266 (F::from(128), x_bits[0].clone().into()),
267 ],
268 F::ZERO,
269 )?;
270
271 let last = y_bytes.len() - 1;
272 y_bytes[last] = self.native_gadget.convert_unsafe(layouter, &last_byte)?;
273
274 Ok(y_bytes.try_into().expect("exactly 32 bytes"))
275 }
276
277 pub fn from_canonical_compressed_bytes(
292 &self,
293 layouter: &mut impl Layouter<F>,
294 compressed_bytes: &[AssignedByte<F>; 32],
295 value: Value<Curve25519Subgroup>,
296 ) -> Result<AssignedForeignEdwardsPoint<F, Curve25519, B>, Error> {
297 let point = self.assign(layouter, value)?;
298 let canonical_bytes = self.to_canonical_compressed_bytes(layouter, &point)?;
299 compressed_bytes.iter().zip(canonical_bytes.iter()).try_for_each(
300 |(com_byte, can_byte)| self.native_gadget.assert_equal(layouter, com_byte, can_byte),
301 )?;
302
303 Ok(point)
304 }
305}
306
307#[derive(Clone, Debug)]
309#[must_use]
310pub struct AssignedForeignEdwardsPoint<F, C, B>
311where
312 F: CircuitField,
313 C: EdwardsCurve,
314 B: FieldEmulationParams<F, C::Base>,
315{
316 point: Value<C::CryptographicGroup>,
317 x: AssignedField<F, C::Base, B>,
318 y: AssignedField<F, C::Base, B>,
319}
320
321impl<F, C, B> PartialEq for AssignedForeignEdwardsPoint<F, C, B>
322where
323 F: CircuitField,
324 C: EdwardsCurve,
325 B: FieldEmulationParams<F, C::Base>,
326{
327 fn eq(&self, other: &Self) -> bool {
328 self.x == other.x && self.y == other.y
329 }
330}
331
332impl<F, C, B> Eq for AssignedForeignEdwardsPoint<F, C, B>
333where
334 F: CircuitField,
335 C: EdwardsCurve,
336 B: FieldEmulationParams<F, C::Base>,
337{
338}
339
340impl<F, C, B> Hash for AssignedForeignEdwardsPoint<F, C, B>
341where
342 F: CircuitField,
343 C: EdwardsCurve,
344 B: FieldEmulationParams<F, C::Base>,
345{
346 fn hash<H: Hasher>(&self, state: &mut H) {
347 self.x.hash(state);
348 self.y.hash(state);
349 }
350}
351
352impl<F, C, B> Instantiable<F> for AssignedForeignEdwardsPoint<F, C, B>
353where
354 F: CircuitField,
355 C: EdwardsCurve,
356 B: FieldEmulationParams<F, C::Base>,
357{
358 fn as_public_input(p: &C::CryptographicGroup) -> Vec<F> {
359 let (x, y) = (*p).into().coordinates().expect("Edwards coordinates cannot fail");
360 [
361 AssignedField::<F, C::Base, B>::as_public_input(&x).as_slice(),
362 AssignedField::<F, C::Base, B>::as_public_input(&y).as_slice(),
363 ]
364 .concat()
365 }
366
367 fn from_public_input(fields: &[F]) -> Option<C::CryptographicGroup> {
368 let nb_limbs_per_batch = (F::CAPACITY / B::LOG2_BASE) as usize;
369 let nb_pi_per_coord = (B::NB_LIMBS as usize).div_ceil(nb_limbs_per_batch);
370 if fields.len() != 2 * nb_pi_per_coord {
371 return None;
372 }
373 let x = AssignedField::<F, C::Base, B>::from_public_input(&fields[..nb_pi_per_coord])?;
374 let y = AssignedField::<F, C::Base, B>::from_public_input(&fields[nb_pi_per_coord..])?;
375 C::from_xy(x, y).map(|p| p.into_subgroup())
376 }
377}
378
379impl<F, C, B> InnerValue for AssignedForeignEdwardsPoint<F, C, B>
380where
381 F: CircuitField,
382 C: EdwardsCurve,
383 B: FieldEmulationParams<F, C::Base>,
384{
385 type Element = C::CryptographicGroup;
386
387 fn value(&self) -> Value<Self::Element> {
388 self.point
389 }
390}
391
392impl<F, C, B> InnerConstants for AssignedForeignEdwardsPoint<F, C, B>
393where
394 F: CircuitField,
395 C: EdwardsCurve,
396 B: FieldEmulationParams<F, C::Base>,
397{
398 fn inner_zero() -> C::CryptographicGroup {
399 C::CryptographicGroup::identity()
400 }
401
402 fn inner_one() -> Self::Element {
403 C::CryptographicGroup::generator()
404 }
405}
406
407#[cfg(any(test, feature = "testing"))]
408impl<F, C, B> Sampleable for AssignedForeignEdwardsPoint<F, C, B>
409where
410 F: CircuitField,
411 C: EdwardsCurve,
412 B: FieldEmulationParams<F, C::Base>,
413{
414 fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
415 C::CryptographicGroup::random(rng)
416 }
417}
418
419impl<F, C, B, S, N> Chip<F> for ForeignEdwardsEccChip<F, C, B, S, N>
420where
421 F: CircuitField,
422 C: EdwardsCurve,
423 B: FieldEmulationParams<F, C::Base>,
424 S: ScalarFieldInstructions<F>,
425 S::Scalar: InnerValue<Element = C::ScalarField>,
426 N: NativeInstructions<F>,
427{
428 type Config = ForeignEdwardsEccConfig<C>;
429 type Loaded = ();
430 fn config(&self) -> &Self::Config {
431 &self.config
432 }
433 fn loaded(&self) -> &Self::Loaded {
434 &()
435 }
436}
437
438impl<F, C, B, S, N> ForeignEdwardsEccChip<F, C, B, S, N>
439where
440 F: CircuitField,
441 C: EdwardsCurve,
442 B: FieldEmulationParams<F, C::Base>,
443 S: ScalarFieldInstructions<F>,
444 S::Scalar: InnerValue<Element = C::ScalarField>,
445 N: NativeInstructions<F>,
446{
447 fn assign_point_unchecked(
450 &self,
451 layouter: &mut impl Layouter<F>,
452 value: Value<C::CryptographicGroup>,
453 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
454 let (val_x, val_y) = value
455 .map(|v| v.into().coordinates().expect("Edwards coordinates cannot fail"))
456 .unzip();
457 let x = self.base_field_chip().assign(layouter, val_x)?;
458 let y = self.base_field_chip().assign(layouter, val_y)?;
459 let p = AssignedForeignEdwardsPoint::<F, C, B> { point: value, x, y };
460
461 Ok(p)
462 }
463
464 fn assert_on_curve(
468 &self,
469 layouter: &mut impl Layouter<F>,
470 x: &AssignedField<F, C::Base, B>,
471 y: &AssignedField<F, C::Base, B>,
472 ) -> Result<(), Error> {
473 let base_chip = self.base_field_chip();
474
475 let x_sq = base_chip.mul(layouter, x, x, None)?;
477 let y_sq = base_chip.mul(layouter, y, y, None)?;
478 let d_xy_sq = base_chip.mul(layouter, &x_sq, &y_sq, Some(C::D))?;
479 let lhs = base_chip.linear_combination(
480 layouter,
481 &[(C::A, x_sq), (C::Base::ONE, y_sq)],
482 -C::Base::ONE,
483 )?;
484
485 base_chip.assert_equal(layouter, &lhs, &d_xy_sq)
487 }
488
489 fn add_constant(
493 &self,
494 layouter: &mut impl Layouter<F>,
495 p: &AssignedForeignEdwardsPoint<F, C, B>,
496 q_val: C::CryptographicGroup,
497 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
498 if let Some(pv) = self.as_known_constant(p) {
500 return self.assign_fixed(layouter, pv + q_val);
501 }
502
503 let (qx, qy) = q_val.into().coordinates().expect("Edwards coordinates cannot fail");
504
505 let base_chip = self.base_field_chip();
506
507 let r_value = p.value().map(|pv| pv + q_val);
508 let r = self.assign_point_unchecked(layouter, r_value)?;
509
510 let px_qx = base_chip.mul_by_constant(layouter, &p.x, qx)?;
511 let py_qy = base_chip.mul_by_constant(layouter, &p.y, qy)?;
512 let px_qy = base_chip.mul_by_constant(layouter, &p.x, qy)?;
513 let py_qx = base_chip.mul_by_constant(layouter, &p.y, qx)?;
514 let neg_a_px_qx = base_chip.mul_by_constant(layouter, &px_qx, -C::A)?;
515 let d_px_py_qx_qy = base_chip.mul(layouter, &px_qx, &py_qy, Some(C::D))?;
516
517 addition::assert_addition_coordinate(
519 layouter,
520 &r.x,
521 &px_qy,
522 &py_qx,
523 &d_px_py_qx_qy,
524 false,
525 base_chip,
526 &self.config.addition_config,
527 )?;
528
529 addition::assert_addition_coordinate(
531 layouter,
532 &r.y,
533 &py_qy,
534 &neg_a_px_qx,
535 &d_px_py_qx_qy,
536 true,
537 base_chip,
538 &self.config.addition_config,
539 )?;
540
541 Ok(AssignedForeignEdwardsPoint {
542 point: r_value,
543 x: r.x,
544 y: r.y,
545 })
546 }
547}
548
549impl<F, C, B, S, N> AssignmentInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
550 for ForeignEdwardsEccChip<F, C, B, S, N>
551where
552 F: CircuitField,
553 C: EdwardsCurve,
554 B: FieldEmulationParams<F, C::Base>,
555 S: ScalarFieldInstructions<F>,
556 S::Scalar: InnerValue<Element = C::ScalarField>,
557 N: NativeInstructions<F>,
558{
559 fn assign(
560 &self,
561 layouter: &mut impl Layouter<F>,
562 p_value: Value<C::CryptographicGroup>,
563 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
564 let cofactor = C::ScalarField::from_u128(C::COFACTOR);
569 let q =
570 self.assign_point_unchecked(layouter, p_value.map(|p| p * cofactor.invert().unwrap()))?;
571
572 self.assert_on_curve(layouter, &q.x, &q.y)?;
573 self.mul_by_constant(layouter, cofactor, &q)
574 }
575
576 fn assign_fixed(
577 &self,
578 layouter: &mut impl Layouter<F>,
579 constant: C::CryptographicGroup,
580 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
581 let (x, y) = constant.into().coordinates().expect("Edwards coordinates cannot fail");
582 let x = self.base_field_chip().assign_fixed(layouter, x)?;
583 let y = self.base_field_chip().assign_fixed(layouter, y)?;
584
585 let p = AssignedForeignEdwardsPoint::<F, C, B> {
586 point: Value::known(constant),
587 x,
588 y,
589 };
590 self.constant_cache.borrow_mut().insert(p.clone(), constant);
591 Ok(p)
592 }
593}
594
595impl<F, C, B, S, N> PublicInputInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
596 for ForeignEdwardsEccChip<F, C, B, S, N>
597where
598 F: CircuitField,
599 C: EdwardsCurve,
600 B: FieldEmulationParams<F, C::Base>,
601 S: ScalarFieldInstructions<F>,
602 S::Scalar: InnerValue<Element = C::ScalarField>,
603 N: NativeInstructions<F> + PublicInputInstructions<F, AssignedBit<F>>,
604{
605 fn as_public_input(
606 &self,
607 layouter: &mut impl Layouter<F>,
608 p: &AssignedForeignEdwardsPoint<F, C, B>,
609 ) -> Result<Vec<AssignedNative<F>>, Error> {
610 Ok([
611 self.base_field_chip.as_public_input(layouter, &p.x)?.as_slice(),
612 self.base_field_chip.as_public_input(layouter, &p.y)?.as_slice(),
613 ]
614 .concat())
615 }
616
617 fn constrain_as_public_input(
618 &self,
619 layouter: &mut impl Layouter<F>,
620 assigned: &AssignedForeignEdwardsPoint<F, C, B>,
621 ) -> Result<(), Error> {
622 self.as_public_input(layouter, assigned)?
623 .iter()
624 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
625 }
626
627 fn assign_as_public_input(
628 &self,
629 layouter: &mut impl Layouter<F>,
630 value: Value<C::CryptographicGroup>,
631 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
632 let point = self.assign(layouter, value)?;
633 self.constrain_as_public_input(layouter, &point)?;
634 Ok(point)
635 }
636}
637
638impl<F, C, B, S, SP, N> AssignmentInstructions<F, AssignedField<F, C::ScalarField, SP>>
641 for ForeignEdwardsEccChip<F, C, B, S, N>
642where
643 F: CircuitField,
644 C: EdwardsCurve,
645 B: FieldEmulationParams<F, C::Base>,
646 S: ScalarFieldInstructions<F, Scalar = AssignedField<F, C::ScalarField, SP>>,
647 SP: FieldEmulationParams<F, C::ScalarField>,
648 N: NativeInstructions<F>,
649{
650 fn assign(
651 &self,
652 layouter: &mut impl Layouter<F>,
653 value: Value<<S::Scalar as InnerValue>::Element>,
654 ) -> Result<S::Scalar, Error> {
655 self.scalar_field_chip().assign(layouter, value)
656 }
657
658 fn assign_fixed(
659 &self,
660 layouter: &mut impl Layouter<F>,
661 constant: <S::Scalar as InnerValue>::Element,
662 ) -> Result<S::Scalar, Error> {
663 self.scalar_field_chip().assign_fixed(layouter, constant)
664 }
665}
666
667impl<F, C, B, S, N> AssertionInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
668 for ForeignEdwardsEccChip<F, C, B, S, N>
669where
670 F: CircuitField,
671 C: EdwardsCurve,
672 B: FieldEmulationParams<F, C::Base>,
673 S: ScalarFieldInstructions<F>,
674 S::Scalar: InnerValue<Element = C::ScalarField>,
675 N: NativeInstructions<F>,
676{
677 fn assert_equal(
678 &self,
679 layouter: &mut impl Layouter<F>,
680 p: &AssignedForeignEdwardsPoint<F, C, B>,
681 q: &AssignedForeignEdwardsPoint<F, C, B>,
682 ) -> Result<(), Error> {
683 self.base_field_chip().assert_equal(layouter, &p.x, &q.x)?;
684 self.base_field_chip().assert_equal(layouter, &p.y, &q.y)
685 }
686
687 fn assert_not_equal(
688 &self,
689 layouter: &mut impl Layouter<F>,
690 p: &AssignedForeignEdwardsPoint<F, C, B>,
691 q: &AssignedForeignEdwardsPoint<F, C, B>,
692 ) -> Result<(), Error> {
693 let p_eq_q = self.is_equal(layouter, p, q)?;
694 self.native_gadget.assert_equal_to_fixed(layouter, &p_eq_q, false)
695 }
696
697 fn assert_equal_to_fixed(
698 &self,
699 layouter: &mut impl Layouter<F>,
700 p: &AssignedForeignEdwardsPoint<F, C, B>,
701 constant: C::CryptographicGroup,
702 ) -> Result<(), Error> {
703 let coordinates = constant.into().coordinates().expect("Edwards coordinates cannot fail");
704 self.base_field_chip().assert_equal_to_fixed(layouter, &p.x, coordinates.0)?;
705 self.base_field_chip().assert_equal_to_fixed(layouter, &p.y, coordinates.1)
706 }
707
708 fn assert_not_equal_to_fixed(
709 &self,
710 layouter: &mut impl Layouter<F>,
711 p: &AssignedForeignEdwardsPoint<F, C, B>,
712 constant: C::CryptographicGroup,
713 ) -> Result<(), Error> {
714 let p_eq_constant = self.is_equal_to_fixed(layouter, p, constant)?;
715 self.native_gadget.assert_equal_to_fixed(layouter, &p_eq_constant, false)
716 }
717}
718
719impl<F, C, B, S, N> EqualityInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
720 for ForeignEdwardsEccChip<F, C, B, S, N>
721where
722 F: CircuitField,
723 C: EdwardsCurve,
724 B: FieldEmulationParams<F, C::Base>,
725 S: ScalarFieldInstructions<F>,
726 S::Scalar: InnerValue<Element = C::ScalarField>,
727 N: NativeInstructions<F>,
728{
729 fn is_equal(
730 &self,
731 layouter: &mut impl Layouter<F>,
732 p: &AssignedForeignEdwardsPoint<F, C, B>,
733 q: &AssignedForeignEdwardsPoint<F, C, B>,
734 ) -> Result<AssignedBit<F>, Error> {
735 let eq_x = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
736 let eq_y = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
737 self.native_gadget.and(layouter, &[eq_x, eq_y])
738 }
739
740 fn is_equal_to_fixed(
741 &self,
742 layouter: &mut impl Layouter<F>,
743 p: &AssignedForeignEdwardsPoint<F, C, B>,
744 constant: <AssignedForeignEdwardsPoint<F, C, B> as InnerValue>::Element,
745 ) -> Result<AssignedBit<F>, Error> {
746 let coordinates = constant.into().coordinates().expect("Edwards coordinates cannot fail");
747 let eq_x = self.base_field_chip().is_equal_to_fixed(layouter, &p.x, coordinates.0)?;
748 let eq_y = self.base_field_chip().is_equal_to_fixed(layouter, &p.y, coordinates.1)?;
749 self.native_gadget.and(layouter, &[eq_x, eq_y])
750 }
751
752 fn is_not_equal(
753 &self,
754 layouter: &mut impl Layouter<F>,
755 x: &AssignedForeignEdwardsPoint<F, C, B>,
756 y: &AssignedForeignEdwardsPoint<F, C, B>,
757 ) -> Result<AssignedBit<F>, Error> {
758 let b = self.is_equal(layouter, x, y)?;
759 self.native_gadget.not(layouter, &b)
760 }
761
762 fn is_not_equal_to_fixed(
763 &self,
764 layouter: &mut impl Layouter<F>,
765 x: &AssignedForeignEdwardsPoint<F, C, B>,
766 constant: <AssignedForeignEdwardsPoint<F, C, B> as InnerValue>::Element,
767 ) -> Result<AssignedBit<F>, Error> {
768 let b = self.is_equal_to_fixed(layouter, x, constant)?;
769 self.native_gadget.not(layouter, &b)
770 }
771}
772
773impl<F, C, B, S, N> ZeroInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
774 for ForeignEdwardsEccChip<F, C, B, S, N>
775where
776 F: CircuitField,
777 C: EdwardsCurve,
778 B: FieldEmulationParams<F, C::Base>,
779 S: ScalarFieldInstructions<F>,
780 S::Scalar: InnerValue<Element = C::ScalarField>,
781 N: NativeInstructions<F>,
782{
783 fn is_zero(
784 &self,
785 layouter: &mut impl Layouter<F>,
786 p: &AssignedForeignEdwardsPoint<F, C, B>,
787 ) -> Result<AssignedBit<F>, Error> {
788 self.is_equal_to_fixed(layouter, p, C::CryptographicGroup::identity())
789 }
790}
791
792impl<F, C, B, S, N> ControlFlowInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
793 for ForeignEdwardsEccChip<F, C, B, S, N>
794where
795 F: CircuitField,
796 C: EdwardsCurve,
797 B: FieldEmulationParams<F, C::Base>,
798 S: ScalarFieldInstructions<F>,
799 S::Scalar: InnerValue<Element = C::ScalarField>,
800 N: NativeInstructions<F>,
801{
802 fn select(
805 &self,
806 layouter: &mut impl Layouter<F>,
807 cond: &AssignedBit<F>,
808 p: &AssignedForeignEdwardsPoint<F, C, B>,
809 q: &AssignedForeignEdwardsPoint<F, C, B>,
810 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
811 let point = p.point.zip(q.point).zip(cond.value()).map(|((p, q), b)| if b { p } else { q });
812 let x = self.base_field_chip().select(layouter, cond, &p.x, &q.x)?;
813 let y = self.base_field_chip().select(layouter, cond, &p.y, &q.y)?;
814 Ok(AssignedForeignEdwardsPoint::<F, C, B> { point, x, y })
815 }
816}
817
818impl<F, C, B, S, N> EccInstructions<F, C> for ForeignEdwardsEccChip<F, C, B, S, N>
819where
820 F: CircuitField,
821 C: EdwardsCurve,
822 B: FieldEmulationParams<F, C::Base>,
823 S: ScalarFieldInstructions<F>,
824 S::Scalar: InnerValue<Element = C::ScalarField>,
825 N: NativeInstructions<F>,
826{
827 type Point = AssignedForeignEdwardsPoint<F, C, B>;
828 type Coordinate = AssignedField<F, C::Base, B>;
829 type Scalar = S::Scalar;
830
831 fn add(
832 &self,
833 layouter: &mut impl Layouter<F>,
834 p: &Self::Point,
835 q: &Self::Point,
836 ) -> Result<Self::Point, Error> {
837 if p == q {
838 return self.double(layouter, p);
839 }
840
841 if let Some(qv) = self.as_known_constant(q) {
843 return self.add_constant(layouter, p, qv);
844 }
845 if let Some(pv) = self.as_known_constant(p) {
846 return self.add_constant(layouter, q, pv);
847 }
848
849 let base_chip = self.base_field_chip();
863
864 let r_value = p.value().zip(q.value()).map(|(p, q)| p + q);
865 let r = self.assign_point_unchecked(layouter, r_value)?;
866
867 let px_qx = base_chip.mul(layouter, &p.x, &q.x, None)?;
868 let py_qy = base_chip.mul(layouter, &p.y, &q.y, None)?;
869 let px_qy = base_chip.mul(layouter, &p.x, &q.y, None)?;
870 let py_qx = base_chip.mul(layouter, &p.y, &q.x, None)?;
871 let neg_a_px_qx = base_chip.mul_by_constant(layouter, &px_qx, -C::A)?;
872 let d_px_py_qx_qy = base_chip.mul(layouter, &px_qx, &py_qy, Some(C::D))?;
873
874 addition::assert_addition_coordinate(
877 layouter,
878 &r.x,
879 &px_qy,
880 &py_qx,
881 &d_px_py_qx_qy,
882 false,
883 base_chip,
884 &self.config.addition_config,
885 )?;
886
887 addition::assert_addition_coordinate(
890 layouter,
891 &r.y,
892 &py_qy,
893 &neg_a_px_qx,
894 &d_px_py_qx_qy,
895 true,
896 base_chip,
897 &self.config.addition_config,
898 )?;
899
900 Ok(AssignedForeignEdwardsPoint {
901 point: r_value,
902 x: r.x,
903 y: r.y,
904 })
905 }
906
907 fn double(
908 &self,
909 layouter: &mut impl Layouter<F>,
910 p: &AssignedForeignEdwardsPoint<F, C, B>,
911 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
912 if let Some(pv) = self.as_known_constant(p) {
913 return self.assign_fixed(layouter, pv + pv);
914 }
915
916 let base_chip = self.base_field_chip();
934
935 let r_value = p.value().map(|p| p + p);
936 let r = self.assign_point_unchecked(layouter, r_value)?;
937
938 let px_sq = base_chip.mul(layouter, &p.x, &p.x, None)?;
939 let py_sq = base_chip.mul(layouter, &p.y, &p.y, None)?;
940 let px_py = base_chip.mul(layouter, &p.x, &p.y, None)?;
941
942 let neg_a_px_sq = base_chip.mul_by_constant(layouter, &px_sq, -C::A)?;
943
944 let w = base_chip.linear_combination(
946 layouter,
947 &[(C::A, px_sq), (C::Base::ONE, py_sq.clone())],
948 -C::Base::ONE,
949 )?;
950
951 addition::assert_addition_coordinate(
953 layouter,
954 &r.x,
955 &px_py,
956 &px_py,
957 &w,
958 false,
959 base_chip,
960 &self.config.addition_config,
961 )?;
962
963 addition::assert_addition_coordinate(
965 layouter,
966 &r.y,
967 &py_sq,
968 &neg_a_px_sq,
969 &w,
970 true,
971 base_chip,
972 &self.config.addition_config,
973 )?;
974
975 Ok(AssignedForeignEdwardsPoint {
976 point: r_value,
977 x: r.x,
978 y: r.y,
979 })
980 }
981
982 fn negate(
983 &self,
984 layouter: &mut impl Layouter<F>,
985 p: &Self::Point,
986 ) -> Result<Self::Point, Error> {
987 if let Some(pv) = self.as_known_constant(p) {
988 return self.assign_fixed(layouter, -pv);
989 }
990
991 let neg_x = self.base_field_chip().neg(layouter, &p.x)?;
993 let neg_x = self.base_field_chip().normalize(layouter, &neg_x)?;
994 Ok(AssignedForeignEdwardsPoint::<F, C, B> {
995 point: -p.point,
996 x: neg_x,
997 y: p.y.clone(),
998 })
999 }
1000
1001 fn msm(
1002 &self,
1003 layouter: &mut impl Layouter<F>,
1004 scalars: &[Self::Scalar],
1005 bases: &[Self::Point],
1006 ) -> Result<Self::Point, Error> {
1007 let scalars = scalars
1008 .iter()
1009 .map(|s| (s.clone(), C::ScalarField::NUM_BITS as usize))
1010 .collect::<Vec<_>>();
1011 self.msm_by_bounded_scalars(layouter, &scalars, bases)
1012 }
1013
1014 fn msm_by_bounded_scalars(
1015 &self,
1016 layouter: &mut impl Layouter<F>,
1017 scalars: &[(S::Scalar, usize)],
1018 bases: &[AssignedForeignEdwardsPoint<F, C, B>],
1019 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1020 if scalars.len() != bases.len() {
1021 panic!("Number of scalars and points should be the same.")
1022 }
1023 let scalar_chip = self.scalar_field_chip();
1024
1025 let (scalars, bases, bases_with_1bit_scalar) =
1026 msm_preprocess(self, scalar_chip, layouter, scalars, bases)?;
1027
1028 const WS: usize = 4;
1030 let scalar_windows: Vec<Vec<AssignedNative<F>>> = scalars
1031 .iter()
1032 .map(|(s, num_bits)| {
1033 let bits = scalar_chip.assigned_to_le_bits(layouter, s, Some(*num_bits), true)?;
1034 bits.chunks(WS)
1035 .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
1036 .collect::<Result<Vec<_>, _>>()
1037 })
1038 .collect::<Result<Vec<_>, _>>()?;
1039
1040 let res = self.windowed_msm::<WS>(layouter, &scalar_windows, &bases)?;
1041
1042 add_1bit_scalar_bases(layouter, self, scalar_chip, &bases_with_1bit_scalar, res)
1044 }
1045
1046 fn mul_by_constant(
1047 &self,
1048 layouter: &mut impl Layouter<F>,
1049 scalar: C::ScalarField,
1050 base: &AssignedForeignEdwardsPoint<F, C, B>,
1051 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1052 if scalar == C::ScalarField::ZERO {
1053 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1054 } else if scalar == C::ScalarField::ONE {
1055 return Ok(base.clone());
1056 }
1057
1058 if let Some(base_val) = self.as_known_constant(base) {
1060 return self.assign_fixed(layouter, base_val * scalar);
1061 }
1062
1063 let scalar_bits = scalar.to_bits_le(None);
1064 let mut p = base.clone();
1065 let mut res = None;
1066
1067 for (i, b) in scalar_bits.iter().enumerate() {
1069 if *b {
1070 res = match res {
1071 None => Some(p.clone()),
1072 Some(acc) => Some(self.add(layouter, &acc, &p)?),
1073 }
1074 }
1075 if i + 1 < scalar_bits.len() {
1077 p = self.double(layouter, &p)?;
1078 }
1079 }
1080
1081 Ok(res.unwrap_or(self.assign_fixed(layouter, C::CryptographicGroup::identity())?))
1082 }
1083
1084 fn point_from_coordinates(
1085 &self,
1086 layouter: &mut impl Layouter<F>,
1087 x: &AssignedField<F, C::Base, B>,
1088 y: &AssignedField<F, C::Base, B>,
1089 ) -> Result<Self::Point, Error> {
1090 let p_value = x.value().zip(y.value()).map_with_result(|(x, y)| {
1091 C::from_xy(x, y)
1092 .map(|p| p.into_subgroup())
1093 .ok_or(Error::Synthesis("invalid coordinates".into()))
1094 })?;
1095
1096 let p = self.assign(layouter, p_value)?;
1097
1098 self.base_field_chip.assert_equal(layouter, x, &self.x_coordinate(&p))?;
1099 self.base_field_chip.assert_equal(layouter, y, &self.y_coordinate(&p))?;
1100
1101 Ok(p)
1102 }
1103
1104 fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
1105 point.x.clone()
1106 }
1107
1108 fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
1109 point.y.clone()
1110 }
1111
1112 fn base_field(&self) -> &impl DecompositionInstructions<F, Self::Coordinate> {
1113 self.base_field_chip()
1114 }
1115
1116 fn assign_without_subgroup_check(
1117 &self,
1118 layouter: &mut impl Layouter<F>,
1119 value: Value<C::CryptographicGroup>,
1120 ) -> Result<Self::Point, Error> {
1121 let p = self.assign_point_unchecked(layouter, value)?;
1122 self.assert_on_curve(layouter, &p.x, &p.y)?;
1123 Ok(p)
1124 }
1125}
1126
1127#[derive(Clone, Debug)]
1129struct PrecomputedTable<F, C, B, const WS: usize>
1130where
1131 F: CircuitField,
1132 C: EdwardsCurve,
1133 B: FieldEmulationParams<F, C::Base>,
1134{
1135 table: Vec<AssignedForeignEdwardsPoint<F, C, B>>,
1137}
1138
1139impl<F, C, B, S, N> ForeignEdwardsEccChip<F, C, B, S, N>
1140where
1141 F: CircuitField,
1142 C: EdwardsCurve,
1143 B: FieldEmulationParams<F, C::Base>,
1144 S: ScalarFieldInstructions<F>,
1145 S::Scalar: InnerValue<Element = C::ScalarField>,
1146 N: NativeInstructions<F>,
1147{
1148 fn precompute<const WS: usize>(
1153 &self,
1154 layouter: &mut impl Layouter<F>,
1155 base: &AssignedForeignEdwardsPoint<F, C, B>,
1156 ) -> Result<PrecomputedTable<F, C, B, WS>, Error> {
1157 let identity = self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
1158 let mut table = vec![identity, base.clone()];
1159 for i in 2..1 << WS {
1160 let entry = if i % 2 == 0 {
1161 self.double(layouter, &table[i / 2])?
1162 } else {
1163 self.add(layouter, &table[i - 1], base)?
1164 };
1165 table.push(entry);
1166 }
1167 Ok(PrecomputedTable { table })
1168 }
1169
1170 #[allow(clippy::type_complexity)]
1172 fn fill_dynamic_lookup_row(
1173 &self,
1174 layouter: &mut impl Layouter<F>,
1175 point: &AssignedForeignEdwardsPoint<F, C, B>,
1176 index: &AssignedNative<F>,
1177 table_tag: F,
1178 enable_lookup: bool,
1179 ) -> Result<(Vec<AssignedNative<F>>, Vec<AssignedNative<F>>), Error> {
1180 fill_dynamic_lookup_row(
1181 layouter,
1182 &point.x.limb_values(),
1183 &point.y.limb_values(),
1184 index,
1185 &self.config.base_field_config.x_cols,
1186 &self.config.base_field_config.z_cols, self.config.idx_col_multi_select,
1188 self.config.tag_col_multi_select,
1189 self.config.q_multi_select,
1190 table_tag,
1191 enable_lookup,
1192 )
1193 }
1194
1195 fn load_multi_select_table(
1198 &self,
1199 layouter: &mut impl Layouter<F>,
1200 point_table: &[AssignedForeignEdwardsPoint<F, C, B>],
1201 table_tag: F,
1202 ) -> Result<(), Error> {
1203 for (i, point) in point_table.iter().enumerate() {
1204 let index = self.native_gadget.assign_fixed(layouter, F::from(i as u64))?;
1205 self.fill_dynamic_lookup_row(layouter, point, &index, table_tag, false)?;
1206 }
1207 Ok(())
1208 }
1209
1210 fn multi_select(
1215 &self,
1216 layouter: &mut impl Layouter<F>,
1217 selector: &AssignedNative<F>,
1218 point_table: &[AssignedForeignEdwardsPoint<F, C, B>],
1219 table_tag: F,
1220 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1221 let mut selector_idx = 0usize;
1222 selector.value().map(|v| {
1223 let digits = v.to_biguint().to_u32_digits();
1224 let digit = if digits.is_empty() { 0 } else { digits[0] };
1225 debug_assert!(digits.len() <= 1);
1226 debug_assert!((digit as usize) < point_table.len());
1227 selector_idx = digit as usize;
1228 });
1229
1230 let selected = point_table[selector_idx].clone();
1231
1232 let (xs, ys) =
1233 self.fill_dynamic_lookup_row(layouter, &selected, selector, table_tag, true)?;
1234 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1235 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1236
1237 Ok(AssignedForeignEdwardsPoint::<F, C, B> {
1238 point: selected.point,
1239 x,
1240 y,
1241 })
1242 }
1243
1244 fn windowed_msm<const WS: usize>(
1250 &self,
1251 layouter: &mut impl Layouter<F>,
1252 scalars: &[Vec<AssignedNative<F>>],
1253 bases: &[AssignedForeignEdwardsPoint<F, C, B>],
1254 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1255 assert_eq!(scalars.len(), bases.len());
1256
1257 if bases.is_empty() {
1258 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1259 }
1260
1261 let num_windows: Vec<usize> = scalars.iter().map(|s| s.len()).collect();
1263 let max_num_windows = *num_windows.iter().max().unwrap();
1264
1265 let tag_cnt = *self.tag_cnt.borrow();
1267 self.tag_cnt.replace(tag_cnt + bases.len() as u64);
1268 debug_assert!(F::NUM_BITS > 64);
1269
1270 let mut tables = vec![];
1271 for (i, base) in bases.iter().enumerate() {
1272 let table = self.precompute::<WS>(layouter, base)?;
1273 self.load_multi_select_table(layouter, &table.table, F::from(tag_cnt + i as u64))?;
1274 tables.push(table);
1275 }
1276
1277 let mut res = self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
1278 for w in (0..max_num_windows).rev() {
1279 if w < max_num_windows - 1 {
1281 for _ in 0..WS {
1282 res = self.double(layouter, &res)?;
1283 }
1284 }
1285 for (i, (windows, nw)) in scalars.iter().zip(&num_windows).enumerate() {
1286 if w >= *nw {
1288 continue;
1289 }
1290 let addend = self.multi_select(
1291 layouter,
1292 &windows[w],
1293 &tables[i].table,
1294 F::from(tag_cnt + i as u64),
1295 )?;
1296 res = self.add(layouter, &res, &addend)?;
1297 }
1298 }
1299
1300 Ok(res)
1301 }
1302}
1303
1304#[derive(Clone, Debug)]
1305#[cfg(any(test, feature = "testing"))]
1306pub struct ForeignEdwardsEccTestConfig<F, C, S, N>
1309where
1310 F: CircuitField,
1311 C: EdwardsCurve,
1312 S: ScalarFieldInstructions<F> + FromScratch<F>,
1313 S::Scalar: InnerValue<Element = C::ScalarField>,
1314 N: NativeInstructions<F> + FromScratch<F>,
1315{
1316 native_gadget_config: <N as FromScratch<F>>::Config,
1317 scalar_field_config: <S as FromScratch<F>>::Config,
1318 ff_ecc_config: ForeignEdwardsEccConfig<C>,
1319}
1320
1321#[cfg(any(test, feature = "testing"))]
1322impl<F, C, B, S, N> FromScratch<F> for ForeignEdwardsEccChip<F, C, B, S, N>
1323where
1324 F: CircuitField,
1325 C: EdwardsCurve,
1326 C::Base: Legendre,
1327 B: FieldEmulationParams<F, C::Base>,
1328 S: ScalarFieldInstructions<F> + FromScratch<F>,
1329 S::Scalar: InnerValue<Element = C::ScalarField>,
1330 N: NativeInstructions<F> + FromScratch<F>,
1331{
1332 type Config = ForeignEdwardsEccTestConfig<F, C, S, N>;
1333
1334 fn new_from_scratch(config: &Self::Config) -> Self {
1335 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
1336 let scalar_field_chip =
1337 <S as FromScratch<F>>::new_from_scratch(&config.scalar_field_config);
1338 ForeignEdwardsEccChip::new(&config.ff_ecc_config, &native_gadget, &scalar_field_chip)
1339 }
1340
1341 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1342 self.native_gadget.load_from_scratch(layouter)?;
1343 self.scalar_field_chip.load_from_scratch(layouter)
1344 }
1345
1346 fn configure_from_scratch(
1347 meta: &mut ConstraintSystem<F>,
1348 advice_columns: &mut Vec<Column<Advice>>,
1349 fixed_columns: &mut Vec<Column<Fixed>>,
1350 instance_columns: &[Column<Instance>; 2],
1351 ) -> ForeignEdwardsEccTestConfig<F, C, S, N> {
1352 use crate::field::foreign::nb_field_chip_columns;
1353
1354 let native_gadget_config = <N as FromScratch<F>>::configure_from_scratch(
1355 meta,
1356 advice_columns,
1357 fixed_columns,
1358 instance_columns,
1359 );
1360 let scalar_field_config = <S as FromScratch<F>>::configure_from_scratch(
1361 meta,
1362 advice_columns,
1363 fixed_columns,
1364 instance_columns,
1365 );
1366 let nb_advice_cols = nb_field_chip_columns::<F, C::Base, B>();
1367 while advice_columns.len() < nb_advice_cols {
1368 advice_columns.push(meta.advice_column());
1369 }
1370 let nb_parallel_range_checks = 4;
1371 let max_bit_len = 8;
1372 let base_field_config = FieldChip::<F, C::Base, B, N>::configure(
1373 meta,
1374 &advice_columns[..nb_advice_cols],
1375 nb_parallel_range_checks,
1376 max_bit_len,
1377 );
1378 let ff_ecc_config = ForeignEdwardsEccChip::<F, C, B, S, N>::configure(
1379 meta,
1380 &base_field_config,
1381 advice_columns,
1382 fixed_columns,
1383 nb_parallel_range_checks,
1384 max_bit_len,
1385 );
1386 ForeignEdwardsEccTestConfig {
1387 native_gadget_config,
1388 scalar_field_config,
1389 ff_ecc_config,
1390 }
1391 }
1392}
1393
1394#[cfg(test)]
1395mod tests {
1396 use ff::Field;
1397 use group::{Group, GroupEncoding};
1398 use midnight_curves::{curve25519::Curve25519, BlsScalar};
1399 use midnight_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit};
1400 use rand::SeedableRng;
1401 use rand_chacha::ChaCha8Rng;
1402
1403 use super::*;
1404 use crate::{
1405 ecc::curves::CircuitCurve,
1406 field::{
1407 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
1408 NativeChip, NativeGadget,
1409 },
1410 instructions::{assertions, control_flow, ecc, equality, public_input, zero},
1411 };
1412
1413 type F = BlsScalar;
1414 type Native<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
1415 type EmulatedField<F, C> = FieldChip<F, <C as Group>::Scalar, MultiEmulationParams, Native<F>>;
1416
1417 macro_rules! test_generic {
1418 ($mod:ident, $op:ident, $native:ty, $curve:ty, $scalar_field:ty,
1419 $name:expr) => {
1420 $mod::tests::$op::<
1421 $native,
1422 AssignedForeignEdwardsPoint<$native, $curve, MultiEmulationParams>,
1423 ForeignEdwardsEccChip<
1424 $native,
1425 $curve,
1426 MultiEmulationParams,
1427 $scalar_field,
1428 Native<$native>,
1429 >,
1430 >($name);
1431 };
1432 }
1433
1434 macro_rules! test {
1435 ($mod:ident, $op:ident) => {
1436 #[test]
1437 fn $op() {
1438 test_generic!($mod, $op, F, Curve25519, EmulatedField<F, Curve25519>, "emulated_curve25519");
1439 }
1440 };
1441 }
1442
1443 test!(assertions, test_assertions);
1444
1445 test!(public_input, test_public_inputs);
1446
1447 test!(equality, test_is_equal);
1448
1449 test!(zero, test_zero_assertions);
1450 test!(zero, test_is_zero);
1451
1452 test!(control_flow, test_select);
1453 test!(control_flow, test_cond_assert_equal);
1454 test!(control_flow, test_cond_swap);
1455
1456 macro_rules! ecc_test {
1457 ($op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
1458 ecc::tests::$op::<
1459 $native,
1460 $curve,
1461 ForeignEdwardsEccChip<
1462 $native,
1463 $curve,
1464 MultiEmulationParams,
1465 $scalar_field,
1466 Native<$native>,
1467 >,
1468 >($name);
1469 };
1470 }
1471
1472 macro_rules! ecc_tests {
1473 ($op:ident) => {
1474 #[test]
1475 fn $op() {
1476 ecc_test!($op, BlsScalar, Curve25519, EmulatedField<BlsScalar, Curve25519>, "emulated_curve25519");
1477 }
1478 };
1479 }
1480
1481 ecc_tests!(test_assign);
1482 ecc_tests!(test_assign_without_subgroup_check);
1483 ecc_tests!(test_add);
1484 ecc_tests!(test_double);
1485 ecc_tests!(test_negate);
1486 ecc_tests!(test_msm);
1487 ecc_tests!(test_msm_by_bounded_scalars);
1488 ecc_tests!(test_mul_by_constant);
1489 ecc_tests!(test_coordinates);
1490
1491 #[test]
1492 fn test_assert_on_curve() {
1493 run_test_assert_on_curve::<Curve25519>();
1494 }
1495
1496 fn run_test_assert_on_curve<C>()
1499 where
1500 C: EdwardsCurve,
1501 C::Base: Legendre,
1502 MultiEmulationParams: FieldEmulationParams<BlsScalar, C::Base>
1503 + FieldEmulationParams<BlsScalar, C::ScalarField>,
1504 {
1505 fn assert_not_on_curve<C: EdwardsCurve>(x: C::Base, y: C::Base)
1506 where
1507 C::Base: Legendre,
1508 MultiEmulationParams: FieldEmulationParams<BlsScalar, C::Base>
1509 + FieldEmulationParams<BlsScalar, C::ScalarField>,
1510 {
1511 let circuit = OnCurveCheckCircuit::<C> { x, y };
1512 let prover = MockProver::run(&circuit, vec![vec![], vec![]])
1513 .expect("proof generation should not fail");
1514 assert!(prover.verify().is_err());
1515 }
1516
1517 let mut rng = ChaCha8Rng::seed_from_u64(0x0);
1518
1519 let point = C::CryptographicGroup::random(&mut rng);
1521 let (x, y) = point.into().coordinates().expect("valid curve point");
1522
1523 assert_not_on_curve::<C>(x, y + C::Base::ONE);
1524 assert_not_on_curve::<C>(C::Base::ONE, C::Base::ONE);
1525 assert_not_on_curve::<C>(C::Base::ZERO, C::Base::ZERO);
1526 }
1527
1528 type EdwardsChip<C> = ForeignEdwardsEccChip<
1529 F,
1530 C,
1531 MultiEmulationParams,
1532 FieldChip<F, <C as CircuitCurve>::ScalarField, MultiEmulationParams, Native<F>>,
1533 Native<F>,
1534 >;
1535
1536 #[derive(Clone, Debug)]
1542 struct OnCurveCheckCircuit<C: EdwardsCurve> {
1543 x: C::Base,
1544 y: C::Base,
1545 }
1546
1547 impl<C> Circuit<F> for OnCurveCheckCircuit<C>
1548 where
1549 C: EdwardsCurve,
1550 C::Base: Legendre,
1551 MultiEmulationParams:
1552 FieldEmulationParams<F, C::Base> + FieldEmulationParams<F, C::ScalarField>,
1553 {
1554 type Config = <EdwardsChip<C> as FromScratch<F>>::Config;
1555 type FloorPlanner = SimpleFloorPlanner;
1556 type Params = ();
1557
1558 fn without_witnesses(&self) -> Self {
1559 unreachable!()
1560 }
1561
1562 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
1563 let committed = meta.instance_column();
1564 let instance = meta.instance_column();
1565 EdwardsChip::<C>::configure_from_scratch(
1566 meta,
1567 &mut vec![],
1568 &mut vec![],
1569 &[committed, instance],
1570 )
1571 }
1572
1573 fn synthesize(
1574 &self,
1575 config: Self::Config,
1576 mut layouter: impl Layouter<F>,
1577 ) -> Result<(), Error> {
1578 let chip = EdwardsChip::<C>::new_from_scratch(&config);
1579
1580 let x = chip.base_field_chip().assign(&mut layouter, Value::known(self.x))?;
1581 let y = chip.base_field_chip().assign(&mut layouter, Value::known(self.y))?;
1582
1583 chip.assert_on_curve(&mut layouter, &x, &y)?;
1584 chip.load_from_scratch(&mut layouter)
1585 }
1586 }
1587
1588 #[derive(Clone, Debug)]
1594 struct FromCompressedBytesCheckCircuit {
1595 point: Curve25519Subgroup,
1596 bytes: [u8; 32],
1597 }
1598
1599 impl Circuit<F> for FromCompressedBytesCheckCircuit {
1600 type Config = <EdwardsChip<Curve25519> as FromScratch<F>>::Config;
1601 type FloorPlanner = SimpleFloorPlanner;
1602 type Params = ();
1603
1604 fn without_witnesses(&self) -> Self {
1605 unreachable!()
1606 }
1607
1608 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
1609 let committed = meta.instance_column();
1610 let instance = meta.instance_column();
1611 EdwardsChip::<Curve25519>::configure_from_scratch(
1612 meta,
1613 &mut vec![],
1614 &mut vec![],
1615 &[committed, instance],
1616 )
1617 }
1618
1619 fn synthesize(
1620 &self,
1621 config: Self::Config,
1622 mut layouter: impl Layouter<F>,
1623 ) -> Result<(), Error> {
1624 let chip = EdwardsChip::<Curve25519>::new_from_scratch(&config);
1625
1626 let byte_cells: [AssignedByte<F>; 32] = self
1627 .bytes
1628 .iter()
1629 .map(|b| chip.native_gadget.assign(&mut layouter, Value::known(*b)))
1630 .collect::<Result<Vec<_>, _>>()?
1631 .try_into()
1632 .expect("exactly 32 bytes");
1633
1634 let _ = chip.from_canonical_compressed_bytes(
1635 &mut layouter,
1636 &byte_cells,
1637 Value::known(self.point),
1638 )?;
1639
1640 chip.load_from_scratch(&mut layouter)
1641 }
1642 }
1643
1644 fn run_test_compressed_bytes(point: Curve25519Subgroup, bytes: [u8; 32], should_accept: bool) {
1645 let circuit = FromCompressedBytesCheckCircuit { point, bytes };
1646 let prover = MockProver::run(&circuit, vec![vec![], vec![]])
1647 .expect("proof generation should not fail");
1648 assert_eq!(prover.verify().is_ok(), should_accept);
1649 }
1650
1651 #[test]
1652 fn test_compressed_bytes() {
1653 let mut canonical = [0; 32];
1655 canonical[0] = 1;
1656 run_test_compressed_bytes(Curve25519Subgroup::identity(), canonical, true);
1657
1658 let mut non_canonical = [0xff_u8; 32];
1660 non_canonical[0] = 0xee;
1661 non_canonical[31] = 0x7f;
1662 run_test_compressed_bytes(Curve25519Subgroup::identity(), non_canonical, false);
1663
1664 let mut non_canonical_with_sign = canonical;
1666 non_canonical_with_sign[31] = 0x80;
1667 run_test_compressed_bytes(
1668 Curve25519Subgroup::identity(),
1669 non_canonical_with_sign,
1670 false,
1671 );
1672
1673 let g = Curve25519Subgroup::generator();
1675 run_test_compressed_bytes(g, Curve25519::from(g).to_bytes(), true);
1676
1677 let mut rng = ChaCha8Rng::seed_from_u64(0x7374727564656C);
1679 let p = Curve25519Subgroup::random(&mut rng);
1680 run_test_compressed_bytes(p, Curve25519::from(p).to_bytes(), true);
1681 }
1682}