@@ -562,6 +562,41 @@ impl EdwardsPoint {
562
562
MontgomeryPoint ( u. as_bytes ( ) )
563
563
}
564
564
565
+ /// Converts a large batch of points to Edwards at once.
566
+ #[ cfg( feature = "alloc" ) ]
567
+ pub fn batch_to_montgomery ( eds : & [ Self ] ) -> alloc:: vec:: Vec < MontgomeryPoint > {
568
+ // "Montgomery's trick" borrowed from https://github.com/bitcoin-core/secp256k1/pull/16
569
+ if eds. is_empty ( ) {
570
+ return vec ! [ ] ;
571
+ }
572
+
573
+ let mut ret = alloc:: vec:: Vec :: with_capacity ( eds. len ( ) ) ;
574
+ let mut w_invs = alloc:: vec:: Vec :: with_capacity ( eds. len ( ) ) ;
575
+
576
+ // First build a list of W1, W1W2, W1W2W3, ...
577
+ w_invs. push ( & eds[ 0 ] . Z - & eds[ 0 ] . Y ) ;
578
+ for i in 1 ..eds. len ( ) {
579
+ let last = w_invs[ i - 1 ] ;
580
+ w_invs. push ( & last * & ( & eds[ i] . Z - & eds[ i] . Y ) ) ;
581
+ }
582
+ // Then invert the final one to get a product of all inverses.
583
+ let mut w_inv = w_invs[ eds. len ( ) - 1 ] . invert ( ) ;
584
+ // Then, going backward, compute all the individual inverses.
585
+ for i in ( 0 ..eds. len ( ) - 1 ) . rev ( ) {
586
+ w_invs[ i + 1 ] = & w_inv * & w_invs[ i] ;
587
+ w_inv *= & ( & eds[ i + 1 ] . Z - & eds[ i + 1 ] . Y ) ;
588
+ }
589
+ w_invs[ 0 ] = w_inv;
590
+
591
+ // Then complete the points
592
+ for ( ed, w_inv) in eds. iter ( ) . zip ( w_invs. iter ( ) ) {
593
+ let u = & ( & ed. Z + & ed. Y ) * w_inv;
594
+ ret. push ( MontgomeryPoint ( u. as_bytes ( ) ) ) ;
595
+ }
596
+
597
+ ret
598
+ }
599
+
565
600
/// Compress this point to `CompressedEdwardsY` format.
566
601
pub fn compress ( & self ) -> CompressedEdwardsY {
567
602
let recip = self . Z . invert ( ) ;
@@ -2106,6 +2141,31 @@ mod test {
2106
2141
}
2107
2142
}
2108
2143
2144
+ #[ test]
2145
+ #[ cfg( feature = "alloc" ) ]
2146
+ fn batch_to_montgomery ( ) {
2147
+ let mut rng = rand:: thread_rng ( ) ;
2148
+
2149
+ let scalars = ( 0 ..128 )
2150
+ . map ( |_| Scalar :: random ( & mut rng) )
2151
+ . collect :: < Vec < _ > > ( ) ;
2152
+
2153
+ let points = scalars
2154
+ . iter ( )
2155
+ . map ( EdwardsPoint :: mul_base)
2156
+ . collect :: < Vec < _ > > ( ) ;
2157
+
2158
+ let single_monts = points
2159
+ . iter ( )
2160
+ . map ( EdwardsPoint :: to_montgomery)
2161
+ . collect :: < Vec < _ > > ( ) ;
2162
+
2163
+ for i in [ 0 , 1 , 2 , 3 , 10 , 50 , 128 ] {
2164
+ let invs = EdwardsPoint :: batch_to_montgomery ( & points[ ..i] ) ;
2165
+ assert_eq ! ( & invs, & single_monts[ ..i] ) ;
2166
+ }
2167
+ }
2168
+
2109
2169
#[ test]
2110
2170
#[ cfg( feature = "alloc" ) ]
2111
2171
fn vartime_precomputed_vs_nonprecomputed_multiscalar ( ) {
0 commit comments