@@ -4052,6 +4052,173 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
40524052 }
40534053}
40544054
4055+ int test_ecmult_multi_random (secp256k1_scratch * scratch ) {
4056+ /* Large random test for ecmult_multi_* functions which exercises:
4057+ * - Few or many inputs (0 up to 128, roughly exponentially distributed).
4058+ * - Few or many 0*P or a*INF inputs (roughly uniformly distributed).
4059+ * - Including or excluding an nonzero a*G term (or such a term at all).
4060+ * - Final expected result equal to infinity or not (roughly 50%).
4061+ * - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch
4062+ */
4063+
4064+ /* These 4 variables define the eventual input to the ecmult_multi function.
4065+ * g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and
4066+ * scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points
4067+ * which form its normal inputs. */
4068+ int filled = 0 ;
4069+ secp256k1_scalar g_scalar = SECP256K1_SCALAR_CONST (0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 );
4070+ secp256k1_scalar scalars [128 ];
4071+ secp256k1_gej gejs [128 ];
4072+ /* The expected result, and the computed result. */
4073+ secp256k1_gej expected , computed ;
4074+ /* Temporaries. */
4075+ secp256k1_scalar sc_tmp ;
4076+ secp256k1_ge ge_tmp ;
4077+ /* Variables needed for the actual input to ecmult_multi. */
4078+ secp256k1_ge ges [128 ];
4079+ ecmult_multi_data data ;
4080+
4081+ int i ;
4082+ /* Which multiplication function to use */
4083+ int fn = secp256k1_testrand_int (3 );
4084+ secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var :
4085+ fn == 1 ? secp256k1_ecmult_strauss_batch_single :
4086+ secp256k1_ecmult_pippenger_batch_single ;
4087+ /* Simulate exponentially distributed num. */
4088+ int num_bits = 2 + secp256k1_testrand_int (6 );
4089+ /* Number of (scalar, point) inputs (excluding g). */
4090+ int num = secp256k1_testrand_int ((1 << num_bits ) + 1 );
4091+ /* Number of those which are nonzero. */
4092+ int num_nonzero = secp256k1_testrand_int (num + 1 );
4093+ /* Whether we're aiming to create an input with nonzero expected result. */
4094+ int nonzero_result = secp256k1_testrand_bits (1 );
4095+ /* Whether we will provide nonzero g multiplicand. In some cases our hand
4096+ * is forced here based on num_nonzero and nonzero_result. */
4097+ int g_nonzero = num_nonzero == 0 ? nonzero_result :
4098+ num_nonzero == 1 && !nonzero_result ? 1 :
4099+ (int )secp256k1_testrand_bits (1 );
4100+ /* Which g_scalar pointer to pass into ecmult_multi(). */
4101+ const secp256k1_scalar * g_scalar_ptr = (g_nonzero || secp256k1_testrand_bits (1 )) ? & g_scalar : NULL ;
4102+ /* How many EC multiplications were performed in this function. */
4103+ int mults = 0 ;
4104+ /* How many randomization steps to apply to the input list. */
4105+ int rands = num_nonzero <= 1 ? 0 : (int )secp256k1_testrand_bits (3 );
4106+
4107+ secp256k1_gej_set_infinity (& expected );
4108+
4109+ if (g_nonzero ) {
4110+ /* If g_nonzero, set g_scalar to nonzero value r. */
4111+ random_scalar_order_test (& g_scalar );
4112+ if (!nonzero_result ) {
4113+ /* And if 0 expected is desired, add a (a*r, -(1/a)*g) term to compensate. */
4114+ CHECK (num_nonzero > filled );
4115+ random_scalar_order_test (& sc_tmp );
4116+ secp256k1_scalar_mul (& scalars [filled ], & sc_tmp , & g_scalar );
4117+ secp256k1_scalar_inverse_var (& sc_tmp , & sc_tmp );
4118+ secp256k1_scalar_negate (& sc_tmp , & sc_tmp );
4119+ secp256k1_ecmult_gen (& ctx -> ecmult_gen_ctx , & gejs [filled ], & sc_tmp );
4120+ ++ filled ;
4121+ ++ mults ;
4122+ }
4123+ }
4124+
4125+ if (nonzero_result && filled < num_nonzero ) {
4126+ /* If a nonzero result is desired, and there is space, add a random nonzero term. */
4127+ random_scalar_order_test (& scalars [filled ]);
4128+ random_group_element_test (& ge_tmp );
4129+ secp256k1_gej_set_ge (& gejs [filled ], & ge_tmp );
4130+ ++ filled ;
4131+ }
4132+
4133+ if (nonzero_result ) {
4134+ /* Compute the expected result using normal ecmult. */
4135+ CHECK (filled <= 1 );
4136+ secp256k1_ecmult (& expected , filled ? & gejs [0 ] : & expected , & scalars [0 ], & g_scalar );
4137+ mults += filled + g_nonzero ;
4138+ }
4139+
4140+ /* At this point we have expected = scalar_g*G + sum(scalars[i]*gejs[i] for i=0..filled-1). */
4141+ CHECK (filled <= 1 + !nonzero_result );
4142+ CHECK (filled <= num_nonzero );
4143+
4144+ /* Add entries to sclalars,gejs so that there are num of them. All the added entries
4145+ * either have scalar=0 or point=infinity, so these do not change the expected result. */
4146+ while (filled < num ) {
4147+ if (secp256k1_testrand_bits (1 )) {
4148+ secp256k1_gej_set_infinity (& gejs [filled ]);
4149+ random_scalar_order_test (& scalars [filled ]);
4150+ } else {
4151+ secp256k1_scalar_set_int (& scalars [filled ], 0 );
4152+ random_group_element_test (& ge_tmp );
4153+ secp256k1_gej_set_ge (& gejs [filled ], & ge_tmp );
4154+ }
4155+ ++ filled ;
4156+ }
4157+
4158+ /* Now perform cheapish transformations on gejs and scalars, for indices
4159+ * 0..num_nonzero-1, which do not change the expected result, but may
4160+ * convert some of them to be both non-0-scalar and non-infinity-point. */
4161+ for (i = 0 ; i < rands ; ++ i ) {
4162+ int j ;
4163+ /* Shuffle the entries. */
4164+ for (j = 0 ; j < num_nonzero ; ++ j ) {
4165+ int k = secp256k1_testrand_int (num_nonzero - j );
4166+ if (k != 0 ) {
4167+ secp256k1_gej gej = gejs [j ];
4168+ secp256k1_scalar sc = scalars [j ];
4169+ gejs [j ] = gejs [j + k ];
4170+ scalars [j ] = scalars [j + k ];
4171+ gejs [j + k ] = gej ;
4172+ scalars [j + k ] = sc ;
4173+ }
4174+ }
4175+ /* Perturb all consecutive pairs of inputs:
4176+ * a*P + b*Q -> (a+b)*P + b*(Q-P). */
4177+ for (j = 0 ; j + 1 < num_nonzero ; j += 2 ) {
4178+ secp256k1_gej gej ;
4179+ secp256k1_scalar_add (& scalars [j ], & scalars [j ], & scalars [j + 1 ]);
4180+ secp256k1_gej_neg (& gej , & gejs [j ]);
4181+ secp256k1_gej_add_var (& gejs [j + 1 ], & gejs [j + 1 ], & gej , NULL );
4182+ }
4183+ /* Transform the last input: a*P -> (v*a) * ((1/v)*P). */
4184+ {
4185+ secp256k1_scalar v , iv ;
4186+ CHECK (num_nonzero >= 1 );
4187+ random_scalar_order_test (& v );
4188+ secp256k1_scalar_inverse (& iv , & v );
4189+ secp256k1_scalar_mul (& scalars [num_nonzero - 1 ], & scalars [num_nonzero - 1 ], & v );
4190+ secp256k1_ecmult (& gejs [num_nonzero - 1 ], & gejs [num_nonzero - 1 ], & iv , NULL );
4191+ ++ mults ;
4192+ }
4193+ }
4194+
4195+ /* Shuffle all entries (0..num-1). */
4196+ for (i = 0 ; i < num ; ++ i ) {
4197+ int j = secp256k1_testrand_int (num - i );
4198+ if (j != 0 ) {
4199+ secp256k1_gej gej = gejs [i ];
4200+ secp256k1_scalar sc = scalars [i ];
4201+ gejs [i ] = gejs [i + j ];
4202+ scalars [i ] = scalars [i + j ];
4203+ gejs [i + j ] = gej ;
4204+ scalars [i + j ] = sc ;
4205+ }
4206+ }
4207+
4208+ /* Compute affine versions of all inputs. */
4209+ secp256k1_ge_set_all_gej_var (ges , gejs , filled );
4210+ /* Invoke ecmult_multi code. */
4211+ data .sc = scalars ;
4212+ data .pt = ges ;
4213+ CHECK (ecmult_multi (& ctx -> error_callback , scratch , & computed , g_scalar_ptr , ecmult_multi_callback , & data , filled ));
4214+ mults += num_nonzero + g_nonzero ;
4215+ /* Compare with expected result. */
4216+ secp256k1_gej_neg (& computed , & computed );
4217+ secp256k1_gej_add_var (& computed , & computed , & expected , NULL );
4218+ CHECK (secp256k1_gej_is_infinity (& computed ));
4219+ return mults ;
4220+ }
4221+
40554222void test_ecmult_multi_batch_single (secp256k1_ecmult_multi_func ecmult_multi ) {
40564223 secp256k1_scalar szero ;
40574224 secp256k1_scalar sc ;
@@ -4242,6 +4409,7 @@ void test_ecmult_multi_batching(void) {
42424409
42434410void run_ecmult_multi_tests (void ) {
42444411 secp256k1_scratch * scratch ;
4412+ int64_t todo = (int64_t )320 * count ;
42454413
42464414 test_secp256k1_pippenger_bucket_window_inv ();
42474415 test_ecmult_multi_pippenger_max_points ();
@@ -4252,6 +4420,9 @@ void run_ecmult_multi_tests(void) {
42524420 test_ecmult_multi_batch_single (secp256k1_ecmult_pippenger_batch_single );
42534421 test_ecmult_multi (scratch , secp256k1_ecmult_strauss_batch_single );
42544422 test_ecmult_multi_batch_single (secp256k1_ecmult_strauss_batch_single );
4423+ while (todo > 0 ) {
4424+ todo -= test_ecmult_multi_random (scratch );
4425+ }
42554426 secp256k1_scratch_destroy (& ctx -> error_callback , scratch );
42564427
42574428 /* Run test_ecmult_multi with space for exactly one point */
0 commit comments