@@ -512,8 +512,18 @@ where
512512 }
513513 let self_affine = self . to_affine ( ) ?;
514514 let ( x, y, infinity) = ( self_affine. x , self_affine. y , self_affine. infinity ) ;
515- // We first handle the non-zero case, and then later
516- // will conditionally select zero if `self` was zero.
515+ // We first handle the non-zero case, and then later will conditionally select
516+ // zero if `self` was zero. However, we also want to make sure that generated
517+ // constraints are satisfiable in both cases.
518+ //
519+ // In particular, using non-sensible values for `x` and `y` in zero-case may cause
520+ // `unchecked` operations to generate constraints that can never be satisfied, depending
521+ // on the curve equation coefficients.
522+ //
523+ // The safest approach is to use coordinates of some point from the curve, thus not
524+ // violating assumptions of `NonZeroAffine`. For instance, generator point.
525+ let x = infinity. select ( & F :: constant ( P :: GENERATOR . x ) , & x) ?;
526+ let y = infinity. select ( & F :: constant ( P :: GENERATOR . y ) , & y) ?;
517527 let non_zero_self = NonZeroAffineVar :: new ( x, y) ;
518528
519529 let mut bits = bits. collect :: < Vec < _ > > ( ) ;
@@ -968,3 +978,61 @@ where
968978 Ok ( bytes)
969979 }
970980}
981+
982+ #[ cfg( test) ]
983+ mod test_sw_curve {
984+ use crate :: {
985+ alloc:: AllocVar ,
986+ eq:: EqGadget ,
987+ fields:: { fp:: FpVar , nonnative:: NonNativeFieldVar } ,
988+ groups:: { curves:: short_weierstrass:: ProjectiveVar , CurveVar } ,
989+ ToBitsGadget ,
990+ } ;
991+ use ark_ec:: {
992+ short_weierstrass:: { Projective , SWCurveConfig } ,
993+ CurveGroup ,
994+ } ;
995+ use ark_ff:: PrimeField ;
996+ use ark_relations:: r1cs:: { ConstraintSystem , Result } ;
997+ use ark_std:: UniformRand ;
998+ use num_traits:: Zero ;
999+
1000+ fn zero_point_scalar_mul_satisfied < G > ( ) -> Result < bool >
1001+ where
1002+ G : CurveGroup ,
1003+ G :: BaseField : PrimeField ,
1004+ G :: Config : SWCurveConfig ,
1005+ {
1006+ let mut rng = ark_std:: test_rng ( ) ;
1007+
1008+ let cs = ConstraintSystem :: new_ref ( ) ;
1009+ let point_in = Projective :: < G :: Config > :: zero ( ) ;
1010+ let point_out = Projective :: < G :: Config > :: zero ( ) ;
1011+ let scalar = G :: ScalarField :: rand ( & mut rng) ;
1012+
1013+ let point_in =
1014+ ProjectiveVar :: < G :: Config , FpVar < G :: BaseField > > :: new_witness ( cs. clone ( ) , || {
1015+ Ok ( point_in)
1016+ } ) ?;
1017+ let point_out =
1018+ ProjectiveVar :: < G :: Config , FpVar < G :: BaseField > > :: new_input ( cs. clone ( ) , || {
1019+ Ok ( point_out)
1020+ } ) ?;
1021+ let scalar = NonNativeFieldVar :: new_input ( cs. clone ( ) , || Ok ( scalar) ) ?;
1022+
1023+ let mul = point_in. scalar_mul_le ( scalar. to_bits_le ( ) . unwrap ( ) . iter ( ) ) ?;
1024+
1025+ point_out. enforce_equal ( & mul) ?;
1026+
1027+ cs. is_satisfied ( )
1028+ }
1029+
1030+ #[ test]
1031+ fn test_zero_point_scalar_mul ( ) {
1032+ assert ! ( zero_point_scalar_mul_satisfied:: <ark_bls12_381:: G1Projective >( ) . unwrap( ) ) ;
1033+ assert ! ( zero_point_scalar_mul_satisfied:: <ark_pallas:: Projective >( ) . unwrap( ) ) ;
1034+ assert ! ( zero_point_scalar_mul_satisfied:: <ark_mnt4_298:: G1Projective >( ) . unwrap( ) ) ;
1035+ assert ! ( zero_point_scalar_mul_satisfied:: <ark_mnt6_298:: G1Projective >( ) . unwrap( ) ) ;
1036+ assert ! ( zero_point_scalar_mul_satisfied:: <ark_bn254:: G1Projective >( ) . unwrap( ) ) ;
1037+ }
1038+ }
0 commit comments