Skip to content

Commit b39d431

Browse files
committed
Merge #1044: Add another ecmult_multi test
22d25c8 Add another ecmult_multi test (Pieter Wuille) Pull request description: ACKs for top commit: jonasnick: ACK 22d25c8 Tree-SHA512: e1394fa1708e65a66d4b324cca60dd49c67e37b23b7da2a3ff0db7a2a25c23976cb03b96a8c8584ee81aaec559feb84fb113dff2e2ebf89110ed466a4a6b158b
2 parents a69df3a + 22d25c8 commit b39d431

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed

src/tests.c

+172
Original file line numberDiff line numberDiff line change
@@ -4052,6 +4052,174 @@ 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 = (int)secp256k1_testrand_bits(3);
4106+
if (rands > num_nonzero) rands = num_nonzero;
4107+
4108+
secp256k1_gej_set_infinity(&expected);
4109+
secp256k1_gej_set_infinity(&gejs[0]);
4110+
secp256k1_scalar_set_int(&scalars[0], 0);
4111+
4112+
if (g_nonzero) {
4113+
/* If g_nonzero, set g_scalar to nonzero value r. */
4114+
random_scalar_order_test(&g_scalar);
4115+
if (!nonzero_result) {
4116+
/* If expected=0 is desired, add a (a*r, -(1/a)*g) term to compensate. */
4117+
CHECK(num_nonzero > filled);
4118+
random_scalar_order_test(&sc_tmp);
4119+
secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar);
4120+
secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp);
4121+
secp256k1_scalar_negate(&sc_tmp, &sc_tmp);
4122+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &gejs[filled], &sc_tmp);
4123+
++filled;
4124+
++mults;
4125+
}
4126+
}
4127+
4128+
if (nonzero_result && filled < num_nonzero) {
4129+
/* If a nonzero result is desired, and there is space, add a random nonzero term. */
4130+
random_scalar_order_test(&scalars[filled]);
4131+
random_group_element_test(&ge_tmp);
4132+
secp256k1_gej_set_ge(&gejs[filled], &ge_tmp);
4133+
++filled;
4134+
}
4135+
4136+
if (nonzero_result) {
4137+
/* Compute the expected result using normal ecmult. */
4138+
CHECK(filled <= 1);
4139+
secp256k1_ecmult(&expected, &gejs[0], &scalars[0], &g_scalar);
4140+
mults += filled + g_nonzero;
4141+
}
4142+
4143+
/* At this point we have expected = scalar_g*G + sum(scalars[i]*gejs[i] for i=0..filled-1). */
4144+
CHECK(filled <= 1 + !nonzero_result);
4145+
CHECK(filled <= num_nonzero);
4146+
4147+
/* Add entries to scalars,gejs so that there are num of them. All the added entries
4148+
* either have scalar=0 or point=infinity, so these do not change the expected result. */
4149+
while (filled < num) {
4150+
if (secp256k1_testrand_bits(1)) {
4151+
secp256k1_gej_set_infinity(&gejs[filled]);
4152+
random_scalar_order_test(&scalars[filled]);
4153+
} else {
4154+
secp256k1_scalar_set_int(&scalars[filled], 0);
4155+
random_group_element_test(&ge_tmp);
4156+
secp256k1_gej_set_ge(&gejs[filled], &ge_tmp);
4157+
}
4158+
++filled;
4159+
}
4160+
4161+
/* Now perform cheapish transformations on gejs and scalars, for indices
4162+
* 0..num_nonzero-1, which do not change the expected result, but may
4163+
* convert some of them to be both non-0-scalar and non-infinity-point. */
4164+
for (i = 0; i < rands; ++i) {
4165+
int j;
4166+
secp256k1_scalar v, iv;
4167+
/* Shuffle the entries. */
4168+
for (j = 0; j < num_nonzero; ++j) {
4169+
int k = secp256k1_testrand_int(num_nonzero - j);
4170+
if (k != 0) {
4171+
secp256k1_gej gej = gejs[j];
4172+
secp256k1_scalar sc = scalars[j];
4173+
gejs[j] = gejs[j + k];
4174+
scalars[j] = scalars[j + k];
4175+
gejs[j + k] = gej;
4176+
scalars[j + k] = sc;
4177+
}
4178+
}
4179+
/* Perturb all consecutive pairs of inputs:
4180+
* a*P + b*Q -> (a+b)*P + b*(Q-P). */
4181+
for (j = 0; j + 1 < num_nonzero; j += 2) {
4182+
secp256k1_gej gej;
4183+
secp256k1_scalar_add(&scalars[j], &scalars[j], &scalars[j+1]);
4184+
secp256k1_gej_neg(&gej, &gejs[j]);
4185+
secp256k1_gej_add_var(&gejs[j+1], &gejs[j+1], &gej, NULL);
4186+
}
4187+
/* Transform the last input: a*P -> (v*a) * ((1/v)*P). */
4188+
CHECK(num_nonzero >= 1);
4189+
random_scalar_order_test(&v);
4190+
secp256k1_scalar_inverse(&iv, &v);
4191+
secp256k1_scalar_mul(&scalars[num_nonzero - 1], &scalars[num_nonzero - 1], &v);
4192+
secp256k1_ecmult(&gejs[num_nonzero - 1], &gejs[num_nonzero - 1], &iv, NULL);
4193+
++mults;
4194+
}
4195+
4196+
/* Shuffle all entries (0..num-1). */
4197+
for (i = 0; i < num; ++i) {
4198+
int j = secp256k1_testrand_int(num - i);
4199+
if (j != 0) {
4200+
secp256k1_gej gej = gejs[i];
4201+
secp256k1_scalar sc = scalars[i];
4202+
gejs[i] = gejs[i + j];
4203+
scalars[i] = scalars[i + j];
4204+
gejs[i + j] = gej;
4205+
scalars[i + j] = sc;
4206+
}
4207+
}
4208+
4209+
/* Compute affine versions of all inputs. */
4210+
secp256k1_ge_set_all_gej_var(ges, gejs, filled);
4211+
/* Invoke ecmult_multi code. */
4212+
data.sc = scalars;
4213+
data.pt = ges;
4214+
CHECK(ecmult_multi(&ctx->error_callback, scratch, &computed, g_scalar_ptr, ecmult_multi_callback, &data, filled));
4215+
mults += num_nonzero + g_nonzero;
4216+
/* Compare with expected result. */
4217+
secp256k1_gej_neg(&computed, &computed);
4218+
secp256k1_gej_add_var(&computed, &computed, &expected, NULL);
4219+
CHECK(secp256k1_gej_is_infinity(&computed));
4220+
return mults;
4221+
}
4222+
40554223
void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
40564224
secp256k1_scalar szero;
40574225
secp256k1_scalar sc;
@@ -4242,6 +4410,7 @@ void test_ecmult_multi_batching(void) {
42424410

42434411
void run_ecmult_multi_tests(void) {
42444412
secp256k1_scratch *scratch;
4413+
int64_t todo = (int64_t)320 * count;
42454414

42464415
test_secp256k1_pippenger_bucket_window_inv();
42474416
test_ecmult_multi_pippenger_max_points();
@@ -4252,6 +4421,9 @@ void run_ecmult_multi_tests(void) {
42524421
test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single);
42534422
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
42544423
test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single);
4424+
while (todo > 0) {
4425+
todo -= test_ecmult_multi_random(scratch);
4426+
}
42554427
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
42564428

42574429
/* Run test_ecmult_multi with space for exactly one point */

0 commit comments

Comments
 (0)