Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add another ecmult_multi test #1044

Merged
merged 1 commit into from
Dec 22, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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. */
sipa marked this conversation as resolved.
Show resolved Hide resolved
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