Skip to content

Commit 71d5816

Browse files
committed
scratch: replace frames with "checkpoint" system
1 parent 0cd6425 commit 71d5816

File tree

4 files changed

+98
-82
lines changed

4 files changed

+98
-82
lines changed

src/ecmult_impl.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -655,15 +655,13 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
655655
secp256k1_scalar* scalars;
656656
struct secp256k1_strauss_state state;
657657
size_t i;
658+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
658659

659660
secp256k1_gej_set_infinity(r);
660661
if (inp_g_sc == NULL && n_points == 0) {
661662
return 1;
662663
}
663664

664-
if (!secp256k1_scratch_allocate_frame(error_callback, scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
665-
return 0;
666-
}
667665
points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
668666
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
669667
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
@@ -676,16 +674,21 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
676674
#endif
677675
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
678676

677+
if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) {
678+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
679+
return 0;
680+
}
681+
679682
for (i = 0; i < n_points; i++) {
680683
secp256k1_ge point;
681684
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
682-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
685+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
683686
return 0;
684687
}
685688
secp256k1_gej_set_ge(&points[i], &point);
686689
}
687690
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
688-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
691+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
689692
return 1;
690693
}
691694

@@ -988,6 +991,7 @@ static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_windo
988991
}
989992

990993
static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_callback, 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) {
994+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
991995
/* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
992996
* sizes. The reason for +1 is that we add the G scalar to the list of
993997
* other scalars. */
@@ -1012,15 +1016,21 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
10121016
}
10131017

10141018
bucket_window = secp256k1_pippenger_bucket_window(n_points);
1015-
if (!secp256k1_scratch_allocate_frame(error_callback, scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
1016-
return 0;
1017-
}
10181019
points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points));
10191020
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars));
10201021
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space));
1022+
if (points == NULL || scalars == NULL || state_space == NULL) {
1023+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
1024+
return 0;
1025+
}
1026+
10211027
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps));
10221028
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
10231029
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<<bucket_window) * sizeof(*buckets));
1030+
if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) {
1031+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
1032+
return 0;
1033+
}
10241034

10251035
if (inp_g_sc != NULL) {
10261036
scalars[0] = *inp_g_sc;
@@ -1034,7 +1044,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
10341044

10351045
while (point_idx < n_points) {
10361046
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
1037-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
1047+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
10381048
return 0;
10391049
}
10401050
idx++;
@@ -1058,7 +1068,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
10581068
for(i = 0; i < 1<<bucket_window; i++) {
10591069
secp256k1_gej_clear(&buckets[i]);
10601070
}
1061-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
1071+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
10621072
return 1;
10631073
}
10641074

src/scratch.h

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

10-
#define SECP256K1_SCRATCH_MAX_FRAMES 5
11-
1210
/* The typedef is used internally; the struct name is used in the public API
1311
* (where it is exposed as a different typedef) */
1412
typedef struct secp256k1_scratch_space_struct {
13+
/** guard against interpreting this object as other types */
1514
unsigned char magic[8];
15+
/** actual allocated data */
1616
void *data;
17-
void *current_frame;
18-
size_t offset[SECP256K1_SCRATCH_MAX_FRAMES];
19-
size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES];
20-
size_t frame;
17+
/** amount that has been allocated (i.e. `date + offset` is the next
18+
* available pointer) */
19+
size_t alloc_size;
20+
/** maximum size available to allocate */
2121
size_t max_size;
2222
} secp256k1_scratch;
2323

2424
static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size);
2525

2626
static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch);
2727

28-
/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */
29-
static int secp256k1_scratch_allocate_frame(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n, size_t objects);
28+
/** Returns an opaque object used to "checkpoint" a scratch space. Used
29+
* with `secp256k1_scratch_apply_checkpoint` to undo allocations. */
30+
static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch);
3031

31-
/** Deallocates a stack frame */
32-
static void secp256k1_scratch_deallocate_frame(const secp256k1_callback* error_callback, secp256k1_scratch* scratch);
32+
/** Applies a check point received from `secp256k1_scratch_checkpoint`,
33+
* undoing all allocations since that point. */
34+
static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint);
3335

3436
/** Returns the maximum allocation the scratch space will allow */
3537
static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t n_objects);

src/scratch_impl.h

Lines changed: 18 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* err
3030

3131
static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
3232
if (scratch != NULL) {
33-
VERIFY_CHECK(scratch->frame == 0);
33+
VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */
3434
if (memcmp(scratch->magic, "scratch", 8) != 0) {
3535
secp256k1_callback_call(error_callback, "invalid scratch space");
3636
return;
@@ -40,73 +40,52 @@ static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback,
4040
}
4141
}
4242

43-
static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
44-
size_t i = 0;
45-
size_t allocated = 0;
43+
static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) {
4644
if (memcmp(scratch->magic, "scratch", 8) != 0) {
4745
secp256k1_callback_call(error_callback, "invalid scratch space");
4846
return 0;
4947
}
50-
for (i = 0; i < scratch->frame; i++) {
51-
allocated += scratch->frame_size[i];
52-
}
53-
if (scratch->max_size - allocated <= objects * (ALIGNMENT - 1)) {
54-
return 0;
55-
}
56-
return scratch->max_size - allocated - objects * (ALIGNMENT - 1);
48+
return scratch->alloc_size;
5749
}
5850

59-
static int secp256k1_scratch_allocate_frame(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n, size_t objects) {
60-
VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES);
61-
51+
static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) {
6252
if (memcmp(scratch->magic, "scratch", 8) != 0) {
6353
secp256k1_callback_call(error_callback, "invalid scratch space");
64-
return 0;
54+
return;
6555
}
66-
67-
if (n <= secp256k1_scratch_max_allocation(error_callback, scratch, objects)) {
68-
n += objects * (ALIGNMENT - 1);
69-
scratch->current_frame = scratch->data;
70-
scratch->data = (void *) ((char *) scratch->data + n);
71-
scratch->frame_size[scratch->frame] = n;
72-
scratch->offset[scratch->frame] = 0;
73-
scratch->frame++;
74-
return 1;
75-
} else {
76-
return 0;
56+
if (checkpoint > scratch->alloc_size) {
57+
secp256k1_callback_call(error_callback, "invalid checkpoint");
58+
return;
7759
}
60+
scratch->alloc_size = checkpoint;
7861
}
7962

80-
static void secp256k1_scratch_deallocate_frame(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
63+
static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
8164
if (memcmp(scratch->magic, "scratch", 8) != 0) {
8265
secp256k1_callback_call(error_callback, "invalid scratch space");
83-
return;
66+
return 0;
8467
}
85-
86-
VERIFY_CHECK(scratch->frame > 0);
87-
88-
scratch->frame--;
89-
scratch->data = (void *) ((char *) scratch->data - scratch->frame_size[scratch->frame]);
68+
if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) {
69+
return 0;
70+
}
71+
return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1);
9072
}
9173

9274
static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) {
9375
void *ret;
94-
size_t frame;
9576

9677
if (memcmp(scratch->magic, "scratch", 8) != 0) {
9778
secp256k1_callback_call(error_callback, "invalid scratch space");
9879
return NULL;
9980
}
10081

101-
frame = scratch->frame - 1;
10282
size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
103-
104-
if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
83+
if (size > scratch->max_size - scratch->alloc_size) {
10584
return NULL;
10685
}
107-
ret = (void *) ((char *) scratch->current_frame + scratch->offset[frame]);
86+
ret = (void *) ((char *) scratch->data + scratch->alloc_size);
10887
memset(ret, 0, size);
109-
scratch->offset[frame] += size;
88+
scratch->alloc_size += size;
11089

11190
return ret;
11291
}

src/tests.c

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,11 @@ void run_context_tests(void) {
256256
}
257257

258258
void run_scratch_tests(void) {
259+
const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
260+
259261
int32_t ecount = 0;
262+
size_t checkpoint;
263+
size_t checkpoint_2;
260264
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
261265
secp256k1_scratch_space *scratch;
262266
secp256k1_scratch_space local_scratch;
@@ -271,43 +275,54 @@ void run_scratch_tests(void) {
271275

272276
/* Test internal API */
273277
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
274-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) < 1000);
275-
276-
/* Allocating 500 bytes with no frame fails */
277-
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
278-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
278+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1));
279+
CHECK(scratch->alloc_size == 0);
280+
CHECK(scratch->alloc_size % ALIGNMENT == 0);
279281

280-
/* ...but pushing a new stack frame does affect the max allocation */
281-
CHECK(secp256k1_scratch_allocate_frame(&none->error_callback, scratch, 500, 1) == 1);
282-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) < 500); /* 500 - (ALIGNMENT - 1) */
282+
/* Allocating 500 bytes succeeds */
283+
checkpoint = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
283284
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
284-
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
285-
286-
CHECK(secp256k1_scratch_allocate_frame(&none->error_callback, scratch, 500, 1) == 0);
285+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
286+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
287+
CHECK(scratch->alloc_size != 0);
288+
CHECK(scratch->alloc_size % ALIGNMENT == 0);
287289

288-
/* ...and this effect is undone by popping the frame */
289-
secp256k1_scratch_deallocate_frame(&none->error_callback, scratch);
290-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
290+
/* Allocating another 500 bytes fails */
291291
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
292+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
293+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
294+
CHECK(scratch->alloc_size != 0);
295+
CHECK(scratch->alloc_size % ALIGNMENT == 0);
296+
297+
/* ...but it succeeds once we apply the checkpoint to undo it */
298+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
299+
CHECK(scratch->alloc_size == 0);
300+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
301+
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
302+
CHECK(scratch->alloc_size != 0);
292303

293-
/* cleanup */
294-
secp256k1_scratch_space_destroy(none, scratch);
304+
/* try to apply a bad checkpoint */
305+
checkpoint_2 = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
306+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
295307
CHECK(ecount == 0);
308+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */
309+
CHECK(ecount == 1);
310+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */
311+
CHECK(ecount == 2);
296312

297313
/* try to use badly initialized scratch space */
298314
memset(&local_scratch, 0, sizeof(local_scratch));
299315
scratch = &local_scratch;
300316
CHECK(!secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0));
301-
CHECK(ecount == 1);
302-
CHECK(secp256k1_scratch_allocate_frame(&none->error_callback, scratch, 500, 1) == 0);
303-
CHECK(ecount == 2);
304-
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
305317
CHECK(ecount == 3);
306-
secp256k1_scratch_deallocate_frame(&none->error_callback, scratch);
318+
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
307319
CHECK(ecount == 4);
308320
secp256k1_scratch_space_destroy(none, scratch);
309321
CHECK(ecount == 5);
310322

323+
/* cleanup */
324+
secp256k1_scratch_space_destroy(none, scratch);
325+
secp256k1_scratch_space_destroy(none, NULL); /* no-op */
311326
secp256k1_context_destroy(none);
312327
}
313328

@@ -2857,16 +2872,26 @@ void test_ecmult_multi_pippenger_max_points(void) {
28572872
int bucket_window = 0;
28582873

28592874
for(; scratch_size < max_size; scratch_size+=256) {
2875+
size_t i;
2876+
size_t total_alloc;
2877+
size_t checkpoint;
28602878
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
28612879
CHECK(scratch != NULL);
2880+
checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch);
28622881
n_points_supported = secp256k1_pippenger_max_points(&ctx->error_callback, scratch);
28632882
if (n_points_supported == 0) {
28642883
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
28652884
continue;
28662885
}
28672886
bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
2868-
CHECK(secp256k1_scratch_allocate_frame(&ctx->error_callback, scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
2869-
secp256k1_scratch_deallocate_frame(&ctx->error_callback, scratch);
2887+
/* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */
2888+
total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window);
2889+
for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) {
2890+
CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 1));
2891+
total_alloc--;
2892+
}
2893+
CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, total_alloc));
2894+
secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint);
28702895
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
28712896
}
28722897
CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);

0 commit comments

Comments
 (0)