diff --git a/src/ecmult.h b/src/ecmult.h index ea1cd8a21f66b..3d75a960f4247 100644 --- a/src/ecmult.h +++ b/src/ecmult.h @@ -37,7 +37,8 @@ typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge * Chooses the right algorithm for a given number of points and scratch space * size. Resets and overwrites the given scratch space. If the points do not * fit in the scratch space the algorithm is repeatedly run with batches of - * points. + * points. If no scratch space is given then a simple algorithm is used that + * simply multiplies the points with the corresponding scalars and adds them up. * 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 diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 508bde8ab376a..7c37821ef4c80 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -1084,6 +1084,33 @@ static size_t secp256k1_pippenger_max_points(secp256k1_scratch *scratch) { return res; } +/* Computes ecmult_multi by simply multiplying and adding each point. Does not + * require a scratch space */ +static int secp256k1_ecmult_multi_simple_var(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) { + size_t point_idx; + secp256k1_scalar szero; + secp256k1_gej tmpj; + + secp256k1_scalar_set_int(&szero, 0); + secp256k1_gej_set_infinity(r); + secp256k1_gej_set_infinity(&tmpj); + /* r = inp_g_sc*G */ + secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc); + for (point_idx = 0; point_idx < n_points; point_idx++) { + secp256k1_ge point; + secp256k1_gej pointj; + secp256k1_scalar scalar; + if (!cb(&scalar, &point, point_idx, cbdata)) { + return 0; + } + /* r += scalar*point */ + secp256k1_gej_set_ge(&pointj, &point); + secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL); + secp256k1_gej_add_var(r, r, &tmpj, NULL); + } + return 1; +} + 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; @@ -1102,6 +1129,9 @@ static int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp2 secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc); return 1; } + if (scratch == NULL) { + return secp256k1_ecmult_multi_simple_var(ctx, r, inp_g_sc, cb, cbdata, n); + } max_points = secp256k1_pippenger_max_points(scratch); if (max_points == 0) { diff --git a/src/tests.c b/src/tests.c index 7189ef594d313..224ddc6722b49 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2926,6 +2926,7 @@ void run_ecmult_multi_tests(void) { test_ecmult_multi_pippenger_max_points(); scratch = secp256k1_scratch_create(&ctx->error_callback, 819200); test_ecmult_multi(scratch, secp256k1_ecmult_multi_var); + test_ecmult_multi(NULL, 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);