1use std::{
22 cell::RefCell,
23 cmp::max,
24 collections::HashMap,
25 fmt::Debug,
26 hash::{Hash, Hasher},
27 rc::Rc,
28};
29
30use ff::{Field, PrimeField};
31use group::Group;
32use midnight_proofs::{
33 circuit::{Chip, Layouter, Value},
34 plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Selector},
35};
36use num_bigint::BigUint;
37use num_traits::One;
38use rand::rngs::OsRng;
39#[cfg(any(test, feature = "testing"))]
40use {
41 crate::testing_utils::Sampleable, crate::utils::util::FromScratch,
42 midnight_proofs::plonk::Instance, rand::RngCore,
43};
44
45use super::gates::weierstrass::{
46 lambda_squared,
47 lambda_squared::LambdaSquaredConfig,
48 on_curve,
49 on_curve::OnCurveConfig,
50 slope::{self, SlopeConfig},
51 tangent,
52 tangent::TangentConfig,
53};
54use crate::{
55 ecc::{
56 curves::WeierstrassCurve,
57 foreign::common::{
58 add_1bit_scalar_bases, configure_multi_select_lookup, fill_dynamic_lookup_row,
59 msm_preprocess,
60 },
61 },
62 field::foreign::{
63 field_chip::{FieldChip, FieldChipConfig},
64 params::FieldEmulationParams,
65 },
66 instructions::{
67 ArithInstructions, AssertionInstructions, AssignmentInstructions, ControlFlowInstructions,
68 DecompositionInstructions, EccInstructions, EqualityInstructions, NativeInstructions,
69 PublicInputInstructions, ScalarFieldInstructions, ZeroInstructions,
70 },
71 types::{AssignedBit, AssignedField, AssignedNative, InnerConstants, InnerValue, Instantiable},
72 utils::util::{big_to_fe, bigint_to_fe, glv_scalar_decomposition},
73 CircuitField,
74};
75
76#[derive(Clone, Debug)]
78pub struct ForeignWeierstrassEccConfig<C>
79where
80 C: WeierstrassCurve,
81{
82 base_field_config: FieldChipConfig,
83 on_curve_config: on_curve::OnCurveConfig<C>,
84 slope_config: slope::SlopeConfig<C>,
85 tangent_config: tangent::TangentConfig<C>,
86 lambda_squared_config: lambda_squared::LambdaSquaredConfig<C>,
87 q_multi_select: Selector,
89 idx_col_multi_select: Column<Advice>,
90 tag_col_multi_select: Column<Fixed>,
91}
92
93pub fn nb_foreign_ecc_chip_columns<F, C, B, S>() -> usize
95where
96 F: CircuitField,
97 C: WeierstrassCurve,
98 B: FieldEmulationParams<F, C::Base>,
99{
100 B::NB_LIMBS as usize + max(B::NB_LIMBS as usize, 2 + B::moduli().len()) + 1
106}
107
108#[derive(Clone, Debug)]
127struct MsmRandomness<F, C, B>
128where
129 F: CircuitField,
130 C: WeierstrassCurve,
131 B: FieldEmulationParams<F, C::Base>,
132{
133 r: AssignedForeignPoint<F, C, B>,
134 neg_alpha: AssignedForeignPoint<F, C, B>,
135}
136
137type MsmRandomnessMap<F, C, B> = HashMap<usize, MsmRandomness<F, C, B>>;
140
141#[derive(Clone, Debug)]
143pub struct ForeignWeierstrassEccChip<F, C, B, S, N>
144where
145 F: CircuitField,
146 C: WeierstrassCurve,
147 B: FieldEmulationParams<F, C::Base>,
148 S: ScalarFieldInstructions<F>,
149 S::Scalar: InnerValue<Element = C::ScalarField>,
150 N: NativeInstructions<F>,
151{
152 config: ForeignWeierstrassEccConfig<C>,
153 native_gadget: N,
154 base_field_chip: FieldChip<F, C::Base, B, N>,
155 scalar_field_chip: S,
156 tag_cnt: Rc<RefCell<u64>>,
161 msm_randomness: Rc<RefCell<MsmRandomnessMap<F, C, B>>>,
169 random_point: C::CryptographicGroup,
173}
174
175#[derive(Clone, Debug)]
184#[must_use]
185pub struct AssignedForeignPoint<F, C, B>
186where
187 F: CircuitField,
188 C: WeierstrassCurve,
189 B: FieldEmulationParams<F, C::Base>,
190{
191 point: Value<C::CryptographicGroup>,
192 is_id: AssignedBit<F>,
193 x: AssignedField<F, C::Base, B>,
194 y: AssignedField<F, C::Base, B>,
195}
196
197impl<F, C, B> PartialEq for AssignedForeignPoint<F, C, B>
198where
199 F: CircuitField,
200 C: WeierstrassCurve,
201 B: FieldEmulationParams<F, C::Base>,
202{
203 fn eq(&self, other: &Self) -> bool {
204 self.is_id == other.is_id && self.x == other.x && self.y == other.y
205 }
206}
207
208impl<F, C, B> Eq for AssignedForeignPoint<F, C, B>
209where
210 F: CircuitField,
211 C: WeierstrassCurve,
212 B: FieldEmulationParams<F, C::Base>,
213{
214}
215
216impl<F, C, B> Hash for AssignedForeignPoint<F, C, B>
217where
218 F: CircuitField,
219 C: WeierstrassCurve,
220 B: FieldEmulationParams<F, C::Base>,
221{
222 fn hash<H: Hasher>(&self, state: &mut H) {
223 self.is_id.hash(state);
224 self.x.hash(state);
225 self.y.hash(state);
226 }
227}
228
229impl<F, C, B> Instantiable<F> for AssignedForeignPoint<F, C, B>
230where
231 F: CircuitField,
232 C: WeierstrassCurve,
233 B: FieldEmulationParams<F, C::Base>,
234{
235 fn as_public_input(p: &C::CryptographicGroup) -> Vec<F> {
236 let (x, y) = (*p).into().coordinates().unwrap_or((C::Base::ZERO, C::Base::ZERO));
237 let mut pis = [
238 AssignedField::<F, C::Base, B>::as_public_input(&x).as_slice(),
239 AssignedField::<F, C::Base, B>::as_public_input(&y).as_slice(),
240 ]
241 .concat();
242
243 let is_id: bool = p.is_identity().into();
244 pis.push(F::from(is_id as u64));
245
246 pis
247 }
248
249 fn from_public_input(fields: &[F]) -> Option<C::CryptographicGroup> {
250 if *fields.last()? == F::ONE {
251 return Some(C::CryptographicGroup::identity());
252 }
253 let nb_limbs_per_batch = (F::CAPACITY / B::LOG2_BASE) as usize;
254 let nb_pi_per_coord = B::NB_LIMBS.div_ceil(nb_limbs_per_batch as u32) as usize;
255 if fields.len() != 2 * nb_pi_per_coord + 1 {
256 return None;
257 }
258 let x = AssignedField::<F, C::Base, B>::from_public_input(&fields[..nb_pi_per_coord])?;
259 let y = AssignedField::<F, C::Base, B>::from_public_input(
260 &fields[nb_pi_per_coord..nb_pi_per_coord * 2],
261 )?;
262 C::from_xy(x, y).map(|p| p.into_subgroup())
263 }
264}
265
266impl<F, C, B> InnerValue for AssignedForeignPoint<F, C, B>
267where
268 F: CircuitField,
269 C: WeierstrassCurve,
270 B: FieldEmulationParams<F, C::Base>,
271{
272 type Element = C::CryptographicGroup;
273
274 fn value(&self) -> Value<Self::Element> {
275 self.point
276 }
277}
278
279impl<F, C, B> InnerConstants for AssignedForeignPoint<F, C, B>
280where
281 F: CircuitField,
282 C: WeierstrassCurve,
283 B: FieldEmulationParams<F, C::Base>,
284{
285 fn inner_zero() -> C::CryptographicGroup {
286 C::CryptographicGroup::identity()
287 }
288
289 fn inner_one() -> Self::Element {
290 C::CryptographicGroup::generator()
291 }
292}
293
294#[cfg(any(test, feature = "testing"))]
295impl<F, C, B> Sampleable for AssignedForeignPoint<F, C, B>
296where
297 F: CircuitField,
298 C: WeierstrassCurve,
299 B: FieldEmulationParams<F, C::Base>,
300{
301 fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
302 C::CryptographicGroup::random(rng)
303 }
304}
305
306impl<F, C, B, S, N> Chip<F> for ForeignWeierstrassEccChip<F, C, B, S, N>
307where
308 F: CircuitField,
309 C: WeierstrassCurve,
310 B: FieldEmulationParams<F, C::Base>,
311 S: ScalarFieldInstructions<F>,
312 S::Scalar: InnerValue<Element = C::ScalarField>,
313 N: NativeInstructions<F>,
314{
315 type Config = ForeignWeierstrassEccConfig<C>;
316 type Loaded = ();
317 fn config(&self) -> &Self::Config {
318 &self.config
319 }
320 fn loaded(&self) -> &Self::Loaded {
321 &()
322 }
323}
324
325impl<F, C, B, S, N> AssignmentInstructions<F, AssignedForeignPoint<F, C, B>>
326 for ForeignWeierstrassEccChip<F, C, B, S, N>
327where
328 F: CircuitField,
329 C: WeierstrassCurve,
330 B: FieldEmulationParams<F, C::Base>,
331 S: ScalarFieldInstructions<F>,
332 S::Scalar: InnerValue<Element = C::ScalarField>,
333 N: NativeInstructions<F>,
334{
335 fn assign(
341 &self,
342 layouter: &mut impl Layouter<F>,
343 value: Value<C::CryptographicGroup>,
344 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
345 if C::COFACTOR > 1 {
346 let cofactor = C::ScalarField::from_u128(C::COFACTOR);
347 let cofactor_root = self.assign_without_subgroup_check(
350 layouter,
351 value.map(|point| point * cofactor.invert().unwrap()),
352 )?;
353 self.mul_by_constant(layouter, cofactor, &cofactor_root)
354 } else {
355 self.assign_without_subgroup_check(layouter, value)
356 }
357 }
358
359 fn assign_fixed(
360 &self,
361 layouter: &mut impl Layouter<F>,
362 constant: C::CryptographicGroup,
363 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
364 let (xv, yv, is_id_value) = if C::CryptographicGroup::is_identity(&constant).into() {
365 (C::Base::ZERO, C::Base::ZERO, true)
366 } else {
367 let coordinates = constant
368 .into()
369 .coordinates()
370 .expect("assign_point_unchecked: invalid point given");
371 (coordinates.0, coordinates.1, false)
372 };
373 let is_id = self.native_gadget.assign_fixed(layouter, is_id_value)?;
374 let x = self.base_field_chip().assign_fixed(layouter, xv)?;
375 let y = self.base_field_chip().assign_fixed(layouter, yv)?;
376 let p = AssignedForeignPoint::<F, C, B> {
377 point: Value::known(constant),
378 is_id,
379 x,
380 y,
381 };
382 Ok(p)
383 }
384}
385
386impl<F, C, B, S, N> PublicInputInstructions<F, AssignedForeignPoint<F, C, B>>
387 for ForeignWeierstrassEccChip<F, C, B, S, N>
388where
389 F: CircuitField,
390 C: WeierstrassCurve,
391 B: FieldEmulationParams<F, C::Base>,
392 S: ScalarFieldInstructions<F>,
393 S::Scalar: InnerValue<Element = C::ScalarField>,
394 N: NativeInstructions<F> + PublicInputInstructions<F, AssignedBit<F>>,
395{
396 fn as_public_input(
397 &self,
398 layouter: &mut impl Layouter<F>,
399 p: &AssignedForeignPoint<F, C, B>,
400 ) -> Result<Vec<AssignedNative<F>>, Error> {
401 let mut pis = [
402 self.base_field_chip.as_public_input(layouter, &p.x)?.as_slice(),
403 self.base_field_chip.as_public_input(layouter, &p.y)?.as_slice(),
404 ]
405 .concat();
406
407 pis.push(p.is_id.clone().into());
408
409 Ok(pis)
410 }
411
412 fn constrain_as_public_input(
413 &self,
414 layouter: &mut impl Layouter<F>,
415 assigned: &AssignedForeignPoint<F, C, B>,
416 ) -> Result<(), Error> {
417 self.as_public_input(layouter, assigned)?
418 .iter()
419 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
420 }
421
422 fn assign_as_public_input(
423 &self,
424 layouter: &mut impl Layouter<F>,
425 value: Value<C::CryptographicGroup>,
426 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
427 let point = self.assign_without_subgroup_check(layouter, value)?;
431 self.constrain_as_public_input(layouter, &point)?;
432 Ok(point)
433 }
434}
435
436impl<F, C, B, S, N> AssignmentInstructions<F, AssignedNative<F>>
442 for ForeignWeierstrassEccChip<F, C, B, S, N>
443where
444 F: CircuitField,
445 C: WeierstrassCurve,
446 B: FieldEmulationParams<F, C::Base>,
447 S: ScalarFieldInstructions<F, Scalar = AssignedNative<F>>,
448 S::Scalar: InnerValue<Element = C::ScalarField>,
449 N: NativeInstructions<F>,
450{
451 fn assign(
452 &self,
453 layouter: &mut impl Layouter<F>,
454 value: Value<<S::Scalar as InnerValue>::Element>,
455 ) -> Result<S::Scalar, Error> {
456 self.scalar_field_chip().assign(layouter, value)
457 }
458
459 fn assign_fixed(
460 &self,
461 layouter: &mut impl Layouter<F>,
462 constant: <S::Scalar as InnerValue>::Element,
463 ) -> Result<S::Scalar, Error> {
464 self.scalar_field_chip().assign_fixed(layouter, constant)
465 }
466}
467
468impl<F, C, B, S, SP, N> AssignmentInstructions<F, AssignedField<F, C::ScalarField, SP>>
473 for ForeignWeierstrassEccChip<F, C, B, S, N>
474where
475 F: CircuitField,
476 C: WeierstrassCurve,
477 B: FieldEmulationParams<F, C::Base>,
478 S: ScalarFieldInstructions<F, Scalar = AssignedField<F, C::ScalarField, SP>>,
479 S::Scalar: InnerValue<Element = C::ScalarField>,
480 SP: FieldEmulationParams<F, C::ScalarField>,
481 N: NativeInstructions<F>,
482{
483 fn assign(
484 &self,
485 layouter: &mut impl Layouter<F>,
486 value: Value<<S::Scalar as InnerValue>::Element>,
487 ) -> Result<S::Scalar, Error> {
488 self.scalar_field_chip().assign(layouter, value)
489 }
490
491 fn assign_fixed(
492 &self,
493 layouter: &mut impl Layouter<F>,
494 constant: <S::Scalar as InnerValue>::Element,
495 ) -> Result<S::Scalar, Error> {
496 self.scalar_field_chip().assign_fixed(layouter, constant)
497 }
498}
499
500impl<F, C, B, S, N> AssertionInstructions<F, AssignedForeignPoint<F, C, B>>
501 for ForeignWeierstrassEccChip<F, C, B, S, N>
502where
503 F: CircuitField,
504 C: WeierstrassCurve,
505 B: FieldEmulationParams<F, C::Base>,
506 S: ScalarFieldInstructions<F>,
507 S::Scalar: InnerValue<Element = C::ScalarField>,
508 N: NativeInstructions<F>,
509{
510 fn assert_equal(
511 &self,
512 layouter: &mut impl Layouter<F>,
513 p: &AssignedForeignPoint<F, C, B>,
514 q: &AssignedForeignPoint<F, C, B>,
515 ) -> Result<(), Error> {
516 self.native_gadget.assert_equal(layouter, &p.is_id, &q.is_id)?;
521 self.base_field_chip().assert_equal(layouter, &p.x, &q.x)?;
522 self.base_field_chip().assert_equal(layouter, &p.y, &q.y)
523 }
524
525 fn assert_not_equal(
526 &self,
527 layouter: &mut impl Layouter<F>,
528 p: &AssignedForeignPoint<F, C, B>,
529 q: &AssignedForeignPoint<F, C, B>,
530 ) -> Result<(), Error> {
531 let equal = self.is_equal(layouter, p, q)?;
532 self.native_gadget.assert_equal_to_fixed(layouter, &equal, false)
533 }
534
535 fn assert_equal_to_fixed(
536 &self,
537 layouter: &mut impl Layouter<F>,
538 p: &AssignedForeignPoint<F, C, B>,
539 constant: C::CryptographicGroup,
540 ) -> Result<(), Error> {
541 if constant.is_identity().into() {
542 self.assert_zero(layouter, p)
543 } else {
544 let coordinates = constant.into().coordinates().expect("Valid point");
545 self.base_field_chip().assert_equal_to_fixed(layouter, &p.x, coordinates.0)?;
546 self.base_field_chip().assert_equal_to_fixed(layouter, &p.y, coordinates.1)?;
547 self.assert_non_zero(layouter, p)
548 }
549 }
550
551 fn assert_not_equal_to_fixed(
552 &self,
553 layouter: &mut impl Layouter<F>,
554 p: &AssignedForeignPoint<F, C, B>,
555 constant: C::CryptographicGroup,
556 ) -> Result<(), Error> {
557 if constant.is_identity().into() {
558 self.assert_non_zero(layouter, p)
559 } else {
560 let equal = self.is_equal_to_fixed(layouter, p, constant)?;
561 self.native_gadget.assert_equal_to_fixed(layouter, &equal, false)
562 }
563 }
564}
565
566impl<F, C, B, S, N> EqualityInstructions<F, AssignedForeignPoint<F, C, B>>
567 for ForeignWeierstrassEccChip<F, C, B, S, N>
568where
569 F: CircuitField,
570 C: WeierstrassCurve,
571 B: FieldEmulationParams<F, C::Base>,
572 S: ScalarFieldInstructions<F>,
573 S::Scalar: InnerValue<Element = C::ScalarField>,
574 N: NativeInstructions<F>,
575{
576 fn is_equal(
577 &self,
578 layouter: &mut impl Layouter<F>,
579 p: &AssignedForeignPoint<F, C, B>,
580 q: &AssignedForeignPoint<F, C, B>,
581 ) -> Result<AssignedBit<F>, Error> {
582 let eq_coordinates = {
585 let eq_x = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
586 let eq_y = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
587 let eq_x_and_y = self.native_gadget.and(layouter, &[eq_x, eq_y])?;
588 let both_are_id =
589 self.native_gadget.and(layouter, &[p.is_id.clone(), q.is_id.clone()])?;
590 self.native_gadget.or(layouter, &[eq_x_and_y, both_are_id])?
591 };
592 let eq_id_flag = self.native_gadget.is_equal(layouter, &p.is_id, &q.is_id)?;
593 self.native_gadget.and(layouter, &[eq_id_flag, eq_coordinates])
594 }
595
596 fn is_not_equal(
597 &self,
598 layouter: &mut impl Layouter<F>,
599 x: &AssignedForeignPoint<F, C, B>,
600 y: &AssignedForeignPoint<F, C, B>,
601 ) -> Result<AssignedBit<F>, Error> {
602 let b = self.is_equal(layouter, x, y)?;
603 self.native_gadget.not(layouter, &b)
604 }
605
606 fn is_equal_to_fixed(
607 &self,
608 layouter: &mut impl Layouter<F>,
609 p: &AssignedForeignPoint<F, C, B>,
610 constant: C::CryptographicGroup,
611 ) -> Result<AssignedBit<F>, Error> {
612 if constant.is_identity().into() {
613 Ok(p.is_id.clone())
614 } else {
615 let coordinates = constant.into().coordinates().expect("Valid point");
616 let eq_x = self.base_field_chip().is_equal_to_fixed(layouter, &p.x, coordinates.0)?;
617 let eq_y = self.base_field_chip().is_equal_to_fixed(layouter, &p.y, coordinates.1)?;
618 let p_is_not_id = self.native_gadget.not(layouter, &p.is_id)?;
619 self.native_gadget.and(layouter, &[eq_x, eq_y, p_is_not_id])
620 }
621 }
622
623 fn is_not_equal_to_fixed(
624 &self,
625 layouter: &mut impl Layouter<F>,
626 x: &AssignedForeignPoint<F, C, B>,
627 constant: C::CryptographicGroup,
628 ) -> Result<AssignedBit<F>, Error> {
629 let b = self.is_equal_to_fixed(layouter, x, constant)?;
630 self.native_gadget.not(layouter, &b)
631 }
632}
633
634impl<F, C, B, S, N> ZeroInstructions<F, AssignedForeignPoint<F, C, B>>
635 for ForeignWeierstrassEccChip<F, C, B, S, N>
636where
637 F: CircuitField,
638 C: WeierstrassCurve,
639 B: FieldEmulationParams<F, C::Base>,
640 S: ScalarFieldInstructions<F>,
641 S::Scalar: InnerValue<Element = C::ScalarField>,
642 N: NativeInstructions<F>,
643{
644 fn assert_zero(
645 &self,
646 layouter: &mut impl Layouter<F>,
647 x: &AssignedForeignPoint<F, C, B>,
648 ) -> Result<(), Error> {
649 self.native_gadget.assert_equal_to_fixed(layouter, &x.is_id, true)
650 }
651
652 fn assert_non_zero(
653 &self,
654 layouter: &mut impl Layouter<F>,
655 x: &AssignedForeignPoint<F, C, B>,
656 ) -> Result<(), Error> {
657 self.native_gadget.assert_equal_to_fixed(layouter, &x.is_id, false)
658 }
659
660 fn is_zero(
661 &self,
662 _layouter: &mut impl Layouter<F>,
663 p: &AssignedForeignPoint<F, C, B>,
664 ) -> Result<AssignedBit<F>, Error> {
665 Ok(p.is_id.clone())
666 }
667}
668
669impl<F, C, B, S, N> ControlFlowInstructions<F, AssignedForeignPoint<F, C, B>>
670 for ForeignWeierstrassEccChip<F, C, B, S, N>
671where
672 F: CircuitField,
673 C: WeierstrassCurve,
674 B: FieldEmulationParams<F, C::Base>,
675 S: ScalarFieldInstructions<F>,
676 S::Scalar: InnerValue<Element = C::ScalarField>,
677 N: NativeInstructions<F>,
678{
679 fn select(
681 &self,
682 layouter: &mut impl Layouter<F>,
683 cond: &AssignedBit<F>,
684 p: &AssignedForeignPoint<F, C, B>,
685 q: &AssignedForeignPoint<F, C, B>,
686 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
687 let point = p.point.zip(q.point).zip(cond.value()).map(|((p, q), b)| if b { p } else { q });
688 let is_id = self.native_gadget.select(layouter, cond, &p.is_id, &q.is_id)?;
689 let x = self.base_field_chip().select(layouter, cond, &p.x, &q.x)?;
690 let y = self.base_field_chip().select(layouter, cond, &p.y, &q.y)?;
691 Ok(AssignedForeignPoint::<F, C, B> { point, is_id, x, y })
692 }
693}
694
695impl<F, C, B, S, N> EccInstructions<F, C> for ForeignWeierstrassEccChip<F, C, B, S, N>
696where
697 F: CircuitField,
698 C: WeierstrassCurve,
699 B: FieldEmulationParams<F, C::Base>,
700 S: ScalarFieldInstructions<F>,
701 S::Scalar: InnerValue<Element = C::ScalarField>,
702 N: NativeInstructions<F>,
703{
704 type Point = AssignedForeignPoint<F, C, B>;
705 type Coordinate = AssignedField<F, C::Base, B>;
706 type Scalar = S::Scalar;
707
708 fn add(
709 &self,
710 layouter: &mut impl Layouter<F>,
711 p: &Self::Point,
712 q: &Self::Point,
713 ) -> Result<Self::Point, Error> {
714 let r_curve = p.value().zip(q.value()).map(|(p, q)| p + q);
715 let r = self.assign_point_unchecked(layouter, r_curve)?;
716
717 let p_or_q_or_r_are_id = self.native_gadget.or(
719 layouter,
720 &[p.is_id.clone(), q.is_id.clone(), r.is_id.clone()],
721 )?;
722 let none_is_id = self.native_gadget.not(layouter, &p_or_q_or_r_are_id)?;
723 let px_eq_qx = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
724 let py_eq_qy = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
725 let px_neq_qx = self.native_gadget.not(layouter, &px_eq_qx)?;
726 let py_eq_neg_qy = {
727 let py_plus_qy = self.base_field_chip().add(layouter, &p.y, &q.y)?;
728 self.base_field_chip().is_zero(layouter, &py_plus_qy)?
729 };
730
731 self.cond_assert_equal(layouter, &p.is_id, &r, q)?;
733
734 self.cond_assert_equal(layouter, &q.is_id, &r, p)?;
736
737 let p_eq_nq = self.native_gadget.and(layouter, &[px_eq_qx.clone(), py_eq_neg_qy])?;
740 self.native_gadget.assert_equal(layouter, &p_eq_nq, &r.is_id)?;
741
742 let cond = self.native_gadget.and(layouter, &[px_eq_qx, py_eq_qy, none_is_id.clone()])?;
746 self.assert_double(layouter, p, &r, &cond)?;
747
748 let cond = self.native_gadget.and(layouter, &[px_neq_qx, none_is_id])?;
753 self.assert_add(layouter, p, q, &r, &cond)?;
754
755 Ok(r)
756 }
757
758 fn double(
759 &self,
760 layouter: &mut impl Layouter<F>,
761 p: &AssignedForeignPoint<F, C, B>,
762 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
763 let r_curve = p.value().map(|p| p + p);
764 let r = self.assign_point_unchecked(layouter, r_curve)?;
765
766 self.native_gadget.assert_equal(layouter, &p.is_id, &r.is_id)?;
768
769 let cond = self.native_gadget.not(layouter, &p.is_id)?;
774 self.assert_double(layouter, p, &r, &cond)?;
775
776 Ok(r)
777 }
778
779 fn negate(
780 &self,
781 layouter: &mut impl Layouter<F>,
782 p: &Self::Point,
783 ) -> Result<Self::Point, Error> {
784 let neg_y = self.base_field_chip().neg(layouter, &p.y)?;
785 let neg_y = self.base_field_chip().normalize(layouter, &neg_y)?;
786 Ok(AssignedForeignPoint::<F, C, B> {
787 point: -p.point,
788 is_id: p.is_id.clone(),
789 x: p.x.clone(),
790 y: neg_y,
791 })
792 }
793
794 fn msm(
795 &self,
796 layouter: &mut impl Layouter<F>,
797 scalars: &[Self::Scalar],
798 bases: &[Self::Point],
799 ) -> Result<Self::Point, Error> {
800 let scalars = scalars
801 .iter()
802 .map(|s| (s.clone(), C::ScalarField::NUM_BITS as usize))
803 .collect::<Vec<_>>();
804 self.msm_by_bounded_scalars(layouter, &scalars, bases)
805 }
806
807 fn msm_by_bounded_scalars(
808 &self,
809 layouter: &mut impl Layouter<F>,
810 scalars: &[(S::Scalar, usize)],
811 bases: &[AssignedForeignPoint<F, C, B>],
812 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
813 assert_eq!(scalars.len(), bases.len(), "`|scalars| != |bases|`");
814
815 const WS: usize = 4;
816 let scalar_chip = self.scalar_field_chip();
817
818 let (scalars, bases, bases_with_1bit_scalar) =
819 msm_preprocess(self, scalar_chip, layouter, scalars, bases)?;
820
821 let mut non_id_bases = vec![];
825 let mut scalars_of_non_id_bases = vec![];
826 let zero: S::Scalar = scalar_chip.assign_fixed(layouter, C::ScalarField::ZERO)?;
827 let g = self.assign_fixed(layouter, C::CryptographicGroup::generator())?;
828 for (s, b) in scalars.iter().zip(bases.iter()) {
829 let new_b = self.select(layouter, &b.is_id, &g, b)?;
830 let new_s = scalar_chip.select(layouter, &b.is_id, &zero, &s.0)?;
831 non_id_bases.push(new_b);
832 scalars_of_non_id_bases.push((new_s, s.1));
833 }
834
835 let nb_bits_per_glv_scalar = C::ScalarField::NUM_BITS.div_ceil(2) as usize;
839 let mut non_glv_scalars = vec![];
840 let mut non_glv_bases = vec![];
841 let mut glv_scalars = vec![];
842 let mut glv_bases = vec![];
843 for (s, b) in scalars_of_non_id_bases.iter().zip(non_id_bases.iter()) {
844 if C::has_cubic_endomorphism() && s.1 > nb_bits_per_glv_scalar + WS {
848 let ((s1, s2), (b1, b2)) = self.glv_split(layouter, &s.0, b)?;
849 glv_scalars.push((s1, nb_bits_per_glv_scalar));
850 glv_scalars.push((s2, nb_bits_per_glv_scalar));
851 glv_bases.push(b1);
852 glv_bases.push(b2);
853 } else {
854 non_glv_scalars.push(s.clone());
855 non_glv_bases.push(b.clone());
856 }
857 }
858
859 let scalars = [glv_scalars, non_glv_scalars].concat();
860 let bases = [glv_bases, non_glv_bases].concat();
861
862 let mut decomposed_scalars = vec![];
863 for (s, nb_bits_s) in scalars.iter() {
864 let s_bits = self.scalar_field_chip().assigned_to_le_chunks(
865 layouter,
866 s,
867 WS,
868 Some(nb_bits_s.div_ceil(WS)),
869 )?;
870 decomposed_scalars.push(s_bits)
871 }
872 let res = self.windowed_msm::<WS>(layouter, &decomposed_scalars, &bases)?;
873
874 add_1bit_scalar_bases(layouter, self, scalar_chip, &bases_with_1bit_scalar, res)
875 }
876
877 fn mul_by_constant(
878 &self,
879 layouter: &mut impl Layouter<F>,
880 scalar: C::ScalarField,
881 base: &Self::Point,
882 ) -> Result<Self::Point, Error> {
883 let scalar_as_big = scalar.to_biguint();
887 if scalar_as_big.bits() <= 128 {
888 let n = scalar_as_big
889 .to_u64_digits()
890 .iter()
891 .rev()
892 .fold(0u128, |acc, limb| (acc << 64) | (*limb as u128));
893
894 let id = self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
898 let g = self.assign_fixed(layouter, C::CryptographicGroup::generator())?;
899 let p = self.select(layouter, &base.is_id, &g, base)?;
900 let r = self.mul_by_u128(layouter, n, &p)?;
901 return self.select(layouter, &base.is_id, &id, &r);
902 }
903 let scalar_bits = scalar
904 .to_bits_le(None)
905 .iter()
906 .map(|b| self.native_gadget.assign_fixed(layouter, *b))
907 .collect::<Result<Vec<_>, Error>>()?;
908 self.msm_by_le_bits(layouter, &[scalar_bits], std::slice::from_ref(base))
909 }
910
911 fn point_from_coordinates(
912 &self,
913 layouter: &mut impl Layouter<F>,
914 x: &AssignedField<F, C::Base, B>,
915 y: &AssignedField<F, C::Base, B>,
916 ) -> Result<Self::Point, Error> {
917 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
918 let cond = self.native_gadget.assign_fixed(layouter, true)?;
919 on_curve::assert_is_on_curve::<F, C, B, N>(
920 layouter,
921 &cond,
922 x,
923 y,
924 self.base_field_chip(),
925 &self.config.on_curve_config,
926 )?;
927 let point = x
931 .value()
932 .zip(y.value())
933 .map(|(x, y)| C::from_xy(x, y).unwrap_or(C::identity()).into_subgroup());
934 Ok(AssignedForeignPoint::<F, C, B> {
935 point,
936 is_id,
937 x: x.clone(),
938 y: y.clone(),
939 })
940 }
941
942 fn assign_without_subgroup_check(
943 &self,
944 layouter: &mut impl Layouter<F>,
945 value: Value<C::CryptographicGroup>,
946 ) -> Result<Self::Point, Error> {
947 let p = self.assign_point_unchecked(layouter, value)?;
948 let is_not_id = self.native_gadget.not(layouter, &p.is_id)?;
949 on_curve::assert_is_on_curve::<F, C, B, N>(
950 layouter,
951 &is_not_id,
952 &p.x,
953 &p.y,
954 self.base_field_chip(),
955 &self.config.on_curve_config,
956 )?;
957 Ok(p)
958 }
959
960 fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
961 point.x.clone()
962 }
963
964 fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
965 point.y.clone()
966 }
967
968 fn base_field(&self) -> &impl DecompositionInstructions<F, Self::Coordinate> {
969 self.base_field_chip()
970 }
971}
972
973impl<F, C, B, S, N> ForeignWeierstrassEccChip<F, C, B, S, N>
974where
975 F: CircuitField,
976 C: WeierstrassCurve,
977 B: FieldEmulationParams<F, C::Base>,
978 S: ScalarFieldInstructions<F>,
979 S::Scalar: InnerValue<Element = C::ScalarField>,
980 N: NativeInstructions<F>,
981{
982 pub fn new(
987 config: &ForeignWeierstrassEccConfig<C>,
988 native_gadget: &N,
989 scalar_field_chip: &S,
990 ) -> Self {
991 let mut rng = OsRng;
992 let random_point = C::random(&mut rng).into_subgroup();
993
994 let base_field_chip = FieldChip::new(&config.base_field_config, native_gadget);
995
996 Self {
997 config: config.clone(),
998 native_gadget: native_gadget.clone(),
999 base_field_chip,
1000 scalar_field_chip: scalar_field_chip.clone(),
1001 tag_cnt: Rc::new(RefCell::new(1)),
1002 msm_randomness: Rc::new(RefCell::new(HashMap::new())),
1003 random_point,
1004 }
1005 }
1006
1007 fn completeness_error_if<V>(value: &Value<V>, f: impl FnOnce(&V) -> bool) -> Result<(), Error> {
1010 value.error_if_known_and(f).map_err(|_| Error::CompletenessFailure)
1011 }
1012
1013 pub fn base_field_chip(&self) -> &FieldChip<F, C::Base, B, N> {
1015 &self.base_field_chip
1016 }
1017
1018 pub fn scalar_field_chip(&self) -> &S {
1020 &self.scalar_field_chip
1021 }
1022
1023 pub fn configure(
1025 meta: &mut ConstraintSystem<F>,
1026 base_field_config: &FieldChipConfig,
1027 advice_columns: &[Column<Advice>],
1028 nb_parallel_range_checks: usize,
1029 max_bit_len: u32,
1030 ) -> ForeignWeierstrassEccConfig<C> {
1031 let cond_col_idx = base_field_config.x_cols.len() + base_field_config.v_cols.len() + 1;
1034 assert!(advice_columns.len() > cond_col_idx);
1035 let cond_col = advice_columns[cond_col_idx];
1036 meta.enable_equality(cond_col);
1037
1038 let on_curve_config = OnCurveConfig::<C>::configure::<F, B>(
1039 meta,
1040 base_field_config,
1041 &cond_col,
1042 nb_parallel_range_checks,
1043 max_bit_len,
1044 );
1045
1046 let slope_config = SlopeConfig::<C>::configure::<F, B>(
1047 meta,
1048 base_field_config,
1049 &cond_col,
1050 nb_parallel_range_checks,
1051 max_bit_len,
1052 );
1053
1054 let tangent_config = TangentConfig::<C>::configure::<F, B>(
1055 meta,
1056 base_field_config,
1057 &cond_col,
1058 nb_parallel_range_checks,
1059 max_bit_len,
1060 );
1061
1062 let lambda_squared_config = LambdaSquaredConfig::<C>::configure::<F, B>(
1063 meta,
1064 base_field_config,
1065 &cond_col,
1066 nb_parallel_range_checks,
1067 max_bit_len,
1068 );
1069
1070 let (q_multi_select, idx_col_multi_select, tag_col_multi_select) =
1071 configure_multi_select_lookup(meta, advice_columns, base_field_config);
1072
1073 ForeignWeierstrassEccConfig {
1074 base_field_config: base_field_config.clone(),
1075 on_curve_config,
1076 slope_config,
1077 tangent_config,
1078 lambda_squared_config,
1079 q_multi_select,
1080 idx_col_multi_select,
1081 tag_col_multi_select,
1082 }
1083 }
1084
1085 fn assign_point_unchecked(
1090 &self,
1091 layouter: &mut impl Layouter<F>,
1092 p: Value<C::CryptographicGroup>,
1093 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1094 let values = p.map(|p| {
1095 if C::CryptographicGroup::is_identity(&p).into() {
1096 (C::Base::ZERO, C::Base::ZERO, true)
1097 } else {
1098 let coordinates =
1099 p.into().coordinates().expect("assign_point_unchecked: invalid point given");
1100 (coordinates.0, coordinates.1, false)
1101 }
1102 });
1103 let x = self.base_field_chip().assign(layouter, values.map(|v| v.0))?;
1104 let y = self.base_field_chip().assign(layouter, values.map(|v| v.1))?;
1105 let is_id = self.native_gadget.assign(layouter, values.map(|v| v.2))?;
1106 let p = AssignedForeignPoint::<F, C, B> {
1107 point: p,
1108 is_id,
1109 x,
1110 y,
1111 };
1112 Ok(p)
1113 }
1114
1115 fn incomplete_add(
1136 &self,
1137 layouter: &mut impl Layouter<F>,
1138 p: &AssignedForeignPoint<F, C, B>,
1139 q: &AssignedForeignPoint<F, C, B>,
1140 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1141 let r_curve = p.value().zip(q.value()).map(|(p, q)| p + q);
1142 let r = self.assign_point_unchecked(layouter, r_curve)?;
1143
1144 self.native_gadget.assert_equal(layouter, &p.is_id, &r.is_id)?;
1146
1147 let one = self.native_gadget.assign_fixed(layouter, true)?;
1154 self.assert_add(layouter, p, q, &r, &one)?;
1155
1156 Ok(r)
1157 }
1158
1159 fn assert_double(
1172 &self,
1173 layouter: &mut impl Layouter<F>,
1174 p: &AssignedForeignPoint<F, C, B>,
1175 r: &AssignedForeignPoint<F, C, B>,
1176 cond: &AssignedBit<F>,
1177 ) -> Result<(), Error> {
1178 let lambda = {
1180 let lambda_value = p.value().map(|p| {
1181 if C::CryptographicGroup::is_identity(&p).into() {
1182 C::Base::ONE
1183 } else {
1184 let p = p.into().coordinates().unwrap();
1185 (C::Base::from(3) * p.0 * p.0 + C::A)
1186 * (C::Base::from(2) * p.1).invert().unwrap()
1187 }
1188 });
1189 self.base_field_chip().assign(layouter, lambda_value)?
1190 };
1191
1192 tangent::assert_tangent::<F, C, B, N>(
1194 layouter,
1195 cond,
1196 (&p.x, &p.y),
1197 &lambda,
1198 self.base_field_chip(),
1199 &self.config.tangent_config,
1200 )?;
1201
1202 lambda_squared::assert_lambda_squared(
1204 layouter,
1205 cond,
1206 (&p.x, &p.x, &r.x),
1207 &lambda,
1208 self.base_field_chip(),
1209 &self.config.lambda_squared_config,
1210 )?;
1211
1212 self.assert_slope(layouter, cond, p, r, true, &lambda)?;
1223
1224 Ok(())
1225 }
1226
1227 fn assert_add(
1255 &self,
1256 layouter: &mut impl Layouter<F>,
1257 p: &AssignedForeignPoint<F, C, B>,
1258 q: &AssignedForeignPoint<F, C, B>,
1259 r: &AssignedForeignPoint<F, C, B>,
1260 cond: &AssignedBit<F>,
1261 ) -> Result<(), Error> {
1262 let lambda = {
1264 let lambda_value = p.value().zip(q.value()).map(|(p, q)| {
1265 if p.is_identity().into() || q.is_identity().into() {
1266 C::Base::ONE
1267 } else {
1268 let p = p.into().coordinates().unwrap();
1269 let q = q.into().coordinates().unwrap();
1270 if p.0 == q.0 {
1271 C::Base::ONE
1272 } else {
1273 (q.1 - p.1) * (q.0 - p.0).invert().unwrap()
1274 }
1275 }
1276 });
1277 self.base_field_chip().assign(layouter, lambda_value)?
1278 };
1279
1280 self.assert_slope(layouter, cond, p, q, false, &lambda)?;
1290
1291 lambda_squared::assert_lambda_squared(
1293 layouter,
1294 cond,
1295 (&p.x, &q.x, &r.x),
1296 &lambda,
1297 self.base_field_chip(),
1298 &self.config.lambda_squared_config,
1299 )?;
1300
1301 self.assert_slope(layouter, cond, p, r, true, &lambda)?;
1318
1319 Ok(())
1320 }
1321
1322 fn assert_slope(
1344 &self,
1345 layouter: &mut impl Layouter<F>,
1346 cond: &AssignedBit<F>,
1347 p: &AssignedForeignPoint<F, C, B>,
1348 q: &AssignedForeignPoint<F, C, B>,
1349 negate_q: bool,
1350 lambda: &AssignedField<F, C::Base, B>,
1351 ) -> Result<(), Error> {
1352 slope::assert_slope::<F, C, B, N>(
1353 layouter,
1354 cond,
1355 (&p.x, &p.y),
1356 (&q.x, &q.y, negate_q),
1357 lambda,
1358 self.base_field_chip(),
1359 &self.config.slope_config,
1360 )
1361 }
1362
1363 #[allow(clippy::type_complexity)]
1368 fn fill_dynamic_lookup_row(
1369 &self,
1370 layouter: &mut impl Layouter<F>,
1371 point: &AssignedForeignPoint<F, C, B>,
1372 index: &AssignedNative<F>,
1373 table_tag: F,
1374 enable_lookup: bool,
1375 ) -> Result<(Vec<AssignedNative<F>>, Vec<AssignedNative<F>>), Error> {
1376 fill_dynamic_lookup_row(
1377 layouter,
1378 &point.x.limb_values(),
1379 &point.y.limb_values(),
1380 index,
1381 &self.config.base_field_config.x_cols,
1382 &self.config.base_field_config.z_cols, self.config.idx_col_multi_select,
1384 self.config.tag_col_multi_select,
1385 self.config.q_multi_select,
1386 table_tag,
1387 enable_lookup,
1388 )
1389 }
1390
1391 fn load_multi_select_table(
1403 &self,
1404 layouter: &mut impl Layouter<F>,
1405 point_table: &[AssignedForeignPoint<F, C, B>],
1406 table_tag: F,
1407 ) -> Result<(), Error> {
1408 for (i, point) in point_table.iter().enumerate() {
1409 let index = self.native_gadget.assign_fixed(layouter, F::from(i as u64))?;
1410 self.fill_dynamic_lookup_row(layouter, point, &index, table_tag, false)?;
1411 }
1412 Ok(())
1413 }
1414
1415 fn multi_select(
1438 &self,
1439 layouter: &mut impl Layouter<F>,
1440 selector: &AssignedNative<F>,
1441 point_table: &[AssignedForeignPoint<F, C, B>],
1442 table_tag: F,
1443 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1444 let mut selector_idx = 0;
1446 selector.value().map(|v| {
1447 let digits = v.to_biguint().to_u32_digits();
1448 let digit = if digits.is_empty() { 0 } else { digits[0] };
1449 debug_assert!(digits.len() <= 1);
1450 debug_assert!(digit < point_table.len() as u32);
1451 selector_idx = digit;
1452 });
1453
1454 let selected = point_table[selector_idx as usize].clone();
1455
1456 let (xs, ys) =
1460 self.fill_dynamic_lookup_row(layouter, &selected, selector, table_tag, true)?;
1461 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1462 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1463 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
1464
1465 let result = AssignedForeignPoint::<F, C, B> {
1466 point: selected.point,
1467 is_id,
1468 x,
1469 y,
1470 };
1471
1472 Ok(result)
1473 }
1474
1475 pub fn k_out_of_n_points(
1493 &self,
1494 layouter: &mut impl Layouter<F>,
1495 table: &[AssignedForeignPoint<F, C, B>],
1496 selected: &[Value<C::CryptographicGroup>],
1497 ) -> Result<Vec<AssignedForeignPoint<F, C, B>>, Error> {
1498 let n = table.len();
1499 let k = selected.len();
1500 assert!(k <= n);
1501
1502 assert!((n as u128) < (1 << (F::NUM_BITS / 2)));
1504
1505 table.iter().try_for_each(|point| self.assert_non_zero(layouter, point))?;
1507
1508 let tag_cnt = *self.tag_cnt.borrow();
1515 self.tag_cnt.replace(tag_cnt + 1);
1516 self.load_multi_select_table(layouter, table, F::from(tag_cnt))?;
1517
1518 let table_values =
1520 Value::<Vec<C::CryptographicGroup>>::from_iter(table.iter().map(|point| point.value()));
1521 let selected_idxs = selected
1522 .iter()
1523 .map(|point_value| {
1524 point_value
1525 .zip(table_values.clone())
1526 .map(|(p, ts)| ts.iter().position(|table_val| *table_val == p).unwrap_or(0))
1527 })
1528 .collect::<Vec<_>>();
1529
1530 Value::<Vec<usize>>::from_iter(selected_idxs.clone())
1533 .error_if_known_and(|idxs| idxs.iter().zip(idxs.iter().skip(1)).any(|(i, j)| i >= j))?;
1534
1535 let assigned_selected_idxs = selected_idxs
1537 .clone()
1538 .iter()
1539 .map(|i_value| self.native_gadget.assign(layouter, i_value.map(|i| F::from(i as u64))))
1540 .collect::<Result<Vec<AssignedNative<F>>, Error>>()?;
1541
1542 let l = BigUint::one() << BigUint::from(n).bits();
1548 assigned_selected_idxs
1549 .iter()
1550 .zip(assigned_selected_idxs.iter().skip(1))
1551 .try_for_each(|(idx, next_idx)| {
1552 let diff_minus_one = self.native_gadget.linear_combination(
1553 layouter,
1554 &[(F::ONE, next_idx.clone()), (-F::ONE, idx.clone())],
1555 -F::ONE,
1556 )?;
1557 self.native_gadget.assert_lower_than_fixed(layouter, &diff_minus_one, &l)
1558 })?;
1559
1560 let mut unwrapped_selected_idxs = vec![0; k];
1562 selected_idxs.iter().enumerate().for_each(|(i, idx)| {
1563 idx.map(|j| unwrapped_selected_idxs[i] = j);
1564 });
1565 let selected_points = unwrapped_selected_idxs
1566 .iter()
1567 .zip(assigned_selected_idxs.iter())
1568 .map(|(i, selected_idx)| {
1569 let (xs, ys) = self.fill_dynamic_lookup_row(
1570 layouter,
1571 &table[*i],
1572 selected_idx,
1573 F::from(tag_cnt),
1574 true,
1575 )?;
1576 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1577 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1578 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
1579 Ok(AssignedForeignPoint::<F, C, B> {
1580 point: table[*i].value(),
1581 is_id,
1582 x,
1583 y,
1584 })
1585 })
1586 .collect::<Result<Vec<_>, Error>>()?;
1587
1588 Ok(selected_points)
1589 }
1590
1591 fn incomplete_assert_different_x(
1598 &self,
1599 layouter: &mut impl Layouter<F>,
1600 p: &AssignedForeignPoint<F, C, B>,
1601 q: &AssignedForeignPoint<F, C, B>,
1602 ) -> Result<(), Error> {
1603 assert!(p.x.is_well_formed());
1604 assert!(q.x.is_well_formed());
1605
1606 let native_gadget = &self.native_gadget;
1619 let base = big_to_fe::<F>(BigUint::one() << B::LOG2_BASE);
1620 let m = bigint_to_fe::<F>(&p.x.modulus());
1621
1622 let mut terms = vec![];
1623 let mut coeff = F::ONE;
1624 for (px_i, qx_i) in p.x.limb_values().iter().zip(q.x.limb_values().iter()) {
1625 terms.push((coeff, px_i.clone()));
1626 terms.push((-coeff, qx_i.clone()));
1627 coeff *= base;
1628 }
1629
1630 let diff = native_gadget.linear_combination(layouter, &terms, F::ZERO)?;
1631
1632 native_gadget.assert_non_zero(layouter, &diff)?;
1636 native_gadget.assert_not_equal_to_fixed(layouter, &diff, m)?;
1637 native_gadget.assert_not_equal_to_fixed(layouter, &diff, -m)
1638 }
1639
1640 fn mul_by_u128(
1650 &self,
1651 layouter: &mut impl Layouter<F>,
1652 n: u128,
1653 p: &AssignedForeignPoint<F, C, B>,
1654 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1655 if n == 0 {
1656 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1657 };
1658
1659 assert!(129 < C::ScalarField::NUM_BITS);
1662
1663 let mut res = None;
1666
1667 let mut tmp = p.clone();
1669
1670 let mut n = n;
1672 while n > 0 {
1673 if !n.is_multiple_of(2) {
1694 res = match res {
1695 None => Some(tmp.clone()),
1696 Some(acc) => Some(self.incomplete_add(layouter, &acc, &tmp)?),
1697 };
1698 }
1699 n >>= 1;
1700
1701 if n > 0 {
1702 tmp = self.double(layouter, &tmp)?
1703 }
1704 }
1705
1706 Ok(res.unwrap())
1707 }
1708
1709 fn msm_randomness<const WS: usize>(
1724 &self,
1725 layouter: &mut impl Layouter<F>,
1726 ) -> Result<MsmRandomness<F, C, B>, Error> {
1727 if let Some(cached) = self.msm_randomness.borrow().get(&WS) {
1728 return Ok(cached.clone());
1729 }
1730
1731 let r = match self.msm_randomness.borrow().values().next().map(|c| c.r.clone()) {
1733 Some(r) => r,
1734 None => {
1735 self.assign_without_subgroup_check(layouter, Value::known(self.random_point))?
1736 }
1737 };
1738 self.assert_non_zero(layouter, &r)?;
1739
1740 let alpha = self.mul_by_u128(layouter, (1u128 << WS) - 1, &r)?;
1741 let neg_alpha = self.negate(layouter, &alpha)?;
1742
1743 let windowed_randomness = MsmRandomness { r, neg_alpha };
1744 self.msm_randomness.borrow_mut().insert(WS, windowed_randomness.clone());
1745 Ok(windowed_randomness)
1746 }
1747
1748 fn windowed_msm<const WS: usize>(
1769 &self,
1770 layouter: &mut impl Layouter<F>,
1771 scalars: &[Vec<AssignedNative<F>>],
1772 bases: &[AssignedForeignPoint<F, C, B>],
1773 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1774 assert_eq!(scalars.len(), bases.len(), "`|scalars| != |bases|`");
1775
1776 if scalars.is_empty() {
1777 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1778 }
1779
1780 for p in bases.iter() {
1782 self.assert_non_zero(layouter, p)?;
1783 }
1784
1785 let zero: AssignedNative<F> = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
1790 let max_len = scalars.iter().fold(0, |m, chunks| max(m, chunks.len()));
1791 let mut padded_scalars = vec![];
1792 for s_bits in scalars.iter() {
1793 let mut s_bits = s_bits.to_vec();
1795 s_bits.resize(max_len, zero.clone());
1796 let rev_s_bits = s_bits.into_iter().rev().collect::<Vec<_>>();
1798 padded_scalars.push(rev_s_bits)
1799 }
1800
1801 let MsmRandomness { r, neg_alpha } = self.msm_randomness::<WS>(layouter)?;
1828
1829 let l_times_r = self.mul_by_u128(layouter, bases.len() as u128, &r)?;
1830
1831 let tag_cnt = *self.tag_cnt.clone().borrow();
1833 self.tag_cnt.replace(tag_cnt + bases.len() as u64);
1834
1835 let mut tables = vec![];
1837 for (i, p) in bases.iter().enumerate() {
1838 self.incomplete_assert_different_x(layouter, &neg_alpha, p)?;
1840 let mut acc = neg_alpha.clone();
1841 let mut p_table = vec![acc.clone()];
1842 for _ in 1..(1usize << WS) {
1843 Self::completeness_error_if(&acc.value().zip(p.value()), |(av, pv)| {
1860 av == pv || *av == -(*pv)
1861 })?;
1862
1863 acc = self.incomplete_add(layouter, &acc, p)?;
1864
1865 assert!(acc.x.is_well_formed() && acc.y.is_well_formed());
1866 p_table.push(acc.clone())
1867 }
1868 self.load_multi_select_table(layouter, &p_table, F::from(tag_cnt + i as u64))?;
1869 tables.push(p_table)
1870 }
1871
1872 let nb_iterations = max_len;
1873 let mut acc = l_times_r.clone();
1874
1875 #[allow(clippy::needless_range_loop)]
1876 for i in 0..nb_iterations {
1877 for _ in 0..WS {
1878 acc = self.double(layouter, &acc)?;
1879 }
1880 for j in 0..bases.len() {
1881 let window = &padded_scalars[j][i];
1882 let addend =
1883 self.multi_select(layouter, window, &tables[j], F::from(tag_cnt + j as u64))?;
1884 Self::completeness_error_if(&acc.value().zip(addend.value()), |(av, addv)| {
1898 av == addv || *av == -(*addv)
1899 })?;
1900
1901 self.incomplete_assert_different_x(layouter, &acc, &addend)?;
1902 acc = self.incomplete_add(layouter, &acc, &addend)?;
1903 }
1904 }
1905
1906 let r_correction = self.negate(layouter, &l_times_r)?;
1907 self.add(layouter, &acc, &r_correction)
1908 }
1909
1910 pub fn msm_by_le_bits(
1926 &self,
1927 layouter: &mut impl Layouter<F>,
1928 scalars: &[Vec<AssignedBit<F>>],
1929 bases: &[AssignedForeignPoint<F, C, B>],
1930 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1931 const WS: usize = 4;
1935 let scalars = scalars
1936 .iter()
1937 .map(|bits| {
1938 bits.chunks(WS)
1939 .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
1940 .collect::<Result<Vec<_>, Error>>()
1941 })
1942 .collect::<Result<Vec<_>, Error>>()?;
1943 self.windowed_msm::<WS>(layouter, &scalars, bases)
1944 }
1945
1946 #[allow(clippy::type_complexity)]
1955 fn glv_split(
1956 &self,
1957 layouter: &mut impl Layouter<F>,
1958 scalar: &S::Scalar,
1959 base: &AssignedForeignPoint<F, C, B>,
1960 ) -> Result<
1961 (
1962 (S::Scalar, S::Scalar),
1963 (AssignedForeignPoint<F, C, B>, AssignedForeignPoint<F, C, B>),
1964 ),
1965 Error,
1966 > {
1967 let zeta_base = C::base_zeta();
1968 let zeta_scalar = C::scalar_zeta();
1969
1970 let decomposed = scalar.value().map(|x| glv_scalar_decomposition(&x, &zeta_scalar));
1971 let s1_value = decomposed.map(|((s1, _), _)| s1);
1972 let x1_value = decomposed.map(|((_, x1), _)| x1);
1973 let s2_value = decomposed.map(|(_, (s2, _))| s2);
1974 let x2_value = decomposed.map(|(_, (_, x2))| x2);
1975
1976 let x1 = self.scalar_field_chip.assign(layouter, x1_value)?;
1977 let x2 = self.scalar_field_chip.assign(layouter, x2_value)?;
1978
1979 let s1 = self.native_gadget.assign(layouter, s1_value)?;
1980 let s2 = self.native_gadget.assign(layouter, s2_value)?;
1981
1982 let neg_x1 = self.scalar_field_chip.neg(layouter, &x1)?;
1983 let neg_x2 = self.scalar_field_chip.neg(layouter, &x2)?;
1984
1985 let signed_x1 = self.scalar_field_chip.select(layouter, &s1, &x1, &neg_x1)?;
1986 let signed_x2 = self.scalar_field_chip.select(layouter, &s2, &x2, &neg_x2)?;
1987
1988 let x = self.scalar_field_chip.linear_combination(
1990 layouter,
1991 &[(C::ScalarField::ONE, signed_x1), (zeta_scalar, signed_x2)],
1992 C::ScalarField::ZERO,
1993 )?;
1994 self.scalar_field_chip.assert_equal(layouter, &x, scalar)?;
1995
1996 let zeta_x = self.base_field_chip.mul_by_constant(layouter, &base.x, zeta_base)?;
1997 let zeta_p = AssignedForeignPoint::<F, C, B> {
1998 point: base.point.map(|p| {
1999 if p.is_identity().into() {
2000 p
2001 } else {
2002 let coordinates = p.into().coordinates().unwrap();
2003 let zeta_x = zeta_base * coordinates.0;
2004 C::from_xy(zeta_x, coordinates.1).unwrap().into_subgroup()
2005 }
2006 }),
2007 is_id: base.is_id.clone(),
2008 x: zeta_x,
2009 y: base.y.clone(),
2010 };
2011
2012 let neg_zeta_p = self.negate(layouter, &zeta_p)?;
2013 let neg_base = self.negate(layouter, base)?;
2014
2015 let p1 = self.select(layouter, &s1, base, &neg_base)?;
2016 let p2 = self.select(layouter, &s2, &zeta_p, &neg_zeta_p)?;
2017
2018 Ok(((x1, x2), (p1, p2)))
2019 }
2020}
2021
2022#[derive(Clone, Debug)]
2023#[cfg(any(test, feature = "testing"))]
2024pub struct ForeignEccTestConfig<F, C, S, N>
2027where
2028 F: CircuitField,
2029 C: WeierstrassCurve,
2030 S: ScalarFieldInstructions<F> + FromScratch<F>,
2031 S::Scalar: InnerValue<Element = C::ScalarField>,
2032 N: NativeInstructions<F> + FromScratch<F>,
2033{
2034 native_gadget_config: <N as FromScratch<F>>::Config,
2035 scalar_field_config: <S as FromScratch<F>>::Config,
2036 ff_ecc_config: ForeignWeierstrassEccConfig<C>,
2037}
2038
2039#[cfg(any(test, feature = "testing"))]
2040impl<F, C, B, S, N> FromScratch<F> for ForeignWeierstrassEccChip<F, C, B, S, N>
2041where
2042 F: CircuitField,
2043 C: WeierstrassCurve,
2044 B: FieldEmulationParams<F, C::Base>,
2045 S: ScalarFieldInstructions<F> + FromScratch<F>,
2046 S::Scalar: InnerValue<Element = C::ScalarField>,
2047 N: NativeInstructions<F> + FromScratch<F>,
2048{
2049 type Config = ForeignEccTestConfig<F, C, S, N>;
2050
2051 fn new_from_scratch(config: &ForeignEccTestConfig<F, C, S, N>) -> Self {
2052 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
2053 let scalar_field_chip =
2054 <S as FromScratch<F>>::new_from_scratch(&config.scalar_field_config);
2055 ForeignWeierstrassEccChip::new(&config.ff_ecc_config, &native_gadget, &scalar_field_chip)
2056 }
2057
2058 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
2059 self.native_gadget.load_from_scratch(layouter)?;
2060 self.scalar_field_chip.load_from_scratch(layouter)
2061 }
2062
2063 fn configure_from_scratch(
2064 meta: &mut ConstraintSystem<F>,
2065 advice_columns: &mut Vec<Column<Advice>>,
2066 fixed_columns: &mut Vec<Column<Fixed>>,
2067 instance_columns: &[Column<Instance>; 2],
2068 ) -> ForeignEccTestConfig<F, C, S, N> {
2069 let native_gadget_config = <N as FromScratch<F>>::configure_from_scratch(
2070 meta,
2071 advice_columns,
2072 fixed_columns,
2073 instance_columns,
2074 );
2075 let scalar_field_config = <S as FromScratch<F>>::configure_from_scratch(
2076 meta,
2077 advice_columns,
2078 fixed_columns,
2079 instance_columns,
2080 );
2081 let nb_advice_cols = nb_foreign_ecc_chip_columns::<F, C, B, S>();
2082 while advice_columns.len() < nb_advice_cols {
2083 advice_columns.push(meta.advice_column());
2084 }
2085 let nb_parallel_range_checks = 4;
2087 let max_bit_len = 8;
2088 let base_field_config = FieldChip::<F, C::Base, B, N>::configure(
2089 meta,
2090 &advice_columns[..nb_advice_cols],
2091 nb_parallel_range_checks,
2092 max_bit_len,
2093 );
2094 let ff_ecc_config = ForeignWeierstrassEccChip::<F, C, B, S, N>::configure(
2095 meta,
2096 &base_field_config,
2097 &advice_columns[..nb_advice_cols],
2098 nb_parallel_range_checks,
2099 max_bit_len,
2100 );
2101 ForeignEccTestConfig {
2102 native_gadget_config,
2103 scalar_field_config,
2104 ff_ecc_config,
2105 }
2106 }
2107}
2108
2109#[cfg(test)]
2110mod tests {
2111 use group::Group;
2112 use midnight_curves::{k256::K256, p256::P256, Fq as BlsScalar, G1Projective as BlsG1};
2113
2114 use super::*;
2115 use crate::{
2116 field::{
2117 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
2118 NativeChip, NativeGadget,
2119 },
2120 instructions::{assertions, control_flow, ecc, equality, public_input, zero},
2121 };
2122
2123 type Native<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
2124
2125 type EmulatedField<F, C> = FieldChip<F, <C as Group>::Scalar, MultiEmulationParams, Native<F>>;
2126
2127 macro_rules! test_generic {
2128 ($mod:ident, $op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
2129 $mod::tests::$op::<
2130 $native,
2131 AssignedForeignPoint<$native, $curve, MultiEmulationParams>,
2132 ForeignWeierstrassEccChip<
2133 $native,
2134 $curve,
2135 MultiEmulationParams,
2136 $scalar_field,
2137 Native<$native>,
2138 >,
2139 >($name);
2140 };
2141 }
2142
2143 macro_rules! test {
2144 ($mod:ident, $op:ident) => {
2145 #[test]
2146 fn $op() {
2147 test_generic!($mod, $op, BlsScalar, K256, EmulatedField<BlsScalar, K256>, "foreign_ecc_k256");
2148 test_generic!($mod, $op, BlsScalar, P256, EmulatedField<BlsScalar, P256>, "foreign_ecc_p256");
2149
2150 test_generic!($mod, $op, BlsScalar, BlsG1, Native<BlsScalar>, "foreign_ecc_bls_over_bls");
2152 }
2153 };
2154 }
2155
2156 test!(assertions, test_assertions);
2157
2158 test!(public_input, test_public_inputs);
2159
2160 test!(equality, test_is_equal);
2161
2162 test!(zero, test_zero_assertions);
2163 test!(zero, test_is_zero);
2164
2165 test!(control_flow, test_select);
2166 test!(control_flow, test_cond_assert_equal);
2167 test!(control_flow, test_cond_swap);
2168
2169 macro_rules! ecc_test {
2170 ($op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
2171 ecc::tests::$op::<
2172 $native,
2173 $curve,
2174 ForeignWeierstrassEccChip<
2175 $native,
2176 $curve,
2177 MultiEmulationParams,
2178 $scalar_field,
2179 Native<$native>,
2180 >,
2181 >($name);
2182 };
2183 }
2184
2185 macro_rules! ecc_tests {
2186 ($op:ident) => {
2187 #[test]
2188 fn $op() {
2189 ecc_test!($op, BlsScalar, K256, EmulatedField<BlsScalar, K256>, "foreign_ecc_k256");
2190 ecc_test!($op, BlsScalar, P256, EmulatedField<BlsScalar, P256>, "foreign_ecc_p256");
2191
2192 ecc_test!($op, BlsScalar, BlsG1, Native<BlsScalar>, "foreign_ecc_bls_over_bls");
2194 }
2195 };
2196 }
2197
2198 ecc_tests!(test_assign);
2199 ecc_tests!(test_assign_without_subgroup_check);
2200 ecc_tests!(test_add);
2201 ecc_tests!(test_double);
2202 ecc_tests!(test_negate);
2203 ecc_tests!(test_msm);
2204 ecc_tests!(test_msm_by_bounded_scalars);
2205 ecc_tests!(test_mul_by_constant);
2206 ecc_tests!(test_coordinates);
2207}