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, Expression, Fixed, Selector},
35 poly::Rotation,
36};
37use num_bigint::BigUint;
38use num_traits::One;
39use rand::{CryptoRng, RngCore};
40#[cfg(any(test, feature = "testing"))]
41use {
42 crate::testing_utils::Sampleable, crate::utils::util::FromScratch,
43 midnight_proofs::plonk::Instance, rand::rngs::OsRng,
44};
45
46use super::gates::{
47 lambda_squared,
48 lambda_squared::LambdaSquaredConfig,
49 on_curve,
50 on_curve::OnCurveConfig,
51 slope::{self, SlopeConfig},
52 tangent,
53 tangent::TangentConfig,
54};
55use crate::{
56 ecc::curves::WeierstrassCurve,
57 field::foreign::{
58 field_chip::{FieldChip, FieldChipConfig},
59 params::FieldEmulationParams,
60 },
61 instructions::{
62 ArithInstructions, AssertionInstructions, AssignmentInstructions, ControlFlowInstructions,
63 DecompositionInstructions, EccInstructions, EqualityInstructions, NativeInstructions,
64 PublicInputInstructions, ScalarFieldInstructions, ZeroInstructions,
65 },
66 types::{AssignedBit, AssignedField, AssignedNative, InnerConstants, InnerValue, Instantiable},
67 utils::util::{big_to_fe, bigint_to_fe, fe_to_big, fe_to_le_bits, glv_scalar_decomposition},
68};
69
70#[derive(Clone, Debug)]
72pub struct ForeignEccConfig<C>
73where
74 C: WeierstrassCurve,
75{
76 base_field_config: FieldChipConfig,
77 on_curve_config: on_curve::OnCurveConfig<C>,
78 slope_config: slope::SlopeConfig<C>,
79 tangent_config: tangent::TangentConfig<C>,
80 lambda_squared_config: lambda_squared::LambdaSquaredConfig<C>,
81 q_multi_select: Selector,
83 idx_col_multi_select: Column<Advice>,
84 tag_col_multi_select: Column<Fixed>,
85}
86
87pub fn nb_foreign_ecc_chip_columns<F, C, B, S>() -> usize
89where
90 F: PrimeField,
91 C: WeierstrassCurve,
92 B: FieldEmulationParams<F, C::Base>,
93{
94 B::NB_LIMBS as usize + max(B::NB_LIMBS as usize, 2 + B::moduli().len()) + 1
100}
101
102#[derive(Clone, Debug)]
104pub struct ForeignEccChip<F, C, B, S, N>
105where
106 F: PrimeField,
107 C: WeierstrassCurve,
108 B: FieldEmulationParams<F, C::Base>,
109 S: ScalarFieldInstructions<F>,
110 S::Scalar: InnerValue<Element = C::Scalar>,
111 N: NativeInstructions<F>,
112{
113 config: ForeignEccConfig<C>,
114 native_gadget: N,
115 base_field_chip: FieldChip<F, C::Base, B, N>,
116 scalar_field_chip: S,
117 tag_cnt: Rc<RefCell<u64>>,
122 random_point: C::CryptographicGroup,
126}
127
128#[derive(Clone, Debug)]
137#[must_use]
138pub struct AssignedForeignPoint<F, C, B>
139where
140 F: PrimeField,
141 C: WeierstrassCurve,
142 B: FieldEmulationParams<F, C::Base>,
143{
144 point: Value<C::CryptographicGroup>,
145 is_id: AssignedBit<F>,
146 x: AssignedField<F, C::Base, B>,
147 y: AssignedField<F, C::Base, B>,
148}
149
150impl<F, C, B> PartialEq for AssignedForeignPoint<F, C, B>
151where
152 F: PrimeField,
153 C: WeierstrassCurve,
154 B: FieldEmulationParams<F, C::Base>,
155{
156 fn eq(&self, other: &Self) -> bool {
157 self.is_id == other.is_id && self.x == other.x && self.y == other.y
158 }
159}
160
161impl<F, C, B> Eq for AssignedForeignPoint<F, C, B>
162where
163 F: PrimeField,
164 C: WeierstrassCurve,
165 B: FieldEmulationParams<F, C::Base>,
166{
167}
168
169impl<F, C, B> Hash for AssignedForeignPoint<F, C, B>
170where
171 F: PrimeField,
172 C: WeierstrassCurve,
173 B: FieldEmulationParams<F, C::Base>,
174{
175 fn hash<H: Hasher>(&self, state: &mut H) {
176 self.is_id.hash(state);
177 self.x.hash(state);
178 self.y.hash(state);
179 }
180}
181
182impl<F, C, B> Instantiable<F> for AssignedForeignPoint<F, C, B>
183where
184 F: PrimeField,
185 C: WeierstrassCurve,
186 B: FieldEmulationParams<F, C::Base>,
187{
188 fn as_public_input(p: &C::CryptographicGroup) -> Vec<F> {
189 let (x, y) = (*p).into().coordinates().unwrap_or((C::Base::ZERO, C::Base::ZERO));
190 let mut pis = [
191 AssignedField::<F, C::Base, B>::as_public_input(&x).as_slice(),
192 AssignedField::<F, C::Base, B>::as_public_input(&y).as_slice(),
193 ]
194 .concat();
195
196 if p.is_identity().into() {
200 pis[0] += F::from(2).pow_vartime([B::LOG2_BASE as u64]);
201 }
202
203 pis
204 }
205
206 fn from_public_input(fields: &[F]) -> Option<C::CryptographicGroup> {
207 let nb_limbs = B::NB_LIMBS as usize;
208 let nb_bits = B::LOG2_BASE as usize;
209
210 if fields.len() != 2 * nb_limbs {
211 return None;
212 }
213
214 if fe_to_le_bits(fields.first()?, Some(nb_bits + 1))[nb_bits] {
215 return Some(C::CryptographicGroup::identity());
216 }
217
218 let x = AssignedField::<F, C::Base, B>::from_public_input(&fields[..nb_limbs])?;
219 let y = AssignedField::<F, C::Base, B>::from_public_input(&fields[nb_limbs..])?;
220 C::from_xy(x, y).map(|p| p.into_subgroup())
221 }
222}
223
224impl<F, C, B> InnerValue for AssignedForeignPoint<F, C, B>
225where
226 F: PrimeField,
227 C: WeierstrassCurve,
228 B: FieldEmulationParams<F, C::Base>,
229{
230 type Element = C::CryptographicGroup;
231
232 fn value(&self) -> Value<Self::Element> {
233 self.point
234 }
235}
236
237impl<F, C, B> InnerConstants for AssignedForeignPoint<F, C, B>
238where
239 F: PrimeField,
240 C: WeierstrassCurve,
241 B: FieldEmulationParams<F, C::Base>,
242{
243 fn inner_zero() -> C::CryptographicGroup {
244 C::CryptographicGroup::identity()
245 }
246
247 fn inner_one() -> Self::Element {
248 C::CryptographicGroup::generator()
249 }
250}
251
252#[cfg(any(test, feature = "testing"))]
253impl<F, C, B> Sampleable for AssignedForeignPoint<F, C, B>
254where
255 F: PrimeField,
256 C: WeierstrassCurve,
257 B: FieldEmulationParams<F, C::Base>,
258{
259 fn sample_inner(rng: impl RngCore) -> C::CryptographicGroup {
260 C::CryptographicGroup::random(rng)
261 }
262}
263
264impl<F, C, B, S, N> Chip<F> for ForeignEccChip<F, C, B, S, N>
265where
266 F: PrimeField,
267 C: WeierstrassCurve,
268 B: FieldEmulationParams<F, C::Base>,
269 S: ScalarFieldInstructions<F>,
270 S::Scalar: InnerValue<Element = C::Scalar>,
271 N: NativeInstructions<F>,
272{
273 type Config = ForeignEccConfig<C>;
274 type Loaded = ();
275 fn config(&self) -> &Self::Config {
276 &self.config
277 }
278 fn loaded(&self) -> &Self::Loaded {
279 &()
280 }
281}
282
283impl<F, C, B, S, N> AssignmentInstructions<F, AssignedForeignPoint<F, C, B>>
284 for ForeignEccChip<F, C, B, S, N>
285where
286 F: PrimeField,
287 C: WeierstrassCurve,
288 B: FieldEmulationParams<F, C::Base>,
289 S: ScalarFieldInstructions<F>,
290 S::Scalar: InnerValue<Element = C::Scalar>,
291 N: NativeInstructions<F>,
292{
293 fn assign(
294 &self,
295 layouter: &mut impl Layouter<F>,
296 value: Value<C::CryptographicGroup>,
297 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
298 let p = self.assign_point_unchecked(layouter, value)?;
299 let is_not_id = self.native_gadget.not(layouter, &p.is_id)?;
300 on_curve::assert_is_on_curve::<F, C, B, N>(
301 layouter,
302 &is_not_id,
303 &p.x,
304 &p.y,
305 self.base_field_chip(),
306 &self.config.on_curve_config,
307 )?;
308 Ok(p)
309 }
310
311 fn assign_fixed(
312 &self,
313 layouter: &mut impl Layouter<F>,
314 constant: C::CryptographicGroup,
315 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
316 let (xv, yv, is_id_value) = if C::CryptographicGroup::is_identity(&constant).into() {
317 (C::Base::ZERO, C::Base::ZERO, true)
318 } else {
319 let coordinates = constant
320 .into()
321 .coordinates()
322 .expect("assign_point_unchecked: invalid point given");
323 (coordinates.0, coordinates.1, false)
324 };
325 let is_id = self.native_gadget.assign_fixed(layouter, is_id_value)?;
326 let x = self.base_field_chip().assign_fixed(layouter, xv)?;
327 let y = self.base_field_chip().assign_fixed(layouter, yv)?;
328 let p = AssignedForeignPoint::<F, C, B> {
329 point: Value::known(constant),
330 is_id,
331 x,
332 y,
333 };
334 Ok(p)
335 }
336}
337
338impl<F, C, B, S, N> PublicInputInstructions<F, AssignedForeignPoint<F, C, B>>
339 for ForeignEccChip<F, C, B, S, N>
340where
341 F: PrimeField,
342 C: WeierstrassCurve,
343 B: FieldEmulationParams<F, C::Base>,
344 S: ScalarFieldInstructions<F>,
345 S::Scalar: InnerValue<Element = C::Scalar>,
346 N: NativeInstructions<F> + PublicInputInstructions<F, AssignedBit<F>>,
347{
348 fn as_public_input(
349 &self,
350 layouter: &mut impl Layouter<F>,
351 p: &AssignedForeignPoint<F, C, B>,
352 ) -> Result<Vec<AssignedNative<F>>, Error> {
353 let mut pis = [
354 self.base_field_chip.as_public_input(layouter, &p.x)?.as_slice(),
355 self.base_field_chip.as_public_input(layouter, &p.y)?.as_slice(),
356 ]
357 .concat();
358
359 let base = F::from(2).pow_vartime([B::LOG2_BASE as u64]);
363 pis[0] = self.native_gadget.linear_combination(
364 layouter,
365 &[(F::ONE, pis[0].clone()), (base, p.is_id.clone().into())],
366 F::ZERO,
367 )?;
368
369 Ok(pis)
370 }
371
372 fn constrain_as_public_input(
373 &self,
374 layouter: &mut impl Layouter<F>,
375 assigned: &AssignedForeignPoint<F, C, B>,
376 ) -> Result<(), Error> {
377 self.as_public_input(layouter, assigned)?
378 .iter()
379 .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
380 }
381
382 fn assign_as_public_input(
383 &self,
384 layouter: &mut impl Layouter<F>,
385 value: Value<C::CryptographicGroup>,
386 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
387 let point = self.assign(layouter, value)?;
391 self.constrain_as_public_input(layouter, &point)?;
392 Ok(point)
393 }
394}
395
396impl<F, C, B, S, N> AssignmentInstructions<F, AssignedNative<F>> for ForeignEccChip<F, C, B, S, N>
402where
403 F: PrimeField,
404 C: WeierstrassCurve,
405 B: FieldEmulationParams<F, C::Base>,
406 S: ScalarFieldInstructions<F, Scalar = AssignedNative<F>>,
407 S::Scalar: InnerValue<Element = C::Scalar>,
408 N: NativeInstructions<F>,
409{
410 fn assign(
411 &self,
412 layouter: &mut impl Layouter<F>,
413 value: Value<<S::Scalar as InnerValue>::Element>,
414 ) -> Result<S::Scalar, Error> {
415 self.scalar_field_chip().assign(layouter, value)
416 }
417
418 fn assign_fixed(
419 &self,
420 layouter: &mut impl Layouter<F>,
421 constant: <S::Scalar as InnerValue>::Element,
422 ) -> Result<S::Scalar, Error> {
423 self.scalar_field_chip().assign_fixed(layouter, constant)
424 }
425}
426
427impl<F, C, B, S, SP, N> AssignmentInstructions<F, AssignedField<F, C::Scalar, SP>>
432 for ForeignEccChip<F, C, B, S, N>
433where
434 F: PrimeField,
435 C: WeierstrassCurve,
436 B: FieldEmulationParams<F, C::Base>,
437 S: ScalarFieldInstructions<F, Scalar = AssignedField<F, C::Scalar, SP>>,
438 S::Scalar: InnerValue<Element = C::Scalar>,
439 SP: FieldEmulationParams<F, C::Scalar>,
440 N: NativeInstructions<F>,
441{
442 fn assign(
443 &self,
444 layouter: &mut impl Layouter<F>,
445 value: Value<<S::Scalar as InnerValue>::Element>,
446 ) -> Result<S::Scalar, Error> {
447 self.scalar_field_chip().assign(layouter, value)
448 }
449
450 fn assign_fixed(
451 &self,
452 layouter: &mut impl Layouter<F>,
453 constant: <S::Scalar as InnerValue>::Element,
454 ) -> Result<S::Scalar, Error> {
455 self.scalar_field_chip().assign_fixed(layouter, constant)
456 }
457}
458
459impl<F, C, B, S, N> AssertionInstructions<F, AssignedForeignPoint<F, C, B>>
460 for ForeignEccChip<F, C, B, S, N>
461where
462 F: PrimeField,
463 C: WeierstrassCurve,
464 B: FieldEmulationParams<F, C::Base>,
465 S: ScalarFieldInstructions<F>,
466 S::Scalar: InnerValue<Element = C::Scalar>,
467 N: NativeInstructions<F>,
468{
469 fn assert_equal(
470 &self,
471 layouter: &mut impl Layouter<F>,
472 p: &AssignedForeignPoint<F, C, B>,
473 q: &AssignedForeignPoint<F, C, B>,
474 ) -> Result<(), Error> {
475 self.native_gadget.assert_equal(layouter, &p.is_id, &q.is_id)?;
480 self.base_field_chip().assert_equal(layouter, &p.x, &q.x)?;
481 self.base_field_chip().assert_equal(layouter, &p.y, &q.y)
482 }
483
484 fn assert_not_equal(
485 &self,
486 layouter: &mut impl Layouter<F>,
487 p: &AssignedForeignPoint<F, C, B>,
488 q: &AssignedForeignPoint<F, C, B>,
489 ) -> Result<(), Error> {
490 let equal = self.is_equal(layouter, p, q)?;
491 self.native_gadget.assert_equal_to_fixed(layouter, &equal, false)
492 }
493
494 fn assert_equal_to_fixed(
495 &self,
496 layouter: &mut impl Layouter<F>,
497 p: &AssignedForeignPoint<F, C, B>,
498 constant: C::CryptographicGroup,
499 ) -> Result<(), Error> {
500 if constant.is_identity().into() {
501 self.native_gadget.assert_equal_to_fixed(layouter, &p.is_id, true)
502 } else {
503 let coordinates = constant.into().coordinates().expect("Valid point");
504 self.base_field_chip().assert_equal_to_fixed(layouter, &p.x, coordinates.0)?;
505 self.base_field_chip().assert_equal_to_fixed(layouter, &p.y, coordinates.1)?;
506 self.native_gadget.assert_equal_to_fixed(layouter, &p.is_id, false)
507 }
508 }
509
510 fn assert_not_equal_to_fixed(
511 &self,
512 layouter: &mut impl Layouter<F>,
513 p: &AssignedForeignPoint<F, C, B>,
514 constant: C::CryptographicGroup,
515 ) -> Result<(), Error> {
516 if constant.is_identity().into() {
517 self.native_gadget.assert_equal_to_fixed(layouter, &p.is_id, false)
518 } else {
519 let equal = self.is_equal_to_fixed(layouter, p, constant)?;
520 self.native_gadget.assert_equal_to_fixed(layouter, &equal, false)
521 }
522 }
523}
524
525impl<F, C, B, S, N> EqualityInstructions<F, AssignedForeignPoint<F, C, B>>
526 for ForeignEccChip<F, C, B, S, N>
527where
528 F: PrimeField,
529 C: WeierstrassCurve,
530 B: FieldEmulationParams<F, C::Base>,
531 S: ScalarFieldInstructions<F>,
532 S::Scalar: InnerValue<Element = C::Scalar>,
533 N: NativeInstructions<F>,
534{
535 fn is_equal(
536 &self,
537 layouter: &mut impl Layouter<F>,
538 p: &AssignedForeignPoint<F, C, B>,
539 q: &AssignedForeignPoint<F, C, B>,
540 ) -> Result<AssignedBit<F>, Error> {
541 let eq_coordinates = {
544 let eq_x = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
545 let eq_y = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
546 let eq_x_and_y = self.native_gadget.and(layouter, &[eq_x, eq_y])?;
547 let both_are_id =
548 self.native_gadget.and(layouter, &[p.is_id.clone(), q.is_id.clone()])?;
549 self.native_gadget.or(layouter, &[eq_x_and_y, both_are_id])?
550 };
551 let eq_id_flag = self.native_gadget.is_equal(layouter, &p.is_id, &q.is_id)?;
552 self.native_gadget.and(layouter, &[eq_id_flag, eq_coordinates])
553 }
554
555 fn is_not_equal(
556 &self,
557 layouter: &mut impl Layouter<F>,
558 x: &AssignedForeignPoint<F, C, B>,
559 y: &AssignedForeignPoint<F, C, B>,
560 ) -> Result<AssignedBit<F>, Error> {
561 let b = self.is_equal(layouter, x, y)?;
562 self.native_gadget.not(layouter, &b)
563 }
564
565 fn is_equal_to_fixed(
566 &self,
567 layouter: &mut impl Layouter<F>,
568 p: &AssignedForeignPoint<F, C, B>,
569 constant: C::CryptographicGroup,
570 ) -> Result<AssignedBit<F>, Error> {
571 if constant.is_identity().into() {
572 Ok(p.is_id.clone())
573 } else {
574 let coordinates = constant.into().coordinates().expect("Valid point");
575 let eq_x = self.base_field_chip().is_equal_to_fixed(layouter, &p.x, coordinates.0)?;
576 let eq_y = self.base_field_chip().is_equal_to_fixed(layouter, &p.y, coordinates.1)?;
577 let p_is_not_id = self.native_gadget.not(layouter, &p.is_id)?;
578 self.native_gadget.and(layouter, &[eq_x, eq_y, p_is_not_id])
579 }
580 }
581
582 fn is_not_equal_to_fixed(
583 &self,
584 layouter: &mut impl Layouter<F>,
585 x: &AssignedForeignPoint<F, C, B>,
586 constant: C::CryptographicGroup,
587 ) -> Result<AssignedBit<F>, Error> {
588 let b = self.is_equal_to_fixed(layouter, x, constant)?;
589 self.native_gadget.not(layouter, &b)
590 }
591}
592
593impl<F, C, B, S, N> ZeroInstructions<F, AssignedForeignPoint<F, C, B>>
594 for ForeignEccChip<F, C, B, S, N>
595where
596 F: PrimeField,
597 C: WeierstrassCurve,
598 B: FieldEmulationParams<F, C::Base>,
599 S: ScalarFieldInstructions<F>,
600 S::Scalar: InnerValue<Element = C::Scalar>,
601 N: NativeInstructions<F>,
602{
603 fn is_zero(
604 &self,
605 _layouter: &mut impl Layouter<F>,
606 p: &AssignedForeignPoint<F, C, B>,
607 ) -> Result<AssignedBit<F>, Error> {
608 Ok(p.is_id.clone())
609 }
610}
611
612impl<F, C, B, S, N> ControlFlowInstructions<F, AssignedForeignPoint<F, C, B>>
613 for ForeignEccChip<F, C, B, S, N>
614where
615 F: PrimeField,
616 C: WeierstrassCurve,
617 B: FieldEmulationParams<F, C::Base>,
618 S: ScalarFieldInstructions<F>,
619 S::Scalar: InnerValue<Element = C::Scalar>,
620 N: NativeInstructions<F>,
621{
622 fn select(
624 &self,
625 layouter: &mut impl Layouter<F>,
626 cond: &AssignedBit<F>,
627 p: &AssignedForeignPoint<F, C, B>,
628 q: &AssignedForeignPoint<F, C, B>,
629 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
630 let is_id = self.native_gadget.select(layouter, cond, &p.is_id, &q.is_id)?;
631 let x = self.base_field_chip().select(layouter, cond, &p.x, &q.x)?;
632 let y = self.base_field_chip().select(layouter, cond, &p.y, &q.y)?;
633
634 let a = cond.value().error_if_known_and(|&v| !v);
642 let point = if a.is_ok() { p.point } else { q.point };
643
644 Ok(AssignedForeignPoint::<F, C, B> { point, is_id, x, y })
645 }
646}
647
648impl<F, C, B, S, N> EccInstructions<F, C> for ForeignEccChip<F, C, B, S, N>
649where
650 F: PrimeField,
651 C: WeierstrassCurve,
652 B: FieldEmulationParams<F, C::Base>,
653 S: ScalarFieldInstructions<F>,
654 S::Scalar: InnerValue<Element = C::Scalar>,
655 N: NativeInstructions<F>,
656{
657 type Point = AssignedForeignPoint<F, C, B>;
658 type Coordinate = AssignedField<F, C::Base, B>;
659 type Scalar = S::Scalar;
660
661 fn add(
662 &self,
663 layouter: &mut impl Layouter<F>,
664 p: &Self::Point,
665 q: &Self::Point,
666 ) -> Result<Self::Point, Error> {
667 let r_curve = p.value().zip(q.value()).map(|(p, q)| (p + q));
668 let r = self.assign_point_unchecked(layouter, r_curve)?;
669
670 let p_or_q_or_r_are_id = self.native_gadget.or(
672 layouter,
673 &[p.is_id.clone(), q.is_id.clone(), r.is_id.clone()],
674 )?;
675 let none_is_id = self.native_gadget.not(layouter, &p_or_q_or_r_are_id)?;
676 let px_eq_qx = self.base_field_chip().is_equal(layouter, &p.x, &q.x)?;
677 let py_eq_qy = self.base_field_chip().is_equal(layouter, &p.y, &q.y)?;
678 let px_neq_qx = self.native_gadget.not(layouter, &px_eq_qx)?;
679 let py_eq_neg_qy = {
680 let py_plus_qy = self.base_field_chip().add(layouter, &p.y, &q.y)?;
681 self.base_field_chip().is_zero(layouter, &py_plus_qy)?
682 };
683
684 self.cond_assert_equal(layouter, &p.is_id, &r, q)?;
686
687 self.cond_assert_equal(layouter, &q.is_id, &r, p)?;
689
690 let p_eq_nq = self.native_gadget.and(layouter, &[px_eq_qx.clone(), py_eq_neg_qy])?;
693 self.native_gadget.assert_equal(layouter, &p_eq_nq, &r.is_id)?;
694
695 let cond = self.native_gadget.and(layouter, &[px_eq_qx, py_eq_qy, none_is_id.clone()])?;
699 self.assert_double(layouter, p, &r, &cond)?;
700
701 let cond = self.native_gadget.and(layouter, &[px_neq_qx, none_is_id])?;
706 self.assert_add(layouter, p, q, &r, &cond)?;
707
708 Ok(r)
709 }
710
711 fn double(
712 &self,
713 layouter: &mut impl Layouter<F>,
714 p: &AssignedForeignPoint<F, C, B>,
715 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
716 let r_curve = p.value().map(|p| (p + p));
717 let r = self.assign_point_unchecked(layouter, r_curve)?;
718
719 self.native_gadget.assert_equal(layouter, &p.is_id, &r.is_id)?;
721
722 let cond = self.native_gadget.not(layouter, &p.is_id)?;
727 self.assert_double(layouter, p, &r, &cond)?;
728
729 Ok(r)
730 }
731
732 fn negate(
733 &self,
734 layouter: &mut impl Layouter<F>,
735 p: &Self::Point,
736 ) -> Result<Self::Point, Error> {
737 let neg_y = self.base_field_chip().neg(layouter, &p.y)?;
738 let neg_y = self.base_field_chip().normalize(layouter, &neg_y)?;
739 Ok(AssignedForeignPoint::<F, C, B> {
740 point: -p.point,
741 is_id: p.is_id.clone(),
742 x: p.x.clone(),
743 y: neg_y,
744 })
745 }
746
747 fn msm(
748 &self,
749 layouter: &mut impl Layouter<F>,
750 scalars: &[Self::Scalar],
751 bases: &[Self::Point],
752 ) -> Result<Self::Point, Error> {
753 let scalars = scalars
754 .iter()
755 .map(|s| (s.clone(), C::Scalar::NUM_BITS as usize))
756 .collect::<Vec<_>>();
757 self.msm_by_bounded_scalars(layouter, &scalars, bases)
758 }
759
760 fn msm_by_bounded_scalars(
761 &self,
762 layouter: &mut impl Layouter<F>,
763 scalars: &[(S::Scalar, usize)],
764 bases: &[AssignedForeignPoint<F, C, B>],
765 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
766 const WS: usize = 4;
767
768 let one: S::Scalar = self.scalar_field_chip.assign_fixed(layouter, C::Scalar::ONE)?;
771 let mut bases_without_coeff = vec![];
772 let mut filtered_scalars = vec![];
773 let mut filtered_bases = vec![];
774 for (scalar, base) in scalars.iter().zip(bases.iter()) {
775 if scalar.0 == one {
776 bases_without_coeff.push(base.clone());
777 } else {
778 filtered_scalars.push(scalar.clone());
779 filtered_bases.push(base.clone());
780 }
781 }
782
783 let scalars = filtered_scalars;
784 let bases = filtered_bases;
785
786 let mut cache_bases: HashMap<AssignedForeignPoint<F, C, B>, (S::Scalar, usize)> =
789 HashMap::new();
790 let mut unique_bases: Vec<AssignedForeignPoint<F, C, B>> = vec![];
791 for (base, scalar) in bases.iter().zip(scalars.iter()) {
792 if let Some(acc) = cache_bases.insert(base.clone(), scalar.clone()) {
793 let new_scalar = self.scalar_field_chip.add(layouter, &acc.0, &scalar.0)?;
794 let new_bound = max(acc.1, scalar.1) + 1;
795 cache_bases.insert(base.clone(), (new_scalar, new_bound));
796 } else {
797 unique_bases.push(base.clone());
798 }
799 }
800 let scalars = unique_bases
801 .iter()
802 .map(|b| cache_bases.get(b).unwrap().clone())
803 .collect::<Vec<_>>();
804 let bases = unique_bases;
805
806 let mut cache_scalars: HashMap<(S::Scalar, usize), AssignedForeignPoint<F, C, B>> =
809 HashMap::new();
810 let mut unique_scalars: Vec<(S::Scalar, usize)> = vec![];
811 for (scalar, base) in scalars.iter().zip(bases.iter()) {
812 if let Some(acc) = cache_scalars.insert(scalar.clone(), base.clone()) {
813 let new_acc = self.add(layouter, &acc, base)?;
814 cache_scalars.insert(scalar.clone(), new_acc);
815 } else {
816 unique_scalars.push(scalar.clone());
817 }
818 }
819 let bases = unique_scalars
820 .iter()
821 .map(|s| cache_scalars.get(s).unwrap().clone())
822 .collect::<Vec<_>>();
823 let scalars = unique_scalars;
824
825 let mut non_id_bases = vec![];
830 let mut scalars_of_non_id_bases = vec![];
831 let scalar_chip = self.scalar_field_chip();
832 let zero: S::Scalar = scalar_chip.assign_fixed(layouter, C::Scalar::ZERO)?;
833 let g = self.assign_fixed(layouter, C::CryptographicGroup::generator())?;
834 for (s, b) in scalars.iter().zip(bases.iter()) {
835 let new_b = self.select(layouter, &b.is_id, &g, b)?;
836 let new_s = scalar_chip.select(layouter, &b.is_id, &zero, &s.0)?;
837 non_id_bases.push(new_b);
838 scalars_of_non_id_bases.push((new_s, s.1));
839 }
840
841 let nb_bits_per_glv_scalar = C::Scalar::NUM_BITS.div_ceil(2) as usize;
845 let mut non_glv_scalars = vec![];
846 let mut non_glv_bases = vec![];
847 let mut glv_scalars = vec![];
848 let mut glv_bases = vec![];
849 for (s, b) in scalars_of_non_id_bases.iter().zip(non_id_bases.iter()) {
850 if s.1 > nb_bits_per_glv_scalar + WS {
854 let ((s1, s2), (b1, b2)) = self.glv_split(layouter, &s.0, b)?;
855 glv_scalars.push((s1, nb_bits_per_glv_scalar));
856 glv_scalars.push((s2, nb_bits_per_glv_scalar));
857 glv_bases.push(b1);
858 glv_bases.push(b2);
859 } else {
860 non_glv_scalars.push(s.clone());
861 non_glv_bases.push(b.clone());
862 }
863 }
864
865 let scalars = [glv_scalars, non_glv_scalars].concat();
866 let bases = [glv_bases, non_glv_bases].concat();
867
868 let mut decomposed_scalars = vec![];
869 for (s, nb_bits_s) in scalars.iter() {
870 let s_bits = self.scalar_field_chip().assigned_to_le_chunks(
871 layouter,
872 s,
873 WS,
874 Some(nb_bits_s.div_ceil(WS)),
875 )?;
876 decomposed_scalars.push(s_bits)
877 }
878 let res = self.windowed_msm::<WS>(layouter, &decomposed_scalars, &bases)?;
879
880 bases_without_coeff.iter().try_fold(res, |acc, b| self.add(layouter, &acc, b))
881 }
882
883 fn mul_by_constant(
884 &self,
885 layouter: &mut impl Layouter<F>,
886 scalar: C::Scalar,
887 base: &Self::Point,
888 ) -> Result<Self::Point, Error> {
889 let scalar_as_big = fe_to_big(scalar);
893 if scalar_as_big.bits() <= 128 {
894 let n = scalar_as_big
895 .to_u64_digits()
896 .iter()
897 .fold(0u128, |acc, limb| acc + *limb as u128);
898
899 let id = self.assign_fixed(layouter, C::CryptographicGroup::identity())?;
903 let g = self.assign_fixed(layouter, C::CryptographicGroup::generator())?;
904 let p = self.select(layouter, &base.is_id, &g, base)?;
905 let r = self.mul_by_u128(layouter, n, &p)?;
906 return self.select(layouter, &base.is_id, &id, &r);
907 }
908 let scalar_bits = fe_to_le_bits(&scalar, None)
909 .iter()
910 .map(|b| self.native_gadget.assign_fixed(layouter, *b))
911 .collect::<Result<Vec<_>, Error>>()?;
912 self.msm_by_le_bits(layouter, &[scalar_bits], &[base.clone()])
913 }
914
915 fn point_from_coordinates(
916 &self,
917 layouter: &mut impl Layouter<F>,
918 x: &AssignedField<F, C::Base, B>,
919 y: &AssignedField<F, C::Base, B>,
920 ) -> Result<Self::Point, Error> {
921 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
922 let cond = self.native_gadget.assign_fixed(layouter, true)?;
923 on_curve::assert_is_on_curve::<F, C, B, N>(
924 layouter,
925 &cond,
926 x,
927 y,
928 self.base_field_chip(),
929 &self.config.on_curve_config,
930 )?;
931 let point = x
935 .value()
936 .zip(y.value())
937 .map(|(x, y)| C::from_xy(x, y).unwrap_or(C::identity()).into_subgroup());
938 Ok(AssignedForeignPoint::<F, C, B> {
939 point,
940 is_id,
941 x: x.clone(),
942 y: y.clone(),
943 })
944 }
945
946 fn x_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
947 point.x.clone()
948 }
949
950 fn y_coordinate(&self, point: &Self::Point) -> Self::Coordinate {
951 point.y.clone()
952 }
953
954 fn base_field(&self) -> &impl DecompositionInstructions<F, Self::Coordinate> {
955 self.base_field_chip()
956 }
957}
958
959impl<F, C, B, S, N> ForeignEccChip<F, C, B, S, N>
960where
961 F: PrimeField,
962 C: WeierstrassCurve,
963 B: FieldEmulationParams<F, C::Base>,
964 S: ScalarFieldInstructions<F>,
965 S::Scalar: InnerValue<Element = C::Scalar>,
966 N: NativeInstructions<F>,
967{
968 pub fn new(
973 config: &ForeignEccConfig<C>,
974 native_gadget: &N,
975 scalar_field_chip: &S,
976 mut rng: impl RngCore + CryptoRng,
977 ) -> Self {
978 let random_point = C::random(&mut rng).into_subgroup();
979 let base_field_chip = FieldChip::new(&config.base_field_config, native_gadget);
980 Self {
981 config: config.clone(),
982 native_gadget: native_gadget.clone(),
983 base_field_chip,
984 scalar_field_chip: scalar_field_chip.clone(),
985 tag_cnt: Rc::new(RefCell::new(1)),
986 random_point,
987 }
988 }
989
990 pub fn base_field_chip(&self) -> &FieldChip<F, C::Base, B, N> {
992 &self.base_field_chip
993 }
994
995 pub fn scalar_field_chip(&self) -> &S {
997 &self.scalar_field_chip
998 }
999
1000 pub fn configure(
1002 meta: &mut ConstraintSystem<F>,
1003 base_field_config: &FieldChipConfig,
1004 advice_columns: &[Column<Advice>],
1005 ) -> ForeignEccConfig<C> {
1006 let cond_col_idx = base_field_config.x_cols.len() + base_field_config.v_cols.len() + 1;
1009 assert!(advice_columns.len() > cond_col_idx);
1010 let cond_col = advice_columns[cond_col_idx];
1011 meta.enable_equality(cond_col);
1012
1013 let on_curve_config =
1014 OnCurveConfig::<C>::configure::<F, B>(meta, base_field_config, &cond_col);
1015
1016 let slope_config = SlopeConfig::<C>::configure::<F, B>(meta, base_field_config, &cond_col);
1017
1018 let tangent_config =
1019 TangentConfig::<C>::configure::<F, B>(meta, base_field_config, &cond_col);
1020
1021 let lambda_squared_config =
1022 LambdaSquaredConfig::<C>::configure::<F, B>(meta, base_field_config, &cond_col);
1023
1024 let q_multi_select = meta.complex_selector();
1044 assert!(advice_columns.len() > 2 * base_field_config.x_cols.len());
1045 let idx_col_multi_select = *advice_columns.last().unwrap();
1046 meta.enable_equality(idx_col_multi_select);
1047
1048 let tag_col_multi_select = meta.fixed_column();
1052
1053 meta.lookup_any("multi_select lookup", |meta| {
1054 let sel = meta.query_selector(q_multi_select);
1055 let not_sel = Expression::from(1) - sel.clone();
1056
1057 let mut identities = [idx_col_multi_select]
1069 .iter()
1070 .chain(base_field_config.x_cols.iter())
1071 .chain(base_field_config.z_cols.iter())
1072 .map(|col| {
1073 let val = meta.query_advice(*col, Rotation::cur());
1074 (val.clone(), not_sel.clone() * val)
1075 })
1076 .collect::<Vec<_>>();
1077
1078 let tag = meta.query_fixed(tag_col_multi_select, Rotation::cur());
1080 identities.push((tag.clone(), not_sel * tag));
1081
1082 identities
1083 });
1084
1085 ForeignEccConfig {
1086 base_field_config: base_field_config.clone(),
1087 on_curve_config,
1088 slope_config,
1089 tangent_config,
1090 lambda_squared_config,
1091 q_multi_select,
1092 idx_col_multi_select,
1093 tag_col_multi_select,
1094 }
1095 }
1096
1097 fn assign_point_unchecked(
1102 &self,
1103 layouter: &mut impl Layouter<F>,
1104 p: Value<C::CryptographicGroup>,
1105 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1106 let values = p.map(|p| {
1107 if C::CryptographicGroup::is_identity(&p).into() {
1108 (C::Base::ZERO, C::Base::ZERO, true)
1109 } else {
1110 let coordinates =
1111 p.into().coordinates().expect("assign_point_unchecked: invalid point given");
1112 (coordinates.0, coordinates.1, false)
1113 }
1114 });
1115 let x = self.base_field_chip().assign(layouter, values.map(|v| v.0))?;
1116 let y = self.base_field_chip().assign(layouter, values.map(|v| v.1))?;
1117 let is_id = self.native_gadget.assign(layouter, values.map(|v| v.2))?;
1118 let p = AssignedForeignPoint::<F, C, B> {
1119 point: p,
1120 is_id,
1121 x,
1122 y,
1123 };
1124 Ok(p)
1125 }
1126
1127 fn incomplete_add(
1148 &self,
1149 layouter: &mut impl Layouter<F>,
1150 p: &AssignedForeignPoint<F, C, B>,
1151 q: &AssignedForeignPoint<F, C, B>,
1152 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1153 let r_curve = p.value().zip(q.value()).map(|(p, q)| (p + q));
1154 let r = self.assign_point_unchecked(layouter, r_curve)?;
1155
1156 self.native_gadget.assert_equal(layouter, &p.is_id, &r.is_id)?;
1158
1159 let one = self.native_gadget.assign_fixed(layouter, true)?;
1166 self.assert_add(layouter, p, q, &r, &one)?;
1167
1168 Ok(r)
1169 }
1170
1171 fn assert_double(
1184 &self,
1185 layouter: &mut impl Layouter<F>,
1186 p: &AssignedForeignPoint<F, C, B>,
1187 r: &AssignedForeignPoint<F, C, B>,
1188 cond: &AssignedBit<F>,
1189 ) -> Result<(), Error> {
1190 let lambda = {
1192 let lambda_value = p.value().map(|p| {
1193 if C::CryptographicGroup::is_identity(&p).into() {
1194 C::Base::ONE
1195 } else {
1196 let p = p.into().coordinates().unwrap();
1197 (C::Base::from(3) * p.0 * p.0) * (C::Base::from(2) * p.1).invert().unwrap()
1198 }
1199 });
1200 self.base_field_chip().assign(layouter, lambda_value)?
1201 };
1202
1203 tangent::assert_tangent::<F, C, B, N>(
1205 layouter,
1206 cond,
1207 (&p.x, &p.y),
1208 &lambda,
1209 self.base_field_chip(),
1210 &self.config.tangent_config,
1211 )?;
1212
1213 lambda_squared::assert_lambda_squared(
1215 layouter,
1216 cond,
1217 (&p.x, &p.x, &r.x),
1218 &lambda,
1219 self.base_field_chip(),
1220 &self.config.lambda_squared_config,
1221 )?;
1222
1223 self.assert_slope(layouter, cond, p, r, true, &lambda)?;
1234
1235 Ok(())
1236 }
1237
1238 fn assert_add(
1266 &self,
1267 layouter: &mut impl Layouter<F>,
1268 p: &AssignedForeignPoint<F, C, B>,
1269 q: &AssignedForeignPoint<F, C, B>,
1270 r: &AssignedForeignPoint<F, C, B>,
1271 cond: &AssignedBit<F>,
1272 ) -> Result<(), Error> {
1273 let lambda = {
1275 let lambda_value = p.value().zip(q.value()).map(|(p, q)| {
1276 if p.is_identity().into() || q.is_identity().into() {
1277 C::Base::ONE
1278 } else {
1279 let p = p.into().coordinates().unwrap();
1280 let q = q.into().coordinates().unwrap();
1281 if p.0 == q.0 {
1282 C::Base::ONE
1283 } else {
1284 (q.1 - p.1) * (q.0 - p.0).invert().unwrap()
1285 }
1286 }
1287 });
1288 self.base_field_chip().assign(layouter, lambda_value)?
1289 };
1290
1291 self.assert_slope(layouter, cond, p, q, false, &lambda)?;
1301
1302 lambda_squared::assert_lambda_squared(
1304 layouter,
1305 cond,
1306 (&p.x, &q.x, &r.x),
1307 &lambda,
1308 self.base_field_chip(),
1309 &self.config.lambda_squared_config,
1310 )?;
1311
1312 self.assert_slope(layouter, cond, p, r, true, &lambda)?;
1329
1330 Ok(())
1331 }
1332
1333 fn assert_slope(
1355 &self,
1356 layouter: &mut impl Layouter<F>,
1357 cond: &AssignedBit<F>,
1358 p: &AssignedForeignPoint<F, C, B>,
1359 q: &AssignedForeignPoint<F, C, B>,
1360 negate_q: bool,
1361 lambda: &AssignedField<F, C::Base, B>,
1362 ) -> Result<(), Error> {
1363 slope::assert_slope::<F, C, B, N>(
1364 layouter,
1365 cond,
1366 (&p.x, &p.y),
1367 (&q.x, &q.y, negate_q),
1368 lambda,
1369 self.base_field_chip(),
1370 &self.config.slope_config,
1371 )
1372 }
1373
1374 #[allow(clippy::type_complexity)]
1387 fn fill_dynamic_lookup_row(
1388 &self,
1389 layouter: &mut impl Layouter<F>,
1390 point: &AssignedForeignPoint<F, C, B>,
1391 index: &AssignedNative<F>,
1392 table_tag: F,
1393 enable_lookup: bool,
1394 ) -> Result<(Vec<AssignedNative<F>>, Vec<AssignedNative<F>>), Error> {
1395 layouter.assign_region(
1396 || "multi_select table",
1397 |mut region| {
1398 if enable_lookup {
1399 self.config.q_multi_select.enable(&mut region, 0)?;
1400 };
1401
1402 let x_limbs = point.x.limb_values();
1403 let x_cols = self.config.base_field_config.x_cols.clone();
1404
1405 let y_limbs = point.y.limb_values();
1407 let y_cols = self.config.base_field_config.z_cols.clone();
1408
1409 let idx_col = self.config.idx_col_multi_select;
1410 let tag_col = self.config.tag_col_multi_select;
1411
1412 let mut xs = vec![];
1413 let mut ys = vec![];
1414 for i in 0..x_limbs.len() {
1415 if enable_lookup {
1420 let x_val = x_limbs[i].value().copied();
1421 let y_val = y_limbs[i].value().copied();
1422 xs.push(region.assign_advice(|| "x", x_cols[i], 0, || x_val)?);
1423 ys.push(region.assign_advice(|| "y", y_cols[i], 0, || y_val)?);
1424 }
1425 else {
1427 xs.push(x_limbs[i].copy_advice(|| "x", &mut region, x_cols[i], 0)?);
1428 ys.push(y_limbs[i].copy_advice(|| "y", &mut region, y_cols[i], 0)?);
1429 }
1430 }
1431 index.copy_advice(|| "x", &mut region, idx_col, 0)?;
1432 region.assign_fixed(|| "assign tag", tag_col, 0, || Value::known(table_tag))?;
1433
1434 Ok((xs, ys))
1435 },
1436 )
1437 }
1438
1439 fn load_multi_select_table(
1451 &self,
1452 layouter: &mut impl Layouter<F>,
1453 point_table: &[AssignedForeignPoint<F, C, B>],
1454 table_tag: F,
1455 ) -> Result<(), Error> {
1456 let indices: Vec<AssignedNative<F>> = (0..point_table.len())
1459 .map(|i| self.native_gadget.assign_fixed(layouter, F::from(i as u64)))
1460 .collect::<Result<_, Error>>()?;
1461
1462 for (i, point) in point_table.iter().enumerate() {
1463 self.fill_dynamic_lookup_row(layouter, point, &indices[i], table_tag, false)?;
1464 }
1465
1466 Ok(())
1467 }
1468
1469 fn multi_select(
1492 &self,
1493 layouter: &mut impl Layouter<F>,
1494 selector: &AssignedNative<F>,
1495 point_table: &[AssignedForeignPoint<F, C, B>],
1496 table_tag: F,
1497 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1498 let mut selector_idx = 0;
1500 selector.value().map(|v| {
1501 let digits = fe_to_big(*v).to_u32_digits();
1502 let digit = if digits.is_empty() { 0 } else { digits[0] };
1503 debug_assert!(digits.len() <= 1);
1504 debug_assert!(digit < point_table.len() as u32);
1505 selector_idx = digit;
1506 });
1507
1508 let selected = point_table[selector_idx as usize].clone();
1509
1510 let (xs, ys) =
1514 self.fill_dynamic_lookup_row(layouter, &selected, selector, table_tag, true)?;
1515 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1516 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1517 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
1518
1519 let result = AssignedForeignPoint::<F, C, B> {
1520 point: selected.point,
1521 is_id,
1522 x,
1523 y,
1524 };
1525
1526 Ok(result)
1527 }
1528
1529 pub fn k_out_of_n_points(
1547 &self,
1548 layouter: &mut impl Layouter<F>,
1549 table: &[AssignedForeignPoint<F, C, B>],
1550 selected: &[Value<C::CryptographicGroup>],
1551 ) -> Result<Vec<AssignedForeignPoint<F, C, B>>, Error> {
1552 let n = table.len();
1553 let k = selected.len();
1554 assert!(k <= n);
1555
1556 assert!((n as u128) < (1 << (F::NUM_BITS / 2)));
1558
1559 table.iter().try_for_each(|point| self.assert_non_zero(layouter, point))?;
1561
1562 let tag_cnt = *self.tag_cnt.borrow();
1569 self.tag_cnt.replace(tag_cnt + 1);
1570 self.load_multi_select_table(layouter, table, F::from(tag_cnt))?;
1571
1572 let table_values =
1574 Value::<Vec<C::CryptographicGroup>>::from_iter(table.iter().map(|point| point.value()));
1575 let selected_idxs = selected
1576 .iter()
1577 .map(|point_value| {
1578 point_value
1579 .zip(table_values.clone())
1580 .map(|(p, ts)| ts.iter().position(|table_val| *table_val == p).unwrap_or(0))
1581 })
1582 .collect::<Vec<_>>();
1583
1584 Value::<Vec<usize>>::from_iter(selected_idxs.clone())
1587 .error_if_known_and(|idxs| idxs.iter().zip(idxs.iter().skip(1)).any(|(i, j)| i >= j))?;
1588
1589 let assigned_selected_idxs = selected_idxs
1591 .clone()
1592 .iter()
1593 .map(|i_value| self.native_gadget.assign(layouter, i_value.map(|i| F::from(i as u64))))
1594 .collect::<Result<Vec<AssignedNative<F>>, Error>>()?;
1595
1596 let l = BigUint::one() << BigUint::from(n).bits();
1602 assigned_selected_idxs
1603 .iter()
1604 .zip(assigned_selected_idxs.iter().skip(1))
1605 .try_for_each(|(idx, next_idx)| {
1606 let diff_minus_one = self.native_gadget.linear_combination(
1607 layouter,
1608 &[(F::ONE, next_idx.clone()), (-F::ONE, idx.clone())],
1609 -F::ONE,
1610 )?;
1611 self.native_gadget.assert_lower_than_fixed(layouter, &diff_minus_one, &l)
1612 })?;
1613
1614 let mut unwrapped_selected_idxs = vec![0; k];
1616 selected_idxs.iter().enumerate().for_each(|(i, idx)| {
1617 idx.map(|j| unwrapped_selected_idxs[i] = j);
1618 });
1619 let selected_points = unwrapped_selected_idxs
1620 .iter()
1621 .zip(assigned_selected_idxs.iter())
1622 .map(|(i, selected_idx)| {
1623 let (xs, ys) = self.fill_dynamic_lookup_row(
1624 layouter,
1625 &table[*i],
1626 selected_idx,
1627 F::from(tag_cnt),
1628 true,
1629 )?;
1630 let x = AssignedField::<F, C::Base, B>::from_limbs_unsafe(xs);
1631 let y = AssignedField::<F, C::Base, B>::from_limbs_unsafe(ys);
1632 let is_id = self.native_gadget.assign_fixed(layouter, false)?;
1633 Ok(AssignedForeignPoint::<F, C, B> {
1634 point: table[*i].value(),
1635 is_id,
1636 x,
1637 y,
1638 })
1639 })
1640 .collect::<Result<Vec<_>, Error>>()?;
1641
1642 Ok(selected_points)
1643 }
1644
1645 fn incomplete_assert_different_x(
1652 &self,
1653 layouter: &mut impl Layouter<F>,
1654 p: &AssignedForeignPoint<F, C, B>,
1655 q: &AssignedForeignPoint<F, C, B>,
1656 ) -> Result<(), Error> {
1657 assert!(p.x.is_well_formed());
1658 assert!(q.x.is_well_formed());
1659
1660 let native_gadget = &self.native_gadget;
1673 let base = big_to_fe::<F>(BigUint::one() << B::LOG2_BASE);
1674 let m = bigint_to_fe::<F>(&p.x.modulus());
1675
1676 let mut terms = vec![];
1677 let mut coeff = F::ONE;
1678 for (px_i, qx_i) in p.x.limb_values().iter().zip(q.x.limb_values().iter()) {
1679 terms.push((coeff, px_i.clone()));
1680 terms.push((-coeff, qx_i.clone()));
1681 coeff *= base;
1682 }
1683
1684 let diff = native_gadget.linear_combination(layouter, &terms, F::ZERO)?;
1685
1686 native_gadget.assert_non_zero(layouter, &diff)?;
1690 native_gadget.assert_not_equal_to_fixed(layouter, &diff, m)?;
1691 native_gadget.assert_not_equal_to_fixed(layouter, &diff, -m)
1692 }
1693
1694 fn mul_by_u128(
1704 &self,
1705 layouter: &mut impl Layouter<F>,
1706 n: u128,
1707 p: &AssignedForeignPoint<F, C, B>,
1708 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1709 if n == 0 {
1710 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1711 };
1712
1713 assert!(129 < C::Scalar::NUM_BITS);
1716
1717 let mut res = None;
1720
1721 let mut tmp = p.clone();
1723
1724 let mut n = n;
1726 while n > 0 {
1727 if n % 2 != 0 {
1748 res = match res {
1749 None => Some(tmp.clone()),
1750 Some(acc) => Some(self.incomplete_add(layouter, &acc, &tmp)?),
1751 };
1752 }
1753 n >>= 1;
1754
1755 if n > 0 {
1756 tmp = self.double(layouter, &tmp)?
1757 }
1758 }
1759
1760 Ok(res.unwrap())
1761 }
1762
1763 fn windowed_msm<const WS: usize>(
1784 &self,
1785 layouter: &mut impl Layouter<F>,
1786 scalars: &[Vec<AssignedNative<F>>],
1787 bases: &[AssignedForeignPoint<F, C, B>],
1788 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1789 if scalars.len() != bases.len() {
1790 panic!("msm: `scalars` and `bases` should have the same length")
1791 };
1792
1793 if scalars.is_empty() {
1794 return self.assign_fixed(layouter, C::CryptographicGroup::identity());
1795 }
1796
1797 for p in bases.iter() {
1799 self.native_gadget.assert_equal_to_fixed(layouter, &p.is_id, false)?
1800 }
1801
1802 let zero: AssignedNative<F> = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
1807 let max_len = scalars.iter().fold(0, |m, chunks| max(m, chunks.len()));
1808 let mut padded_scalars = vec![];
1809 for s_bits in scalars.iter() {
1810 let mut s_bits = s_bits.to_vec();
1812 s_bits.resize(max_len, zero.clone());
1813 let rev_s_bits = s_bits.into_iter().rev().collect::<Vec<_>>();
1815 padded_scalars.push(rev_s_bits)
1816 }
1817
1818 let r: AssignedForeignPoint<F, C, B> =
1842 self.assign(layouter, Value::known(self.random_point))?;
1843
1844 self.base_field_chip
1846 .native_gadget
1847 .assert_equal_to_fixed(layouter, &r.is_id, false)?;
1848
1849 let l_times_r = self.mul_by_u128(layouter, bases.len() as u128, &r)?;
1850 let alpha = self.mul_by_u128(layouter, (1u128 << WS) - 1, &r)?;
1851 let neg_alpha = self.negate(layouter, &alpha)?;
1852
1853 let tag_cnt = *self.tag_cnt.clone().borrow();
1855 self.tag_cnt.replace(tag_cnt + bases.len() as u64);
1856
1857 let mut tables = vec![];
1859 for (i, p) in bases.iter().enumerate() {
1860 self.incomplete_assert_different_x(layouter, &alpha, p)?;
1861 let mut acc = neg_alpha.clone();
1862 let mut p_table = vec![acc.clone()];
1863 for _ in 1..(1usize << WS) {
1864 acc = self.incomplete_add(layouter, &acc, p)?;
1881
1882 assert!(acc.x.is_well_formed() && acc.y.is_well_formed());
1883 p_table.push(acc.clone())
1884 }
1885 self.load_multi_select_table(layouter, &p_table, F::from(tag_cnt + i as u64))?;
1886 tables.push(p_table)
1887 }
1888
1889 let nb_iterations = max_len;
1890 let mut acc = l_times_r.clone();
1891
1892 for i in 0..nb_iterations {
1893 for _ in 0..WS {
1894 acc = self.double(layouter, &acc)?;
1895 }
1896 for j in 0..bases.len() {
1897 let window = &padded_scalars[j][i];
1898 let addend =
1899 self.multi_select(layouter, window, &tables[j], F::from(tag_cnt + j as u64))?;
1900 self.incomplete_assert_different_x(layouter, &acc, &addend)?;
1914 acc = self.incomplete_add(layouter, &acc, &addend)?;
1915 }
1916 }
1917
1918 let r_correction = self.negate(layouter, &l_times_r)?;
1919 self.add(layouter, &acc, &r_correction)
1920 }
1921
1922 pub fn msm_by_le_bits(
1938 &self,
1939 layouter: &mut impl Layouter<F>,
1940 scalars: &[Vec<AssignedBit<F>>],
1941 bases: &[AssignedForeignPoint<F, C, B>],
1942 ) -> Result<AssignedForeignPoint<F, C, B>, Error> {
1943 const WS: usize = 4;
1947 let scalars = scalars
1948 .iter()
1949 .map(|bits| {
1950 bits.chunks(WS)
1951 .map(|chunk| self.native_gadget.assigned_from_le_bits(layouter, chunk))
1952 .collect::<Result<Vec<_>, Error>>()
1953 })
1954 .collect::<Result<Vec<_>, Error>>()?;
1955 self.windowed_msm::<WS>(layouter, &scalars, bases)
1956 }
1957
1958 #[allow(clippy::type_complexity)]
1967 fn glv_split(
1968 &self,
1969 layouter: &mut impl Layouter<F>,
1970 scalar: &S::Scalar,
1971 base: &AssignedForeignPoint<F, C, B>,
1972 ) -> Result<
1973 (
1974 (S::Scalar, S::Scalar),
1975 (AssignedForeignPoint<F, C, B>, AssignedForeignPoint<F, C, B>),
1976 ),
1977 Error,
1978 > {
1979 let zeta_base = C::BASE_ZETA;
1980 let zeta_scalar = C::SCALAR_ZETA;
1981
1982 let decomposed = scalar.value().map(|x| glv_scalar_decomposition(&x, &zeta_scalar));
1983 let s1_value = decomposed.map(|((s1, _), _)| s1);
1984 let x1_value = decomposed.map(|((_, x1), _)| x1);
1985 let s2_value = decomposed.map(|(_, (s2, _))| s2);
1986 let x2_value = decomposed.map(|(_, (_, x2))| x2);
1987
1988 let x1 = self.scalar_field_chip.assign(layouter, x1_value)?;
1989 let x2 = self.scalar_field_chip.assign(layouter, x2_value)?;
1990
1991 let s1 = self.native_gadget.assign(layouter, s1_value)?;
1992 let s2 = self.native_gadget.assign(layouter, s2_value)?;
1993
1994 let neg_x1 = self.scalar_field_chip.neg(layouter, &x1)?;
1995 let neg_x2 = self.scalar_field_chip.neg(layouter, &x2)?;
1996
1997 let signed_x1 = self.scalar_field_chip.select(layouter, &s1, &x1, &neg_x1)?;
1998 let signed_x2 = self.scalar_field_chip.select(layouter, &s2, &x2, &neg_x2)?;
1999
2000 let x = self.scalar_field_chip.linear_combination(
2002 layouter,
2003 &[(C::Scalar::ONE, signed_x1), (zeta_scalar, signed_x2)],
2004 C::Scalar::ZERO,
2005 )?;
2006 self.scalar_field_chip.assert_equal(layouter, &x, scalar)?;
2007
2008 let zeta_x = self.base_field_chip.mul_by_constant(layouter, &base.x, zeta_base)?;
2009 let zeta_p = AssignedForeignPoint::<F, C, B> {
2010 point: base.point.map(|p| {
2011 if p.is_identity().into() {
2012 p
2013 } else {
2014 let coordinates = p.into().coordinates().unwrap();
2015 let zeta_x = zeta_base * coordinates.0;
2016 C::from_xy(zeta_x, coordinates.1).unwrap().into_subgroup()
2017 }
2018 }),
2019 is_id: base.is_id.clone(),
2020 x: zeta_x,
2021 y: base.y.clone(),
2022 };
2023
2024 let neg_zeta_p = self.negate(layouter, &zeta_p)?;
2025 let neg_base = self.negate(layouter, base)?;
2026
2027 let p1 = self.select(layouter, &s1, base, &neg_base)?;
2028 let p2 = self.select(layouter, &s2, &zeta_p, &neg_zeta_p)?;
2029
2030 Ok(((x1, x2), (p1, p2)))
2031 }
2032}
2033
2034use midnight_curves::G1Projective;
2037
2038use crate::field::{
2039 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams as MEP,
2040 NativeChip, NativeGadget,
2041};
2042
2043type F = midnight_curves::Fq;
2044type NG = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
2045type Bls12381Chip = ForeignEccChip<F, midnight_curves::G1Projective, MEP, NG, NG>;
2046
2047impl Bls12381Chip {
2048 pub fn assert_in_bls12_381_subgroup(
2050 &self,
2051 layouter: &mut impl Layouter<F>,
2052 p: &<Bls12381Chip as EccInstructions<F, G1Projective>>::Point,
2053 ) -> Result<(), Error> {
2054 let cofactor = F::from_raw([0x8c00aaab0000aaab, 0x396c8c005555e156, 0, 0]);
2058 let cofactor_root: <Bls12381Chip as EccInstructions<F, G1Projective>>::Point =
2059 self.assign(layouter, p.value().map(|p| p * cofactor.invert().unwrap()))?;
2060
2061 let cofactor_root_times_cofactor =
2062 self.mul_by_constant(layouter, cofactor, &cofactor_root)?;
2063
2064 self.assert_equal(layouter, p, &cofactor_root_times_cofactor)
2065 }
2066}
2067
2068#[derive(Clone, Debug)]
2069#[cfg(any(test, feature = "testing"))]
2070pub struct ForeignEccTestConfig<F, C, S, N>
2073where
2074 F: PrimeField,
2075 C: WeierstrassCurve,
2076 S: ScalarFieldInstructions<F> + FromScratch<F>,
2077 S::Scalar: InnerValue<Element = C::Scalar>,
2078 N: NativeInstructions<F> + FromScratch<F>,
2079{
2080 native_gadget_config: <N as FromScratch<F>>::Config,
2081 scalar_field_config: <S as FromScratch<F>>::Config,
2082 ff_ecc_config: ForeignEccConfig<C>,
2083}
2084
2085#[cfg(any(test, feature = "testing"))]
2086impl<F, C, B, S, N> FromScratch<F> for ForeignEccChip<F, C, B, S, N>
2087where
2088 F: PrimeField,
2089 C: WeierstrassCurve,
2090 B: FieldEmulationParams<F, C::Base>,
2091 S: ScalarFieldInstructions<F> + FromScratch<F>,
2092 S::Scalar: InnerValue<Element = C::Scalar>,
2093 N: NativeInstructions<F> + FromScratch<F>,
2094{
2095 type Config = ForeignEccTestConfig<F, C, S, N>;
2096
2097 fn new_from_scratch(config: &ForeignEccTestConfig<F, C, S, N>) -> Self {
2098 let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
2099 let scalar_field_chip =
2100 <S as FromScratch<F>>::new_from_scratch(&config.scalar_field_config);
2101 ForeignEccChip::new(
2102 &config.ff_ecc_config,
2103 &native_gadget,
2104 &scalar_field_chip,
2105 OsRng,
2106 )
2107 }
2108
2109 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
2110 self.native_gadget.load_from_scratch(layouter)?;
2111 self.scalar_field_chip.load_from_scratch(layouter)
2112 }
2113
2114 fn configure_from_scratch(
2115 meta: &mut ConstraintSystem<F>,
2116 instance_columns: &[Column<Instance>; 2],
2117 ) -> ForeignEccTestConfig<F, C, S, N> {
2118 let native_gadget_config =
2119 <N as FromScratch<F>>::configure_from_scratch(meta, instance_columns);
2120 let scalar_field_config =
2121 <S as FromScratch<F>>::configure_from_scratch(meta, instance_columns);
2122 let nb_advice_cols = nb_foreign_ecc_chip_columns::<F, C, B, S>();
2123 let advice_columns = (0..nb_advice_cols).map(|_| meta.advice_column()).collect::<Vec<_>>();
2124 let base_field_config = FieldChip::<F, C::Base, B, N>::configure(meta, &advice_columns);
2125 let ff_ecc_config =
2126 ForeignEccChip::<F, C, B, S, N>::configure(meta, &base_field_config, &advice_columns);
2127 ForeignEccTestConfig {
2128 native_gadget_config,
2129 scalar_field_config,
2130 ff_ecc_config,
2131 }
2132 }
2133}
2134
2135#[cfg(test)]
2136mod tests {
2137 use group::Group;
2138 use midnight_curves::{secp256k1::Secp256k1, Fq as BlsScalar, G1Projective as BlsG1};
2139
2140 use super::*;
2141 use crate::{
2142 field::{
2143 decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
2144 NativeChip, NativeGadget,
2145 },
2146 instructions::{assertions, control_flow, ecc, equality, public_input, zero},
2147 };
2148
2149 type Native<F> = NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>;
2150
2151 type EmulatedField<F, C> = FieldChip<F, <C as Group>::Scalar, MultiEmulationParams, Native<F>>;
2152
2153 macro_rules! test_generic {
2154 ($mod:ident, $op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
2155 $mod::tests::$op::<
2156 $native,
2157 AssignedForeignPoint<$native, $curve, MultiEmulationParams>,
2158 ForeignEccChip<
2159 $native,
2160 $curve,
2161 MultiEmulationParams,
2162 $scalar_field,
2163 Native<$native>,
2164 >,
2165 >($name);
2166 };
2167 }
2168
2169 macro_rules! test {
2170 ($mod:ident, $op:ident) => {
2171 #[test]
2172 fn $op() {
2173 test_generic!($mod, $op, BlsScalar, Secp256k1, EmulatedField<BlsScalar, Secp256k1>, "foreign_ecc_secp");
2174
2175 test_generic!($mod, $op, BlsScalar, BlsG1, Native<BlsScalar>, "foreign_ecc_bls_over_bls");
2177 }
2178 };
2179 }
2180
2181 test!(assertions, test_assertions);
2182
2183 test!(public_input, test_public_inputs);
2184
2185 test!(equality, test_is_equal);
2186
2187 test!(zero, test_zero_assertions);
2188 test!(zero, test_is_zero);
2189
2190 test!(control_flow, test_select);
2191 test!(control_flow, test_cond_assert_equal);
2192 test!(control_flow, test_cond_swap);
2193
2194 macro_rules! ecc_test {
2195 ($op:ident, $native:ty, $curve:ty, $scalar_field:ty, $name:expr) => {
2196 ecc::tests::$op::<
2197 $native,
2198 $curve,
2199 ForeignEccChip<
2200 $native,
2201 $curve,
2202 MultiEmulationParams,
2203 $scalar_field,
2204 Native<$native>,
2205 >,
2206 >($name);
2207 };
2208 }
2209
2210 macro_rules! ecc_tests {
2211 ($op:ident) => {
2212 #[test]
2213 fn $op() {
2214 ecc_test!($op, BlsScalar, Secp256k1, EmulatedField<BlsScalar, Secp256k1>, "foreign_ecc_secp");
2215
2216 ecc_test!($op, BlsScalar, BlsG1, Native<BlsScalar>, "foreign_ecc_bls_over_bls");
2218 }
2219 };
2220 }
2221
2222 ecc_tests!(test_add);
2223 ecc_tests!(test_double);
2224 ecc_tests!(test_negate);
2225 ecc_tests!(test_msm);
2226 ecc_tests!(test_msm_by_bounded_scalars);
2227 ecc_tests!(test_mul_by_constant);
2228 ecc_tests!(test_coordinates);
2229}