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 if p.is_identity().into() {
247 pis[0] += F::from(2).pow_vartime([B::LOG2_BASE as u64]);
248 }
249
250 pis
251 }
252}
253
254impl<F, C, B> InnerValue for AssignedForeignPoint<F, C, B>
255where
256 F: CircuitField,
257 C: WeierstrassCurve,
258 B: FieldEmulationParams<F, C::Base>,
259{
260 type Element = C::CryptographicGroup;
261
262 fn value(&self) -> Value<Self::Element> {
263 self.point
264 }
265}
266
267impl<F, C, B> InnerConstants for AssignedForeignPoint<F, C, B>
268where
269 F: CircuitField,
270 C: WeierstrassCurve,
271 B: FieldEmulationParams<F, C::Base>,
272{
273 fn inner_zero() -> C::CryptographicGroup {
274 C::CryptographicGroup::identity()
275 }
276
277 fn inner_one() -> Self::Element {
278 C::CryptographicGroup::generator()
279 }
280}
281
282#[cfg(any(test, feature = "testing"))]
283impl<F, C, B> Sampleable for AssignedForeignPoint<F, C, B>
284where
285 F: CircuitField,
286 C: WeierstrassCurve,
287 B: FieldEmulationParams<F, C::Base>,
288{
289 fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
290 C::CryptographicGroup::random(rng)
291 }
292}
293
294impl<F, C, B, S, N> Chip<F> for ForeignWeierstrassEccChip<F, C, B, S, N>
295where
296 F: CircuitField,
297 C: WeierstrassCurve,
298 B: FieldEmulationParams<F, C::Base>,
299 S: ScalarFieldInstructions<F>,
300 S::Scalar: InnerValue<Element = C::ScalarField>,
301 N: NativeInstructions<F>,
302{
303 type Config = ForeignWeierstrassEccConfig<C>;
304 type Loaded = ();
305 fn config(&self) -> &Self::Config {
306 &self.config
307 }
308 fn loaded(&self) -> &Self::Loaded {
309 &()
310 }
311}
312
313impl<F, C, B, S, N> AssignmentInstructions<F, AssignedForeignPoint<F, C, B>>
314 for ForeignWeierstrassEccChip<F, C, B, S, N>
315where
316 F: CircuitField,
317 C: WeierstrassCurve,
318 B: FieldEmulationParams<F, C::Base>,
319 S: ScalarFieldInstructions<F>,
320 S::Scalar: InnerValue<Element = C::ScalarField>,
321 N: NativeInstructions<F>,
322{
323 fn assign(
329 &self,
330 layouter: &mut impl Layouter<F>,
331 value: Value<C::CryptographicGroup>,
332 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
333 if C::COFACTOR > 1 {
334 let cofactor = C::ScalarField::from_u128(C::COFACTOR);
335 let cofactor_root = self.assign_without_subgroup_check(
338 layouter,
339 value.map(|point| point * cofactor.invert().unwrap()),
340 )?;
341 self.mul_by_constant(layouter, cofactor, &cofactor_root)
342 } else {
343 self.assign_without_subgroup_check(layouter, value)
344 }
345 }
346
347 fn assign_fixed(
348 &self,
349 layouter: &mut impl Layouter<F>,
350 constant: C::CryptographicGroup,
351 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
352 let (xv, yv, is_id_value) = if C::CryptographicGroup::is_identity(&constant).into() {
353 (C::Base::ZERO, C::Base::ZERO, true)
354 } else {
355 let coordinates = constant
356 .into()
357 .coordinates()
358 .expect("assign_point_unchecked: invalid point given");
359 (coordinates.0, coordinates.1, false)
360 };
361 let is_id = self.native_gadget.assign_fixed(layouter, is_id_value)?;
362 let x = self.base_field_chip().assign_fixed(layouter, xv)?;
363 let y = self.base_field_chip().assign_fixed(layouter, yv)?;
364 let p = AssignedForeignPoint::<F, C, B> {
365 point: Value::known(constant),
366 is_id,
367 x,
368 y,
369 };
370 Ok(p)
371 }
372}
373
374impl<F, C, B, S, N> PublicInputInstructions<F, AssignedForeignPoint<F, C, B>>
375 for ForeignWeierstrassEccChip<F, C, B, S, N>
376where
377 F: CircuitField,
378 C: WeierstrassCurve,
379 B: FieldEmulationParams<F, C::Base>,
380 S: ScalarFieldInstructions<F>,
381 S::Scalar: InnerValue<Element = C::ScalarField>,
382 N: NativeInstructions<F> + PublicInputInstructions<F, AssignedBit<F>>,
383{
384 fn as_public_input(
385 &self,
386 layouter: &mut impl Layouter<F>,
387 p: &AssignedForeignPoint<F, C, B>,
388 ) -> Result<Vec<AssignedNative<F>>, Error> {
389 let mut pis = [
390 self.base_field_chip.as_public_input(layouter, &p.x)?.as_slice(),
391 self.base_field_chip.as_public_input(layouter, &p.y)?.as_slice(),
392 ]
393 .concat();
394
395 let base = F::from(2).pow_vartime([B::LOG2_BASE as u64]);
399 pis[0] = self.native_gadget.linear_combination(
400 layouter,
401 &[(F::ONE, pis[0].clone()), (base, p.is_id.clone().into())],
402 F::ZERO,
403 )?;
404
405 Ok(pis)
406 }
407
408 fn constrain_as_public_input(
409 &self,
410 layouter: &mut impl Layouter<F>,
411 assigned: &AssignedForeignPoint<F, C, B>,
412 ) -> Result<(), Error> {
413 self.as_public_input(layouter, assigned)?
414 .iter()
415 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
416 }
417
418 fn assign_as_public_input(
419 &self,
420 layouter: &mut impl Layouter<F>,
421 value: Value<C::CryptographicGroup>,
422 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
423 let point = self.assign_without_subgroup_check(layouter, value)?;
427 self.constrain_as_public_input(layouter, &point)?;
428 Ok(point)
429 }
430}
431
432impl<F, C, B, S, N> AssignmentInstructions<F, AssignedNative<F>>
438 for ForeignWeierstrassEccChip<F, C, B, S, N>
439where
440 F: CircuitField,
441 C: WeierstrassCurve,
442 B: FieldEmulationParams<F, C::Base>,
443 S: ScalarFieldInstructions<F, Scalar = AssignedNative<F>>,
444 S::Scalar: InnerValue<Element = C::ScalarField>,
445 N: NativeInstructions<F>,
446{
447 fn assign(
448 &self,
449 layouter: &mut impl Layouter<F>,
450 value: Value<<S::Scalar as InnerValue>::Element>,
451 ) -> Result<S::Scalar, Error> {
452 self.scalar_field_chip().assign(layouter, value)
453 }
454
455 fn assign_fixed(
456 &self,
457 layouter: &mut impl Layouter<F>,
458 constant: <S::Scalar as InnerValue>::Element,
459 ) -> Result<S::Scalar, Error> {
460 self.scalar_field_chip().assign_fixed(layouter, constant)
461 }
462}
463
464impl<F, C, B, S, SP, N> AssignmentInstructions<F, AssignedField<F, C::ScalarField, SP>>
469 for ForeignWeierstrassEccChip<F, C, B, S, N>
470where
471 F: CircuitField,
472 C: WeierstrassCurve,
473 B: FieldEmulationParams<F, C::Base>,
474 S: ScalarFieldInstructions<F, Scalar = AssignedField<F, C::ScalarField, SP>>,
475 S::Scalar: InnerValue<Element = C::ScalarField>,
476 SP: FieldEmulationParams<F, C::ScalarField>,
477 N: NativeInstructions<F>,
478{
479 fn assign(
480 &self,
481 layouter: &mut impl Layouter<F>,
482 value: Value<<S::Scalar as InnerValue>::Element>,
483 ) -> Result<S::Scalar, Error> {
484 self.scalar_field_chip().assign(layouter, value)
485 }
486
487 fn assign_fixed(
488 &self,
489 layouter: &mut impl Layouter<F>,
490 constant: <S::Scalar as InnerValue>::Element,
491 ) -> Result<S::Scalar, Error> {
492 self.scalar_field_chip().assign_fixed(layouter, constant)
493 }
494}
495
496impl<F, C, B, S, N> AssertionInstructions<F, AssignedForeignPoint<F, C, B>>
497 for ForeignWeierstrassEccChip<F, C, B, S, N>
498where
499 F: CircuitField,
500 C: WeierstrassCurve,
501 B: FieldEmulationParams<F, C::Base>,
502 S: ScalarFieldInstructions<F>,
503 S::Scalar: InnerValue<Element = C::ScalarField>,
504 N: NativeInstructions<F>,
505{
506 fn assert_equal(
507 &self,
508 layouter: &mut impl Layouter<F>,
509 p: &AssignedForeignPoint<F, C, B>,
510 q: &AssignedForeignPoint<F, C, B>,
511 ) -> Result<(), Error> {
512 self.native_gadget.assert_equal(layouter, &p.is_id, &q.is_id)?;
517 self.base_field_chip().assert_equal(layouter, &p.x, &q.x)?;
518 self.base_field_chip().assert_equal(layouter, &p.y, &q.y)
519 }
520
521 fn assert_not_equal(
522 &self,
523 layouter: &mut impl Layouter<F>,
524 p: &AssignedForeignPoint<F, C, B>,
525 q: &AssignedForeignPoint<F, C, B>,
526 ) -> Result<(), Error> {
527 let equal = self.is_equal(layouter, p, q)?;
528 self.native_gadget.assert_equal_to_fixed(layouter, &equal, false)
529 }
530
531 fn assert_equal_to_fixed(
532 &self,
533 layouter: &mut impl Layouter<F>,
534 p: &AssignedForeignPoint<F, C, B>,
535 constant: C::CryptographicGroup,
536 ) -> Result<(), Error> {
537 if constant.is_identity().into() {
538 self.assert_zero(layouter, p)
539 } else {
540 let coordinates = constant.into().coordinates().expect("Valid point");
541 self.base_field_chip().assert_equal_to_fixed(layouter, &p.x, coordinates.0)?;
542 self.base_field_chip().assert_equal_to_fixed(layouter, &p.y, coordinates.1)?;
543 self.assert_non_zero(layouter, p)
544 }
545 }
546
547 fn assert_not_equal_to_fixed(
548 &self,
549 layouter: &mut impl Layouter<F>,
550 p: &AssignedForeignPoint<F, C, B>,
551 constant: C::CryptographicGroup,
552 ) -> Result<(), Error> {
553 if constant.is_identity().into() {
554 self.assert_non_zero(layouter, p)
555 } else {
556 let equal = self.is_equal_to_fixed(layouter, p, constant)?;
557 self.native_gadget.assert_equal_to_fixed(layouter, &equal, false)
558 }
559 }
560}
561
562impl<F, C, B, S, N> EqualityInstructions<F, AssignedForeignPoint<F, C, B>>
563 for ForeignWeierstrassEccChip<F, C, B, S, N>
564where
565 F: CircuitField,
566 C: WeierstrassCurve,
567 B: FieldEmulationParams<F, C::Base>,
568 S: ScalarFieldInstructions<F>,
569 S::Scalar: InnerValue<Element = C::ScalarField>,
570 N: NativeInstructions<F>,
571{
572 fn is_equal(
573 &self,
574 layouter: &mut impl Layouter<F>,
575 p: &AssignedForeignPoint<F, C, B>,
576 q: &AssignedForeignPoint<F, C, B>,
577 ) -> Result<AssignedBit<F>, Error> {
578 let eq_coordinates = {
581 let eq_x = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
582 let eq_y = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
583 let eq_x_and_y = self.native_gadget.and(layouter, &[eq_x, eq_y])?;
584 let both_are_id =
585 self.native_gadget.and(layouter, &[p.is_id.clone(), q.is_id.clone()])?;
586 self.native_gadget.or(layouter, &[eq_x_and_y, both_are_id])?
587 };
588 let eq_id_flag = self.native_gadget.is_equal(layouter, &p.is_id, &q.is_id)?;
589 self.native_gadget.and(layouter, &[eq_id_flag, eq_coordinates])
590 }
591
592 fn is_not_equal(
593 &self,
594 layouter: &mut impl Layouter<F>,
595 x: &AssignedForeignPoint<F, C, B>,
596 y: &AssignedForeignPoint<F, C, B>,
597 ) -> Result<AssignedBit<F>, Error> {
598 let b = self.is_equal(layouter, x, y)?;
599 self.native_gadget.not(layouter, &b)
600 }
601
602 fn is_equal_to_fixed(
603 &self,
604 layouter: &mut impl Layouter<F>,
605 p: &AssignedForeignPoint<F, C, B>,
606 constant: C::CryptographicGroup,
607 ) -> Result<AssignedBit<F>, Error> {
608 if constant.is_identity().into() {
609 Ok(p.is_id.clone())
610 } else {
611 let coordinates = constant.into().coordinates().expect("Valid point");
612 let eq_x = self.base_field_chip().is_equal_to_fixed(layouter, &p.x, coordinates.0)?;
613 let eq_y = self.base_field_chip().is_equal_to_fixed(layouter, &p.y, coordinates.1)?;
614 let p_is_not_id = self.native_gadget.not(layouter, &p.is_id)?;
615 self.native_gadget.and(layouter, &[eq_x, eq_y, p_is_not_id])
616 }
617 }
618
619 fn is_not_equal_to_fixed(
620 &self,
621 layouter: &mut impl Layouter<F>,
622 x: &AssignedForeignPoint<F, C, B>,
623 constant: C::CryptographicGroup,
624 ) -> Result<AssignedBit<F>, Error> {
625 let b = self.is_equal_to_fixed(layouter, x, constant)?;
626 self.native_gadget.not(layouter, &b)
627 }
628}
629
630impl<F, C, B, S, N> ZeroInstructions<F, AssignedForeignPoint<F, C, B>>
631 for ForeignWeierstrassEccChip<F, C, B, S, N>
632where
633 F: CircuitField,
634 C: WeierstrassCurve,
635 B: FieldEmulationParams<F, C::Base>,
636 S: ScalarFieldInstructions<F>,
637 S::Scalar: InnerValue<Element = C::ScalarField>,
638 N: NativeInstructions<F>,
639{
640 fn assert_zero(
641 &self,
642 layouter: &mut impl Layouter<F>,
643 x: &AssignedForeignPoint<F, C, B>,
644 ) -> Result<(), Error> {
645 self.native_gadget.assert_equal_to_fixed(layouter, &x.is_id, true)
646 }
647
648 fn assert_non_zero(
649 &self,
650 layouter: &mut impl Layouter<F>,
651 x: &AssignedForeignPoint<F, C, B>,
652 ) -> Result<(), Error> {
653 self.native_gadget.assert_equal_to_fixed(layouter, &x.is_id, false)
654 }
655
656 fn is_zero(
657 &self,
658 _layouter: &mut impl Layouter<F>,
659 p: &AssignedForeignPoint<F, C, B>,
660 ) -> Result<AssignedBit<F>, Error> {
661 Ok(p.is_id.clone())
662 }
663}
664
665impl<F, C, B, S, N> ControlFlowInstructions<F, AssignedForeignPoint<F, C, B>>
666 for ForeignWeierstrassEccChip<F, C, B, S, N>
667where
668 F: CircuitField,
669 C: WeierstrassCurve,
670 B: FieldEmulationParams<F, C::Base>,
671 S: ScalarFieldInstructions<F>,
672 S::Scalar: InnerValue<Element = C::ScalarField>,
673 N: NativeInstructions<F>,
674{
675 fn select(
677 &self,
678 layouter: &mut impl Layouter<F>,
679 cond: &AssignedBit<F>,
680 p: &AssignedForeignPoint<F, C, B>,
681 q: &AssignedForeignPoint<F, C, B>,
682 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
683 let point = p.point.zip(q.point).zip(cond.value()).map(|((p, q), b)| if b { p } else { q });
684 let is_id = self.native_gadget.select(layouter, cond, &p.is_id, &q.is_id)?;
685 let x = self.base_field_chip().select(layouter, cond, &p.x, &q.x)?;
686 let y = self.base_field_chip().select(layouter, cond, &p.y, &q.y)?;
687 Ok(AssignedForeignPoint::<F, C, B> { point, is_id, x, y })
688 }
689}
690
691impl<F, C, B, S, N> EccInstructions<F, C> for ForeignWeierstrassEccChip<F, C, B, S, N>
692where
693 F: CircuitField,
694 C: WeierstrassCurve,
695 B: FieldEmulationParams<F, C::Base>,
696 S: ScalarFieldInstructions<F>,
697 S::Scalar: InnerValue<Element = C::ScalarField>,
698 N: NativeInstructions<F>,
699{
700 type Point = AssignedForeignPoint<F, C, B>;
701 type Coordinate = AssignedField<F, C::Base, B>;
702 type Scalar = S::Scalar;
703
704 fn add(
705 &self,
706 layouter: &mut impl Layouter<F>,
707 p: &Self::Point,
708 q: &Self::Point,
709 ) -> Result<Self::Point, Error> {
710 let r_curve = p.value().zip(q.value()).map(|(p, q)| p + q);
711 let r = self.assign_point_unchecked(layouter, r_curve)?;
712
713 let p_or_q_or_r_are_id = self.native_gadget.or(
715 layouter,
716 &[p.is_id.clone(), q.is_id.clone(), r.is_id.clone()],
717 )?;
718 let none_is_id = self.native_gadget.not(layouter, &p_or_q_or_r_are_id)?;
719 let px_eq_qx = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
720 let py_eq_qy = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
721 let px_neq_qx = self.native_gadget.not(layouter, &px_eq_qx)?;
722 let py_eq_neg_qy = {
723 let py_plus_qy = self.base_field_chip().add(layouter, &p.y, &q.y)?;
724 self.base_field_chip().is_zero(layouter, &py_plus_qy)?
725 };
726
727 self.cond_assert_equal(layouter, &p.is_id, &r, q)?;
729
730 self.cond_assert_equal(layouter, &q.is_id, &r, p)?;
732
733 let p_eq_nq = self.native_gadget.and(layouter, &[px_eq_qx.clone(), py_eq_neg_qy])?;
736 self.native_gadget.assert_equal(layouter, &p_eq_nq, &r.is_id)?;
737
738 let cond = self.native_gadget.and(layouter, &[px_eq_qx, py_eq_qy, none_is_id.clone()])?;
742 self.assert_double(layouter, p, &r, &cond)?;
743
744 let cond = self.native_gadget.and(layouter, &[px_neq_qx, none_is_id])?;
749 self.assert_add(layouter, p, q, &r, &cond)?;
750
751 Ok(r)
752 }
753
754 fn double(
755 &self,
756 layouter: &mut impl Layouter<F>,
757 p: &AssignedForeignPoint<F, C, B>,
758 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
759 let r_curve = p.value().map(|p| p + p);
760 let r = self.assign_point_unchecked(layouter, r_curve)?;
761
762 self.native_gadget.assert_equal(layouter, &p.is_id, &r.is_id)?;
764
765 let cond = self.native_gadget.not(layouter, &p.is_id)?;
770 self.assert_double(layouter, p, &r, &cond)?;
771
772 Ok(r)
773 }
774
775 fn negate(
776 &self,
777 layouter: &mut impl Layouter<F>,
778 p: &Self::Point,
779 ) -> Result<Self::Point, Error> {
780 let neg_y = self.base_field_chip().neg(layouter, &p.y)?;
781 let neg_y = self.base_field_chip().normalize(layouter, &neg_y)?;
782 Ok(AssignedForeignPoint::<F, C, B> {
783 point: -p.point,
784 is_id: p.is_id.clone(),
785 x: p.x.clone(),
786 y: neg_y,
787 })
788 }
789
790 fn msm(
791 &self,
792 layouter: &mut impl Layouter<F>,
793 scalars: &[Self::Scalar],
794 bases: &[Self::Point],
795 ) -> Result<Self::Point, Error> {
796 let scalars = scalars
797 .iter()
798 .map(|s| (s.clone(), C::ScalarField::NUM_BITS as usize))
799 .collect::<Vec<_>>();
800 self.msm_by_bounded_scalars(layouter, &scalars, bases)
801 }
802
803 fn msm_by_bounded_scalars(
804 &self,
805 layouter: &mut impl Layouter<F>,
806 scalars: &[(S::Scalar, usize)],
807 bases: &[AssignedForeignPoint<F, C, B>],
808 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
809 assert_eq!(scalars.len(), bases.len(), "`|scalars| != |bases|`");
810
811 const WS: usize = 4;
812 let scalar_chip = self.scalar_field_chip();
813
814 let (scalars, bases, bases_with_1bit_scalar) =
815 msm_preprocess(self, scalar_chip, layouter, scalars, bases)?;
816
817 let mut non_id_bases = vec![];
821 let mut scalars_of_non_id_bases = vec![];
822 let zero: S::Scalar = scalar_chip.assign_fixed(layouter, C::ScalarField::ZERO)?;
823 let g = self.assign_fixed(layouter, C::CryptographicGroup::generator())?;
824 for (s, b) in scalars.iter().zip(bases.iter()) {
825 let new_b = self.select(layouter, &b.is_id, &g, b)?;
826 let new_s = scalar_chip.select(layouter, &b.is_id, &zero, &s.0)?;
827 non_id_bases.push(new_b);
828 scalars_of_non_id_bases.push((new_s, s.1));
829 }
830
831 let nb_bits_per_glv_scalar = C::ScalarField::NUM_BITS.div_ceil(2) as usize;
835 let mut non_glv_scalars = vec![];
836 let mut non_glv_bases = vec![];
837 let mut glv_scalars = vec![];
838 let mut glv_bases = vec![];
839 for (s, b) in scalars_of_non_id_bases.iter().zip(non_id_bases.iter()) {
840 if C::has_cubic_endomorphism() && s.1 > nb_bits_per_glv_scalar + WS {
844 let ((s1, s2), (b1, b2)) = self.glv_split(layouter, &s.0, b)?;
845 glv_scalars.push((s1, nb_bits_per_glv_scalar));
846 glv_scalars.push((s2, nb_bits_per_glv_scalar));
847 glv_bases.push(b1);
848 glv_bases.push(b2);
849 } else {
850 non_glv_scalars.push(s.clone());
851 non_glv_bases.push(b.clone());
852 }
853 }
854
855 let scalars = [glv_scalars, non_glv_scalars].concat();
856 let bases = [glv_bases, non_glv_bases].concat();
857
858 let mut decomposed_scalars = vec![];
859 for (s, nb_bits_s) in scalars.iter() {
860 let s_bits = self.scalar_field_chip().assigned_to_le_chunks(
861 layouter,
862 s,
863 WS,
864 Some(nb_bits_s.div_ceil(WS)),
865 )?;
866 decomposed_scalars.push(s_bits)
867 }
868 let res = self.windowed_msm::<WS>(layouter, &decomposed_scalars, &bases)?;
869
870 add_1bit_scalar_bases(layouter, self, scalar_chip, &bases_with_1bit_scalar, res)
871 }
872
873 fn mul_by_constant(
874 &self,
875 layouter: &mut impl Layouter<F>,
876 scalar: C::ScalarField,
877 base: &Self::Point,
878 ) -> Result<Self::Point, Error> {
879 let scalar_as_big = scalar.to_biguint();
883 if scalar_as_big.bits() <= 128 {
884 let n = scalar_as_big
885 .to_u64_digits()
886 .iter()
887 .rev()
888 .fold(0u128, |acc, limb| (acc << 64) | (*limb as u128));
889
890 let id = self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
894 let g = self.assign_fixed(layouter, C::CryptographicGroup::generator())?;
895 let p = self.select(layouter, &base.is_id, &g, base)?;
896 let r = self.mul_by_u128(layouter, n, &p)?;
897 return self.select(layouter, &base.is_id, &id, &r);
898 }
899 let scalar_bits = scalar
900 .to_bits_le(None)
901 .iter()
902 .map(|b| self.native_gadget.assign_fixed(layouter, *b))
903 .collect::<Result<Vec<_>, Error>>()?;
904 self.msm_by_le_bits(layouter, &[scalar_bits], std::slice::from_ref(base))
905 }
906
907 fn point_from_coordinates(
908 &self,
909 layouter: &mut impl Layouter<F>,
910 x: &AssignedField<F, C::Base, B>,
911 y: &AssignedField<F, C::Base, B>,
912 ) -> Result<Self::Point, Error> {
913 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
914 let cond = self.native_gadget.assign_fixed(layouter, true)?;
915 on_curve::assert_is_on_curve::<F, C, B, N>(
916 layouter,
917 &cond,
918 x,
919 y,
920 self.base_field_chip(),
921 &self.config.on_curve_config,
922 )?;
923 let point = x
927 .value()
928 .zip(y.value())
929 .map(|(x, y)| C::from_xy(x, y).unwrap_or(C::identity()).into_subgroup());
930 Ok(AssignedForeignPoint::<F, C, B> {
931 point,
932 is_id,
933 x: x.clone(),
934 y: y.clone(),
935 })
936 }
937
938 fn assign_without_subgroup_check(
939 &self,
940 layouter: &mut impl Layouter<F>,
941 value: Value<C::CryptographicGroup>,
942 ) -> Result<Self::Point, Error> {
943 let p = self.assign_point_unchecked(layouter, value)?;
944 let is_not_id = self.native_gadget.not(layouter, &p.is_id)?;
945 on_curve::assert_is_on_curve::<F, C, B, N>(
946 layouter,
947 &is_not_id,
948 &p.x,
949 &p.y,
950 self.base_field_chip(),
951 &self.config.on_curve_config,
952 )?;
953 Ok(p)
954 }
955
956 fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
957 point.x.clone()
958 }
959
960 fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
961 point.y.clone()
962 }
963
964 fn base_field(&self) -> &impl DecompositionInstructions<F, Self::Coordinate> {
965 self.base_field_chip()
966 }
967}
968
969impl<F, C, B, S, N> ForeignWeierstrassEccChip<F, C, B, S, N>
970where
971 F: CircuitField,
972 C: WeierstrassCurve,
973 B: FieldEmulationParams<F, C::Base>,
974 S: ScalarFieldInstructions<F>,
975 S::Scalar: InnerValue<Element = C::ScalarField>,
976 N: NativeInstructions<F>,
977{
978 pub fn new(
983 config: &ForeignWeierstrassEccConfig<C>,
984 native_gadget: &N,
985 scalar_field_chip: &S,
986 ) -> Self {
987 let mut rng = OsRng;
988 let random_point = C::random(&mut rng).into_subgroup();
989
990 let base_field_chip = FieldChip::new(&config.base_field_config, native_gadget);
991
992 Self {
993 config: config.clone(),
994 native_gadget: native_gadget.clone(),
995 base_field_chip,
996 scalar_field_chip: scalar_field_chip.clone(),
997 tag_cnt: Rc::new(RefCell::new(1)),
998 msm_randomness: Rc::new(RefCell::new(HashMap::new())),
999 random_point,
1000 }
1001 }
1002
1003 fn completeness_error_if<V>(value: &Value<V>, f: impl FnOnce(&V) -> bool) -> Result<(), Error> {
1006 value.error_if_known_and(f).map_err(|_| Error::CompletenessFailure)
1007 }
1008
1009 pub fn base_field_chip(&self) -> &FieldChip<F, C::Base, B, N> {
1011 &self.base_field_chip
1012 }
1013
1014 pub fn scalar_field_chip(&self) -> &S {
1016 &self.scalar_field_chip
1017 }
1018
1019 pub fn configure(
1021 meta: &mut ConstraintSystem<F>,
1022 base_field_config: &FieldChipConfig,
1023 advice_columns: &[Column<Advice>],
1024 nb_parallel_range_checks: usize,
1025 max_bit_len: u32,
1026 ) -> ForeignWeierstrassEccConfig<C> {
1027 let cond_col_idx = base_field_config.x_cols.len() + base_field_config.v_cols.len() + 1;
1030 assert!(advice_columns.len() > cond_col_idx);
1031 let cond_col = advice_columns[cond_col_idx];
1032 meta.enable_equality(cond_col);
1033
1034 let on_curve_config = OnCurveConfig::<C>::configure::<F, B>(
1035 meta,
1036 base_field_config,
1037 &cond_col,
1038 nb_parallel_range_checks,
1039 max_bit_len,
1040 );
1041
1042 let slope_config = SlopeConfig::<C>::configure::<F, B>(
1043 meta,
1044 base_field_config,
1045 &cond_col,
1046 nb_parallel_range_checks,
1047 max_bit_len,
1048 );
1049
1050 let tangent_config = TangentConfig::<C>::configure::<F, B>(
1051 meta,
1052 base_field_config,
1053 &cond_col,
1054 nb_parallel_range_checks,
1055 max_bit_len,
1056 );
1057
1058 let lambda_squared_config = LambdaSquaredConfig::<C>::configure::<F, B>(
1059 meta,
1060 base_field_config,
1061 &cond_col,
1062 nb_parallel_range_checks,
1063 max_bit_len,
1064 );
1065
1066 let (q_multi_select, idx_col_multi_select, tag_col_multi_select) =
1067 configure_multi_select_lookup(meta, advice_columns, base_field_config);
1068
1069 ForeignWeierstrassEccConfig {
1070 base_field_config: base_field_config.clone(),
1071 on_curve_config,
1072 slope_config,
1073 tangent_config,
1074 lambda_squared_config,
1075 q_multi_select,
1076 idx_col_multi_select,
1077 tag_col_multi_select,
1078 }
1079 }
1080
1081 fn assign_point_unchecked(
1086 &self,
1087 layouter: &mut impl Layouter<F>,
1088 p: Value<C::CryptographicGroup>,
1089 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1090 let values = p.map(|p| {
1091 if C::CryptographicGroup::is_identity(&p).into() {
1092 (C::Base::ZERO, C::Base::ZERO, true)
1093 } else {
1094 let coordinates =
1095 p.into().coordinates().expect("assign_point_unchecked: invalid point given");
1096 (coordinates.0, coordinates.1, false)
1097 }
1098 });
1099 let x = self.base_field_chip().assign(layouter, values.map(|v| v.0))?;
1100 let y = self.base_field_chip().assign(layouter, values.map(|v| v.1))?;
1101 let is_id = self.native_gadget.assign(layouter, values.map(|v| v.2))?;
1102 let p = AssignedForeignPoint::<F, C, B> {
1103 point: p,
1104 is_id,
1105 x,
1106 y,
1107 };
1108 Ok(p)
1109 }
1110
1111 fn incomplete_add(
1132 &self,
1133 layouter: &mut impl Layouter<F>,
1134 p: &AssignedForeignPoint<F, C, B>,
1135 q: &AssignedForeignPoint<F, C, B>,
1136 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1137 let r_curve = p.value().zip(q.value()).map(|(p, q)| p + q);
1138 let r = self.assign_point_unchecked(layouter, r_curve)?;
1139
1140 self.native_gadget.assert_equal(layouter, &p.is_id, &r.is_id)?;
1142
1143 let one = self.native_gadget.assign_fixed(layouter, true)?;
1150 self.assert_add(layouter, p, q, &r, &one)?;
1151
1152 Ok(r)
1153 }
1154
1155 fn assert_double(
1168 &self,
1169 layouter: &mut impl Layouter<F>,
1170 p: &AssignedForeignPoint<F, C, B>,
1171 r: &AssignedForeignPoint<F, C, B>,
1172 cond: &AssignedBit<F>,
1173 ) -> Result<(), Error> {
1174 let lambda = {
1176 let lambda_value = p.value().map(|p| {
1177 if C::CryptographicGroup::is_identity(&p).into() {
1178 C::Base::ONE
1179 } else {
1180 let p = p.into().coordinates().unwrap();
1181 (C::Base::from(3) * p.0 * p.0 + C::A)
1182 * (C::Base::from(2) * p.1).invert().unwrap()
1183 }
1184 });
1185 self.base_field_chip().assign(layouter, lambda_value)?
1186 };
1187
1188 tangent::assert_tangent::<F, C, B, N>(
1190 layouter,
1191 cond,
1192 (&p.x, &p.y),
1193 &lambda,
1194 self.base_field_chip(),
1195 &self.config.tangent_config,
1196 )?;
1197
1198 lambda_squared::assert_lambda_squared(
1200 layouter,
1201 cond,
1202 (&p.x, &p.x, &r.x),
1203 &lambda,
1204 self.base_field_chip(),
1205 &self.config.lambda_squared_config,
1206 )?;
1207
1208 self.assert_slope(layouter, cond, p, r, true, &lambda)?;
1219
1220 Ok(())
1221 }
1222
1223 fn assert_add(
1251 &self,
1252 layouter: &mut impl Layouter<F>,
1253 p: &AssignedForeignPoint<F, C, B>,
1254 q: &AssignedForeignPoint<F, C, B>,
1255 r: &AssignedForeignPoint<F, C, B>,
1256 cond: &AssignedBit<F>,
1257 ) -> Result<(), Error> {
1258 let lambda = {
1260 let lambda_value = p.value().zip(q.value()).map(|(p, q)| {
1261 if p.is_identity().into() || q.is_identity().into() {
1262 C::Base::ONE
1263 } else {
1264 let p = p.into().coordinates().unwrap();
1265 let q = q.into().coordinates().unwrap();
1266 if p.0 == q.0 {
1267 C::Base::ONE
1268 } else {
1269 (q.1 - p.1) * (q.0 - p.0).invert().unwrap()
1270 }
1271 }
1272 });
1273 self.base_field_chip().assign(layouter, lambda_value)?
1274 };
1275
1276 self.assert_slope(layouter, cond, p, q, false, &lambda)?;
1286
1287 lambda_squared::assert_lambda_squared(
1289 layouter,
1290 cond,
1291 (&p.x, &q.x, &r.x),
1292 &lambda,
1293 self.base_field_chip(),
1294 &self.config.lambda_squared_config,
1295 )?;
1296
1297 self.assert_slope(layouter, cond, p, r, true, &lambda)?;
1314
1315 Ok(())
1316 }
1317
1318 fn assert_slope(
1340 &self,
1341 layouter: &mut impl Layouter<F>,
1342 cond: &AssignedBit<F>,
1343 p: &AssignedForeignPoint<F, C, B>,
1344 q: &AssignedForeignPoint<F, C, B>,
1345 negate_q: bool,
1346 lambda: &AssignedField<F, C::Base, B>,
1347 ) -> Result<(), Error> {
1348 slope::assert_slope::<F, C, B, N>(
1349 layouter,
1350 cond,
1351 (&p.x, &p.y),
1352 (&q.x, &q.y, negate_q),
1353 lambda,
1354 self.base_field_chip(),
1355 &self.config.slope_config,
1356 )
1357 }
1358
1359 #[allow(clippy::type_complexity)]
1364 fn fill_dynamic_lookup_row(
1365 &self,
1366 layouter: &mut impl Layouter<F>,
1367 point: &AssignedForeignPoint<F, C, B>,
1368 index: &AssignedNative<F>,
1369 table_tag: F,
1370 enable_lookup: bool,
1371 ) -> Result<(Vec<AssignedNative<F>>, Vec<AssignedNative<F>>), Error> {
1372 fill_dynamic_lookup_row(
1373 layouter,
1374 &point.x.limb_values(),
1375 &point.y.limb_values(),
1376 index,
1377 &self.config.base_field_config.x_cols,
1378 &self.config.base_field_config.z_cols, self.config.idx_col_multi_select,
1380 self.config.tag_col_multi_select,
1381 self.config.q_multi_select,
1382 table_tag,
1383 enable_lookup,
1384 )
1385 }
1386
1387 fn load_multi_select_table(
1399 &self,
1400 layouter: &mut impl Layouter<F>,
1401 point_table: &[AssignedForeignPoint<F, C, B>],
1402 table_tag: F,
1403 ) -> Result<(), Error> {
1404 for (i, point) in point_table.iter().enumerate() {
1405 let index = self.native_gadget.assign_fixed(layouter, F::from(i as u64))?;
1406 self.fill_dynamic_lookup_row(layouter, point, &index, table_tag, false)?;
1407 }
1408 Ok(())
1409 }
1410
1411 fn multi_select(
1434 &self,
1435 layouter: &mut impl Layouter<F>,
1436 selector: &AssignedNative<F>,
1437 point_table: &[AssignedForeignPoint<F, C, B>],
1438 table_tag: F,
1439 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1440 let mut selector_idx = 0;
1442 selector.value().map(|v| {
1443 let digits = v.to_biguint().to_u32_digits();
1444 let digit = if digits.is_empty() { 0 } else { digits[0] };
1445 debug_assert!(digits.len() <= 1);
1446 debug_assert!(digit < point_table.len() as u32);
1447 selector_idx = digit;
1448 });
1449
1450 let selected = point_table[selector_idx as usize].clone();
1451
1452 let (xs, ys) =
1456 self.fill_dynamic_lookup_row(layouter, &selected, selector, table_tag, true)?;
1457 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1458 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1459 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
1460
1461 let result = AssignedForeignPoint::<F, C, B> {
1462 point: selected.point,
1463 is_id,
1464 x,
1465 y,
1466 };
1467
1468 Ok(result)
1469 }
1470
1471 pub fn k_out_of_n_points(
1489 &self,
1490 layouter: &mut impl Layouter<F>,
1491 table: &[AssignedForeignPoint<F, C, B>],
1492 selected: &[Value<C::CryptographicGroup>],
1493 ) -> Result<Vec<AssignedForeignPoint<F, C, B>>, Error> {
1494 let n = table.len();
1495 let k = selected.len();
1496 assert!(k <= n);
1497
1498 assert!((n as u128) < (1 << (F::NUM_BITS / 2)));
1500
1501 table.iter().try_for_each(|point| self.assert_non_zero(layouter, point))?;
1503
1504 let tag_cnt = *self.tag_cnt.borrow();
1511 self.tag_cnt.replace(tag_cnt + 1);
1512 self.load_multi_select_table(layouter, table, F::from(tag_cnt))?;
1513
1514 let table_values =
1516 Value::<Vec<C::CryptographicGroup>>::from_iter(table.iter().map(|point| point.value()));
1517 let selected_idxs = selected
1518 .iter()
1519 .map(|point_value| {
1520 point_value
1521 .zip(table_values.clone())
1522 .map(|(p, ts)| ts.iter().position(|table_val| *table_val == p).unwrap_or(0))
1523 })
1524 .collect::<Vec<_>>();
1525
1526 Value::<Vec<usize>>::from_iter(selected_idxs.clone())
1529 .error_if_known_and(|idxs| idxs.iter().zip(idxs.iter().skip(1)).any(|(i, j)| i >= j))?;
1530
1531 let assigned_selected_idxs = selected_idxs
1533 .clone()
1534 .iter()
1535 .map(|i_value| self.native_gadget.assign(layouter, i_value.map(|i| F::from(i as u64))))
1536 .collect::<Result<Vec<AssignedNative<F>>, Error>>()?;
1537
1538 let l = BigUint::one() << BigUint::from(n).bits();
1544 assigned_selected_idxs
1545 .iter()
1546 .zip(assigned_selected_idxs.iter().skip(1))
1547 .try_for_each(|(idx, next_idx)| {
1548 let diff_minus_one = self.native_gadget.linear_combination(
1549 layouter,
1550 &[(F::ONE, next_idx.clone()), (-F::ONE, idx.clone())],
1551 -F::ONE,
1552 )?;
1553 self.native_gadget.assert_lower_than_fixed(layouter, &diff_minus_one, &l)
1554 })?;
1555
1556 let mut unwrapped_selected_idxs = vec![0; k];
1558 selected_idxs.iter().enumerate().for_each(|(i, idx)| {
1559 idx.map(|j| unwrapped_selected_idxs[i] = j);
1560 });
1561 let selected_points = unwrapped_selected_idxs
1562 .iter()
1563 .zip(assigned_selected_idxs.iter())
1564 .map(|(i, selected_idx)| {
1565 let (xs, ys) = self.fill_dynamic_lookup_row(
1566 layouter,
1567 &table[*i],
1568 selected_idx,
1569 F::from(tag_cnt),
1570 true,
1571 )?;
1572 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1573 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1574 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
1575 Ok(AssignedForeignPoint::<F, C, B> {
1576 point: table[*i].value(),
1577 is_id,
1578 x,
1579 y,
1580 })
1581 })
1582 .collect::<Result<Vec<_>, Error>>()?;
1583
1584 Ok(selected_points)
1585 }
1586
1587 fn incomplete_assert_different_x(
1594 &self,
1595 layouter: &mut impl Layouter<F>,
1596 p: &AssignedForeignPoint<F, C, B>,
1597 q: &AssignedForeignPoint<F, C, B>,
1598 ) -> Result<(), Error> {
1599 assert!(p.x.is_well_formed());
1600 assert!(q.x.is_well_formed());
1601
1602 let native_gadget = &self.native_gadget;
1615 let base = big_to_fe::<F>(BigUint::one() << B::LOG2_BASE);
1616 let m = bigint_to_fe::<F>(&p.x.modulus());
1617
1618 let mut terms = vec![];
1619 let mut coeff = F::ONE;
1620 for (px_i, qx_i) in p.x.limb_values().iter().zip(q.x.limb_values().iter()) {
1621 terms.push((coeff, px_i.clone()));
1622 terms.push((-coeff, qx_i.clone()));
1623 coeff *= base;
1624 }
1625
1626 let diff = native_gadget.linear_combination(layouter, &terms, F::ZERO)?;
1627
1628 native_gadget.assert_non_zero(layouter, &diff)?;
1632 native_gadget.assert_not_equal_to_fixed(layouter, &diff, m)?;
1633 native_gadget.assert_not_equal_to_fixed(layouter, &diff, -m)
1634 }
1635
1636 fn mul_by_u128(
1646 &self,
1647 layouter: &mut impl Layouter<F>,
1648 n: u128,
1649 p: &AssignedForeignPoint<F, C, B>,
1650 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1651 if n == 0 {
1652 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1653 };
1654
1655 assert!(129 < C::ScalarField::NUM_BITS);
1658
1659 let mut res = None;
1662
1663 let mut tmp = p.clone();
1665
1666 let mut n = n;
1668 while n > 0 {
1669 if !n.is_multiple_of(2) {
1690 res = match res {
1691 None => Some(tmp.clone()),
1692 Some(acc) => Some(self.incomplete_add(layouter, &acc, &tmp)?),
1693 };
1694 }
1695 n >>= 1;
1696
1697 if n > 0 {
1698 tmp = self.double(layouter, &tmp)?
1699 }
1700 }
1701
1702 Ok(res.unwrap())
1703 }
1704
1705 fn msm_randomness<const WS: usize>(
1720 &self,
1721 layouter: &mut impl Layouter<F>,
1722 ) -> Result<MsmRandomness<F, C, B>, Error> {
1723 if let Some(cached) = self.msm_randomness.borrow().get(&WS) {
1724 return Ok(cached.clone());
1725 }
1726
1727 let r = match self.msm_randomness.borrow().values().next().map(|c| c.r.clone()) {
1729 Some(r) => r,
1730 None => {
1731 self.assign_without_subgroup_check(layouter, Value::known(self.random_point))?
1732 }
1733 };
1734 self.assert_non_zero(layouter, &r)?;
1735
1736 let alpha = self.mul_by_u128(layouter, (1u128 << WS) - 1, &r)?;
1737 let neg_alpha = self.negate(layouter, &alpha)?;
1738
1739 let windowed_randomness = MsmRandomness { r, neg_alpha };
1740 self.msm_randomness.borrow_mut().insert(WS, windowed_randomness.clone());
1741 Ok(windowed_randomness)
1742 }
1743
1744 fn windowed_msm<const WS: usize>(
1765 &self,
1766 layouter: &mut impl Layouter<F>,
1767 scalars: &[Vec<AssignedNative<F>>],
1768 bases: &[AssignedForeignPoint<F, C, B>],
1769 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1770 assert_eq!(scalars.len(), bases.len(), "`|scalars| != |bases|`");
1771
1772 if scalars.is_empty() {
1773 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1774 }
1775
1776 for p in bases.iter() {
1778 self.assert_non_zero(layouter, p)?;
1779 }
1780
1781 let zero: AssignedNative<F> = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
1786 let max_len = scalars.iter().fold(0, |m, chunks| max(m, chunks.len()));
1787 let mut padded_scalars = vec![];
1788 for s_bits in scalars.iter() {
1789 let mut s_bits = s_bits.to_vec();
1791 s_bits.resize(max_len, zero.clone());
1792 let rev_s_bits = s_bits.into_iter().rev().collect::<Vec<_>>();
1794 padded_scalars.push(rev_s_bits)
1795 }
1796
1797 let MsmRandomness { r, neg_alpha } = self.msm_randomness::<WS>(layouter)?;
1824
1825 let l_times_r = self.mul_by_u128(layouter, bases.len() as u128, &r)?;
1826
1827 let tag_cnt = *self.tag_cnt.clone().borrow();
1829 self.tag_cnt.replace(tag_cnt + bases.len() as u64);
1830
1831 let mut tables = vec![];
1833 for (i, p) in bases.iter().enumerate() {
1834 self.incomplete_assert_different_x(layouter, &neg_alpha, p)?;
1836 let mut acc = neg_alpha.clone();
1837 let mut p_table = vec![acc.clone()];
1838 for _ in 1..(1usize << WS) {
1839 Self::completeness_error_if(&acc.value().zip(p.value()), |(av, pv)| {
1856 av == pv || *av == -(*pv)
1857 })?;
1858
1859 acc = self.incomplete_add(layouter, &acc, p)?;
1860
1861 assert!(acc.x.is_well_formed() && acc.y.is_well_formed());
1862 p_table.push(acc.clone())
1863 }
1864 self.load_multi_select_table(layouter, &p_table, F::from(tag_cnt + i as u64))?;
1865 tables.push(p_table)
1866 }
1867
1868 let nb_iterations = max_len;
1869 let mut acc = l_times_r.clone();
1870
1871 #[allow(clippy::needless_range_loop)]
1872 for i in 0..nb_iterations {
1873 for _ in 0..WS {
1874 acc = self.double(layouter, &acc)?;
1875 }
1876 for j in 0..bases.len() {
1877 let window = &padded_scalars[j][i];
1878 let addend =
1879 self.multi_select(layouter, window, &tables[j], F::from(tag_cnt + j as u64))?;
1880 Self::completeness_error_if(&acc.value().zip(addend.value()), |(av, addv)| {
1894 av == addv || *av == -(*addv)
1895 })?;
1896
1897 self.incomplete_assert_different_x(layouter, &acc, &addend)?;
1898 acc = self.incomplete_add(layouter, &acc, &addend)?;
1899 }
1900 }
1901
1902 let r_correction = self.negate(layouter, &l_times_r)?;
1903 self.add(layouter, &acc, &r_correction)
1904 }
1905
1906 pub fn msm_by_le_bits(
1922 &self,
1923 layouter: &mut impl Layouter<F>,
1924 scalars: &[Vec<AssignedBit<F>>],
1925 bases: &[AssignedForeignPoint<F, C, B>],
1926 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1927 const WS: usize = 4;
1931 let scalars = scalars
1932 .iter()
1933 .map(|bits| {
1934 bits.chunks(WS)
1935 .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
1936 .collect::<Result<Vec<_>, Error>>()
1937 })
1938 .collect::<Result<Vec<_>, Error>>()?;
1939 self.windowed_msm::<WS>(layouter, &scalars, bases)
1940 }
1941
1942 #[allow(clippy::type_complexity)]
1951 fn glv_split(
1952 &self,
1953 layouter: &mut impl Layouter<F>,
1954 scalar: &S::Scalar,
1955 base: &AssignedForeignPoint<F, C, B>,
1956 ) -> Result<
1957 (
1958 (S::Scalar, S::Scalar),
1959 (AssignedForeignPoint<F, C, B>, AssignedForeignPoint<F, C, B>),
1960 ),
1961 Error,
1962 > {
1963 let zeta_base = C::base_zeta();
1964 let zeta_scalar = C::scalar_zeta();
1965
1966 let decomposed = scalar.value().map(|x| glv_scalar_decomposition(&x, &zeta_scalar));
1967 let s1_value = decomposed.map(|((s1, _), _)| s1);
1968 let x1_value = decomposed.map(|((_, x1), _)| x1);
1969 let s2_value = decomposed.map(|(_, (s2, _))| s2);
1970 let x2_value = decomposed.map(|(_, (_, x2))| x2);
1971
1972 let x1 = self.scalar_field_chip.assign(layouter, x1_value)?;
1973 let x2 = self.scalar_field_chip.assign(layouter, x2_value)?;
1974
1975 let s1 = self.native_gadget.assign(layouter, s1_value)?;
1976 let s2 = self.native_gadget.assign(layouter, s2_value)?;
1977
1978 let neg_x1 = self.scalar_field_chip.neg(layouter, &x1)?;
1979 let neg_x2 = self.scalar_field_chip.neg(layouter, &x2)?;
1980
1981 let signed_x1 = self.scalar_field_chip.select(layouter, &s1, &x1, &neg_x1)?;
1982 let signed_x2 = self.scalar_field_chip.select(layouter, &s2, &x2, &neg_x2)?;
1983
1984 let x = self.scalar_field_chip.linear_combination(
1986 layouter,
1987 &[(C::ScalarField::ONE, signed_x1), (zeta_scalar, signed_x2)],
1988 C::ScalarField::ZERO,
1989 )?;
1990 self.scalar_field_chip.assert_equal(layouter, &x, scalar)?;
1991
1992 let zeta_x = self.base_field_chip.mul_by_constant(layouter, &base.x, zeta_base)?;
1993 let zeta_p = AssignedForeignPoint::<F, C, B> {
1994 point: base.point.map(|p| {
1995 if p.is_identity().into() {
1996 p
1997 } else {
1998 let coordinates = p.into().coordinates().unwrap();
1999 let zeta_x = zeta_base * coordinates.0;
2000 C::from_xy(zeta_x, coordinates.1).unwrap().into_subgroup()
2001 }
2002 }),
2003 is_id: base.is_id.clone(),
2004 x: zeta_x,
2005 y: base.y.clone(),
2006 };
2007
2008 let neg_zeta_p = self.negate(layouter, &zeta_p)?;
2009 let neg_base = self.negate(layouter, base)?;
2010
2011 let p1 = self.select(layouter, &s1, base, &neg_base)?;
2012 let p2 = self.select(layouter, &s2, &zeta_p, &neg_zeta_p)?;
2013
2014 Ok(((x1, x2), (p1, p2)))
2015 }
2016}
2017
2018#[derive(Clone, Debug)]
2019#[cfg(any(test, feature = "testing"))]
2020pub struct ForeignEccTestConfig<F, C, S, N>
2023where
2024 F: CircuitField,
2025 C: WeierstrassCurve,
2026 S: ScalarFieldInstructions<F> + FromScratch<F>,
2027 S::Scalar: InnerValue<Element = C::ScalarField>,
2028 N: NativeInstructions<F> + FromScratch<F>,
2029{
2030 native_gadget_config: <N as FromScratch<F>>::Config,
2031 scalar_field_config: <S as FromScratch<F>>::Config,
2032 ff_ecc_config: ForeignWeierstrassEccConfig<C>,
2033}
2034
2035#[cfg(any(test, feature = "testing"))]
2036impl<F, C, B, S, N> FromScratch<F> for ForeignWeierstrassEccChip<F, C, B, S, N>
2037where
2038 F: CircuitField,
2039 C: WeierstrassCurve,
2040 B: FieldEmulationParams<F, C::Base>,
2041 S: ScalarFieldInstructions<F> + FromScratch<F>,
2042 S::Scalar: InnerValue<Element = C::ScalarField>,
2043 N: NativeInstructions<F> + FromScratch<F>,
2044{
2045 type Config = ForeignEccTestConfig<F, C, S, N>;
2046
2047 fn new_from_scratch(config: &ForeignEccTestConfig<F, C, S, N>) -> Self {
2048 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
2049 let scalar_field_chip =
2050 <S as FromScratch<F>>::new_from_scratch(&config.scalar_field_config);
2051 ForeignWeierstrassEccChip::new(&config.ff_ecc_config, &native_gadget, &scalar_field_chip)
2052 }
2053
2054 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
2055 self.native_gadget.load_from_scratch(layouter)?;
2056 self.scalar_field_chip.load_from_scratch(layouter)
2057 }
2058
2059 fn configure_from_scratch(
2060 meta: &mut ConstraintSystem<F>,
2061 advice_columns: &mut Vec<Column<Advice>>,
2062 fixed_columns: &mut Vec<Column<Fixed>>,
2063 instance_columns: &[Column<Instance>; 2],
2064 ) -> ForeignEccTestConfig<F, C, S, N> {
2065 let native_gadget_config = <N as FromScratch<F>>::configure_from_scratch(
2066 meta,
2067 advice_columns,
2068 fixed_columns,
2069 instance_columns,
2070 );
2071 let scalar_field_config = <S as FromScratch<F>>::configure_from_scratch(
2072 meta,
2073 advice_columns,
2074 fixed_columns,
2075 instance_columns,
2076 );
2077 let nb_advice_cols = nb_foreign_ecc_chip_columns::<F, C, B, S>();
2078 while advice_columns.len() < nb_advice_cols {
2079 advice_columns.push(meta.advice_column());
2080 }
2081 let nb_parallel_range_checks = 4;
2083 let max_bit_len = 8;
2084 let base_field_config = FieldChip::<F, C::Base, B, N>::configure(
2085 meta,
2086 &advice_columns[..nb_advice_cols],
2087 nb_parallel_range_checks,
2088 max_bit_len,
2089 );
2090 let ff_ecc_config = ForeignWeierstrassEccChip::<F, C, B, S, N>::configure(
2091 meta,
2092 &base_field_config,
2093 &advice_columns[..nb_advice_cols],
2094 nb_parallel_range_checks,
2095 max_bit_len,
2096 );
2097 ForeignEccTestConfig {
2098 native_gadget_config,
2099 scalar_field_config,
2100 ff_ecc_config,
2101 }
2102 }
2103}
2104
2105#[cfg(test)]
2106mod tests {
2107 use group::Group;
2108 use midnight_curves::{k256::K256, p256::P256, Fq as BlsScalar, G1Projective as BlsG1};
2109
2110 use super::*;
2111 use crate::{
2112 field::{
2113 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
2114 NativeChip, NativeGadget,
2115 },
2116 instructions::{assertions, control_flow, ecc, equality, public_input, zero},
2117 };
2118
2119 type Native<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
2120
2121 type EmulatedField<F, C> = FieldChip<F, <C as Group>::Scalar, MultiEmulationParams, Native<F>>;
2122
2123 macro_rules! test_generic {
2124 ($mod:ident, $op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
2125 $mod::tests::$op::<
2126 $native,
2127 AssignedForeignPoint<$native, $curve, MultiEmulationParams>,
2128 ForeignWeierstrassEccChip<
2129 $native,
2130 $curve,
2131 MultiEmulationParams,
2132 $scalar_field,
2133 Native<$native>,
2134 >,
2135 >($name);
2136 };
2137 }
2138
2139 macro_rules! test {
2140 ($mod:ident, $op:ident) => {
2141 #[test]
2142 fn $op() {
2143 test_generic!($mod, $op, BlsScalar, K256, EmulatedField<BlsScalar, K256>, "foreign_ecc_k256");
2144 test_generic!($mod, $op, BlsScalar, P256, EmulatedField<BlsScalar, P256>, "foreign_ecc_p256");
2145
2146 test_generic!($mod, $op, BlsScalar, BlsG1, Native<BlsScalar>, "foreign_ecc_bls_over_bls");
2148 }
2149 };
2150 }
2151
2152 test!(assertions, test_assertions);
2153
2154 test!(public_input, test_public_inputs);
2155
2156 test!(equality, test_is_equal);
2157
2158 test!(zero, test_zero_assertions);
2159 test!(zero, test_is_zero);
2160
2161 test!(control_flow, test_select);
2162 test!(control_flow, test_cond_assert_equal);
2163 test!(control_flow, test_cond_swap);
2164
2165 macro_rules! ecc_test {
2166 ($op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
2167 ecc::tests::$op::<
2168 $native,
2169 $curve,
2170 ForeignWeierstrassEccChip<
2171 $native,
2172 $curve,
2173 MultiEmulationParams,
2174 $scalar_field,
2175 Native<$native>,
2176 >,
2177 >($name);
2178 };
2179 }
2180
2181 macro_rules! ecc_tests {
2182 ($op:ident) => {
2183 #[test]
2184 fn $op() {
2185 ecc_test!($op, BlsScalar, K256, EmulatedField<BlsScalar, K256>, "foreign_ecc_k256");
2186 ecc_test!($op, BlsScalar, P256, EmulatedField<BlsScalar, P256>, "foreign_ecc_p256");
2187
2188 ecc_test!($op, BlsScalar, BlsG1, Native<BlsScalar>, "foreign_ecc_bls_over_bls");
2190 }
2191 };
2192 }
2193
2194 ecc_tests!(test_assign);
2195 ecc_tests!(test_assign_without_subgroup_check);
2196 ecc_tests!(test_add);
2197 ecc_tests!(test_double);
2198 ecc_tests!(test_negate);
2199 ecc_tests!(test_msm);
2200 ecc_tests!(test_msm_by_bounded_scalars);
2201 ecc_tests!(test_mul_by_constant);
2202 ecc_tests!(test_coordinates);
2203}