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
368impl<F, C, B> InnerValue for AssignedForeignEdwardsPoint<F, C, B>
369where
370 F: CircuitField,
371 C: EdwardsCurve,
372 B: FieldEmulationParams<F, C::Base>,
373{
374 type Element = C::CryptographicGroup;
375
376 fn value(&self) -> Value<Self::Element> {
377 self.point
378 }
379}
380
381impl<F, C, B> InnerConstants for AssignedForeignEdwardsPoint<F, C, B>
382where
383 F: CircuitField,
384 C: EdwardsCurve,
385 B: FieldEmulationParams<F, C::Base>,
386{
387 fn inner_zero() -> C::CryptographicGroup {
388 C::CryptographicGroup::identity()
389 }
390
391 fn inner_one() -> Self::Element {
392 C::CryptographicGroup::generator()
393 }
394}
395
396#[cfg(any(test, feature = "testing"))]
397impl<F, C, B> Sampleable for AssignedForeignEdwardsPoint<F, C, B>
398where
399 F: CircuitField,
400 C: EdwardsCurve,
401 B: FieldEmulationParams<F, C::Base>,
402{
403 fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
404 C::CryptographicGroup::random(rng)
405 }
406}
407
408impl<F, C, B, S, N> Chip<F> for ForeignEdwardsEccChip<F, C, B, S, N>
409where
410 F: CircuitField,
411 C: EdwardsCurve,
412 B: FieldEmulationParams<F, C::Base>,
413 S: ScalarFieldInstructions<F>,
414 S::Scalar: InnerValue<Element = C::ScalarField>,
415 N: NativeInstructions<F>,
416{
417 type Config = ForeignEdwardsEccConfig<C>;
418 type Loaded = ();
419 fn config(&self) -> &Self::Config {
420 &self.config
421 }
422 fn loaded(&self) -> &Self::Loaded {
423 &()
424 }
425}
426
427impl<F, C, B, S, N> ForeignEdwardsEccChip<F, C, B, S, N>
428where
429 F: CircuitField,
430 C: EdwardsCurve,
431 B: FieldEmulationParams<F, C::Base>,
432 S: ScalarFieldInstructions<F>,
433 S::Scalar: InnerValue<Element = C::ScalarField>,
434 N: NativeInstructions<F>,
435{
436 fn assign_point_unchecked(
439 &self,
440 layouter: &mut impl Layouter<F>,
441 value: Value<C::CryptographicGroup>,
442 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
443 let (val_x, val_y) = value
444 .map(|v| v.into().coordinates().expect("Edwards coordinates cannot fail"))
445 .unzip();
446 let x = self.base_field_chip().assign(layouter, val_x)?;
447 let y = self.base_field_chip().assign(layouter, val_y)?;
448 let p = AssignedForeignEdwardsPoint::<F, C, B> { point: value, x, y };
449
450 Ok(p)
451 }
452
453 fn assert_on_curve(
457 &self,
458 layouter: &mut impl Layouter<F>,
459 x: &AssignedField<F, C::Base, B>,
460 y: &AssignedField<F, C::Base, B>,
461 ) -> Result<(), Error> {
462 let base_chip = self.base_field_chip();
463
464 let x_sq = base_chip.mul(layouter, x, x, None)?;
466 let y_sq = base_chip.mul(layouter, y, y, None)?;
467 let d_xy_sq = base_chip.mul(layouter, &x_sq, &y_sq, Some(C::D))?;
468 let lhs = base_chip.linear_combination(
469 layouter,
470 &[(C::A, x_sq), (C::Base::ONE, y_sq)],
471 -C::Base::ONE,
472 )?;
473
474 base_chip.assert_equal(layouter, &lhs, &d_xy_sq)
476 }
477
478 fn add_constant(
482 &self,
483 layouter: &mut impl Layouter<F>,
484 p: &AssignedForeignEdwardsPoint<F, C, B>,
485 q_val: C::CryptographicGroup,
486 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
487 if let Some(pv) = self.as_known_constant(p) {
489 return self.assign_fixed(layouter, pv + q_val);
490 }
491
492 let (qx, qy) = q_val.into().coordinates().expect("Edwards coordinates cannot fail");
493
494 let base_chip = self.base_field_chip();
495
496 let r_value = p.value().map(|pv| pv + q_val);
497 let r = self.assign_point_unchecked(layouter, r_value)?;
498
499 let px_qx = base_chip.mul_by_constant(layouter, &p.x, qx)?;
500 let py_qy = base_chip.mul_by_constant(layouter, &p.y, qy)?;
501 let px_qy = base_chip.mul_by_constant(layouter, &p.x, qy)?;
502 let py_qx = base_chip.mul_by_constant(layouter, &p.y, qx)?;
503 let neg_a_px_qx = base_chip.mul_by_constant(layouter, &px_qx, -C::A)?;
504 let d_px_py_qx_qy = base_chip.mul(layouter, &px_qx, &py_qy, Some(C::D))?;
505
506 addition::assert_addition_coordinate(
508 layouter,
509 &r.x,
510 &px_qy,
511 &py_qx,
512 &d_px_py_qx_qy,
513 false,
514 base_chip,
515 &self.config.addition_config,
516 )?;
517
518 addition::assert_addition_coordinate(
520 layouter,
521 &r.y,
522 &py_qy,
523 &neg_a_px_qx,
524 &d_px_py_qx_qy,
525 true,
526 base_chip,
527 &self.config.addition_config,
528 )?;
529
530 Ok(AssignedForeignEdwardsPoint {
531 point: r_value,
532 x: r.x,
533 y: r.y,
534 })
535 }
536}
537
538impl<F, C, B, S, N> AssignmentInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
539 for ForeignEdwardsEccChip<F, C, B, S, N>
540where
541 F: CircuitField,
542 C: EdwardsCurve,
543 B: FieldEmulationParams<F, C::Base>,
544 S: ScalarFieldInstructions<F>,
545 S::Scalar: InnerValue<Element = C::ScalarField>,
546 N: NativeInstructions<F>,
547{
548 fn assign(
549 &self,
550 layouter: &mut impl Layouter<F>,
551 p_value: Value<C::CryptographicGroup>,
552 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
553 let cofactor = C::ScalarField::from_u128(C::COFACTOR);
558 let q =
559 self.assign_point_unchecked(layouter, p_value.map(|p| p * cofactor.invert().unwrap()))?;
560
561 self.assert_on_curve(layouter, &q.x, &q.y)?;
562 self.mul_by_constant(layouter, cofactor, &q)
563 }
564
565 fn assign_fixed(
566 &self,
567 layouter: &mut impl Layouter<F>,
568 constant: C::CryptographicGroup,
569 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
570 let (x, y) = constant.into().coordinates().expect("Edwards coordinates cannot fail");
571 let x = self.base_field_chip().assign_fixed(layouter, x)?;
572 let y = self.base_field_chip().assign_fixed(layouter, y)?;
573
574 let p = AssignedForeignEdwardsPoint::<F, C, B> {
575 point: Value::known(constant),
576 x,
577 y,
578 };
579 self.constant_cache.borrow_mut().insert(p.clone(), constant);
580 Ok(p)
581 }
582}
583
584impl<F, C, B, S, N> PublicInputInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
585 for ForeignEdwardsEccChip<F, C, B, S, N>
586where
587 F: CircuitField,
588 C: EdwardsCurve,
589 B: FieldEmulationParams<F, C::Base>,
590 S: ScalarFieldInstructions<F>,
591 S::Scalar: InnerValue<Element = C::ScalarField>,
592 N: NativeInstructions<F> + PublicInputInstructions<F, AssignedBit<F>>,
593{
594 fn as_public_input(
595 &self,
596 layouter: &mut impl Layouter<F>,
597 p: &AssignedForeignEdwardsPoint<F, C, B>,
598 ) -> Result<Vec<AssignedNative<F>>, Error> {
599 Ok([
600 self.base_field_chip.as_public_input(layouter, &p.x)?.as_slice(),
601 self.base_field_chip.as_public_input(layouter, &p.y)?.as_slice(),
602 ]
603 .concat())
604 }
605
606 fn constrain_as_public_input(
607 &self,
608 layouter: &mut impl Layouter<F>,
609 assigned: &AssignedForeignEdwardsPoint<F, C, B>,
610 ) -> Result<(), Error> {
611 self.as_public_input(layouter, assigned)?
612 .iter()
613 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
614 }
615
616 fn assign_as_public_input(
617 &self,
618 layouter: &mut impl Layouter<F>,
619 value: Value<C::CryptographicGroup>,
620 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
621 let point = self.assign(layouter, value)?;
622 self.constrain_as_public_input(layouter, &point)?;
623 Ok(point)
624 }
625}
626
627impl<F, C, B, S, SP, N> AssignmentInstructions<F, AssignedField<F, C::ScalarField, SP>>
630 for ForeignEdwardsEccChip<F, C, B, S, N>
631where
632 F: CircuitField,
633 C: EdwardsCurve,
634 B: FieldEmulationParams<F, C::Base>,
635 S: ScalarFieldInstructions<F, Scalar = AssignedField<F, C::ScalarField, SP>>,
636 SP: FieldEmulationParams<F, C::ScalarField>,
637 N: NativeInstructions<F>,
638{
639 fn assign(
640 &self,
641 layouter: &mut impl Layouter<F>,
642 value: Value<<S::Scalar as InnerValue>::Element>,
643 ) -> Result<S::Scalar, Error> {
644 self.scalar_field_chip().assign(layouter, value)
645 }
646
647 fn assign_fixed(
648 &self,
649 layouter: &mut impl Layouter<F>,
650 constant: <S::Scalar as InnerValue>::Element,
651 ) -> Result<S::Scalar, Error> {
652 self.scalar_field_chip().assign_fixed(layouter, constant)
653 }
654}
655
656impl<F, C, B, S, N> AssertionInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
657 for ForeignEdwardsEccChip<F, C, B, S, N>
658where
659 F: CircuitField,
660 C: EdwardsCurve,
661 B: FieldEmulationParams<F, C::Base>,
662 S: ScalarFieldInstructions<F>,
663 S::Scalar: InnerValue<Element = C::ScalarField>,
664 N: NativeInstructions<F>,
665{
666 fn assert_equal(
667 &self,
668 layouter: &mut impl Layouter<F>,
669 p: &AssignedForeignEdwardsPoint<F, C, B>,
670 q: &AssignedForeignEdwardsPoint<F, C, B>,
671 ) -> Result<(), Error> {
672 self.base_field_chip().assert_equal(layouter, &p.x, &q.x)?;
673 self.base_field_chip().assert_equal(layouter, &p.y, &q.y)
674 }
675
676 fn assert_not_equal(
677 &self,
678 layouter: &mut impl Layouter<F>,
679 p: &AssignedForeignEdwardsPoint<F, C, B>,
680 q: &AssignedForeignEdwardsPoint<F, C, B>,
681 ) -> Result<(), Error> {
682 let p_eq_q = self.is_equal(layouter, p, q)?;
683 self.native_gadget.assert_equal_to_fixed(layouter, &p_eq_q, false)
684 }
685
686 fn assert_equal_to_fixed(
687 &self,
688 layouter: &mut impl Layouter<F>,
689 p: &AssignedForeignEdwardsPoint<F, C, B>,
690 constant: C::CryptographicGroup,
691 ) -> Result<(), Error> {
692 let coordinates = constant.into().coordinates().expect("Edwards coordinates cannot fail");
693 self.base_field_chip().assert_equal_to_fixed(layouter, &p.x, coordinates.0)?;
694 self.base_field_chip().assert_equal_to_fixed(layouter, &p.y, coordinates.1)
695 }
696
697 fn assert_not_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 p_eq_constant = self.is_equal_to_fixed(layouter, p, constant)?;
704 self.native_gadget.assert_equal_to_fixed(layouter, &p_eq_constant, false)
705 }
706}
707
708impl<F, C, B, S, N> EqualityInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
709 for ForeignEdwardsEccChip<F, C, B, S, N>
710where
711 F: CircuitField,
712 C: EdwardsCurve,
713 B: FieldEmulationParams<F, C::Base>,
714 S: ScalarFieldInstructions<F>,
715 S::Scalar: InnerValue<Element = C::ScalarField>,
716 N: NativeInstructions<F>,
717{
718 fn is_equal(
719 &self,
720 layouter: &mut impl Layouter<F>,
721 p: &AssignedForeignEdwardsPoint<F, C, B>,
722 q: &AssignedForeignEdwardsPoint<F, C, B>,
723 ) -> Result<AssignedBit<F>, Error> {
724 let eq_x = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
725 let eq_y = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
726 self.native_gadget.and(layouter, &[eq_x, eq_y])
727 }
728
729 fn is_equal_to_fixed(
730 &self,
731 layouter: &mut impl Layouter<F>,
732 p: &AssignedForeignEdwardsPoint<F, C, B>,
733 constant: <AssignedForeignEdwardsPoint<F, C, B> as InnerValue>::Element,
734 ) -> Result<AssignedBit<F>, Error> {
735 let coordinates = constant.into().coordinates().expect("Edwards coordinates cannot fail");
736 let eq_x = self.base_field_chip().is_equal_to_fixed(layouter, &p.x, coordinates.0)?;
737 let eq_y = self.base_field_chip().is_equal_to_fixed(layouter, &p.y, coordinates.1)?;
738 self.native_gadget.and(layouter, &[eq_x, eq_y])
739 }
740
741 fn is_not_equal(
742 &self,
743 layouter: &mut impl Layouter<F>,
744 x: &AssignedForeignEdwardsPoint<F, C, B>,
745 y: &AssignedForeignEdwardsPoint<F, C, B>,
746 ) -> Result<AssignedBit<F>, Error> {
747 let b = self.is_equal(layouter, x, y)?;
748 self.native_gadget.not(layouter, &b)
749 }
750
751 fn is_not_equal_to_fixed(
752 &self,
753 layouter: &mut impl Layouter<F>,
754 x: &AssignedForeignEdwardsPoint<F, C, B>,
755 constant: <AssignedForeignEdwardsPoint<F, C, B> as InnerValue>::Element,
756 ) -> Result<AssignedBit<F>, Error> {
757 let b = self.is_equal_to_fixed(layouter, x, constant)?;
758 self.native_gadget.not(layouter, &b)
759 }
760}
761
762impl<F, C, B, S, N> ZeroInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
763 for ForeignEdwardsEccChip<F, C, B, S, N>
764where
765 F: CircuitField,
766 C: EdwardsCurve,
767 B: FieldEmulationParams<F, C::Base>,
768 S: ScalarFieldInstructions<F>,
769 S::Scalar: InnerValue<Element = C::ScalarField>,
770 N: NativeInstructions<F>,
771{
772 fn is_zero(
773 &self,
774 layouter: &mut impl Layouter<F>,
775 p: &AssignedForeignEdwardsPoint<F, C, B>,
776 ) -> Result<AssignedBit<F>, Error> {
777 self.is_equal_to_fixed(layouter, p, C::CryptographicGroup::identity())
778 }
779}
780
781impl<F, C, B, S, N> ControlFlowInstructions<F, AssignedForeignEdwardsPoint<F, C, B>>
782 for ForeignEdwardsEccChip<F, C, B, S, N>
783where
784 F: CircuitField,
785 C: EdwardsCurve,
786 B: FieldEmulationParams<F, C::Base>,
787 S: ScalarFieldInstructions<F>,
788 S::Scalar: InnerValue<Element = C::ScalarField>,
789 N: NativeInstructions<F>,
790{
791 fn select(
794 &self,
795 layouter: &mut impl Layouter<F>,
796 cond: &AssignedBit<F>,
797 p: &AssignedForeignEdwardsPoint<F, C, B>,
798 q: &AssignedForeignEdwardsPoint<F, C, B>,
799 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
800 let point = p.point.zip(q.point).zip(cond.value()).map(|((p, q), b)| if b { p } else { q });
801 let x = self.base_field_chip().select(layouter, cond, &p.x, &q.x)?;
802 let y = self.base_field_chip().select(layouter, cond, &p.y, &q.y)?;
803 Ok(AssignedForeignEdwardsPoint::<F, C, B> { point, x, y })
804 }
805}
806
807impl<F, C, B, S, N> EccInstructions<F, C> for ForeignEdwardsEccChip<F, C, B, S, N>
808where
809 F: CircuitField,
810 C: EdwardsCurve,
811 B: FieldEmulationParams<F, C::Base>,
812 S: ScalarFieldInstructions<F>,
813 S::Scalar: InnerValue<Element = C::ScalarField>,
814 N: NativeInstructions<F>,
815{
816 type Point = AssignedForeignEdwardsPoint<F, C, B>;
817 type Coordinate = AssignedField<F, C::Base, B>;
818 type Scalar = S::Scalar;
819
820 fn add(
821 &self,
822 layouter: &mut impl Layouter<F>,
823 p: &Self::Point,
824 q: &Self::Point,
825 ) -> Result<Self::Point, Error> {
826 if p == q {
827 return self.double(layouter, p);
828 }
829
830 if let Some(qv) = self.as_known_constant(q) {
832 return self.add_constant(layouter, p, qv);
833 }
834 if let Some(pv) = self.as_known_constant(p) {
835 return self.add_constant(layouter, q, pv);
836 }
837
838 let base_chip = self.base_field_chip();
852
853 let r_value = p.value().zip(q.value()).map(|(p, q)| p + q);
854 let r = self.assign_point_unchecked(layouter, r_value)?;
855
856 let px_qx = base_chip.mul(layouter, &p.x, &q.x, None)?;
857 let py_qy = base_chip.mul(layouter, &p.y, &q.y, None)?;
858 let px_qy = base_chip.mul(layouter, &p.x, &q.y, None)?;
859 let py_qx = base_chip.mul(layouter, &p.y, &q.x, None)?;
860 let neg_a_px_qx = base_chip.mul_by_constant(layouter, &px_qx, -C::A)?;
861 let d_px_py_qx_qy = base_chip.mul(layouter, &px_qx, &py_qy, Some(C::D))?;
862
863 addition::assert_addition_coordinate(
866 layouter,
867 &r.x,
868 &px_qy,
869 &py_qx,
870 &d_px_py_qx_qy,
871 false,
872 base_chip,
873 &self.config.addition_config,
874 )?;
875
876 addition::assert_addition_coordinate(
879 layouter,
880 &r.y,
881 &py_qy,
882 &neg_a_px_qx,
883 &d_px_py_qx_qy,
884 true,
885 base_chip,
886 &self.config.addition_config,
887 )?;
888
889 Ok(AssignedForeignEdwardsPoint {
890 point: r_value,
891 x: r.x,
892 y: r.y,
893 })
894 }
895
896 fn double(
897 &self,
898 layouter: &mut impl Layouter<F>,
899 p: &AssignedForeignEdwardsPoint<F, C, B>,
900 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
901 if let Some(pv) = self.as_known_constant(p) {
902 return self.assign_fixed(layouter, pv + pv);
903 }
904
905 let base_chip = self.base_field_chip();
923
924 let r_value = p.value().map(|p| p + p);
925 let r = self.assign_point_unchecked(layouter, r_value)?;
926
927 let px_sq = base_chip.mul(layouter, &p.x, &p.x, None)?;
928 let py_sq = base_chip.mul(layouter, &p.y, &p.y, None)?;
929 let px_py = base_chip.mul(layouter, &p.x, &p.y, None)?;
930
931 let neg_a_px_sq = base_chip.mul_by_constant(layouter, &px_sq, -C::A)?;
932
933 let w = base_chip.linear_combination(
935 layouter,
936 &[(C::A, px_sq), (C::Base::ONE, py_sq.clone())],
937 -C::Base::ONE,
938 )?;
939
940 addition::assert_addition_coordinate(
942 layouter,
943 &r.x,
944 &px_py,
945 &px_py,
946 &w,
947 false,
948 base_chip,
949 &self.config.addition_config,
950 )?;
951
952 addition::assert_addition_coordinate(
954 layouter,
955 &r.y,
956 &py_sq,
957 &neg_a_px_sq,
958 &w,
959 true,
960 base_chip,
961 &self.config.addition_config,
962 )?;
963
964 Ok(AssignedForeignEdwardsPoint {
965 point: r_value,
966 x: r.x,
967 y: r.y,
968 })
969 }
970
971 fn negate(
972 &self,
973 layouter: &mut impl Layouter<F>,
974 p: &Self::Point,
975 ) -> Result<Self::Point, Error> {
976 if let Some(pv) = self.as_known_constant(p) {
977 return self.assign_fixed(layouter, -pv);
978 }
979
980 let neg_x = self.base_field_chip().neg(layouter, &p.x)?;
982 let neg_x = self.base_field_chip().normalize(layouter, &neg_x)?;
983 Ok(AssignedForeignEdwardsPoint::<F, C, B> {
984 point: -p.point,
985 x: neg_x,
986 y: p.y.clone(),
987 })
988 }
989
990 fn msm(
991 &self,
992 layouter: &mut impl Layouter<F>,
993 scalars: &[Self::Scalar],
994 bases: &[Self::Point],
995 ) -> Result<Self::Point, Error> {
996 let scalars = scalars
997 .iter()
998 .map(|s| (s.clone(), C::ScalarField::NUM_BITS as usize))
999 .collect::<Vec<_>>();
1000 self.msm_by_bounded_scalars(layouter, &scalars, bases)
1001 }
1002
1003 fn msm_by_bounded_scalars(
1004 &self,
1005 layouter: &mut impl Layouter<F>,
1006 scalars: &[(S::Scalar, usize)],
1007 bases: &[AssignedForeignEdwardsPoint<F, C, B>],
1008 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1009 if scalars.len() != bases.len() {
1010 panic!("Number of scalars and points should be the same.")
1011 }
1012 let scalar_chip = self.scalar_field_chip();
1013
1014 let (scalars, bases, bases_with_1bit_scalar) =
1015 msm_preprocess(self, scalar_chip, layouter, scalars, bases)?;
1016
1017 const WS: usize = 4;
1019 let scalar_windows: Vec<Vec<AssignedNative<F>>> = scalars
1020 .iter()
1021 .map(|(s, num_bits)| {
1022 let bits = scalar_chip.assigned_to_le_bits(layouter, s, Some(*num_bits), true)?;
1023 bits.chunks(WS)
1024 .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
1025 .collect::<Result<Vec<_>, _>>()
1026 })
1027 .collect::<Result<Vec<_>, _>>()?;
1028
1029 let res = self.windowed_msm::<WS>(layouter, &scalar_windows, &bases)?;
1030
1031 add_1bit_scalar_bases(layouter, self, scalar_chip, &bases_with_1bit_scalar, res)
1033 }
1034
1035 fn mul_by_constant(
1036 &self,
1037 layouter: &mut impl Layouter<F>,
1038 scalar: C::ScalarField,
1039 base: &AssignedForeignEdwardsPoint<F, C, B>,
1040 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1041 if scalar == C::ScalarField::ZERO {
1042 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1043 } else if scalar == C::ScalarField::ONE {
1044 return Ok(base.clone());
1045 }
1046
1047 if let Some(base_val) = self.as_known_constant(base) {
1049 return self.assign_fixed(layouter, base_val * scalar);
1050 }
1051
1052 let scalar_bits = scalar.to_bits_le(None);
1053 let mut p = base.clone();
1054 let mut res = None;
1055
1056 for (i, b) in scalar_bits.iter().enumerate() {
1058 if *b {
1059 res = match res {
1060 None => Some(p.clone()),
1061 Some(acc) => Some(self.add(layouter, &acc, &p)?),
1062 }
1063 }
1064 if i + 1 < scalar_bits.len() {
1066 p = self.double(layouter, &p)?;
1067 }
1068 }
1069
1070 Ok(res.unwrap_or(self.assign_fixed(layouter, C::CryptographicGroup::identity())?))
1071 }
1072
1073 fn point_from_coordinates(
1074 &self,
1075 layouter: &mut impl Layouter<F>,
1076 x: &AssignedField<F, C::Base, B>,
1077 y: &AssignedField<F, C::Base, B>,
1078 ) -> Result<Self::Point, Error> {
1079 let p_value = x.value().zip(y.value()).map_with_result(|(x, y)| {
1080 C::from_xy(x, y)
1081 .map(|p| p.into_subgroup())
1082 .ok_or(Error::Synthesis("invalid coordinates".into()))
1083 })?;
1084
1085 let p = self.assign(layouter, p_value)?;
1086
1087 self.base_field_chip.assert_equal(layouter, x, &self.x_coordinate(&p))?;
1088 self.base_field_chip.assert_equal(layouter, y, &self.y_coordinate(&p))?;
1089
1090 Ok(p)
1091 }
1092
1093 fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
1094 point.x.clone()
1095 }
1096
1097 fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
1098 point.y.clone()
1099 }
1100
1101 fn base_field(&self) -> &impl DecompositionInstructions<F, Self::Coordinate> {
1102 self.base_field_chip()
1103 }
1104
1105 fn assign_without_subgroup_check(
1106 &self,
1107 layouter: &mut impl Layouter<F>,
1108 value: Value<C::CryptographicGroup>,
1109 ) -> Result<Self::Point, Error> {
1110 let p = self.assign_point_unchecked(layouter, value)?;
1111 self.assert_on_curve(layouter, &p.x, &p.y)?;
1112 Ok(p)
1113 }
1114}
1115
1116#[derive(Clone, Debug)]
1118struct PrecomputedTable<F, C, B, const WS: usize>
1119where
1120 F: CircuitField,
1121 C: EdwardsCurve,
1122 B: FieldEmulationParams<F, C::Base>,
1123{
1124 table: Vec<AssignedForeignEdwardsPoint<F, C, B>>,
1126}
1127
1128impl<F, C, B, S, N> ForeignEdwardsEccChip<F, C, B, S, N>
1129where
1130 F: CircuitField,
1131 C: EdwardsCurve,
1132 B: FieldEmulationParams<F, C::Base>,
1133 S: ScalarFieldInstructions<F>,
1134 S::Scalar: InnerValue<Element = C::ScalarField>,
1135 N: NativeInstructions<F>,
1136{
1137 fn precompute<const WS: usize>(
1142 &self,
1143 layouter: &mut impl Layouter<F>,
1144 base: &AssignedForeignEdwardsPoint<F, C, B>,
1145 ) -> Result<PrecomputedTable<F, C, B, WS>, Error> {
1146 let identity = self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
1147 let mut table = vec![identity, base.clone()];
1148 for i in 2..1 << WS {
1149 let entry = if i % 2 == 0 {
1150 self.double(layouter, &table[i / 2])?
1151 } else {
1152 self.add(layouter, &table[i - 1], base)?
1153 };
1154 table.push(entry);
1155 }
1156 Ok(PrecomputedTable { table })
1157 }
1158
1159 #[allow(clippy::type_complexity)]
1161 fn fill_dynamic_lookup_row(
1162 &self,
1163 layouter: &mut impl Layouter<F>,
1164 point: &AssignedForeignEdwardsPoint<F, C, B>,
1165 index: &AssignedNative<F>,
1166 table_tag: F,
1167 enable_lookup: bool,
1168 ) -> Result<(Vec<AssignedNative<F>>, Vec<AssignedNative<F>>), Error> {
1169 fill_dynamic_lookup_row(
1170 layouter,
1171 &point.x.limb_values(),
1172 &point.y.limb_values(),
1173 index,
1174 &self.config.base_field_config.x_cols,
1175 &self.config.base_field_config.z_cols, self.config.idx_col_multi_select,
1177 self.config.tag_col_multi_select,
1178 self.config.q_multi_select,
1179 table_tag,
1180 enable_lookup,
1181 )
1182 }
1183
1184 fn load_multi_select_table(
1187 &self,
1188 layouter: &mut impl Layouter<F>,
1189 point_table: &[AssignedForeignEdwardsPoint<F, C, B>],
1190 table_tag: F,
1191 ) -> Result<(), Error> {
1192 for (i, point) in point_table.iter().enumerate() {
1193 let index = self.native_gadget.assign_fixed(layouter, F::from(i as u64))?;
1194 self.fill_dynamic_lookup_row(layouter, point, &index, table_tag, false)?;
1195 }
1196 Ok(())
1197 }
1198
1199 fn multi_select(
1204 &self,
1205 layouter: &mut impl Layouter<F>,
1206 selector: &AssignedNative<F>,
1207 point_table: &[AssignedForeignEdwardsPoint<F, C, B>],
1208 table_tag: F,
1209 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1210 let mut selector_idx = 0usize;
1211 selector.value().map(|v| {
1212 let digits = v.to_biguint().to_u32_digits();
1213 let digit = if digits.is_empty() { 0 } else { digits[0] };
1214 debug_assert!(digits.len() <= 1);
1215 debug_assert!((digit as usize) < point_table.len());
1216 selector_idx = digit as usize;
1217 });
1218
1219 let selected = point_table[selector_idx].clone();
1220
1221 let (xs, ys) =
1222 self.fill_dynamic_lookup_row(layouter, &selected, selector, table_tag, true)?;
1223 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1224 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1225
1226 Ok(AssignedForeignEdwardsPoint::<F, C, B> {
1227 point: selected.point,
1228 x,
1229 y,
1230 })
1231 }
1232
1233 fn windowed_msm<const WS: usize>(
1239 &self,
1240 layouter: &mut impl Layouter<F>,
1241 scalars: &[Vec<AssignedNative<F>>],
1242 bases: &[AssignedForeignEdwardsPoint<F, C, B>],
1243 ) -> Result<AssignedForeignEdwardsPoint<F, C, B>, Error> {
1244 assert_eq!(scalars.len(), bases.len());
1245
1246 if bases.is_empty() {
1247 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1248 }
1249
1250 let num_windows: Vec<usize> = scalars.iter().map(|s| s.len()).collect();
1252 let max_num_windows = *num_windows.iter().max().unwrap();
1253
1254 let tag_cnt = *self.tag_cnt.borrow();
1256 self.tag_cnt.replace(tag_cnt + bases.len() as u64);
1257 debug_assert!(F::NUM_BITS > 64);
1258
1259 let mut tables = vec![];
1260 for (i, base) in bases.iter().enumerate() {
1261 let table = self.precompute::<WS>(layouter, base)?;
1262 self.load_multi_select_table(layouter, &table.table, F::from(tag_cnt + i as u64))?;
1263 tables.push(table);
1264 }
1265
1266 let mut res = self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
1267 for w in (0..max_num_windows).rev() {
1268 if w < max_num_windows - 1 {
1270 for _ in 0..WS {
1271 res = self.double(layouter, &res)?;
1272 }
1273 }
1274 for (i, (windows, nw)) in scalars.iter().zip(&num_windows).enumerate() {
1275 if w >= *nw {
1277 continue;
1278 }
1279 let addend = self.multi_select(
1280 layouter,
1281 &windows[w],
1282 &tables[i].table,
1283 F::from(tag_cnt + i as u64),
1284 )?;
1285 res = self.add(layouter, &res, &addend)?;
1286 }
1287 }
1288
1289 Ok(res)
1290 }
1291}
1292
1293#[derive(Clone, Debug)]
1294#[cfg(any(test, feature = "testing"))]
1295pub struct ForeignEdwardsEccTestConfig<F, C, S, N>
1298where
1299 F: CircuitField,
1300 C: EdwardsCurve,
1301 S: ScalarFieldInstructions<F> + FromScratch<F>,
1302 S::Scalar: InnerValue<Element = C::ScalarField>,
1303 N: NativeInstructions<F> + FromScratch<F>,
1304{
1305 native_gadget_config: <N as FromScratch<F>>::Config,
1306 scalar_field_config: <S as FromScratch<F>>::Config,
1307 ff_ecc_config: ForeignEdwardsEccConfig<C>,
1308}
1309
1310#[cfg(any(test, feature = "testing"))]
1311impl<F, C, B, S, N> FromScratch<F> for ForeignEdwardsEccChip<F, C, B, S, N>
1312where
1313 F: CircuitField,
1314 C: EdwardsCurve,
1315 C::Base: Legendre,
1316 B: FieldEmulationParams<F, C::Base>,
1317 S: ScalarFieldInstructions<F> + FromScratch<F>,
1318 S::Scalar: InnerValue<Element = C::ScalarField>,
1319 N: NativeInstructions<F> + FromScratch<F>,
1320{
1321 type Config = ForeignEdwardsEccTestConfig<F, C, S, N>;
1322
1323 fn new_from_scratch(config: &Self::Config) -> Self {
1324 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
1325 let scalar_field_chip =
1326 <S as FromScratch<F>>::new_from_scratch(&config.scalar_field_config);
1327 ForeignEdwardsEccChip::new(&config.ff_ecc_config, &native_gadget, &scalar_field_chip)
1328 }
1329
1330 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1331 self.native_gadget.load_from_scratch(layouter)?;
1332 self.scalar_field_chip.load_from_scratch(layouter)
1333 }
1334
1335 fn configure_from_scratch(
1336 meta: &mut ConstraintSystem<F>,
1337 advice_columns: &mut Vec<Column<Advice>>,
1338 fixed_columns: &mut Vec<Column<Fixed>>,
1339 instance_columns: &[Column<Instance>; 2],
1340 ) -> ForeignEdwardsEccTestConfig<F, C, S, N> {
1341 use crate::field::foreign::nb_field_chip_columns;
1342
1343 let native_gadget_config = <N as FromScratch<F>>::configure_from_scratch(
1344 meta,
1345 advice_columns,
1346 fixed_columns,
1347 instance_columns,
1348 );
1349 let scalar_field_config = <S as FromScratch<F>>::configure_from_scratch(
1350 meta,
1351 advice_columns,
1352 fixed_columns,
1353 instance_columns,
1354 );
1355 let nb_advice_cols = nb_field_chip_columns::<F, C::Base, B>();
1356 while advice_columns.len() < nb_advice_cols {
1357 advice_columns.push(meta.advice_column());
1358 }
1359 let nb_parallel_range_checks = 4;
1360 let max_bit_len = 8;
1361 let base_field_config = FieldChip::<F, C::Base, B, N>::configure(
1362 meta,
1363 &advice_columns[..nb_advice_cols],
1364 nb_parallel_range_checks,
1365 max_bit_len,
1366 );
1367 let ff_ecc_config = ForeignEdwardsEccChip::<F, C, B, S, N>::configure(
1368 meta,
1369 &base_field_config,
1370 advice_columns,
1371 fixed_columns,
1372 nb_parallel_range_checks,
1373 max_bit_len,
1374 );
1375 ForeignEdwardsEccTestConfig {
1376 native_gadget_config,
1377 scalar_field_config,
1378 ff_ecc_config,
1379 }
1380 }
1381}
1382
1383#[cfg(test)]
1384mod tests {
1385 use ff::Field;
1386 use group::{Group, GroupEncoding};
1387 use midnight_curves::{curve25519::Curve25519, BlsScalar};
1388 use midnight_proofs::{circuit::SimpleFloorPlanner, dev::MockProver, plonk::Circuit};
1389 use rand::SeedableRng;
1390 use rand_chacha::ChaCha8Rng;
1391
1392 use super::*;
1393 use crate::{
1394 ecc::curves::CircuitCurve,
1395 field::{
1396 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
1397 NativeChip, NativeGadget,
1398 },
1399 instructions::{assertions, control_flow, ecc, equality, public_input, zero},
1400 };
1401
1402 type F = BlsScalar;
1403 type Native<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
1404 type EmulatedField<F, C> = FieldChip<F, <C as Group>::Scalar, MultiEmulationParams, Native<F>>;
1405
1406 macro_rules! test_generic {
1407 ($mod:ident, $op:ident, $native:ty, $curve:ty, $scalar_field:ty,
1408 $name:expr) => {
1409 $mod::tests::$op::<
1410 $native,
1411 AssignedForeignEdwardsPoint<$native, $curve, MultiEmulationParams>,
1412 ForeignEdwardsEccChip<
1413 $native,
1414 $curve,
1415 MultiEmulationParams,
1416 $scalar_field,
1417 Native<$native>,
1418 >,
1419 >($name);
1420 };
1421 }
1422
1423 macro_rules! test {
1424 ($mod:ident, $op:ident) => {
1425 #[test]
1426 fn $op() {
1427 test_generic!($mod, $op, F, Curve25519, EmulatedField<F, Curve25519>, "emulated_curve25519");
1428 }
1429 };
1430 }
1431
1432 test!(assertions, test_assertions);
1433
1434 test!(public_input, test_public_inputs);
1435
1436 test!(equality, test_is_equal);
1437
1438 test!(zero, test_zero_assertions);
1439 test!(zero, test_is_zero);
1440
1441 test!(control_flow, test_select);
1442 test!(control_flow, test_cond_assert_equal);
1443 test!(control_flow, test_cond_swap);
1444
1445 macro_rules! ecc_test {
1446 ($op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
1447 ecc::tests::$op::<
1448 $native,
1449 $curve,
1450 ForeignEdwardsEccChip<
1451 $native,
1452 $curve,
1453 MultiEmulationParams,
1454 $scalar_field,
1455 Native<$native>,
1456 >,
1457 >($name);
1458 };
1459 }
1460
1461 macro_rules! ecc_tests {
1462 ($op:ident) => {
1463 #[test]
1464 fn $op() {
1465 ecc_test!($op, BlsScalar, Curve25519, EmulatedField<BlsScalar, Curve25519>, "emulated_curve25519");
1466 }
1467 };
1468 }
1469
1470 ecc_tests!(test_assign);
1471 ecc_tests!(test_assign_without_subgroup_check);
1472 ecc_tests!(test_add);
1473 ecc_tests!(test_double);
1474 ecc_tests!(test_negate);
1475 ecc_tests!(test_msm);
1476 ecc_tests!(test_msm_by_bounded_scalars);
1477 ecc_tests!(test_mul_by_constant);
1478 ecc_tests!(test_coordinates);
1479
1480 #[test]
1481 fn test_assert_on_curve() {
1482 run_test_assert_on_curve::<Curve25519>();
1483 }
1484
1485 fn run_test_assert_on_curve<C>()
1488 where
1489 C: EdwardsCurve,
1490 C::Base: Legendre,
1491 MultiEmulationParams: FieldEmulationParams<BlsScalar, C::Base>
1492 + FieldEmulationParams<BlsScalar, C::ScalarField>,
1493 {
1494 fn assert_not_on_curve<C: EdwardsCurve>(x: C::Base, y: C::Base)
1495 where
1496 C::Base: Legendre,
1497 MultiEmulationParams: FieldEmulationParams<BlsScalar, C::Base>
1498 + FieldEmulationParams<BlsScalar, C::ScalarField>,
1499 {
1500 let circuit = OnCurveCheckCircuit::<C> { x, y };
1501 let prover = MockProver::run(&circuit, vec![vec![], vec![]])
1502 .expect("proof generation should not fail");
1503 assert!(prover.verify().is_err());
1504 }
1505
1506 let mut rng = ChaCha8Rng::seed_from_u64(0x0);
1507
1508 let point = C::CryptographicGroup::random(&mut rng);
1510 let (x, y) = point.into().coordinates().expect("valid curve point");
1511
1512 assert_not_on_curve::<C>(x, y + C::Base::ONE);
1513 assert_not_on_curve::<C>(C::Base::ONE, C::Base::ONE);
1514 assert_not_on_curve::<C>(C::Base::ZERO, C::Base::ZERO);
1515 }
1516
1517 type EdwardsChip<C> = ForeignEdwardsEccChip<
1518 F,
1519 C,
1520 MultiEmulationParams,
1521 FieldChip<F, <C as CircuitCurve>::ScalarField, MultiEmulationParams, Native<F>>,
1522 Native<F>,
1523 >;
1524
1525 #[derive(Clone, Debug)]
1531 struct OnCurveCheckCircuit<C: EdwardsCurve> {
1532 x: C::Base,
1533 y: C::Base,
1534 }
1535
1536 impl<C> Circuit<F> for OnCurveCheckCircuit<C>
1537 where
1538 C: EdwardsCurve,
1539 C::Base: Legendre,
1540 MultiEmulationParams:
1541 FieldEmulationParams<F, C::Base> + FieldEmulationParams<F, C::ScalarField>,
1542 {
1543 type Config = <EdwardsChip<C> as FromScratch<F>>::Config;
1544 type FloorPlanner = SimpleFloorPlanner;
1545 type Params = ();
1546
1547 fn without_witnesses(&self) -> Self {
1548 unreachable!()
1549 }
1550
1551 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
1552 let committed = meta.instance_column();
1553 let instance = meta.instance_column();
1554 EdwardsChip::<C>::configure_from_scratch(
1555 meta,
1556 &mut vec![],
1557 &mut vec![],
1558 &[committed, instance],
1559 )
1560 }
1561
1562 fn synthesize(
1563 &self,
1564 config: Self::Config,
1565 mut layouter: impl Layouter<F>,
1566 ) -> Result<(), Error> {
1567 let chip = EdwardsChip::<C>::new_from_scratch(&config);
1568
1569 let x = chip.base_field_chip().assign(&mut layouter, Value::known(self.x))?;
1570 let y = chip.base_field_chip().assign(&mut layouter, Value::known(self.y))?;
1571
1572 chip.assert_on_curve(&mut layouter, &x, &y)?;
1573 chip.load_from_scratch(&mut layouter)
1574 }
1575 }
1576
1577 #[derive(Clone, Debug)]
1583 struct FromCompressedBytesCheckCircuit {
1584 point: Curve25519Subgroup,
1585 bytes: [u8; 32],
1586 }
1587
1588 impl Circuit<F> for FromCompressedBytesCheckCircuit {
1589 type Config = <EdwardsChip<Curve25519> as FromScratch<F>>::Config;
1590 type FloorPlanner = SimpleFloorPlanner;
1591 type Params = ();
1592
1593 fn without_witnesses(&self) -> Self {
1594 unreachable!()
1595 }
1596
1597 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
1598 let committed = meta.instance_column();
1599 let instance = meta.instance_column();
1600 EdwardsChip::<Curve25519>::configure_from_scratch(
1601 meta,
1602 &mut vec![],
1603 &mut vec![],
1604 &[committed, instance],
1605 )
1606 }
1607
1608 fn synthesize(
1609 &self,
1610 config: Self::Config,
1611 mut layouter: impl Layouter<F>,
1612 ) -> Result<(), Error> {
1613 let chip = EdwardsChip::<Curve25519>::new_from_scratch(&config);
1614
1615 let byte_cells: [AssignedByte<F>; 32] = self
1616 .bytes
1617 .iter()
1618 .map(|b| chip.native_gadget.assign(&mut layouter, Value::known(*b)))
1619 .collect::<Result<Vec<_>, _>>()?
1620 .try_into()
1621 .expect("exactly 32 bytes");
1622
1623 let _ = chip.from_canonical_compressed_bytes(
1624 &mut layouter,
1625 &byte_cells,
1626 Value::known(self.point),
1627 )?;
1628
1629 chip.load_from_scratch(&mut layouter)
1630 }
1631 }
1632
1633 fn run_test_compressed_bytes(point: Curve25519Subgroup, bytes: [u8; 32], should_accept: bool) {
1634 let circuit = FromCompressedBytesCheckCircuit { point, bytes };
1635 let prover = MockProver::run(&circuit, vec![vec![], vec![]])
1636 .expect("proof generation should not fail");
1637 assert_eq!(prover.verify().is_ok(), should_accept);
1638 }
1639
1640 #[test]
1641 fn test_compressed_bytes() {
1642 let mut canonical = [0; 32];
1644 canonical[0] = 1;
1645 run_test_compressed_bytes(Curve25519Subgroup::identity(), canonical, true);
1646
1647 let mut non_canonical = [0xff_u8; 32];
1649 non_canonical[0] = 0xee;
1650 non_canonical[31] = 0x7f;
1651 run_test_compressed_bytes(Curve25519Subgroup::identity(), non_canonical, false);
1652
1653 let mut non_canonical_with_sign = canonical;
1655 non_canonical_with_sign[31] = 0x80;
1656 run_test_compressed_bytes(
1657 Curve25519Subgroup::identity(),
1658 non_canonical_with_sign,
1659 false,
1660 );
1661
1662 let g = Curve25519Subgroup::generator();
1664 run_test_compressed_bytes(g, Curve25519::from(g).to_bytes(), true);
1665
1666 let mut rng = ChaCha8Rng::seed_from_u64(0x7374727564656C);
1668 let p = Curve25519Subgroup::random(&mut rng);
1669 run_test_compressed_bytes(p, Curve25519::from(p).to_bytes(), true);
1670 }
1671}