Add pippenger_wnaf ecmult_multi

This commit is contained in:
Jonas Nick 2017-09-14 17:55:13 +02:00
parent bc65aa794e
commit 355a38f113
6 changed files with 626 additions and 90 deletions

View File

@ -63,7 +63,7 @@ static void bench_ecmult(void* arg) {
size_t iter;
for (iter = 0; iter < iters; ++iter) {
secp256k1_ecmult_multi(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
secp256k1_ecmult_multi_var(&data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
data->offset1 = (data->offset1 + count) % POINTS;
data->offset2 = (data->offset2 + count - 1) % POINTS;
}

View File

@ -32,7 +32,12 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data);
/** Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai. */
static int secp256k1_ecmult_multi(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
/**
* Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai.
* Returns: 1 on success (including when inp_g_sc is NULL and n is 0)
* 0 if there is not enough scratch space for a single point or
* callback returns 0
*/
static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n);
#endif /* SECP256K1_ECMULT_H */

View File

@ -12,13 +12,6 @@
#include "ecmult_const.h"
#include "ecmult_impl.h"
#ifdef USE_ENDOMORPHISM
#define WNAF_BITS 128
#else
#define WNAF_BITS 256
#endif
#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w))
/* This is like `ECMULT_TABLE_GET_GE` but is constant time */
#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
int m; \

View File

@ -1,8 +1,8 @@
/**********************************************************************
* Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
/*****************************************************************************
* Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php. *
*****************************************************************************/
#ifndef SECP256K1_ECMULT_IMPL_H
#define SECP256K1_ECMULT_IMPL_H
@ -41,9 +41,27 @@
#endif
#endif
#ifdef USE_ENDOMORPHISM
#define WNAF_BITS 128
#else
#define WNAF_BITS 256
#endif
#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w))
/** The number of entries a table with precomputed multiples needs to have. */
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
/* The number of objects allocated on the scratch space for ecmult_multi algorithms */
#define PIPPENGER_SCRATCH_OBJECTS 6
#define STRAUSS_SCRATCH_OBJECTS 6
/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */
#ifdef USE_ENDOMORPHISM
#define ECMULT_PIPPENGER_THRESHOLD 96
#else
#define ECMULT_PIPPENGER_THRESHOLD 156
#endif
/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain
* the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will
* contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z.
@ -477,74 +495,414 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng);
}
static int secp256k1_ecmult_multi_split_strauss_wnaf(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
static size_t secp256k1_strauss_scratch_size(size_t n_points) {
#ifdef USE_ENDOMORPHISM
static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
#else
static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
#endif
return n_points*point_size;
}
static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
secp256k1_gej* points;
secp256k1_scalar* scalars;
secp256k1_gej acc;
size_t in_pos = 0, out_pos = 0;
int first = 1;
#ifdef USE_ENDOMORPHISM
static const size_t point_size = (sizeof(secp256k1_gej) + sizeof(secp256k1_fe) + sizeof(secp256k1_ge) * 2) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
#else
static const size_t point_size = (sizeof(secp256k1_gej) + sizeof(secp256k1_fe) + sizeof(secp256k1_ge)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
#endif
size_t max_points = secp256k1_scratch_max_allocation(scratch, 6) / point_size;
size_t n_batches, points_per_batch;
struct secp256k1_strauss_state state;
size_t i;
if (max_points == 0) return 0;
if (max_points > 160) max_points = 160; /* At this point, gains are not longer compensating for locality degradation */
n_batches = (n + max_points - 1) / max_points;
points_per_batch = (n + n_batches - 1) / n_batches;
/* Attempt to allocate sufficient space for Strauss */
while (!secp256k1_scratch_resize(scratch, max_points * point_size, 6)) {
max_points /= 2;
if (max_points == 0) {
return 0;
}
}
secp256k1_scratch_reset(scratch);
points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, max_points * sizeof(secp256k1_gej));
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, max_points * sizeof(secp256k1_scalar));
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, max_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, max_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
#ifdef USE_ENDOMORPHISM
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, max_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.pre_a_lam = state.pre_a + max_points * ECMULT_TABLE_SIZE(WINDOW_A);
#else
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, max_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
#endif
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, max_points * sizeof(struct secp256k1_strauss_point_state));
if (n == 0 && inp_g_sc) {
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 0, NULL, NULL, inp_g_sc);
secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n_points == 0) {
return 1;
}
while (in_pos < n) {
if (!secp256k1_scratch_resize(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
return 0;
}
secp256k1_scratch_reset(scratch);
points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej));
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
#ifdef USE_ENDOMORPHISM
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A);
#else
state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
#endif
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
for (i = 0; i < n_points; i++) {
secp256k1_ge point;
if (!cb(&scalars[out_pos], &point, in_pos, cbdata)) return 0;
secp256k1_gej_set_ge(&points[out_pos], &point);
++in_pos;
++out_pos;
if (out_pos == points_per_batch || in_pos == n) {
secp256k1_ecmult_strauss_wnaf(ctx, &state, first ? r : &acc, out_pos, points, scalars, first ? inp_g_sc : NULL);
if (!first) {
secp256k1_gej_add_var(r, r, &acc, NULL);
}
first = 0;
out_pos = 0;
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) return 0;
secp256k1_gej_set_ge(&points[i], &point);
}
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
return 1;
}
/* Wrapper for secp256k1_ecmult_multi_func interface */
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
/** Convert a number to WNAF notation.
* The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
* It has the following guarantees:
* - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w)
* - the number of words set is always WNAF_SIZE(w)
* - the returned skew is 0 without endomorphism, or 0 or 1 with endomorphism
*/
static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) {
int sign = 0;
int skew = 0;
int pos = 1;
#ifndef USE_ENDOMORPHISM
secp256k1_scalar neg_s;
#endif
const secp256k1_scalar *work = s;
if (secp256k1_scalar_is_zero(s)) {
while (pos * w < WNAF_BITS) {
wnaf[pos] = 0;
++pos;
}
return 0;
}
if (secp256k1_scalar_is_even(s)) {
#ifdef USE_ENDOMORPHISM
skew = 1;
#else
secp256k1_scalar_negate(&neg_s, s);
work = &neg_s;
sign = -1;
#endif
}
wnaf[0] = (secp256k1_scalar_get_bits_var(work, 0, w) + skew + sign) ^ sign;
while (pos * w < WNAF_BITS) {
int now = w;
int val;
if (now + pos * w > WNAF_BITS) {
now = WNAF_BITS - pos * w;
}
val = secp256k1_scalar_get_bits_var(work, pos * w, now);
if ((val & 1) == 0) {
wnaf[pos - 1] -= ((1 << w) + sign) ^ sign;
wnaf[pos] = (val + 1 + sign) ^ sign;
} else {
wnaf[pos] = (val + sign) ^ sign;
}
++pos;
}
VERIFY_CHECK(pos == WNAF_SIZE(w));
return skew;
}
struct secp256k1_pippenger_point_state {
int skew_na;
size_t input_pos;
};
struct secp256k1_pippenger_state {
int *wnaf_na;
struct secp256k1_pippenger_point_state* ps;
};
/*
* pippenger_wnaf computes the result of a multi-point multiplication as
* follows: The scalars are brought into wnaf with n_wnaf elements each. Then
* for every i < n_wnaf, first each point is added to a "bucket" corresponding
* to the point's wnaf[i]. Second, the buckets are added together such that
* r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ...
*/
static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, secp256k1_scalar *sc, secp256k1_ge *pt, size_t num) {
size_t n_wnaf = WNAF_SIZE(bucket_window+1);
size_t np;
size_t no = 0;
int i;
int j;
for (np = 0; np < num; ++np) {
if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) {
continue;
}
state->ps[no].input_pos = np;
state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1);
no++;
}
secp256k1_gej_set_infinity(r);
if (no == 0) {
return 1;
}
for (i = n_wnaf - 1; i >= 0; i--) {
secp256k1_gej running_sum;
secp256k1_gej walking_sum;
for(j = 0; j < ECMULT_TABLE_SIZE(bucket_window+2); j++) {
secp256k1_gej_set_infinity(&buckets[j]);
}
for(j = 0; j < bucket_window+1; j++) {
secp256k1_gej_double_var(r, r, NULL);
}
for (np = 0; np < no; ++np) {
int n = state->wnaf_na[np*n_wnaf + i];
struct secp256k1_pippenger_point_state point_state = state->ps[np];
secp256k1_ge tmp;
int idx;
#ifdef USE_ENDOMORPHISM
if (i == 0) {
/* correct for wnaf skew */
int skew = point_state.skew_na;
if (skew) {
secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL);
}
}
#endif
if (n > 0) {
idx = (n - 1)/2;
secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &pt[point_state.input_pos], NULL);
} else if (n < 0) {
idx = -(n + 1)/2;
secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]);
secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &tmp, NULL);
}
}
secp256k1_gej_set_infinity(&running_sum);
secp256k1_gej_set_infinity(&walking_sum);
/* Compute walking_sum as bucket[0] + 3*bucket[1] + 5*bucket[2] + ...
* by first setting
* running_sum = bucket[0] + bucket[1] + bucket[2] + ...
* walking_sum = bucket[0] + 2*bucket[1] + 3*bucket[2] + ...
* and then computing
* walking_sum = 2*walking_sum - running_sum
*/
for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j >= 0; j--) {
secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[j], NULL);
secp256k1_gej_add_var(&walking_sum, &walking_sum, &running_sum, NULL);
}
secp256k1_gej_double_var(&walking_sum, &walking_sum, NULL);
secp256k1_gej_neg(&running_sum, &running_sum);
secp256k1_gej_add_var(&walking_sum, &walking_sum, &running_sum, NULL);
secp256k1_gej_add_var(r, r, &walking_sum, NULL);
}
return 1;
}
static int secp256k1_ecmult_multi(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_multi_split_strauss_wnaf(ctx, scratch, r, inp_g_sc, cb, cbdata, n);
/**
* Returns optimal bucket_window (number of bits of a scalar represented by a
* set of buckets) for a given number of points.
*/
static int secp256k1_pippenger_bucket_window(size_t n) {
#ifdef USE_ENDOMORPHISM
if (n <= 4) {
return 1;
} else if (n <= 8) {
return 2;
} else if (n <= 40) {
return 3;
} else if (n <= 117) {
return 4;
} else if (n <= 280) {
return 5;
} else if (n <= 480) {
return 6;
} else if (n <= 2560) {
return 7;
} else if (n <= 9200) {
return 9;
} else if (n <= 17400) {
return 10;
} else if (n <= 28600) {
return 11;
} else {
return 12;
}
#else
if (n <= 2) {
return 1;
} else if (n <= 9) {
return 2;
} else if (n <= 42) {
return 3;
} else if (n <= 100) {
return 4;
} else if (n <= 280) {
return 5;
} else if (n <= 610) {
return 6;
} else if (n <= 1920) {
return 7;
} else if (n <= 3400) {
return 8;
} else if (n <= 10240) {
return 9;
} else if (n <= 19000) {
return 10;
} else if (n <= 35000) {
return 11;
} else {
return 12;
}
#endif
}
#ifdef USE_ENDOMORPHISM
SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) {
secp256k1_scalar tmp = *s1;
secp256k1_scalar_split_lambda(s1, s2, &tmp);
secp256k1_ge_mul_lambda(p2, p1);
if (secp256k1_scalar_is_high(s1)) {
secp256k1_scalar_negate(s1, s1);
secp256k1_ge_neg(p1, p1);
}
if (secp256k1_scalar_is_high(s2)) {
secp256k1_scalar_negate(s2, s2);
secp256k1_ge_neg(p2, p2);
}
}
#endif
/**
* Returns the scratch size required for a given number of points (excluding
* base point G) without considering alignment.
*/
static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) {
#ifdef USE_ENDOMORPHISM
size_t entries = 2*n_points + 2;
#else
size_t entries = n_points + 1;
#endif
size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int);
return ((1<<bucket_window) * sizeof(secp256k1_gej) + sizeof(struct secp256k1_pippenger_state) + entries * entry_size);
}
static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) {
/* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
* sizes. The reason for +1 is that we add the G scalar to the list of
* other scalars. */
#ifdef USE_ENDOMORPHISM
size_t entries = 2*n_points + 2;
#else
size_t entries = n_points + 1;
#endif
secp256k1_ge *points;
secp256k1_scalar *scalars;
secp256k1_gej *buckets;
struct secp256k1_pippenger_state *state_space;
size_t idx = 0;
size_t point_idx = 0;
int i, j;
int bucket_window;
(void)ctx;
secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n_points == 0) {
return 1;
}
bucket_window = secp256k1_pippenger_bucket_window(n_points);
if (!secp256k1_scratch_resize(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
return 0;
}
secp256k1_scratch_reset(scratch);
points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps));
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, (1<<bucket_window) * sizeof(*buckets));
if (inp_g_sc != NULL) {
scalars[0] = *inp_g_sc;
points[0] = secp256k1_ge_const_g;
idx++;
#ifdef USE_ENDOMORPHISM
secp256k1_ecmult_endo_split(&scalars[0], &scalars[1], &points[0], &points[1]);
idx++;
#endif
}
while (point_idx < n_points) {
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
return 0;
}
idx++;
#ifdef USE_ENDOMORPHISM
secp256k1_ecmult_endo_split(&scalars[idx - 1], &scalars[idx], &points[idx - 1], &points[idx]);
idx++;
#endif
point_idx++;
}
secp256k1_ecmult_pippenger_wnaf(buckets, bucket_window, state_space, r, scalars, points, idx);
/* Clear data */
for(i = 0; (size_t)i < idx; i++) {
secp256k1_scalar_clear(&scalars[i]);
state_space->ps[i].skew_na = 0;
for(j = 0; j < WNAF_SIZE(bucket_window+1); j++) {
state_space->wnaf_na[i * WNAF_SIZE(bucket_window+1) + j] = 0;
}
}
for(i = 0; i < 1<<bucket_window; i++) {
secp256k1_gej_clear(&buckets[i]);
}
return 1;
}
/* Wrapper for secp256k1_ecmult_multi_func interface */
static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
return secp256k1_ecmult_pippenger_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
}
#define MAX_BATCH_SIZE 1024
typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t);
static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) {
size_t i;
size_t n_batches;
size_t n_batch_points;
secp256k1_gej_set_infinity(r);
if (inp_g_sc == NULL && n == 0) {
return 1;
} else if (n == 0) {
secp256k1_scalar szero;
secp256k1_scalar_set_int(&szero, 0);
secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc);
return 1;
}
if(n <= ECMULT_PIPPENGER_THRESHOLD) {
if(!secp256k1_ecmult_strauss_batch(ctx, scratch, r, inp_g_sc, cb, cbdata, n, 0)) {
return 0;
}
} else {
n_batches = (n+MAX_BATCH_SIZE-1)/MAX_BATCH_SIZE;
n_batch_points = (n+n_batches-1)/n_batches;
for(i = 0; i < n_batches; i++) {
size_t nbp = n < n_batch_points ? n : n_batch_points;
size_t offset = n_batch_points*i;
secp256k1_gej tmp;
if(!secp256k1_ecmult_pippenger_batch(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) {
return 0;
}
secp256k1_gej_add_var(r, r, &tmp, NULL);
n -= nbp;
}
}
return 1;
}
#endif /* SECP256K1_ECMULT_IMPL_H */

View File

@ -2534,7 +2534,15 @@ static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t
return 1;
}
void run_ecmult_multi_tests(void) {
static int ecmult_multi_false_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *cbdata) {
(void)sc;
(void)pt;
(void)idx;
(void)cbdata;
return 0;
}
void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func ecmult_multi) {
int ncount;
secp256k1_scalar szero;
secp256k1_scalar sc[32];
@ -2542,12 +2550,15 @@ void run_ecmult_multi_tests(void) {
secp256k1_gej r;
secp256k1_gej r2;
ecmult_multi_data data;
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 1024, 8192);
secp256k1_scratch *scratch_empty;
data.sc = sc;
data.pt = pt;
secp256k1_scalar_set_int(&szero, 0);
secp256k1_scratch_reset(scratch);
/* No points to multiply */
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
/* Check 1- and 2-point multiplies against ecmult */
for (ncount = 0; ncount < count; ncount++) {
@ -2561,23 +2572,38 @@ void run_ecmult_multi_tests(void) {
pt[0] = ptg;
pt[1] = secp256k1_ge_const_g;
/* 1-point */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1));
/* only G scalar */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &szero, &sc[0]);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[0], ecmult_multi_callback, &data, 0));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
/* 1-point */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &szero);
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 1));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
/* Try to multiply 1 point, but scratch space is empty */
scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0, 0);
CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
secp256k1_scratch_destroy(scratch_empty);
/* Try to multiply 1 point, but callback returns false */
CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_false_callback, &data, 1));
/* 2-point */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 2));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
/* 2-point with G scalar */
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &ptgj, &sc[0], &sc[1]);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &sc[1], ecmult_multi_callback, &data, 1));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
@ -2594,7 +2620,7 @@ void run_ecmult_multi_tests(void) {
random_scalar_order(&sc[i]);
secp256k1_ge_set_infinity(&pt[i]);
}
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@ -2604,7 +2630,7 @@ void run_ecmult_multi_tests(void) {
pt[i] = ptg;
secp256k1_scalar_set_int(&sc[i], 0);
}
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@ -2617,7 +2643,7 @@ void run_ecmult_multi_tests(void) {
pt[2 * i + 1] = ptg;
}
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
random_scalar_order(&sc[0]);
@ -2630,7 +2656,7 @@ void run_ecmult_multi_tests(void) {
secp256k1_ge_neg(&pt[2*i+1], &pt[2*i]);
}
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, sizes[j]));
CHECK(secp256k1_gej_is_infinity(&r));
}
@ -2645,7 +2671,7 @@ void run_ecmult_multi_tests(void) {
secp256k1_scalar_negate(&sc[i], &sc[i]);
}
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 32));
CHECK(secp256k1_gej_is_infinity(&r));
}
@ -2664,7 +2690,7 @@ void run_ecmult_multi_tests(void) {
}
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r, &sc[0], &szero);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
@ -2687,7 +2713,7 @@ void run_ecmult_multi_tests(void) {
secp256k1_gej_set_ge(&p0j, &pt[0]);
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &p0j, &rs, &szero);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_gej_neg(&r2, &r2);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
@ -2695,13 +2721,13 @@ void run_ecmult_multi_tests(void) {
/* Sanity check that zero scalars don't cause problems */
secp256k1_scalar_clear(&sc[0]);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 20));
secp256k1_scalar_clear(&sc[1]);
secp256k1_scalar_clear(&sc[2]);
secp256k1_scalar_clear(&sc[3]);
secp256k1_scalar_clear(&sc[4]);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 6));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, &szero, ecmult_multi_callback, &data, 5));
CHECK(secp256k1_gej_is_infinity(&r));
/* Run through s0*(t0*P) + s1*(t1*P) exhaustively for many small values of s0, s1, t0, t1 */
@ -2746,7 +2772,7 @@ void run_ecmult_multi_tests(void) {
secp256k1_scalar_add(&tmp1, &tmp1, &tmp2);
secp256k1_ecmult(&ctx->ecmult_ctx, &expected, &ptgj, &tmp1, &szero);
CHECK(secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2));
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &actual, &szero, ecmult_multi_callback, &data, 2));
secp256k1_gej_neg(&expected, &expected);
secp256k1_gej_add_var(&actual, &actual, &expected, NULL);
CHECK(secp256k1_gej_is_infinity(&actual));
@ -2755,8 +2781,104 @@ void run_ecmult_multi_tests(void) {
}
}
}
}
void test_ecmult_multi_batching(void) {
static const int n_points = 3*MAX_BATCH_SIZE;
secp256k1_scalar scG;
secp256k1_scalar szero;
secp256k1_scalar *sc = (secp256k1_scalar *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_scalar) * n_points);
secp256k1_ge *pt = (secp256k1_ge *)checked_malloc(&ctx->error_callback, sizeof(secp256k1_ge) * n_points);
secp256k1_gej r;
secp256k1_gej r2;
ecmult_multi_data data;
int i;
secp256k1_scratch *scratch;
int test_n_points[] = { MAX_BATCH_SIZE, MAX_BATCH_SIZE + 1, MAX_BATCH_SIZE + 2, 2*MAX_BATCH_SIZE, 2*MAX_BATCH_SIZE+1, 3*MAX_BATCH_SIZE };
secp256k1_gej_set_infinity(&r2);
secp256k1_scalar_set_int(&szero, 0);
/* Get random scalars and group elements */
random_scalar_order(&scG);
secp256k1_ecmult(&ctx->ecmult_ctx, &r2, &r2, &szero, &scG);
for(i = 0; i < n_points; i++) {
secp256k1_ge ptg;
random_group_element_test(&ptg);
pt[i] = ptg;
random_scalar_order(&sc[i]);
}
data.sc = sc;
data.pt = pt;
/* Test with empty scratch space */
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, 0);
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
secp256k1_scratch_destroy(scratch);
/* Test with space for 1 point in pippenger. That's not enough because
* ecmult_multi selects strauss which requires more memory. */
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
secp256k1_scratch_destroy(scratch);
/* Run secp256k1_ecmult_multi_var with i points and a scratch space
* restricted to i points. */
for(i = 1; i <= ECMULT_PIPPENGER_THRESHOLD+2; i++) {
secp256k1_gej ptgj;
if (i > ECMULT_PIPPENGER_THRESHOLD) {
int bucket_window = secp256k1_pippenger_bucket_window(i);
size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window);
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
} else {
size_t scratch_size = secp256k1_strauss_scratch_size(i);
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
}
CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, i));
/* compute running result */
secp256k1_gej_set_ge(&ptgj, &pt[i-1]);
secp256k1_ecmult(&ctx->ecmult_ctx, &ptgj, &ptgj, &sc[i-1], NULL);
secp256k1_gej_add_var(&r2, &r2, &ptgj, NULL);
secp256k1_gej_neg(&r, &r);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
secp256k1_scratch_destroy(scratch);
}
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, secp256k1_strauss_scratch_size(n_points) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
for(i = 0; i < (int)(sizeof(test_n_points) / sizeof(test_n_points[0])); i++) {
secp256k1_gej ptgj;
CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, test_n_points[i]-1));
secp256k1_gej_set_infinity(&r2);
secp256k1_gej_add_var(&r2, &r2, &r, NULL);
CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, test_n_points[i]));
secp256k1_gej_set_ge(&ptgj, &pt[test_n_points[i]-1]);
secp256k1_ecmult(&ctx->ecmult_ctx, &ptgj, &ptgj, &sc[test_n_points[i]-1], NULL);
secp256k1_gej_add_var(&r2, &r2, &ptgj, NULL);
secp256k1_gej_neg(&r, &r);
secp256k1_gej_add_var(&r, &r, &r2, NULL);
CHECK(secp256k1_gej_is_infinity(&r));
}
secp256k1_scratch_destroy(scratch);
free(sc);
free(pt);
}
void run_ecmult_multi_tests(void) {
secp256k1_scratch *scratch;
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, 819200);
test_ecmult_multi(scratch, &secp256k1_ecmult_multi_var);
test_ecmult_multi(scratch, &secp256k1_ecmult_pippenger_batch_single);
test_ecmult_multi(scratch, &secp256k1_ecmult_strauss_batch_single);
secp256k1_scratch_destroy(scratch);
test_ecmult_multi_batching();
}
void test_wnaf(const secp256k1_scalar *number, int w) {
@ -2847,6 +2969,61 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
CHECK(secp256k1_scalar_eq(&x, &num));
}
void test_fixed_wnaf(const secp256k1_scalar *number, int w) {
secp256k1_scalar x, shift;
int wnaf[256] = {0};
int i;
int skew;
secp256k1_scalar num = *number;
secp256k1_scalar_set_int(&x, 0);
secp256k1_scalar_set_int(&shift, 1 << w);
/* With USE_ENDOMORPHISM on we only consider 128-bit numbers */
#ifdef USE_ENDOMORPHISM
for (i = 0; i < 16; ++i) {
secp256k1_scalar_shr_int(&num, 8);
}
#endif
skew = secp256k1_wnaf_fixed(wnaf, &num, w);
for (i = WNAF_SIZE(w)-1; i >= 0; --i) {
secp256k1_scalar t;
int v = wnaf[i];
CHECK(v != 0); /* check nonzero */
CHECK(v & 1); /* check parity */
CHECK(v > -(1 << w)); /* check range above */
CHECK(v < (1 << w)); /* check range below */
secp256k1_scalar_mul(&x, &x, &shift);
if (v >= 0) {
secp256k1_scalar_set_int(&t, v);
} else {
secp256k1_scalar_set_int(&t, -v);
secp256k1_scalar_negate(&t, &t);
}
secp256k1_scalar_add(&x, &x, &t);
}
/* If skew is 1 then add 1 to num */
secp256k1_scalar_cadd_bit(&num, 0, skew == 1);
CHECK(secp256k1_scalar_eq(&x, &num));
}
void test_fixed_wnaf_zero(int w) {
int wnaf[256] = {0};
int i;
int skew;
secp256k1_scalar num;
secp256k1_scalar_set_int(&num, 0);
skew = secp256k1_wnaf_fixed(wnaf, &num, w);
for (i = WNAF_SIZE(w)-1; i >= 0; --i) {
int v = wnaf[i];
CHECK(v == 0);
}
CHECK(skew == 0);
}
void run_wnaf(void) {
int i;
secp256k1_scalar n = {{0}};
@ -2857,12 +3034,15 @@ void run_wnaf(void) {
test_constant_wnaf(&n, 4);
n.d[0] = 2;
test_constant_wnaf(&n, 4);
/* Test 0 */
test_fixed_wnaf_zero(4);
/* Random tests */
for (i = 0; i < count; i++) {
random_scalar_order(&n);
test_wnaf(&n, 4+(i%10));
test_constant_wnaf_negate(&n);
test_constant_wnaf(&n, 4 + (i % 10));
test_fixed_wnaf(&n, 4 + (i % 10));
}
secp256k1_scalar_set_int(&n, 0);
CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1);

View File

@ -212,7 +212,7 @@ void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_
data.pt[0] = group[x];
data.pt[1] = group[y];
secp256k1_ecmult_multi(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &tmp, &g_sc, ecmult_multi_callback, &data, 2);
ge_equals_gej(&group[(i * x + j * y + k) % order], &tmp);
}
}