Skip to content

Commit 6fe5043

Browse files
committed
scratch: add stack frame support
1 parent 9bc2e26 commit 6fe5043

File tree

8 files changed

+92
-76
lines changed

8 files changed

+92
-76
lines changed

include/secp256k1.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,10 @@ SECP256K1_API void secp256k1_context_set_error_callback(
260260
*
261261
* Returns: a newly created scratch space.
262262
* Args: ctx: an existing context object (cannot be NULL)
263-
* In: init_size: initial amount of memory to allocate
264-
* max_size: maximum amount of memory to allocate
263+
* In: max_size: maximum amount of memory to allocate
265264
*/
266265
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
267266
const secp256k1_context* ctx,
268-
size_t init_size,
269267
size_t max_size
270268
) SECP256K1_ARG_NONNULL(1);
271269

src/bench_ecmult.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ int main(int argc, char **argv) {
154154
/* Allocate stuff */
155155
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
156156
scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
157-
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size, scratch_size);
157+
data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
158158
data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
159159
data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
160160
data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);

src/ecmult_impl.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -525,10 +525,9 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, s
525525
return 1;
526526
}
527527

528-
if (!secp256k1_scratch_resize(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
528+
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
529529
return 0;
530530
}
531-
secp256k1_scratch_reset(scratch);
532531
points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej));
533532
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar));
534533
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
@@ -543,10 +542,14 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, s
543542

544543
for (i = 0; i < n_points; i++) {
545544
secp256k1_ge point;
546-
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) return 0;
545+
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
546+
secp256k1_scratch_deallocate_frame(scratch);
547+
return 0;
548+
}
547549
secp256k1_gej_set_ge(&points[i], &point);
548550
}
549551
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
552+
secp256k1_scratch_deallocate_frame(scratch);
550553
return 1;
551554
}
552555

@@ -873,10 +876,9 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
873876
}
874877

875878
bucket_window = secp256k1_pippenger_bucket_window(n_points);
876-
if (!secp256k1_scratch_resize(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
879+
if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
877880
return 0;
878881
}
879-
secp256k1_scratch_reset(scratch);
880882
points = (secp256k1_ge *) secp256k1_scratch_alloc(scratch, entries * sizeof(*points));
881883
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(scratch, entries * sizeof(*scalars));
882884
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(scratch, sizeof(*state_space));
@@ -896,6 +898,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
896898

897899
while (point_idx < n_points) {
898900
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
901+
secp256k1_scratch_deallocate_frame(scratch);
899902
return 0;
900903
}
901904
idx++;
@@ -919,6 +922,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_ecmult_context *ctx,
919922
for(i = 0; i < 1<<bucket_window; i++) {
920923
secp256k1_gej_clear(&buckets[i]);
921924
}
925+
secp256k1_scratch_deallocate_frame(scratch);
922926
return 1;
923927
}
924928

src/scratch.h

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,33 @@
77
#ifndef _SECP256K1_SCRATCH_
88
#define _SECP256K1_SCRATCH_
99

10+
#define SECP256K1_SCRATCH_MAX_FRAMES 5
11+
1012
/* The typedef is used internally; the struct name is used in the public API
1113
* (where it is exposed as a different typedef) */
1214
typedef struct secp256k1_scratch_space_struct {
13-
void *data;
14-
size_t offset;
15-
size_t init_size;
15+
void *data[SECP256K1_SCRATCH_MAX_FRAMES];
16+
size_t offset[SECP256K1_SCRATCH_MAX_FRAMES];
17+
size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES];
18+
size_t frame;
1619
size_t max_size;
1720
const secp256k1_callback* error_callback;
1821
} secp256k1_scratch;
1922

20-
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t init_size, size_t max_size);
23+
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size);
24+
2125
static void secp256k1_scratch_destroy(secp256k1_scratch* scratch);
2226

27+
/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */
28+
static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects);
29+
30+
/** Deallocates a stack frame */
31+
static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch);
32+
2333
/** Returns the maximum allocation the scratch space will allow */
2434
static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects);
2535

26-
/** Attempts to allocate so that there are `n` available bytes. Returns 1 on success, 0 on failure */
27-
static int secp256k1_scratch_resize(secp256k1_scratch* scratch, size_t n, size_t n_objects);
28-
29-
/** Returns a pointer into the scratch space or NULL if there is insufficient available space */
36+
/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */
3037
static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n);
3138

32-
/** Resets the returned pointer to the beginning of space */
33-
static void secp256k1_scratch_reset(secp256k1_scratch* scratch);
34-
3539
#endif

src/scratch_impl.h

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,10 @@
1515
* TODO: Determine this at configure time. */
1616
#define ALIGNMENT 16
1717

18-
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t init_size, size_t max_size) {
18+
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) {
1919
secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret));
2020
if (ret != NULL) {
21-
ret->data = checked_malloc(error_callback, init_size);
22-
if (ret->data == NULL) {
23-
free (ret);
24-
return NULL;
25-
}
26-
ret->offset = 0;
27-
ret->init_size = init_size;
21+
memset(ret, 0, sizeof(*ret));
2822
ret->max_size = max_size;
2923
ret->error_callback = error_callback;
3024
}
@@ -33,45 +27,60 @@ static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* err
3327

3428
static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) {
3529
if (scratch != NULL) {
36-
free(scratch->data);
30+
VERIFY_CHECK(scratch->frame == 0);
3731
free(scratch);
3832
}
3933
}
4034

4135
static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) {
42-
if (scratch->max_size <= objects * ALIGNMENT) {
36+
size_t i = 0;
37+
size_t allocated = 0;
38+
for (i = 0; i < scratch->frame; i++) {
39+
allocated += scratch->frame_size[i];
40+
}
41+
if (scratch->max_size - allocated <= objects * ALIGNMENT) {
4342
return 0;
4443
}
45-
return scratch->max_size - objects * ALIGNMENT;
44+
return scratch->max_size - allocated - objects * ALIGNMENT;
4645
}
4746

48-
static int secp256k1_scratch_resize(secp256k1_scratch* scratch, size_t n, size_t objects) {
49-
n += objects * ALIGNMENT;
50-
if (n > scratch->init_size && n <= scratch->max_size) {
51-
void *tmp = checked_realloc(scratch->error_callback, scratch->data, n);
52-
if (tmp == NULL) {
47+
static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) {
48+
VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES);
49+
50+
if (n <= secp256k1_scratch_max_allocation(scratch, objects)) {
51+
n += objects * ALIGNMENT;
52+
scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n);
53+
if (scratch->data[scratch->frame] == NULL) {
5354
return 0;
5455
}
55-
scratch->init_size = n;
56-
scratch->data = tmp;
56+
scratch->frame_size[scratch->frame] = n;
57+
scratch->offset[scratch->frame] = 0;
58+
scratch->frame++;
59+
return 1;
60+
} else {
61+
return 0;
5762
}
58-
return n <= scratch->max_size;
63+
}
64+
65+
static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) {
66+
VERIFY_CHECK(scratch->frame > 0);
67+
scratch->frame -= 1;
68+
free(scratch->data[scratch->frame]);
5969
}
6070

6171
static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
6272
void *ret;
73+
size_t frame = scratch->frame - 1;
6374
size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
64-
if (size + scratch->offset > scratch->init_size) {
75+
76+
if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
6577
return NULL;
6678
}
67-
ret = (void *) ((unsigned char *) scratch->data + scratch->offset);
79+
ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]);
6880
memset(ret, 0, size);
69-
scratch->offset += size;
70-
return ret;
71-
}
81+
scratch->offset[frame] += size;
7282

73-
static void secp256k1_scratch_reset(secp256k1_scratch* scratch) {
74-
scratch->offset = 0;
83+
return ret;
7584
}
7685

7786
#endif

src/secp256k1.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,9 @@ void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(co
115115
ctx->error_callback.data = data;
116116
}
117117

118-
secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t init_size, size_t max_size) {
118+
secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t max_size) {
119119
VERIFY_CHECK(ctx != NULL);
120-
ARG_CHECK(max_size >= init_size);
121-
122-
return secp256k1_scratch_create(&ctx->error_callback, init_size, max_size);
120+
return secp256k1_scratch_create(&ctx->error_callback, max_size);
123121
}
124122

125123
void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) {

src/tests.c

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -258,28 +258,31 @@ void run_scratch_tests(void) {
258258

259259
/* Test public API */
260260
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
261-
scratch = secp256k1_scratch_space_create(none, 100, 10);
262-
CHECK(scratch == NULL);
263-
CHECK(ecount == 1);
264-
265-
scratch = secp256k1_scratch_space_create(none, 100, 100);
266-
CHECK(scratch != NULL);
267-
CHECK(ecount == 1);
268-
secp256k1_scratch_space_destroy(scratch);
269261

270-
scratch = secp256k1_scratch_space_create(none, 100, 1000);
262+
scratch = secp256k1_scratch_space_create(none, 1000);
271263
CHECK(scratch != NULL);
272-
CHECK(ecount == 1);
264+
CHECK(ecount == 0);
273265

274266
/* Test internal API */
275267
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
276268
CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000);
277-
CHECK(secp256k1_scratch_resize(scratch, 50, 1) == 1); /* no-op */
278-
CHECK(secp256k1_scratch_resize(scratch, 200, 1) == 1);
279-
CHECK(secp256k1_scratch_resize(scratch, 950, 1) == 1);
280-
CHECK(secp256k1_scratch_resize(scratch, 1000, 1) == 0);
281-
CHECK(secp256k1_scratch_resize(scratch, 2000, 1) == 0);
269+
270+
/* Allocating 500 bytes with no frame fails */
271+
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
272+
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
273+
274+
/* ...but pushing a new stack frame does affect the max allocation */
275+
CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1 == 1));
276+
CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 500); /* 500 - ALIGNMENT */
277+
CHECK(secp256k1_scratch_alloc(scratch, 500) != NULL);
278+
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
279+
280+
CHECK(secp256k1_scratch_allocate_frame(scratch, 500, 1) == 0);
281+
282+
/* ...and this effect is undone by popping the frame */
283+
secp256k1_scratch_deallocate_frame(scratch);
282284
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
285+
CHECK(secp256k1_scratch_alloc(scratch, 500) == NULL);
283286

284287
/* cleanup */
285288
secp256k1_scratch_space_destroy(scratch);
@@ -2558,7 +2561,6 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
25582561
data.sc = sc;
25592562
data.pt = pt;
25602563
secp256k1_scalar_set_int(&szero, 0);
2561-
secp256k1_scratch_reset(scratch);
25622564

25632565
/* No points to multiply */
25642566
CHECK(ecmult_multi(&ctx->ecmult_ctx, scratch, &r, NULL, ecmult_multi_callback, &data, 0));
@@ -2590,7 +2592,7 @@ void test_ecmult_multi(secp256k1_scratch *scratch, secp256k1_ecmult_multi_func e
25902592
CHECK(secp256k1_gej_is_infinity(&r));
25912593

25922594
/* Try to multiply 1 point, but scratch space is empty */
2593-
scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0, 0);
2595+
scratch_empty = secp256k1_scratch_create(&ctx->error_callback, 0);
25942596
CHECK(!ecmult_multi(&ctx->ecmult_ctx, scratch_empty, &r, &szero, ecmult_multi_callback, &data, 1));
25952597
secp256k1_scratch_destroy(scratch_empty);
25962598

@@ -2816,15 +2818,16 @@ void test_ecmult_multi_pippenger_max_points(void) {
28162818
int bucket_window = 0;
28172819

28182820
for(; scratch_size < max_size; scratch_size+=256) {
2819-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size);
2821+
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
28202822
CHECK(scratch != NULL);
28212823
n_points_supported = secp256k1_pippenger_max_points(scratch);
28222824
if (n_points_supported == 0) {
28232825
secp256k1_scratch_destroy(scratch);
28242826
continue;
28252827
}
28262828
bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
2827-
CHECK(secp256k1_scratch_resize(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
2829+
CHECK(secp256k1_scratch_allocate_frame(scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
2830+
secp256k1_scratch_deallocate_frame(scratch);
28282831
secp256k1_scratch_destroy(scratch);
28292832
}
28302833
CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);
@@ -2866,13 +2869,13 @@ void test_ecmult_multi_batching(void) {
28662869
data.pt = pt;
28672870

28682871
/* Test with empty scratch space */
2869-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, 0);
2872+
scratch = secp256k1_scratch_create(&ctx->error_callback, 0);
28702873
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
28712874
secp256k1_scratch_destroy(scratch);
28722875

28732876
/* Test with space for 1 point in pippenger. That's not enough because
28742877
* ecmult_multi selects strauss which requires more memory. */
2875-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
2878+
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_pippenger_scratch_size(1, 1) + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
28762879
CHECK(!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, 1));
28772880
secp256k1_scratch_destroy(scratch);
28782881

@@ -2881,10 +2884,10 @@ void test_ecmult_multi_batching(void) {
28812884
if (i > ECMULT_PIPPENGER_THRESHOLD) {
28822885
int bucket_window = secp256k1_pippenger_bucket_window(i);
28832886
size_t scratch_size = secp256k1_pippenger_scratch_size(i, bucket_window);
2884-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
2887+
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + PIPPENGER_SCRATCH_OBJECTS*ALIGNMENT);
28852888
} else {
28862889
size_t scratch_size = secp256k1_strauss_scratch_size(i);
2887-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
2890+
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
28882891
}
28892892
CHECK(secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &r, &scG, ecmult_multi_callback, &data, n_points));
28902893
secp256k1_gej_add_var(&r, &r, &r2, NULL);
@@ -2900,14 +2903,14 @@ void run_ecmult_multi_tests(void) {
29002903

29012904
test_secp256k1_pippenger_bucket_window_inv();
29022905
test_ecmult_multi_pippenger_max_points();
2903-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, 819200);
2906+
scratch = secp256k1_scratch_create(&ctx->error_callback, 819200);
29042907
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
29052908
test_ecmult_multi(scratch, secp256k1_ecmult_pippenger_batch_single);
29062909
test_ecmult_multi(scratch, secp256k1_ecmult_strauss_batch_single);
29072910
secp256k1_scratch_destroy(scratch);
29082911

29092912
/* Run test_ecmult_multi with space for exactly one point */
2910-
scratch = secp256k1_scratch_create(&ctx->error_callback, 0, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
2913+
scratch = secp256k1_scratch_create(&ctx->error_callback, secp256k1_strauss_scratch_size(1) + STRAUSS_SCRATCH_OBJECTS*ALIGNMENT);
29112914
test_ecmult_multi(scratch, secp256k1_ecmult_multi_var);
29122915
secp256k1_scratch_destroy(scratch);
29132916

src/tests_exhaustive.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ static int ecmult_multi_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t
196196

197197
void test_exhaustive_ecmult_multi(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
198198
int i, j, k, x, y;
199-
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 1024, 4096);
199+
secp256k1_scratch *scratch = secp256k1_scratch_create(&ctx->error_callback, 4096);
200200
for (i = 0; i < order; i++) {
201201
for (j = 0; j < order; j++) {
202202
for (k = 0; k < order; k++) {

0 commit comments

Comments
 (0)