Skip to content

Commit 0bfb6bc

Browse files
sipaPiRK
authored andcommitted
[secp256k1] Add another ecmult_multi test
Summary: This is a backport of [[bitcoin-core/secp256k1#1044 | secp256k1#1044]] Test Plan: `ninja check-secp256k1` Reviewers: #bitcoin_abc, Fabien Reviewed By: #bitcoin_abc, Fabien Differential Revision: https://reviews.bitcoinabc.org/D19433
1 parent 2b04034 commit 0bfb6bc

1 file changed

Lines changed: 172 additions & 0 deletions

File tree

src/secp256k1/src/tests.c

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4082,6 +4082,174 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
40824082
}
40834083
}
40844084

4085+
int test_ecmult_multi_random(secp256k1_scratch *scratch) {
4086+
/* Large random test for ecmult_multi_* functions which exercises:
4087+
* - Few or many inputs (0 up to 128, roughly exponentially distributed).
4088+
* - Few or many 0*P or a*INF inputs (roughly uniformly distributed).
4089+
* - Including or excluding an nonzero a*G term (or such a term at all).
4090+
* - Final expected result equal to infinity or not (roughly 50%).
4091+
* - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch
4092+
*/
4093+
4094+
/* These 4 variables define the eventual input to the ecmult_multi function.
4095+
* g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and
4096+
* scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points
4097+
* which form its normal inputs. */
4098+
int filled = 0;
4099+
secp256k1_scalar g_scalar = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
4100+
secp256k1_scalar scalars[128];
4101+
secp256k1_gej gejs[128];
4102+
/* The expected result, and the computed result. */
4103+
secp256k1_gej expected, computed;
4104+
/* Temporaries. */
4105+
secp256k1_scalar sc_tmp;
4106+
secp256k1_ge ge_tmp;
4107+
/* Variables needed for the actual input to ecmult_multi. */
4108+
secp256k1_ge ges[128];
4109+
ecmult_multi_data data;
4110+
4111+
int i;
4112+
/* Which multiplication function to use */
4113+
int fn = secp256k1_testrand_int(3);
4114+
secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var :
4115+
fn == 1 ? secp256k1_ecmult_strauss_batch_single :
4116+
secp256k1_ecmult_pippenger_batch_single;
4117+
/* Simulate exponentially distributed num. */
4118+
int num_bits = 2 + secp256k1_testrand_int(6);
4119+
/* Number of (scalar, point) inputs (excluding g). */
4120+
int num = secp256k1_testrand_int((1 << num_bits) + 1);
4121+
/* Number of those which are nonzero. */
4122+
int num_nonzero = secp256k1_testrand_int(num + 1);
4123+
/* Whether we're aiming to create an input with nonzero expected result. */
4124+
int nonzero_result = secp256k1_testrand_bits(1);
4125+
/* Whether we will provide nonzero g multiplicand. In some cases our hand
4126+
* is forced here based on num_nonzero and nonzero_result. */
4127+
int g_nonzero = num_nonzero == 0 ? nonzero_result :
4128+
num_nonzero == 1 && !nonzero_result ? 1 :
4129+
(int)secp256k1_testrand_bits(1);
4130+
/* Which g_scalar pointer to pass into ecmult_multi(). */
4131+
const secp256k1_scalar* g_scalar_ptr = (g_nonzero || secp256k1_testrand_bits(1)) ? &g_scalar : NULL;
4132+
/* How many EC multiplications were performed in this function. */
4133+
int mults = 0;
4134+
/* How many randomization steps to apply to the input list. */
4135+
int rands = (int)secp256k1_testrand_bits(3);
4136+
if (rands > num_nonzero) rands = num_nonzero;
4137+
4138+
secp256k1_gej_set_infinity(&expected);
4139+
secp256k1_gej_set_infinity(&gejs[0]);
4140+
secp256k1_scalar_set_int(&scalars[0], 0);
4141+
4142+
if (g_nonzero) {
4143+
/* If g_nonzero, set g_scalar to nonzero value r. */
4144+
random_scalar_order_test(&g_scalar);
4145+
if (!nonzero_result) {
4146+
/* If expected=0 is desired, add a (a*r, -(1/a)*g) term to compensate. */
4147+
CHECK(num_nonzero > filled);
4148+
random_scalar_order_test(&sc_tmp);
4149+
secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar);
4150+
secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp);
4151+
secp256k1_scalar_negate(&sc_tmp, &sc_tmp);
4152+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &gejs[filled], &sc_tmp);
4153+
++filled;
4154+
++mults;
4155+
}
4156+
}
4157+
4158+
if (nonzero_result && filled < num_nonzero) {
4159+
/* If a nonzero result is desired, and there is space, add a random nonzero term. */
4160+
random_scalar_order_test(&scalars[filled]);
4161+
random_group_element_test(&ge_tmp);
4162+
secp256k1_gej_set_ge(&gejs[filled], &ge_tmp);
4163+
++filled;
4164+
}
4165+
4166+
if (nonzero_result) {
4167+
/* Compute the expected result using normal ecmult. */
4168+
CHECK(filled <= 1);
4169+
secp256k1_ecmult(&expected, &gejs[0], &scalars[0], &g_scalar);
4170+
mults += filled + g_nonzero;
4171+
}
4172+
4173+
/* At this point we have expected = scalar_g*G + sum(scalars[i]*gejs[i] for i=0..filled-1). */
4174+
CHECK(filled <= 1 + !nonzero_result);
4175+
CHECK(filled <= num_nonzero);
4176+
4177+
/* Add entries to scalars,gejs so that there are num of them. All the added entries
4178+
* either have scalar=0 or point=infinity, so these do not change the expected result. */
4179+
while (filled < num) {
4180+
if (secp256k1_testrand_bits(1)) {
4181+
secp256k1_gej_set_infinity(&gejs[filled]);
4182+
random_scalar_order_test(&scalars[filled]);
4183+
} else {
4184+
secp256k1_scalar_set_int(&scalars[filled], 0);
4185+
random_group_element_test(&ge_tmp);
4186+
secp256k1_gej_set_ge(&gejs[filled], &ge_tmp);
4187+
}
4188+
++filled;
4189+
}
4190+
4191+
/* Now perform cheapish transformations on gejs and scalars, for indices
4192+
* 0..num_nonzero-1, which do not change the expected result, but may
4193+
* convert some of them to be both non-0-scalar and non-infinity-point. */
4194+
for (i = 0; i < rands; ++i) {
4195+
int j;
4196+
secp256k1_scalar v, iv;
4197+
/* Shuffle the entries. */
4198+
for (j = 0; j < num_nonzero; ++j) {
4199+
int k = secp256k1_testrand_int(num_nonzero - j);
4200+
if (k != 0) {
4201+
secp256k1_gej gej = gejs[j];
4202+
secp256k1_scalar sc = scalars[j];
4203+
gejs[j] = gejs[j + k];
4204+
scalars[j] = scalars[j + k];
4205+
gejs[j + k] = gej;
4206+
scalars[j + k] = sc;
4207+
}
4208+
}
4209+
/* Perturb all consecutive pairs of inputs:
4210+
* a*P + b*Q -> (a+b)*P + b*(Q-P). */
4211+
for (j = 0; j + 1 < num_nonzero; j += 2) {
4212+
secp256k1_gej gej;
4213+
secp256k1_scalar_add(&scalars[j], &scalars[j], &scalars[j+1]);
4214+
secp256k1_gej_neg(&gej, &gejs[j]);
4215+
secp256k1_gej_add_var(&gejs[j+1], &gejs[j+1], &gej, NULL);
4216+
}
4217+
/* Transform the last input: a*P -> (v*a) * ((1/v)*P). */
4218+
CHECK(num_nonzero >= 1);
4219+
random_scalar_order_test(&v);
4220+
secp256k1_scalar_inverse(&iv, &v);
4221+
secp256k1_scalar_mul(&scalars[num_nonzero - 1], &scalars[num_nonzero - 1], &v);
4222+
secp256k1_ecmult(&gejs[num_nonzero - 1], &gejs[num_nonzero - 1], &iv, NULL);
4223+
++mults;
4224+
}
4225+
4226+
/* Shuffle all entries (0..num-1). */
4227+
for (i = 0; i < num; ++i) {
4228+
int j = secp256k1_testrand_int(num - i);
4229+
if (j != 0) {
4230+
secp256k1_gej gej = gejs[i];
4231+
secp256k1_scalar sc = scalars[i];
4232+
gejs[i] = gejs[i + j];
4233+
scalars[i] = scalars[i + j];
4234+
gejs[i + j] = gej;
4235+
scalars[i + j] = sc;
4236+
}
4237+
}
4238+
4239+
/* Compute affine versions of all inputs. */
4240+
secp256k1_ge_set_all_gej_var(ges, gejs, filled);
4241+
/* Invoke ecmult_multi code. */
4242+
data.sc = scalars;
4243+
data.pt = ges;
4244+
CHECK(ecmult_multi(&ctx->error_callback, scratch, &computed, g_scalar_ptr, ecmult_multi_callback, &data, filled));
4245+
mults += num_nonzero + g_nonzero;
4246+
/* Compare with expected result. */
4247+
secp256k1_gej_neg(&computed, &computed);
4248+
secp256k1_gej_add_var(&computed, &computed, &expected, NULL);
4249+
CHECK(secp256k1_gej_is_infinity(&computed));
4250+
return mults;
4251+
}
4252+
40854253
void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
40864254
secp256k1_scalar szero;
40874255
secp256k1_scalar sc;
@@ -4272,6 +4440,7 @@ void test_ecmult_multi_batching(void) {
42724440

42734441
void run_ecmult_multi_tests(void) {
42744442
secp256k1_scratch *scratch;
4443+
int64_t todo = (int64_t)320 * count;
42754444

42764445
test_secp256k1_pippenger_bucket_window_inv();
42774446
test_ecmult_multi_pippenger_max_points();
@@ -4282,6 +4451,9 @@ void run_ecmult_multi_tests(void) {
42824451
test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single);
42834452
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
42844453
test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single);
4454+
while (todo > 0) {
4455+
todo -= test_ecmult_multi_random(scratch);
4456+
}
42854457
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
42864458

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

0 commit comments

Comments
 (0)