@@ -982,18 +982,51 @@ SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, s
982982}
983983#endif
984984
985+
986+ static size_t secp256k1_pippenger_scratch_size_constant (int bucket_window ) {
987+ size_t size = 0 ;
988+ size += ROUND_TO_ALIGN (sizeof (struct secp256k1_pippenger_state ));
989+ size += ROUND_TO_ALIGN (sizeof (secp256k1_gej ) << bucket_window );
990+ return size ;
991+ }
992+
993+ static SECP256K1_INLINE size_t secp256k1_pippenger_entries (size_t n_points ) {
994+ #ifdef USE_ENDOMORPHISM
995+ return 2 * n_points + 2 ;
996+ #else
997+ return n_points + 1 ;
998+ #endif
999+ }
1000+
1001+ /**
1002+ * Returns the scratch size required for a given number of points excluding
1003+ * base point G and excluding the parts not dependent on the number of points.
1004+ * If called with 0 n_points it'll return the size required for the base point
1005+ * G. If the aligned argument is 0, this function will not take alignment into
1006+ * account. Otherwise it will align the individual parts in the same way as
1007+ * secp256k1_scratch_alloc.
1008+ */
1009+ static size_t secp256k1_pippenger_scratch_size_points (size_t n_points , int bucket_window , int aligned ) {
1010+ size_t entries = secp256k1_pippenger_entries (n_points );
1011+ size_t size = 0 ;
1012+ size += entries * sizeof (secp256k1_ge );
1013+ size = aligned ? ROUND_TO_ALIGN (size ) : size ;
1014+ size += entries * sizeof (secp256k1_scalar );
1015+ size = aligned ? ROUND_TO_ALIGN (size ) : size ;
1016+ size += entries * sizeof (struct secp256k1_pippenger_point_state );
1017+ size = aligned ? ROUND_TO_ALIGN (size ) : size ;
1018+ size += entries * WNAF_SIZE (bucket_window + 1 ) * sizeof (int );
1019+ size = aligned ? ROUND_TO_ALIGN (size ) : size ;
1020+ return size ;
1021+ }
1022+
9851023/**
9861024 * Returns the scratch size required for a given number of points (excluding
9871025 * base point G) without considering alignment.
9881026 */
9891027static size_t secp256k1_pippenger_scratch_size (size_t n_points , int bucket_window ) {
990- #ifdef USE_ENDOMORPHISM
991- size_t entries = 2 * n_points + 2 ;
992- #else
993- size_t entries = n_points + 1 ;
994- #endif
995- size_t entry_size = sizeof (secp256k1_ge ) + sizeof (secp256k1_scalar ) + sizeof (struct secp256k1_pippenger_point_state ) + (WNAF_SIZE (bucket_window + 1 )+ 1 )* sizeof (int );
996- return (sizeof (secp256k1_gej ) << bucket_window ) + sizeof (struct secp256k1_pippenger_state ) + entries * entry_size ;
1028+ return secp256k1_pippenger_scratch_size_constant (bucket_window )
1029+ + secp256k1_pippenger_scratch_size_points (n_points , bucket_window , 1 );
9971030}
9981031
9991032static int secp256k1_ecmult_pippenger_batch_allocate (const secp256k1_callback * error_callback , secp256k1_scratch * scratch , size_t scratch_checkpoint , size_t entries , int bucket_window , secp256k1_ge * * points , secp256k1_scalar * * scalars , secp256k1_gej * * buckets , struct secp256k1_pippenger_state * * state_space ) {
@@ -1019,11 +1052,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
10191052 /* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
10201053 * sizes. The reason for +1 is that we add the G scalar to the list of
10211054 * other scalars. */
1022- #ifdef USE_ENDOMORPHISM
1023- size_t entries = 2 * n_points + 2 ;
1024- #else
1025- size_t entries = n_points + 1 ;
1026- #endif
1055+ size_t entries = secp256k1_pippenger_entries (n_points );
10271056 secp256k1_ge * points ;
10281057 secp256k1_scalar * scalars ;
10291058 secp256k1_gej * buckets ;
@@ -1095,27 +1124,39 @@ static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* err
10951124 * used.
10961125 */
10971126static size_t secp256k1_pippenger_max_points (const secp256k1_callback * error_callback , secp256k1_scratch * scratch ) {
1098- size_t max_alloc = secp256k1_scratch_max_allocation (error_callback , scratch , PIPPENGER_SCRATCH_OBJECTS );
1127+ /* Call max_allocation with 0 objects because it would assume worst case
1128+ * padding but in this function we want to get an exact number (and
1129+ * pippenger_scratch_size_points will already account for alignment). */
1130+ size_t max_alloc = secp256k1_scratch_max_allocation (error_callback , scratch , 0 );
10991131 int bucket_window ;
11001132 size_t res = 0 ;
11011133
11021134 for (bucket_window = 1 ; bucket_window <= PIPPENGER_MAX_BUCKET_WINDOW ; bucket_window ++ ) {
11031135 size_t n_points ;
11041136 size_t max_points = secp256k1_pippenger_bucket_window_inv (bucket_window );
11051137 size_t space_for_points ;
1106- size_t space_overhead ;
1107- size_t entry_size = sizeof ( secp256k1_ge ) + sizeof ( secp256k1_scalar ) + sizeof ( struct secp256k1_pippenger_point_state ) + ( WNAF_SIZE ( bucket_window + 1 ) + 1 ) * sizeof ( int );
1138+ size_t space_constant ;
1139+ size_t entry_size = secp256k1_pippenger_scratch_size_points ( 0 , bucket_window , 0 );
11081140
1109- #ifdef USE_ENDOMORPHISM
1110- entry_size = 2 * entry_size ;
1111- #endif
1112- space_overhead = (sizeof (secp256k1_gej ) << bucket_window ) + entry_size + sizeof (struct secp256k1_pippenger_state );
1113- if (space_overhead > max_alloc ) {
1141+ space_constant = secp256k1_pippenger_scratch_size_constant (bucket_window );
1142+ if (space_constant + entry_size > max_alloc ) {
11141143 break ;
11151144 }
1116- space_for_points = max_alloc - space_overhead ;
1145+ space_for_points = max_alloc - space_constant ;
1146+
1147+ /* Compute an upper bound for the number of points after subtracting
1148+ * space for the base point G. It's an upper bound because alignment is
1149+ * not taken into account. */
1150+ n_points = (space_for_points - entry_size )/entry_size ;
1151+ if (n_points > 0
1152+ && space_for_points < secp256k1_pippenger_scratch_size_points (n_points , bucket_window , 1 )) {
1153+ /* If there's not enough space after alignment is taken into
1154+ * account it suffices to decrease n_points by one. This is because
1155+ * the maximum padding required is less than an entry. */
1156+ n_points -= 1 ;
1157+ VERIFY_CHECK (space_for_points >= secp256k1_pippenger_scratch_size_points (n_points , bucket_window , 1 ));
1158+ }
11171159
1118- n_points = space_for_points /entry_size ;
11191160 n_points = n_points > max_points ? max_points : n_points ;
11201161 if (n_points > res ) {
11211162 res = n_points ;
0 commit comments