1use ark_ec::{
2 short_weierstrass::{Affine as SWAffine, Projective as SWProjective, SWCurveConfig},
3 AffineRepr, CurveConfig, CurveGroup,
4};
5use ark_ff::{AdditiveGroup, BitIteratorBE, Field, One, PrimeField, Zero};
6use ark_relations::gr1cs::{ConstraintSystemRef, Namespace, SynthesisError};
7use ark_std::{borrow::Borrow, marker::PhantomData, ops::Mul};
8use educe::Educe;
9use non_zero_affine::NonZeroAffineVar;
10
11use crate::{
12 convert::ToConstraintFieldGadget,
13 fields::{emulated_fp::EmulatedFpVar, fp::FpVar},
14 prelude::*,
15 Vec,
16};
17
18pub mod bls12;
21
22pub mod mnt4;
26pub mod mnt6;
30
31pub mod non_zero_affine;
40
41type BasePrimeField<P> = <<P as CurveConfig>::BaseField as Field>::BasePrimeField;
42
43#[derive(Educe)]
47#[educe(Debug, Clone)]
48#[must_use]
49pub struct ProjectiveVar<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>>
50where
51 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
52{
53 pub x: F,
55 pub y: F,
57 pub z: F,
59 #[educe(Debug(ignore))]
60 _params: PhantomData<P>,
61}
62
63#[derive(Educe)]
65#[educe(Debug, Clone)]
66#[must_use]
67pub struct AffineVar<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>>
68where
69 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
70{
71 pub x: F,
73 pub y: F,
75 pub infinity: Boolean<BasePrimeField<P>>,
77 #[educe(Debug(ignore))]
78 _params: PhantomData<P>,
79}
80
81impl<P, F> AffineVar<P, F>
82where
83 P: SWCurveConfig,
84 F: FieldVar<P::BaseField, BasePrimeField<P>>,
85 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
86{
87 fn new(x: F, y: F, infinity: Boolean<BasePrimeField<P>>) -> Self {
88 Self {
89 x,
90 y,
91 infinity,
92 _params: PhantomData,
93 }
94 }
95
96 pub fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
99 Ok(match self.infinity.value()? {
100 true => SWAffine::identity(),
101 false => SWAffine::new(self.x.value()?, self.y.value()?),
102 })
103 }
104}
105
106impl<P, F> ToConstraintFieldGadget<BasePrimeField<P>> for AffineVar<P, F>
107where
108 P: SWCurveConfig,
109 F: FieldVar<P::BaseField, BasePrimeField<P>>,
110 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
111 F: ToConstraintFieldGadget<BasePrimeField<P>>,
112{
113 fn to_constraint_field(&self) -> Result<Vec<FpVar<BasePrimeField<P>>>, SynthesisError> {
114 let mut res = Vec::<FpVar<BasePrimeField<P>>>::new();
115
116 res.extend_from_slice(&self.x.to_constraint_field()?);
117 res.extend_from_slice(&self.y.to_constraint_field()?);
118 res.extend_from_slice(&self.infinity.to_constraint_field()?);
119
120 Ok(res)
121 }
122}
123
124impl<P, F> GR1CSVar<BasePrimeField<P>> for ProjectiveVar<P, F>
125where
126 P: SWCurveConfig,
127 F: FieldVar<P::BaseField, BasePrimeField<P>>,
128 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
129{
130 type Value = SWProjective<P>;
131
132 fn cs(&self) -> ConstraintSystemRef<BasePrimeField<P>> {
133 self.x.cs().or(self.y.cs()).or(self.z.cs())
134 }
135
136 fn value(&self) -> Result<Self::Value, SynthesisError> {
137 let (x, y, z) = (self.x.value()?, self.y.value()?, self.z.value()?);
138 let result = if let Some(z_inv) = z.inverse() {
139 SWAffine::new(x * &z_inv, y * &z_inv)
140 } else {
141 SWAffine::identity()
142 };
143 Ok(result.into())
144 }
145}
146
147impl<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>> ProjectiveVar<P, F>
148where
149 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
150{
151 pub fn new(x: F, y: F, z: F) -> Self {
153 Self {
154 x,
155 y,
156 z,
157 _params: PhantomData,
158 }
159 }
160
161 #[tracing::instrument(target = "gr1cs")]
163 pub fn to_affine(&self) -> Result<AffineVar<P, F>, SynthesisError> {
164 if self.is_constant() {
165 let point = self.value()?.into_affine();
166 let x = F::new_constant(ConstraintSystemRef::None, point.x)?;
167 let y = F::new_constant(ConstraintSystemRef::None, point.y)?;
168 let infinity = Boolean::constant(point.is_zero());
169 Ok(AffineVar::new(x, y, infinity))
170 } else {
171 let cs = self.cs();
172 let infinity = self.is_zero()?;
173 let zero_affine = SWAffine::<P>::zero();
174 let zero_x = F::new_constant(cs.clone(), &zero_affine.x)?;
175 let zero_y = F::new_constant(cs.clone(), &zero_affine.y)?;
176 let z_inv = F::new_witness(ark_relations::ns!(cs, "z_inverse"), || {
179 Ok(self.z.value()?.inverse().unwrap_or_else(P::BaseField::zero))
180 })?;
181 z_inv.mul_equals(&self.z, &F::from(!&infinity))?;
187
188 let non_zero_x = &self.x * &z_inv;
189 let non_zero_y = &self.y * &z_inv;
190
191 let x = infinity.select(&zero_x, &non_zero_x)?;
192 let y = infinity.select(&zero_y, &non_zero_y)?;
193
194 Ok(AffineVar::new(x, y, infinity))
195 }
196 }
197
198 #[tracing::instrument(target = "gr1cs", skip(cs, f))]
202 pub fn new_variable_omit_on_curve_check(
203 cs: impl Into<Namespace<BasePrimeField<P>>>,
204 f: impl FnOnce() -> Result<SWProjective<P>, SynthesisError>,
205 mode: AllocationMode,
206 ) -> Result<Self, SynthesisError> {
207 let ns = cs.into();
208 let cs = ns.cs();
209
210 let (x, y, z) = match f() {
211 Ok(ge) => {
212 let ge = ge.into_affine();
213 if ge.is_zero() {
214 (
217 Ok(P::BaseField::zero()),
218 Ok(P::BaseField::one()),
219 Ok(P::BaseField::zero()),
220 )
221 } else {
222 (Ok(ge.x), Ok(ge.y), Ok(P::BaseField::one()))
223 }
224 },
225 _ => (
226 Err(SynthesisError::AssignmentMissing),
227 Err(SynthesisError::AssignmentMissing),
228 Err(SynthesisError::AssignmentMissing),
229 ),
230 };
231
232 let x = F::new_variable(ark_relations::ns!(cs, "x"), || x, mode)?;
233 let y = F::new_variable(ark_relations::ns!(cs, "y"), || y, mode)?;
234 let z = F::new_variable(ark_relations::ns!(cs, "z"), || z, mode)?;
235
236 Ok(Self::new(x, y, z))
237 }
238
239 #[tracing::instrument(target = "gr1cs", skip(self, other))]
242 pub(crate) fn add_mixed(&self, other: &NonZeroAffineVar<P, F>) -> Result<Self, SynthesisError> {
243 let three_b = P::COEFF_B.double() + &P::COEFF_B;
252 let (x1, y1, z1) = (&self.x, &self.y, &self.z);
253 let (x2, y2) = (&other.x, &other.y);
254
255 let xx = x1 * x2; let yy = y1 * y2; let xy_pairs = ((x1 + y1) * &(x2 + y2)) - (&xx + &yy); let xz_pairs = (x2 * z1) + x1; let yz_pairs = (y2 * z1) + y1; let axz = mul_by_coeff_a::<P, F>(&xz_pairs); let bz3_part = &axz + z1 * three_b; let yy_m_bz3 = &yy - &bz3_part; let yy_p_bz3 = &yy + &bz3_part; let azz = mul_by_coeff_a::<P, F>(z1); let xx3_p_azz = xx.double().unwrap() + &xx + &azz; let bxz3 = &xz_pairs * three_b; let b3_xz_pairs = mul_by_coeff_a::<P, F>(&(&xx - &azz)) + &bxz3; let x = (&yy_m_bz3 * &xy_pairs) - &yz_pairs * &b3_xz_pairs; let y = (&yy_p_bz3 * &yy_m_bz3) + &xx3_p_azz * b3_xz_pairs; let z = (&yy_p_bz3 * &yz_pairs) + xy_pairs * xx3_p_azz; Ok(ProjectiveVar::new(x, y, z))
279 }
280
281 #[tracing::instrument(
284 target = "gr1cs",
285 skip(self, mul_result, multiple_of_power_of_two, bits)
286 )]
287 fn fixed_scalar_mul_le(
288 &self,
289 mul_result: &mut Self,
290 multiple_of_power_of_two: &mut NonZeroAffineVar<P, F>,
291 bits: &[&Boolean<BasePrimeField<P>>],
292 ) -> Result<(), SynthesisError> {
293 let scalar_modulus_bits = <P::ScalarField as PrimeField>::MODULUS_BIT_SIZE as usize;
294
295 assert!(scalar_modulus_bits >= bits.len());
296 let split_len = ark_std::cmp::min(scalar_modulus_bits - 2, bits.len());
297 let (affine_bits, proj_bits) = bits.split_at(split_len);
298 let mut accumulator = multiple_of_power_of_two.clone();
330 let initial_acc_value = accumulator.into_projective();
331
332 multiple_of_power_of_two.double_in_place()?;
334
335 for bit in affine_bits.iter().skip(1) {
338 if bit.is_constant() {
339 if *bit == &Boolean::TRUE {
340 accumulator = accumulator.add_unchecked(multiple_of_power_of_two)?;
341 }
342 } else {
343 let temp = accumulator.add_unchecked(multiple_of_power_of_two)?;
344 accumulator = bit.select(&temp, &accumulator)?;
345 }
346 multiple_of_power_of_two.double_in_place()?;
347 }
348 let result = accumulator.into_projective();
354 let subtrahend = bits[0].select(&Self::zero(), &initial_acc_value)?;
356 *mul_result += result - subtrahend;
357
358 for bit in proj_bits {
360 if bit.is_constant() {
361 if *bit == &Boolean::TRUE {
362 *mul_result += &multiple_of_power_of_two.into_projective();
363 }
364 } else {
365 let temp = &*mul_result + &multiple_of_power_of_two.into_projective();
366 *mul_result = bit.select(&temp, &mul_result)?;
367 }
368 multiple_of_power_of_two.double_in_place()?;
369 }
370 Ok(())
371 }
372}
373
374impl<P, F> CurveVar<SWProjective<P>, BasePrimeField<P>> for ProjectiveVar<P, F>
375where
376 P: SWCurveConfig,
377 F: FieldVar<P::BaseField, BasePrimeField<P>>,
378 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
379{
380 type BaseFieldVar = F;
381
382 fn constant(g: SWProjective<P>) -> Self {
383 let cs = ConstraintSystemRef::None;
384 Self::new_variable_omit_on_curve_check(cs, || Ok(g), AllocationMode::Constant).unwrap()
385 }
386
387 fn zero() -> Self {
388 Self::new(F::zero(), F::one(), F::zero())
389 }
390
391 fn is_zero(&self) -> Result<Boolean<BasePrimeField<P>>, SynthesisError> {
392 self.z.is_zero()
393 }
394
395 #[tracing::instrument(target = "gr1cs", skip(cs, f))]
396 fn new_variable_omit_prime_order_check(
397 cs: impl Into<Namespace<BasePrimeField<P>>>,
398 f: impl FnOnce() -> Result<SWProjective<P>, SynthesisError>,
399 mode: AllocationMode,
400 ) -> Result<Self, SynthesisError> {
401 let ns = cs.into();
402 let cs = ns.cs();
403 let g = Self::new_variable_omit_on_curve_check(cs, f, mode)?;
416
417 if mode != AllocationMode::Constant {
418 let b = P::COEFF_B;
420 let a = P::COEFF_A;
421
422 let x2 = g.x.square()?;
423 let y2 = g.y.square()?;
424 let z2 = g.z.square()?;
425 let t = &g.x * (x2 + &z2 * a);
426
427 g.z.mul_equals(&(y2 - z2 * b), &t)?;
428 }
429 Ok(g)
430 }
431
432 #[tracing::instrument(target = "gr1cs")]
439 fn enforce_prime_order(&self) -> Result<(), SynthesisError> {
440 unimplemented!("cannot enforce prime order");
441 }
454
455 #[inline]
456 #[tracing::instrument(target = "gr1cs")]
457 fn double_in_place(&mut self) -> Result<(), SynthesisError> {
458 let three_b = P::COEFF_B.double() + &P::COEFF_B;
467
468 let xx = self.x.square()?; let yy = self.y.square()?; let zz = self.z.square()?; let xy2 = (&self.x * &self.y).double()?; let xz2 = (&self.x * &self.z).double()?; let axz2 = mul_by_coeff_a::<P, F>(&xz2); let bzz3_part = &axz2 + &zz * three_b; let yy_m_bzz3 = &yy - &bzz3_part; let yy_p_bzz3 = &yy + &bzz3_part; let y_frag = yy_p_bzz3 * &yy_m_bzz3; let x_frag = yy_m_bzz3 * &xy2; let bxz3 = xz2 * three_b; let azz = mul_by_coeff_a::<P, F>(&zz); let b3_xz_pairs = mul_by_coeff_a::<P, F>(&(&xx - &azz)) + &bxz3; let xx3_p_azz = (xx.double()? + &xx + &azz) * &b3_xz_pairs; let y = y_frag + &xx3_p_azz; let yz2 = (&self.y * &self.z).double()?; let x = x_frag - &(b3_xz_pairs * &yz2); let z = (yz2 * &yy).double()?.double()?; self.x = x;
492 self.y = y;
493 self.z = z;
494 Ok(())
495 }
496
497 #[tracing::instrument(target = "gr1cs")]
498 fn negate(&self) -> Result<Self, SynthesisError> {
499 Ok(Self::new(self.x.clone(), self.y.negate()?, self.z.clone()))
500 }
501
502 #[tracing::instrument(target = "gr1cs", skip(bits))]
505 fn scalar_mul_le<'a>(
506 &self,
507 bits: impl Iterator<Item = &'a Boolean<BasePrimeField<P>>>,
508 ) -> Result<Self, SynthesisError> {
509 if self.is_constant() && self.value().unwrap().is_zero() {
510 return Ok(self.clone());
511 }
512 let self_affine = self.to_affine()?;
513 let (x, y, infinity) = (self_affine.x, self_affine.y, self_affine.infinity);
514 let x = infinity.select(&F::constant(P::GENERATOR.x), &x)?;
526 let y = infinity.select(&F::constant(P::GENERATOR.y), &y)?;
527 let non_zero_self = NonZeroAffineVar::new(x, y);
528
529 let mut bits = bits.collect::<Vec<_>>();
530 if bits.len() == 0 {
531 return Ok(Self::zero());
532 }
533 bits = bits
535 .into_iter()
536 .rev()
538 .skip_while(|b| b.is_constant() && (b.value().unwrap() == false))
540 .collect();
541 bits.reverse();
544
545 let scalar_modulus_bits = <P::ScalarField as PrimeField>::MODULUS_BIT_SIZE;
546 let mut mul_result = Self::zero();
547 let mut power_of_two_times_self = non_zero_self;
548 for bits in bits.chunks(scalar_modulus_bits as usize) {
550 self.fixed_scalar_mul_le(&mut mul_result, &mut power_of_two_times_self, bits)?;
551 }
552
553 infinity.select(&Self::zero(), &mul_result)
559 }
560
561 #[tracing::instrument(target = "gr1cs", skip(scalar_bits_with_bases))]
562 fn precomputed_base_scalar_mul_le<'a, I, B>(
563 &mut self,
564 scalar_bits_with_bases: I,
565 ) -> Result<(), SynthesisError>
566 where
567 I: Iterator<Item = (B, &'a SWProjective<P>)>,
568 B: Borrow<Boolean<BasePrimeField<P>>>,
569 {
570 let (bits, bases): (Vec<_>, Vec<_>) = scalar_bits_with_bases
572 .map(|(b, c)| (b.borrow().clone(), *c))
573 .unzip();
574 let base = bases[0];
575 *self += Self::constant(base).scalar_mul_le(bits.iter())?;
576 Ok(())
577 }
578
579 fn affine_xy(&self) -> Result<(F, F), SynthesisError> {
580 let self_affine = self.to_affine()?;
581 Ok((self_affine.x, self_affine.y))
582 }
583}
584
585impl<P, F> ToConstraintFieldGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
586where
587 P: SWCurveConfig,
588 F: FieldVar<P::BaseField, BasePrimeField<P>>,
589 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
590 F: ToConstraintFieldGadget<BasePrimeField<P>>,
591{
592 fn to_constraint_field(&self) -> Result<Vec<FpVar<BasePrimeField<P>>>, SynthesisError> {
593 self.to_affine()?.to_constraint_field()
594 }
595}
596
597fn mul_by_coeff_a<P: SWCurveConfig, F: FieldVar<P::BaseField, BasePrimeField<P>>>(f: &F) -> F
598where
599 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
600{
601 if !P::COEFF_A.is_zero() {
602 f * P::COEFF_A
603 } else {
604 F::zero()
605 }
606}
607
608impl_bounded_ops!(
609 ProjectiveVar<P, F>,
610 SWProjective<P>,
611 Add,
612 add,
613 AddAssign,
614 add_assign,
615 |mut this: &'a ProjectiveVar<P, F>, mut other: &'a ProjectiveVar<P, F>| {
616 if this.is_constant() {
622 core::mem::swap(&mut this, &mut other);
624 }
625
626 if other.is_constant() {
627 let other = other.value().unwrap();
629 if other.is_zero() {
630 this.clone()
632 } else {
633 let x = F::constant(other.x);
635 let y = F::constant(other.y);
636 this.add_mixed(&NonZeroAffineVar::new(x, y)).unwrap()
637 }
638 } else {
639 let three_b = P::COEFF_B.double() + &P::COEFF_B;
648 let (x1, y1, z1) = (&this.x, &this.y, &this.z);
649 let (x2, y2, z2) = (&other.x, &other.y, &other.z);
650
651 let xx = x1 * x2; let yy = y1 * y2; let zz = z1 * z2; let xy_pairs = ((x1 + y1) * &(x2 + y2)) - (&xx + &yy); let xz_pairs = ((x1 + z1) * &(x2 + z2)) - (&xx + &zz); let yz_pairs = ((y1 + z1) * &(y2 + z2)) - (&yy + &zz); let axz = mul_by_coeff_a::<P, F>(&xz_pairs); let bzz3_part = &axz + &zz * three_b; let yy_m_bzz3 = &yy - &bzz3_part; let yy_p_bzz3 = &yy + &bzz3_part; let azz = mul_by_coeff_a::<P, F>(&zz);
666 let xx3_p_azz = xx.double().unwrap() + &xx + &azz; let bxz3 = &xz_pairs * three_b; let b3_xz_pairs = mul_by_coeff_a::<P, F>(&(&xx - &azz)) + &bxz3; let x = (&yy_m_bzz3 * &xy_pairs) - &yz_pairs * &b3_xz_pairs; let y = (&yy_p_bzz3 * &yy_m_bzz3) + &xx3_p_azz * b3_xz_pairs; let z = (&yy_p_bzz3 * &yz_pairs) + xy_pairs * xx3_p_azz; ProjectiveVar::new(x, y, z)
676 }
677
678 },
679 |this: &'a ProjectiveVar<P, F>, other: SWProjective<P>| {
680 this + ProjectiveVar::constant(other)
681 },
682 (F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig),
683 for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
684);
685
686impl_bounded_ops!(
687 ProjectiveVar<P, F>,
688 SWProjective<P>,
689 Sub,
690 sub,
691 SubAssign,
692 sub_assign,
693 |this: &'a ProjectiveVar<P, F>, other: &'a ProjectiveVar<P, F>| this + other.negate().unwrap(),
694 |this: &'a ProjectiveVar<P, F>, other: SWProjective<P>| this - ProjectiveVar::constant(other),
695 (F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig),
696 for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>
697);
698
699impl_bounded_ops_diff!(
700 ProjectiveVar<P, F>,
701 SWProjective<P>,
702 EmulatedFpVar<P::ScalarField, BasePrimeField<P>>,
703 P::ScalarField,
704 Mul,
705 mul,
706 MulAssign,
707 mul_assign,
708 |this: &'a ProjectiveVar<P, F>, other: &'a EmulatedFpVar<P::ScalarField, BasePrimeField<P>>| {
709 if this.is_constant() && other.is_constant() {
710 assert!(this.is_constant() && other.is_constant());
711 ProjectiveVar::constant(this.value().unwrap() * &other.value().unwrap())
712 } else {
713 let bits = other.to_bits_le().unwrap();
714 this.scalar_mul_le(bits.iter()).unwrap()
715 }
716 },
717 |this: &'a ProjectiveVar<P, F>, other: P::ScalarField| this * EmulatedFpVar::constant(other),
718 (F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig),
719 for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
720);
721
722impl<'a, P, F> GroupOpsBounds<'a, SWProjective<P>, ProjectiveVar<P, F>> for ProjectiveVar<P, F>
723where
724 P: SWCurveConfig,
725 F: FieldVar<P::BaseField, BasePrimeField<P>>,
726 for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
727{
728}
729
730impl<'a, P, F> GroupOpsBounds<'a, SWProjective<P>, ProjectiveVar<P, F>> for &'a ProjectiveVar<P, F>
731where
732 P: SWCurveConfig,
733 F: FieldVar<P::BaseField, BasePrimeField<P>>,
734 for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
735{
736}
737
738impl<P, F> CondSelectGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
739where
740 P: SWCurveConfig,
741 F: FieldVar<P::BaseField, BasePrimeField<P>>,
742 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
743{
744 #[inline]
745 #[tracing::instrument(target = "gr1cs")]
746 fn conditionally_select(
747 cond: &Boolean<BasePrimeField<P>>,
748 true_value: &Self,
749 false_value: &Self,
750 ) -> Result<Self, SynthesisError> {
751 let x = cond.select(&true_value.x, &false_value.x)?;
752 let y = cond.select(&true_value.y, &false_value.y)?;
753 let z = cond.select(&true_value.z, &false_value.z)?;
754
755 Ok(Self::new(x, y, z))
756 }
757}
758
759impl<P, F> EqGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
760where
761 P: SWCurveConfig,
762 F: FieldVar<P::BaseField, BasePrimeField<P>>,
763 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
764{
765 #[tracing::instrument(target = "gr1cs")]
766 fn is_eq(&self, other: &Self) -> Result<Boolean<BasePrimeField<P>>, SynthesisError> {
767 let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?;
768 let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?;
769 let coordinates_equal = x_equal & y_equal;
770 let both_are_zero = self.is_zero()? & other.is_zero()?;
771 Ok(both_are_zero | coordinates_equal)
772 }
773
774 #[inline]
775 #[tracing::instrument(target = "gr1cs")]
776 fn conditional_enforce_equal(
777 &self,
778 other: &Self,
779 condition: &Boolean<BasePrimeField<P>>,
780 ) -> Result<(), SynthesisError> {
781 let x_equal = (&self.x * &other.z).is_eq(&(&other.x * &self.z))?;
782 let y_equal = (&self.y * &other.z).is_eq(&(&other.y * &self.z))?;
783 let coordinates_equal = x_equal & y_equal;
784 let both_are_zero = self.is_zero()? & other.is_zero()?;
785 (both_are_zero | coordinates_equal).conditional_enforce_equal(&Boolean::TRUE, condition)
786 }
787
788 #[inline]
789 #[tracing::instrument(target = "gr1cs")]
790 fn conditional_enforce_not_equal(
791 &self,
792 other: &Self,
793 condition: &Boolean<BasePrimeField<P>>,
794 ) -> Result<(), SynthesisError> {
795 let is_equal = self.is_eq(other)?;
796 (is_equal & condition).enforce_equal(&Boolean::FALSE)
797 }
798}
799
800impl<P, F> AllocVar<SWAffine<P>, BasePrimeField<P>> for ProjectiveVar<P, F>
801where
802 P: SWCurveConfig,
803 F: FieldVar<P::BaseField, BasePrimeField<P>>,
804 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
805{
806 fn new_variable<T: Borrow<SWAffine<P>>>(
807 cs: impl Into<Namespace<BasePrimeField<P>>>,
808 f: impl FnOnce() -> Result<T, SynthesisError>,
809 mode: AllocationMode,
810 ) -> Result<Self, SynthesisError> {
811 Self::new_variable(
812 cs,
813 || f().map(|b| SWProjective::from((*b.borrow()).clone())),
814 mode,
815 )
816 }
817}
818
819impl<P, F> AllocVar<SWProjective<P>, BasePrimeField<P>> for ProjectiveVar<P, F>
820where
821 P: SWCurveConfig,
822 F: FieldVar<P::BaseField, BasePrimeField<P>>,
823 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
824{
825 fn new_variable<T: Borrow<SWProjective<P>>>(
826 cs: impl Into<Namespace<BasePrimeField<P>>>,
827 f: impl FnOnce() -> Result<T, SynthesisError>,
828 mode: AllocationMode,
829 ) -> Result<Self, SynthesisError> {
830 let ns = cs.into();
831 let cs = ns.cs();
832 let f = || Ok(*f()?.borrow());
833 match mode {
834 AllocationMode::Constant => Self::new_variable_omit_prime_order_check(cs, f, mode),
835 AllocationMode::Input => Self::new_variable_omit_prime_order_check(cs, f, mode),
836 AllocationMode::Witness => {
837 let mut power_of_2: u32 = 0;
842 let mut cofactor = P::COFACTOR.to_vec();
843 while cofactor[0] % 2 == 0 {
844 div2(&mut cofactor);
845 power_of_2 += 1;
846 }
847
848 let cofactor_weight = BitIteratorBE::new(cofactor.as_slice())
849 .filter(|b| *b)
850 .count();
851 let modulus_minus_1 = (-P::ScalarField::one()).into_bigint(); let modulus_minus_1_weight =
853 BitIteratorBE::new(modulus_minus_1).filter(|b| *b).count();
854
855 let (mut ge, iter) = if cofactor_weight < modulus_minus_1_weight {
864 let ge = Self::new_variable_omit_prime_order_check(
865 ark_relations::ns!(cs, "Witness without subgroup check with cofactor mul"),
866 || f().map(|g| g.into_affine().mul_by_cofactor_inv().into()),
867 mode,
868 )?;
869 (
870 ge,
871 BitIteratorBE::without_leading_zeros(cofactor.as_slice()),
872 )
873 } else {
874 let ge = Self::new_variable_omit_prime_order_check(
875 ark_relations::ns!(cs, "Witness without subgroup check with `r` check"),
876 || {
877 f().map(|g| {
878 let g = g.into_affine();
879 let power_of_two = P::ScalarField::ONE.into_bigint() << power_of_2;
880 let power_of_two_inv = P::ScalarField::from_bigint(power_of_two)
881 .and_then(|n| n.inverse())
882 .unwrap();
883 g.mul(power_of_two_inv)
884 })
885 },
886 mode,
887 )?;
888
889 (
890 ge,
891 BitIteratorBE::without_leading_zeros(modulus_minus_1.as_ref()),
892 )
893 };
894 for _ in 0..power_of_2 {
896 ge.double_in_place()?;
897 }
898
899 let mut result = Self::zero();
900 for b in iter {
901 result.double_in_place()?;
902
903 if b {
904 result += &ge
905 }
906 }
907 if cofactor_weight < modulus_minus_1_weight {
908 Ok(result)
909 } else {
910 ge.enforce_equal(&ge)?;
911 Ok(ge)
912 }
913 },
914 }
915 }
916}
917
918#[inline]
919fn div2(limbs: &mut [u64]) {
920 let mut t = 0;
921 for i in limbs.iter_mut().rev() {
922 let t2 = *i << 63;
923 *i >>= 1;
924 *i |= t;
925 t = t2;
926 }
927}
928
929impl<P, F> ToBitsGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
930where
931 P: SWCurveConfig,
932 F: FieldVar<P::BaseField, BasePrimeField<P>>,
933 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
934{
935 #[tracing::instrument(target = "gr1cs")]
936 fn to_bits_le(&self) -> Result<Vec<Boolean<BasePrimeField<P>>>, SynthesisError> {
937 let g = self.to_affine()?;
938 let mut bits = g.x.to_bits_le()?;
939 let y_bits = g.y.to_bits_le()?;
940 bits.extend_from_slice(&y_bits);
941 bits.push(g.infinity);
942 Ok(bits)
943 }
944
945 #[tracing::instrument(target = "gr1cs")]
946 fn to_non_unique_bits_le(&self) -> Result<Vec<Boolean<BasePrimeField<P>>>, SynthesisError> {
947 let g = self.to_affine()?;
948 let mut bits = g.x.to_non_unique_bits_le()?;
949 let y_bits = g.y.to_non_unique_bits_le()?;
950 bits.extend_from_slice(&y_bits);
951 bits.push(g.infinity);
952 Ok(bits)
953 }
954}
955
956impl<P, F> ToBytesGadget<BasePrimeField<P>> for ProjectiveVar<P, F>
957where
958 P: SWCurveConfig,
959 F: FieldVar<P::BaseField, BasePrimeField<P>>,
960 for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
961{
962 #[tracing::instrument(target = "gr1cs")]
963 fn to_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
964 let g = self.to_affine()?;
965 let mut bytes = g.x.to_bytes_le()?;
966 let y_bytes = g.y.to_bytes_le()?;
967 let inf_bytes = g.infinity.to_bytes_le()?;
968 bytes.extend_from_slice(&y_bytes);
969 bytes.extend_from_slice(&inf_bytes);
970 Ok(bytes)
971 }
972
973 #[tracing::instrument(target = "gr1cs")]
974 fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
975 let g = self.to_affine()?;
976 let mut bytes = g.x.to_non_unique_bytes_le()?;
977 let y_bytes = g.y.to_non_unique_bytes_le()?;
978 let inf_bytes = g.infinity.to_non_unique_bytes_le()?;
979 bytes.extend_from_slice(&y_bytes);
980 bytes.extend_from_slice(&inf_bytes);
981 Ok(bytes)
982 }
983}
984
985#[cfg(test)]
986mod test_sw_curve {
987 use crate::{
988 alloc::AllocVar,
989 convert::ToBitsGadget,
990 eq::EqGadget,
991 fields::{emulated_fp::EmulatedFpVar, fp::FpVar},
992 groups::{curves::short_weierstrass::ProjectiveVar, CurveVar},
993 };
994 use ark_ec::{
995 short_weierstrass::{Projective, SWCurveConfig},
996 CurveGroup,
997 };
998 use ark_ff::PrimeField;
999 use ark_relations::gr1cs::{ConstraintSystem, Result};
1000 use ark_std::UniformRand;
1001 use num_traits::Zero;
1002
1003 fn zero_point_scalar_mul_satisfied<G>() -> Result<bool>
1004 where
1005 G: CurveGroup,
1006 G::BaseField: PrimeField,
1007 G::Config: SWCurveConfig,
1008 {
1009 let mut rng = ark_std::test_rng();
1010
1011 let cs = ConstraintSystem::new_ref();
1012 let point_in = Projective::<G::Config>::zero();
1013 let point_out = Projective::<G::Config>::zero();
1014 let scalar = G::ScalarField::rand(&mut rng);
1015
1016 let point_in =
1017 ProjectiveVar::<G::Config, FpVar<G::BaseField>>::new_witness(cs.clone(), || {
1018 Ok(point_in)
1019 })?;
1020 let point_out =
1021 ProjectiveVar::<G::Config, FpVar<G::BaseField>>::new_input(cs.clone(), || {
1022 Ok(point_out)
1023 })?;
1024 let scalar = EmulatedFpVar::new_input(cs.clone(), || Ok(scalar))?;
1025
1026 let mul = point_in.scalar_mul_le(scalar.to_bits_le().unwrap().iter())?;
1027
1028 point_out.enforce_equal(&mul)?;
1029
1030 cs.is_satisfied()
1031 }
1032
1033 #[test]
1034 fn test_zero_point_scalar_mul() {
1035 assert!(zero_point_scalar_mul_satisfied::<ark_bls12_381::G1Projective>().unwrap());
1036 assert!(zero_point_scalar_mul_satisfied::<ark_pallas::Projective>().unwrap());
1037 assert!(zero_point_scalar_mul_satisfied::<ark_mnt4_298::G1Projective>().unwrap());
1038 assert!(zero_point_scalar_mul_satisfied::<ark_mnt6_298::G1Projective>().unwrap());
1039 assert!(zero_point_scalar_mul_satisfied::<ark_bn254::G1Projective>().unwrap());
1040 }
1041}