Skip to content

Commit 2b3e262

Browse files
committed
Make strauss_ and pippenger_max_points more precise by taking actual
alignment into account instead of assuming the worst case. This gives strauss_max_points the property that a given number of points, calling this function with a scratch space with strauss_scratch_size left will return the given number.
1 parent c7d8c9c commit 2b3e262

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

src/ecmult_impl.h

+44-6
Original file line numberDiff line numberDiff line change
@@ -732,9 +732,29 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
732732
static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error_callback, 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) {
733733
return secp256k1_ecmult_strauss_batch(error_callback, actx, scratch, r, inp_g_sc, cb, cbdata, n, 0);
734734
}
735-
735+
/**
736+
* Returns the maximum number of points in addition to G that can be used with
737+
* a given scratch space. For a given number of points, calling this function
738+
* with a scratch space with strauss_scratch_size left will return the given
739+
* number.
740+
*/
736741
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
737-
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
742+
/* Call max_allocation with 0 objects because otherwise it would assume
743+
* worst case padding but in this function we want to be exact. */
744+
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, 0);
745+
size_t unpadded_single_size = secp256k1_strauss_scratch_size_raw(1, 0);
746+
size_t n_points = max_alloc / unpadded_single_size;
747+
if (n_points > 0
748+
&& max_alloc < secp256k1_strauss_scratch_size(n_points)) {
749+
/* If there's not enough space after alignment is taken into
750+
* account, it suffices to decrease n_points by one. This is because
751+
* the maximum padding required is less than an entry. */
752+
n_points -= 1;
753+
VERIFY_CHECK(max_alloc >= secp256k1_strauss_scratch_size(n_points));
754+
VERIFY_CHECK(max_alloc - secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) < unpadded_single_size);
755+
}
756+
757+
return n_points;
738758
}
739759

740760
/** Convert a number to WNAF notation.
@@ -1151,12 +1171,17 @@ static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* err
11511171
}
11521172

11531173
/**
1154-
* Returns the maximum number of points in addition to G that can be used with
1155-
* a given scratch space. The function ensures that fewer points may also be
1156-
* used.
1174+
* Returns the (near) maximum number of points in addition to G that can be
1175+
* used with a given scratch space. It may not return the actual maximum number
1176+
* of points possible. Otherwise, fewer points would not fit into the scratch
1177+
* space in general. For a given number of points, calling this function with a
1178+
* scratch space with pippenger_scratch_size left will return less than or
1179+
* equal than the given number.
11571180
*/
11581181
static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
1159-
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS);
1182+
/* Call max_allocation with 0 objects because otherwise it would assume
1183+
* worst case padding but in this function we want to be exact. */
1184+
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, 0);
11601185
int bucket_window;
11611186
size_t res = 0;
11621187

@@ -1174,7 +1199,20 @@ static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_cal
11741199
}
11751200
space_for_points = max_alloc - space_constant;
11761201

1202+
/* Compute an upper bound for the number of points after subtracting
1203+
* space for the base point G. It's an upper bound because alignment is
1204+
* not taken into account. */
11771205
n_points = (space_for_points - entry_size)/entry_size;
1206+
if (n_points > 0
1207+
&& space_for_points < secp256k1_pippenger_scratch_size_points(n_points, bucket_window, 1)) {
1208+
/* If there's not enough space after alignment is taken into
1209+
* account, it suffices to decrease n_points by one. This is because
1210+
* the maximum padding required is less than an entry. */
1211+
n_points -= 1;
1212+
VERIFY_CHECK(max_alloc - secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS) < entry_size);
1213+
VERIFY_CHECK(space_for_points >= secp256k1_pippenger_scratch_size_points(n_points, bucket_window, 1));
1214+
}
1215+
11781216
n_points = n_points > max_points ? max_points : n_points;
11791217
if (n_points > res) {
11801218
res = n_points;

src/tests.c

+16
Original file line numberDiff line numberDiff line change
@@ -2965,6 +2965,7 @@ void test_ecmult_multi_strauss_scratch_size(void) {
29652965
for(n_points = 0; n_points < ECMULT_PIPPENGER_THRESHOLD*2; n_points++) {
29662966
size_t scratch_size = secp256k1_strauss_scratch_size(n_points);
29672967
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
2968+
CHECK(n_points == secp256k1_strauss_max_points(&ctx->error_callback, scratch));
29682969
{
29692970
secp256k1_gej *points;
29702971
secp256k1_scalar *scalars;
@@ -2978,6 +2979,20 @@ void test_ecmult_multi_strauss_scratch_size(void) {
29782979
}
29792980
}
29802981

2982+
/* Spot check that any scratch space size is greater or equal to then the size required
2983+
* to fit secp256k1_strauss_max_points many points. */
2984+
void test_ecmult_multi_strauss_max_points(void) {
2985+
size_t scratch_size = secp256k1_strauss_scratch_size_raw(1, 0);
2986+
size_t max_scratch_size = secp256k1_strauss_scratch_size_raw(1, 1) + 1;
2987+
for (; scratch_size < max_scratch_size; scratch_size++) {
2988+
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
2989+
size_t n_points = secp256k1_strauss_max_points(&ctx->error_callback, scratch);
2990+
CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 0) == scratch_size);
2991+
CHECK(scratch_size >= secp256k1_strauss_scratch_size(n_points));
2992+
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
2993+
}
2994+
}
2995+
29812996
void test_secp256k1_pippenger_bucket_window_inv(void) {
29822997
int i;
29832998

@@ -3159,6 +3174,7 @@ void run_ecmult_multi_tests(void) {
31593174
secp256k1_scratch *scratch;
31603175

31613176
test_ecmult_multi_strauss_scratch_size();
3177+
test_ecmult_multi_strauss_max_points();
31623178
test_secp256k1_pippenger_bucket_window_inv();
31633179
test_ecmult_multi_pippenger_max_points();
31643180
scratch = secp256k1_scratch_create(&ctx->error_callback, 819200);

0 commit comments

Comments
 (0)