Add pippenger_wnaf ecmult_multi
This commit is contained in:
parent
bc65aa794e
commit
355a38f113
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra *
|
||||
/*****************************************************************************
|
||||
* 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.*
|
||||
**********************************************************************/
|
||||
* 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);
|
||||
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) return 0;
|
||||
secp256k1_gej_set_ge(&points[i], &point);
|
||||
}
|
||||
first = 0;
|
||||
out_pos = 0;
|
||||
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 */
|
||||
|
|
218
src/tests.c
218
src/tests.c
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue