Skip to content

Commit

Permalink
Add another ecmult_multi test
Browse files Browse the repository at this point in the history
  • Loading branch information
sipa committed Dec 21, 2021
1 parent be6944a commit 22d25c8
Showing 1 changed file with 172 additions and 0 deletions.
172 changes: 172 additions & 0 deletions src/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -4052,6 +4052,174 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
}
}

int test_ecmult_multi_random(secp256k1_scratch *scratch) {
/* Large random test for ecmult_multi_* functions which exercises:
* - Few or many inputs (0 up to 128, roughly exponentially distributed).
* - Few or many 0*P or a*INF inputs (roughly uniformly distributed).
* - Including or excluding an nonzero a*G term (or such a term at all).
* - Final expected result equal to infinity or not (roughly 50%).
* - ecmult_multi_var, ecmult_strauss_single_batch, ecmult_pippenger_single_batch
*/

/* These 4 variables define the eventual input to the ecmult_multi function.
* g_scalar is the G scalar fed to it (or NULL, possibly, if g_scalar=0), and
* scalars[0..filled-1] and gejs[0..filled-1] are the scalars and points
* which form its normal inputs. */
int filled = 0;
secp256k1_scalar g_scalar = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
secp256k1_scalar scalars[128];
secp256k1_gej gejs[128];
/* The expected result, and the computed result. */
secp256k1_gej expected, computed;
/* Temporaries. */
secp256k1_scalar sc_tmp;
secp256k1_ge ge_tmp;
/* Variables needed for the actual input to ecmult_multi. */
secp256k1_ge ges[128];
ecmult_multi_data data;

int i;
/* Which multiplication function to use */
int fn = secp256k1_testrand_int(3);
secp256k1_ecmult_multi_func ecmult_multi = fn == 0 ? secp256k1_ecmult_multi_var :
fn == 1 ? secp256k1_ecmult_strauss_batch_single :
secp256k1_ecmult_pippenger_batch_single;
/* Simulate exponentially distributed num. */
int num_bits = 2 + secp256k1_testrand_int(6);
/* Number of (scalar, point) inputs (excluding g). */
int num = secp256k1_testrand_int((1 << num_bits) + 1);
/* Number of those which are nonzero. */
int num_nonzero = secp256k1_testrand_int(num + 1);
/* Whether we're aiming to create an input with nonzero expected result. */
int nonzero_result = secp256k1_testrand_bits(1);
/* Whether we will provide nonzero g multiplicand. In some cases our hand
* is forced here based on num_nonzero and nonzero_result. */
int g_nonzero = num_nonzero == 0 ? nonzero_result :
num_nonzero == 1 && !nonzero_result ? 1 :
(int)secp256k1_testrand_bits(1);
/* Which g_scalar pointer to pass into ecmult_multi(). */
const secp256k1_scalar* g_scalar_ptr = (g_nonzero || secp256k1_testrand_bits(1)) ? &g_scalar : NULL;
/* How many EC multiplications were performed in this function. */
int mults = 0;
/* How many randomization steps to apply to the input list. */
int rands = (int)secp256k1_testrand_bits(3);
if (rands > num_nonzero) rands = num_nonzero;

secp256k1_gej_set_infinity(&expected);
secp256k1_gej_set_infinity(&gejs[0]);
secp256k1_scalar_set_int(&scalars[0], 0);

if (g_nonzero) {
/* If g_nonzero, set g_scalar to nonzero value r. */
random_scalar_order_test(&g_scalar);
if (!nonzero_result) {
/* If expected=0 is desired, add a (a*r, -(1/a)*g) term to compensate. */
CHECK(num_nonzero > filled);
random_scalar_order_test(&sc_tmp);
secp256k1_scalar_mul(&scalars[filled], &sc_tmp, &g_scalar);
secp256k1_scalar_inverse_var(&sc_tmp, &sc_tmp);
secp256k1_scalar_negate(&sc_tmp, &sc_tmp);
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &gejs[filled], &sc_tmp);
++filled;
++mults;
}
}

if (nonzero_result && filled < num_nonzero) {
/* If a nonzero result is desired, and there is space, add a random nonzero term. */
random_scalar_order_test(&scalars[filled]);
random_group_element_test(&ge_tmp);
secp256k1_gej_set_ge(&gejs[filled], &ge_tmp);
++filled;
}

if (nonzero_result) {
/* Compute the expected result using normal ecmult. */
CHECK(filled <= 1);
secp256k1_ecmult(&expected, &gejs[0], &scalars[0], &g_scalar);
mults += filled + g_nonzero;
}

/* At this point we have expected = scalar_g*G + sum(scalars[i]*gejs[i] for i=0..filled-1). */
CHECK(filled <= 1 + !nonzero_result);
CHECK(filled <= num_nonzero);

/* Add entries to scalars,gejs so that there are num of them. All the added entries
* either have scalar=0 or point=infinity, so these do not change the expected result. */
while (filled < num) {
if (secp256k1_testrand_bits(1)) {
secp256k1_gej_set_infinity(&gejs[filled]);
random_scalar_order_test(&scalars[filled]);
} else {
secp256k1_scalar_set_int(&scalars[filled], 0);
random_group_element_test(&ge_tmp);
secp256k1_gej_set_ge(&gejs[filled], &ge_tmp);
}
++filled;
}

/* Now perform cheapish transformations on gejs and scalars, for indices
* 0..num_nonzero-1, which do not change the expected result, but may
* convert some of them to be both non-0-scalar and non-infinity-point. */
for (i = 0; i < rands; ++i) {
int j;
secp256k1_scalar v, iv;
/* Shuffle the entries. */
for (j = 0; j < num_nonzero; ++j) {
int k = secp256k1_testrand_int(num_nonzero - j);
if (k != 0) {
secp256k1_gej gej = gejs[j];
secp256k1_scalar sc = scalars[j];
gejs[j] = gejs[j + k];
scalars[j] = scalars[j + k];
gejs[j + k] = gej;
scalars[j + k] = sc;
}
}
/* Perturb all consecutive pairs of inputs:
* a*P + b*Q -> (a+b)*P + b*(Q-P). */
for (j = 0; j + 1 < num_nonzero; j += 2) {
secp256k1_gej gej;
secp256k1_scalar_add(&scalars[j], &scalars[j], &scalars[j+1]);
secp256k1_gej_neg(&gej, &gejs[j]);
secp256k1_gej_add_var(&gejs[j+1], &gejs[j+1], &gej, NULL);
}
/* Transform the last input: a*P -> (v*a) * ((1/v)*P). */
CHECK(num_nonzero >= 1);
random_scalar_order_test(&v);
secp256k1_scalar_inverse(&iv, &v);
secp256k1_scalar_mul(&scalars[num_nonzero - 1], &scalars[num_nonzero - 1], &v);
secp256k1_ecmult(&gejs[num_nonzero - 1], &gejs[num_nonzero - 1], &iv, NULL);
++mults;
}

/* Shuffle all entries (0..num-1). */
for (i = 0; i < num; ++i) {
int j = secp256k1_testrand_int(num - i);
if (j != 0) {
secp256k1_gej gej = gejs[i];
secp256k1_scalar sc = scalars[i];
gejs[i] = gejs[i + j];
scalars[i] = scalars[i + j];
gejs[i + j] = gej;
scalars[i + j] = sc;
}
}

/* Compute affine versions of all inputs. */
secp256k1_ge_set_all_gej_var(ges, gejs, filled);
/* Invoke ecmult_multi code. */
data.sc = scalars;
data.pt = ges;
CHECK(ecmult_multi(&ctx->error_callback, scratch, &computed, g_scalar_ptr, ecmult_multi_callback, &data, filled));
mults += num_nonzero + g_nonzero;
/* Compare with expected result. */
secp256k1_gej_neg(&computed, &computed);
secp256k1_gej_add_var(&computed, &computed, &expected, NULL);
CHECK(secp256k1_gej_is_infinity(&computed));
return mults;
}

void test_ecmult_multi_batch_single(secp256k1_ecmult_multi_func ecmult_multi) {
secp256k1_scalar szero;
secp256k1_scalar sc;
Expand Down Expand Up @@ -4242,6 +4410,7 @@ void test_ecmult_multi_batching(void) {

void run_ecmult_multi_tests(void) {
secp256k1_scratch *scratch;
int64_t todo = (int64_t)320 * count;

test_secp256k1_pippenger_bucket_window_inv();
test_ecmult_multi_pippenger_max_points();
Expand All @@ -4252,6 +4421,9 @@ void run_ecmult_multi_tests(void) {
test_ecmult_multi_batch_single(secp256k1_ecmult_pippenger_batch_single);
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
test_ecmult_multi_batch_single(secp256k1_ecmult_strauss_batch_single);
while (todo > 0) {
todo -= test_ecmult_multi_random(scratch);
}
secp256k1_scratch_destroy(&ctx->error_callback, scratch);

/* Run test_ecmult_multi with space for exactly one point */
Expand Down

0 comments on commit 22d25c8

Please sign in to comment.