Skip to content

Commit e1b1e04

Browse files
committed
ecmult: make strauss_ and pippenger_max_points more precise
Take actual alignment into account instead of assuming the worst case. This allows more precise tests for strauss, because if a scratch space has exactly strauss_scratch_size(n_points) left, then secp256k1_strauss_max_points(cb, scratch) = n_points.
1 parent ed848c2 commit e1b1e04

File tree

2 files changed

+59
-6
lines changed

2 files changed

+59
-6
lines changed

src/ecmult_impl.h

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,27 @@ static int secp256k1_ecmult_strauss_batch_single(const secp256k1_callback* error
437437
return secp256k1_ecmult_strauss_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
438438
}
439439

440+
/* Returns the maximum number of points in addition to G that can be used with a
441+
* given scratch space. If a scratch space has exactly
442+
* `strauss_scratch_size(n_points)` left, then
443+
* `strauss_max_points(cb, scratch) = n_points`. */
440444
static size_t secp256k1_strauss_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
441-
return secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1);
445+
/* Call max_allocation with 0 objects because otherwise it would assume
446+
* worst case padding but in this function we want to be exact. */
447+
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, 0);
448+
size_t unpadded_single_size = secp256k1_strauss_scratch_size_raw(1, 0);
449+
size_t n_points = max_alloc / unpadded_single_size;
450+
if (n_points > 0
451+
&& max_alloc < secp256k1_strauss_scratch_size(n_points)) {
452+
/* If there's not enough space after alignment is taken into
453+
* account, it suffices to decrease n_points by one. This is because
454+
* the maximum padding required is less than an entry. */
455+
n_points -= 1;
456+
VERIFY_CHECK(max_alloc >= secp256k1_strauss_scratch_size(n_points));
457+
VERIFY_CHECK(max_alloc - secp256k1_scratch_max_allocation(error_callback, scratch, STRAUSS_SCRATCH_OBJECTS) < unpadded_single_size);
458+
}
459+
460+
return n_points;
442461
}
443462

444463
/** Convert a number to WNAF notation.
@@ -801,13 +820,17 @@ static int secp256k1_ecmult_pippenger_batch_single(const secp256k1_callback* err
801820
return secp256k1_ecmult_pippenger_batch(error_callback, scratch, r, inp_g_sc, cb, cbdata, n, 0);
802821
}
803822

804-
/**
805-
* Returns the maximum number of points in addition to G that can be used with
806-
* a given scratch space. The function ensures that fewer points may also be
807-
* used.
823+
/* Returns the (near) maximum number of points in addition to G that can be
824+
* used with a given scratch space. It may not return the actual maximum number
825+
* of points possible. Otherwise, fewer points would not fit into the scratch
826+
* space in general. If a scratch space has exactly
827+
* `pippenger_scratch_size(n_points)` left, then
828+
* `pippenger_max_points(cb, scratch) <= n_points`.
808829
*/
809830
static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_callback, secp256k1_scratch *scratch) {
810-
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS);
831+
/* Call max_allocation with 0 objects because otherwise it would assume
832+
* worst case padding but in this function we want to be exact. */
833+
size_t max_alloc = secp256k1_scratch_max_allocation(error_callback, scratch, 0);
811834
int bucket_window;
812835
size_t res = 0;
813836

@@ -828,6 +851,20 @@ static size_t secp256k1_pippenger_max_points(const secp256k1_callback* error_cal
828851
/* Compute an upper bound for the number excluding the base point G.
829852
* It's an upper bound because alignment is not taken into account. */
830853
n_points = space_for_points / entry_size - 1;
854+
/* Compute an upper bound for the number of points after subtracting
855+
* space for the base point G. It's an upper bound because alignment is
856+
* not taken into account. */
857+
n_points = (space_for_points - entry_size)/entry_size;
858+
if (n_points > 0
859+
&& space_for_points < secp256k1_pippenger_scratch_size_points(n_points, bucket_window, 1)) {
860+
/* If there's not enough space after alignment is taken into
861+
* account, it suffices to decrease n_points by one. This is because
862+
* the maximum padding required is less than an entry. */
863+
n_points -= 1;
864+
VERIFY_CHECK(max_alloc - secp256k1_scratch_max_allocation(error_callback, scratch, PIPPENGER_SCRATCH_OBJECTS) < entry_size);
865+
VERIFY_CHECK(space_for_points >= secp256k1_pippenger_scratch_size_points(n_points, bucket_window, 1));
866+
}
867+
831868
n_points = n_points > max_points ? max_points : n_points;
832869
if (n_points > res) {
833870
res = n_points;

src/tests.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4085,6 +4085,7 @@ void test_ecmult_multi_strauss_scratch_size(void) {
40854085
for(n_points = 0; n_points < ECMULT_PIPPENGER_THRESHOLD*2; n_points++) {
40864086
size_t scratch_size = secp256k1_strauss_scratch_size(n_points);
40874087
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
4088+
CHECK(n_points == secp256k1_strauss_max_points(&ctx->error_callback, scratch));
40884089
{
40894090
secp256k1_gej *points;
40904091
secp256k1_scalar *scalars;
@@ -4098,6 +4099,20 @@ void test_ecmult_multi_strauss_scratch_size(void) {
40984099
}
40994100
}
41004101

4102+
/* Spot check that any scratch space is large enough to fit
4103+
* `strauss_max_points(scratch)` many points. */
4104+
void test_ecmult_multi_strauss_max_points(void) {
4105+
size_t scratch_size = secp256k1_strauss_scratch_size_raw(1, 0);
4106+
size_t max_scratch_size = secp256k1_strauss_scratch_size_raw(1, 1) + 1;
4107+
for (; scratch_size < max_scratch_size; scratch_size++) {
4108+
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
4109+
size_t n_points = secp256k1_strauss_max_points(&ctx->error_callback, scratch);
4110+
CHECK(secp256k1_scratch_max_allocation(&ctx->error_callback, scratch, 0) == scratch_size);
4111+
CHECK(scratch_size >= secp256k1_strauss_scratch_size(n_points));
4112+
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
4113+
}
4114+
}
4115+
41014116
void test_secp256k1_pippenger_bucket_window_inv(void) {
41024117
int i;
41034118

@@ -4277,6 +4292,7 @@ void run_ecmult_multi_tests(void) {
42774292
secp256k1_scratch *scratch;
42784293

42794294
test_ecmult_multi_strauss_scratch_size();
4295+
test_ecmult_multi_strauss_max_points();
42804296
test_secp256k1_pippenger_bucket_window_inv();
42814297
test_ecmult_multi_pippenger_max_points();
42824298
scratch = secp256k1_scratch_create(&ctx->error_callback, 819200);

0 commit comments

Comments
 (0)