@@ -169,8 +169,13 @@ impl G1Affine {
169169 }
170170 }
171171
172- pub fn hinted_check_add ( t : ark_bn254:: G1Affine , q : ark_bn254:: G1Affine ) -> ( Script , Vec < Hint > ) {
172+ pub fn hinted_check_add_prevent_degenerate (
173+ t : ark_bn254:: G1Affine ,
174+ q : ark_bn254:: G1Affine ,
175+ ) -> ( Script , Vec < Hint > ) {
173176 let mut hints = vec ! [ ] ;
177+ assert_ne ! ( t, q) ;
178+ assert_ne ! ( t, -q) ;
174179
175180 let ( alpha, bias) = if !t. is_zero ( ) && !q. is_zero ( ) {
176181 let alpha = ( t. y - q. y ) / ( t. x - q. x ) ;
@@ -226,6 +231,106 @@ impl G1Affine {
226231 ( script, hints)
227232 }
228233
234+ pub fn hinted_check_add ( t : ark_bn254:: G1Affine , q : ark_bn254:: G1Affine ) -> ( Script , Vec < Hint > ) {
235+ let mut hints = vec ! [ ] ;
236+
237+ let ( alpha, bias) = if t. is_zero ( ) || q. is_zero ( ) {
238+ // no hint, dummy zero
239+ ( ark_bn254:: Fq :: ZERO , ark_bn254:: Fq :: ZERO )
240+ } else if t == -q {
241+ // no hint, dummy zero
242+ ( ark_bn254:: Fq :: ZERO , ark_bn254:: Fq :: ZERO )
243+ } else if t == q {
244+ // doubling hints
245+ let alpha = ( t. x . square ( ) + t. x . square ( ) + t. x . square ( ) ) / ( t. y + t. y ) ;
246+ let bias = t. y - alpha * t. x ;
247+ ( alpha, bias)
248+ } else {
249+ // adding hints
250+ let alpha = ( t. y - q. y ) / ( t. x - q. x ) ;
251+ let bias = t. y - alpha * t. x ;
252+ ( alpha, bias)
253+ } ;
254+
255+ let ( hinted_script1, hint1) = Self :: hinted_check_chord_line ( t, q, alpha) ;
256+ let ( hinted_script2, hint2) = Self :: hinted_add ( t. x , q. x , alpha) ;
257+ let ( hinted_script3, hint3) = Self :: hinted_check_tangent_line ( t, alpha) ;
258+
259+ let script = script ! { // tx ty qx qy
260+ { G1Affine :: is_zero_keep_element( ) }
261+ OP_IF
262+ { G1Affine :: drop( ) }
263+ OP_ELSE
264+ { G1Affine :: roll( 2 ) }
265+ { G1Affine :: is_zero_keep_element( ) }
266+ OP_IF
267+ { G1Affine :: drop( ) }
268+ OP_ELSE // qx qy tx ty
269+ { Fq :: equal_keep_elements( 3 , 1 ) } // t == q or t == -q
270+ OP_DUP
271+ OP_TOALTSTACK
272+ OP_TOALTSTACK
273+ { Fq :: equal_keep_elements( 2 , 0 ) }
274+ OP_NOT
275+ OP_FROMALTSTACK
276+ OP_BOOLAND
277+ OP_IF // case: t == -q
278+ OP_FROMALTSTACK OP_DROP
279+ { G1Affine :: drop( ) }
280+ { G1Affine :: drop( ) }
281+ { G1Affine :: identity( ) }
282+ OP_ELSE // case: t != -q
283+ for _ in 0 ..Fq :: N_LIMBS {
284+ OP_DEPTH OP_1SUB OP_ROLL
285+ }
286+ { Fq :: check_validity_and_keep_element( ) }
287+ for _ in 0 ..Fq :: N_LIMBS {
288+ OP_DEPTH OP_1SUB OP_ROLL
289+ } // qx qy tx ty c3 c4
290+ { Fq :: check_validity_and_keep_element( ) }
291+ { Fq :: copy( 1 ) } // qx qy tx ty c3 c4 c3
292+ { Fq :: copy( 1 ) } // qx qy tx ty c3 c4 c3 c4
293+ { Fq :: copy( 5 ) } // qx qy tx ty c3 c4 c3 c4 tx
294+ { Fq :: roll( 5 ) } // qx qy tx c3 c4 c3 c4 tx ty
295+ OP_FROMALTSTACK
296+ OP_IF // case: t == q
297+ { hinted_script3 } // qx qy tx c3 c4 is_tangent_line_correct
298+ OP_VERIFY // qx qy tx c3 c4
299+ { Fq :: roll( 3 ) } // qx tx c3 c4 qy
300+ { Fq :: drop( ) } // qx tx c3 c4
301+ OP_ELSE // case: general case
302+ { Fq :: copy( 8 ) } // qx qy tx c3 c4 c3 c4 tx ty qx
303+ { Fq :: roll( 8 ) } // qx tx c3 c4 c3 c4 tx ty qx qy
304+ { hinted_script1 } // qx tx c3 c4 0/1
305+ OP_VERIFY // qx tx c3 c4
306+ OP_ENDIF
307+ { Fq :: roll( 2 ) } // qx c3 c4 tx
308+ { Fq :: roll( 3 ) } // c3 c4 tx qx
309+ { hinted_script2 } // x' y'
310+ OP_ENDIF
311+ OP_ENDIF
312+ OP_ENDIF
313+ } ;
314+
315+ if t. is_zero ( ) || q. is_zero ( ) {
316+ // no hint
317+ } else if t == -q {
318+ // no hint
319+ } else if t == q {
320+ hints. push ( Hint :: Fq ( alpha) ) ;
321+ hints. push ( Hint :: Fq ( -bias) ) ;
322+ hints. extend ( hint3) ;
323+ hints. extend ( hint2) ;
324+ } else {
325+ hints. push ( Hint :: Fq ( alpha) ) ;
326+ hints. push ( Hint :: Fq ( -bias) ) ;
327+ hints. extend ( hint1) ;
328+ hints. extend ( hint2) ;
329+ } ;
330+
331+ ( script, hints)
332+ }
333+
229334 /// add two points T and Q
230335 /// x' = alpha^2 - T.x - Q.x
231336 /// y' = -bias - alpha * x'
@@ -673,50 +778,68 @@ mod test {
673778 }
674779
675780 #[ test]
676- fn test_g1_affine_hinted_check_add ( ) {
677- //println!("G1.hinted_add: {} bytes", G1Affine::check_add().len());
781+ fn test_g1_affine_hinted_check_add_prevent_degenerate ( ) {
678782 let mut prng = ChaCha20Rng :: seed_from_u64 ( 0 ) ;
679783 let t = ark_bn254:: G1Affine :: rand ( & mut prng) ;
680784 let q = ark_bn254:: G1Affine :: rand ( & mut prng) ;
681- let alpha = ( t. y - q. y ) / ( t. x - q. x ) ;
682- // -bias
683- let bias_minus = alpha * t. x - t. y ;
684-
685- let x = alpha. square ( ) - t. x - q. x ;
686- let y = bias_minus - alpha * x;
785+ let r = ( t + q) . into_affine ( ) ;
687786
688- let ( hinted_check_add, hints) = G1Affine :: hinted_check_add ( t, q) ;
787+ let ( hinted_check_add, hints) = G1Affine :: hinted_check_add_prevent_degenerate ( t, q) ;
689788
690789 let script = script ! {
691790 for hint in hints {
692791 { hint. push( ) }
693792 }
694- { Fq :: push( t. x) }
695- { Fq :: push( t. y) }
696- { Fq :: push( q. x) }
697- { Fq :: push( q. y) }
793+ { G1Affine :: push( t) }
794+ { G1Affine :: push( q) }
698795 { hinted_check_add. clone( ) }
699- // [x']
700- { Fq :: push( y) }
701- // [x', y', y]
702- { Fq :: equalverify( 1 , 0 ) }
703- // [x']
704- { Fq :: push( x) }
705- // [x', x]
706- { Fq :: equalverify( 1 , 0 ) }
707- // []
796+ { G1Affine :: push( r) }
797+ { G1Affine :: equalverify( ) }
708798 OP_TRUE
709- // [OP_TRUE]
710799 } ;
711800 let exec_result = execute_script ( script) ;
712801 assert ! ( exec_result. success) ;
713802 println ! (
714- "hinted_check_add : {} @ {} stack" ,
803+ "hinted_check_add_prevent_degenerate : {} @ {} stack" ,
715804 hinted_check_add. len( ) ,
716805 exec_result. stats. max_nb_stack_items
717806 ) ;
718807 }
719808
809+ #[ test]
810+ fn test_g1_affine_hinted_check_add ( ) {
811+ let mut prng = ChaCha20Rng :: seed_from_u64 ( 0 ) ;
812+ let t = ark_bn254:: G1Affine :: rand ( & mut prng) ;
813+ let q = ark_bn254:: G1Affine :: rand ( & mut prng) ;
814+ let r = ( t + q) . into_affine ( ) ;
815+ let tt = ( t + t) . into_affine ( ) ;
816+ let negt = -t;
817+ let z = ark_bn254:: G1Affine :: zero ( ) ;
818+
819+ for ( t, q, r) in [ ( t, q, r) , ( t, t, tt) , ( t, negt, z) , ( t, z, t) ] {
820+ let ( hinted_check_add, hints) = G1Affine :: hinted_check_add ( t, q) ;
821+
822+ let script = script ! {
823+ for hint in hints {
824+ { hint. push( ) }
825+ }
826+ { G1Affine :: push( t) }
827+ { G1Affine :: push( q) }
828+ { hinted_check_add. clone( ) }
829+ { G1Affine :: push( r) }
830+ { G1Affine :: equalverify( ) }
831+ OP_TRUE
832+ } ;
833+ let exec_result = execute_script ( script) ;
834+ assert ! ( exec_result. success) ;
835+ println ! (
836+ "hinted_check_add: {} @ {} stack" ,
837+ hinted_check_add. len( ) ,
838+ exec_result. stats. max_nb_stack_items
839+ ) ;
840+ }
841+ }
842+
720843 #[ test]
721844 fn test_g1_affine_hinted_check_double ( ) {
722845 //println!("G1.hinted_add: {} bytes", G1Affine::check_add().len());
0 commit comments