Skip to content

Commit 98836b1

Browse files
committed
scratch: replace frames with "checkpoint" system
1 parent 7623cf2 commit 98836b1

File tree

4 files changed

+98
-80
lines changed

4 files changed

+98
-80
lines changed

src/ecmult_impl.h

+20-10
Original file line numberDiff line numberDiff line change
@@ -653,15 +653,13 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
653653
secp256k1_scalar* scalars;
654654
struct secp256k1_strauss_state state;
655655
size_t i;
656+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
656657

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

662-
if (!secp256k1_scratch_allocate_frame(error_callback, scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) {
663-
return 0;
664-
}
665663
points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
666664
scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
667665
state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
@@ -674,16 +672,21 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
674672
#endif
675673
state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
676674

675+
if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL) {
676+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
677+
return 0;
678+
}
679+
677680
for (i = 0; i < n_points; i++) {
678681
secp256k1_ge point;
679682
if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) {
680-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
683+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
681684
return 0;
682685
}
683686
secp256k1_gej_set_ge(&points[i], &point);
684687
}
685688
secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc);
686-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
689+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
687690
return 1;
688691
}
689692

@@ -986,6 +989,7 @@ static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_windo
986989
}
987990

988991
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) {
992+
const size_t scratch_checkpoint = secp256k1_scratch_checkpoint(error_callback, scratch);
989993
/* Use 2(n+1) with the endomorphism, n+1 without, when calculating batch
990994
* sizes. The reason for +1 is that we add the G scalar to the list of
991995
* other scalars. */
@@ -1010,15 +1014,21 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
10101014
}
10111015

10121016
bucket_window = secp256k1_pippenger_bucket_window(n_points);
1013-
if (!secp256k1_scratch_allocate_frame(error_callback, scratch, secp256k1_pippenger_scratch_size(n_points, bucket_window), PIPPENGER_SCRATCH_OBJECTS)) {
1014-
return 0;
1015-
}
10161017
points = (secp256k1_ge *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*points));
10171018
scalars = (secp256k1_scalar *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*scalars));
10181019
state_space = (struct secp256k1_pippenger_state *) secp256k1_scratch_alloc(error_callback, scratch, sizeof(*state_space));
1020+
if (points == NULL || scalars == NULL || state_space == NULL) {
1021+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
1022+
return 0;
1023+
}
1024+
10191025
state_space->ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(error_callback, scratch, entries * sizeof(*state_space->ps));
10201026
state_space->wnaf_na = (int *) secp256k1_scratch_alloc(error_callback, scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int));
10211027
buckets = (secp256k1_gej *) secp256k1_scratch_alloc(error_callback, scratch, (1<<bucket_window) * sizeof(*buckets));
1028+
if (state_space->ps == NULL || state_space->wnaf_na == NULL || buckets == NULL) {
1029+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
1030+
return 0;
1031+
}
10221032

10231033
if (inp_g_sc != NULL) {
10241034
scalars[0] = *inp_g_sc;
@@ -1032,7 +1042,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
10321042

10331043
while (point_idx < n_points) {
10341044
if (!cb(&scalars[idx], &points[idx], point_idx + cb_offset, cbdata)) {
1035-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
1045+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
10361046
return 0;
10371047
}
10381048
idx++;
@@ -1056,7 +1066,7 @@ static int secp256k1_ecmult_pippenger_batch(const secp256k1_callback* error_call
10561066
for(i = 0; i < 1<<bucket_window; i++) {
10571067
secp256k1_gej_clear(&buckets[i]);
10581068
}
1059-
secp256k1_scratch_deallocate_frame(error_callback, scratch);
1069+
secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
10601070
return 1;
10611071
}
10621072

src/scratch.h

+12-10
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. `data + 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

+18-37
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* err
2525

2626
static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
2727
if (scratch != NULL) {
28-
VERIFY_CHECK(scratch->frame == 0);
28+
VERIFY_CHECK(scratch->alloc_size == 0); /* all checkpoints should be applied */
2929
if (memcmp(scratch->magic, "scratch", 8) != 0) {
3030
secp256k1_callback_call(error_callback, "invalid scratch space");
3131
return;
@@ -35,71 +35,52 @@ static void secp256k1_scratch_destroy(const secp256k1_callback* error_callback,
3535
}
3636
}
3737

38-
static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
39-
size_t i = 0;
40-
size_t allocated = 0;
38+
static size_t secp256k1_scratch_checkpoint(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch) {
4139
if (memcmp(scratch->magic, "scratch", 8) != 0) {
4240
secp256k1_callback_call(error_callback, "invalid scratch space");
4341
return 0;
4442
}
45-
for (i = 0; i < scratch->frame; i++) {
46-
allocated += scratch->frame_size[i];
47-
}
48-
if (scratch->max_size - allocated <= objects * (ALIGNMENT - 1)) {
49-
return 0;
50-
}
51-
return scratch->max_size - allocated - objects * (ALIGNMENT - 1);
43+
return scratch->alloc_size;
5244
}
5345

54-
static int secp256k1_scratch_allocate_frame(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t n, size_t objects) {
55-
VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES);
56-
46+
static void secp256k1_scratch_apply_checkpoint(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t checkpoint) {
5747
if (memcmp(scratch->magic, "scratch", 8) != 0) {
5848
secp256k1_callback_call(error_callback, "invalid scratch space");
59-
return 0;
49+
return;
6050
}
61-
62-
if (n <= secp256k1_scratch_max_allocation(error_callback, scratch, objects)) {
63-
n += objects * (ALIGNMENT - 1);
64-
scratch->current_frame = scratch->data;
65-
scratch->data = (void *) ((char *) scratch->data + n);
66-
scratch->frame_size[scratch->frame] = n;
67-
scratch->offset[scratch->frame] = 0;
68-
scratch->frame++;
69-
return 1;
70-
} else {
71-
return 0;
51+
if (checkpoint > scratch->alloc_size) {
52+
secp256k1_callback_call(error_callback, "invalid checkpoint");
53+
return;
7254
}
55+
scratch->alloc_size = checkpoint;
7356
}
7457

75-
static void secp256k1_scratch_deallocate_frame(const secp256k1_callback* error_callback, secp256k1_scratch* scratch) {
58+
static size_t secp256k1_scratch_max_allocation(const secp256k1_callback* error_callback, const secp256k1_scratch* scratch, size_t objects) {
7659
if (memcmp(scratch->magic, "scratch", 8) != 0) {
7760
secp256k1_callback_call(error_callback, "invalid scratch space");
78-
return;
61+
return 0;
7962
}
80-
81-
VERIFY_CHECK(scratch->frame > 0);
82-
83-
scratch->frame--;
84-
scratch->data = (void *) ((char *) scratch->data - scratch->frame_size[scratch->frame]);
63+
if (scratch->max_size - scratch->alloc_size <= objects * (ALIGNMENT - 1)) {
64+
return 0;
65+
}
66+
return scratch->max_size - scratch->alloc_size - objects * (ALIGNMENT - 1);
8567
}
8668

8769
static void *secp256k1_scratch_alloc(const secp256k1_callback* error_callback, secp256k1_scratch* scratch, size_t size) {
8870
void *ret;
89-
size_t frame = scratch->frame - 1;
9071
size = ROUND_TO_ALIGN(size);
9172

9273
if (memcmp(scratch->magic, "scratch", 8) != 0) {
9374
secp256k1_callback_call(error_callback, "invalid scratch space");
9475
return NULL;
9576
}
9677

97-
if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) {
78+
if (size > scratch->max_size - scratch->alloc_size) {
9879
return NULL;
9980
}
100-
ret = (void *) ((char *) scratch->current_frame + scratch->offset[frame]);
81+
ret = (void *) ((char *) scratch->data + scratch->alloc_size);
10182
memset(ret, 0, size);
102-
scratch->offset[frame] += size;
83+
scratch->alloc_size += size;
10384

10485
return ret;
10586
}

src/tests.c

+48-23
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,11 @@ void run_context_tests(int use_prealloc) {
333333
}
334334

335335
void run_scratch_tests(void) {
336+
const size_t adj_alloc = ((500 + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
337+
336338
int32_t ecount = 0;
339+
size_t checkpoint;
340+
size_t checkpoint_2;
337341
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
338342
secp256k1_scratch_space *scratch;
339343
secp256k1_scratch_space local_scratch;
@@ -348,43 +352,54 @@ void run_scratch_tests(void) {
348352

349353
/* Test internal API */
350354
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
351-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) < 1000);
352-
353-
/* Allocating 500 bytes with no frame fails */
354-
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
355-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
355+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - (ALIGNMENT - 1));
356+
CHECK(scratch->alloc_size == 0);
357+
CHECK(scratch->alloc_size % ALIGNMENT == 0);
356358

357-
/* ...but pushing a new stack frame does affect the max allocation */
358-
CHECK(secp256k1_scratch_allocate_frame(&none->error_callback, scratch, 500, 1) == 1);
359-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) < 500); /* 500 - (ALIGNMENT - 1) */
359+
/* Allocating 500 bytes succeeds */
360+
checkpoint = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
360361
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
361-
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
362-
363-
CHECK(secp256k1_scratch_allocate_frame(&none->error_callback, scratch, 500, 1) == 0);
362+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
363+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
364+
CHECK(scratch->alloc_size != 0);
365+
CHECK(scratch->alloc_size % ALIGNMENT == 0);
364366

365-
/* ...and this effect is undone by popping the frame */
366-
secp256k1_scratch_deallocate_frame(&none->error_callback, scratch);
367-
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
367+
/* Allocating another 500 bytes fails */
368368
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
369+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000 - adj_alloc);
370+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 1) == 1000 - adj_alloc - (ALIGNMENT - 1));
371+
CHECK(scratch->alloc_size != 0);
372+
CHECK(scratch->alloc_size % ALIGNMENT == 0);
373+
374+
/* ...but it succeeds once we apply the checkpoint to undo it */
375+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
376+
CHECK(scratch->alloc_size == 0);
377+
CHECK(secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0) == 1000);
378+
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) != NULL);
379+
CHECK(scratch->alloc_size != 0);
369380

370-
/* cleanup */
371-
secp256k1_scratch_space_destroy(none, scratch);
381+
/* try to apply a bad checkpoint */
382+
checkpoint_2 = secp256k1_scratch_checkpoint(&none->error_callback, scratch);
383+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint);
372384
CHECK(ecount == 0);
385+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, checkpoint_2); /* checkpoint_2 is after checkpoint */
386+
CHECK(ecount == 1);
387+
secp256k1_scratch_apply_checkpoint(&none->error_callback, scratch, (size_t) -1); /* this is just wildly invalid */
388+
CHECK(ecount == 2);
373389

374390
/* try to use badly initialized scratch space */
391+
secp256k1_scratch_space_destroy(none, scratch);
375392
memset(&local_scratch, 0, sizeof(local_scratch));
376393
scratch = &local_scratch;
377394
CHECK(!secp256k1_scratch_max_allocation(&none->error_callback, scratch, 0));
378-
CHECK(ecount == 1);
379-
CHECK(secp256k1_scratch_allocate_frame(&none->error_callback, scratch, 500, 1) == 0);
380-
CHECK(ecount == 2);
381-
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
382395
CHECK(ecount == 3);
383-
secp256k1_scratch_deallocate_frame(&none->error_callback, scratch);
396+
CHECK(secp256k1_scratch_alloc(&none->error_callback, scratch, 500) == NULL);
384397
CHECK(ecount == 4);
385398
secp256k1_scratch_space_destroy(none, scratch);
386399
CHECK(ecount == 5);
387400

401+
/* cleanup */
402+
secp256k1_scratch_space_destroy(none, NULL); /* no-op */
388403
secp256k1_context_destroy(none);
389404
}
390405

@@ -2946,16 +2961,26 @@ void test_ecmult_multi_pippenger_max_points(void) {
29462961
int bucket_window = 0;
29472962

29482963
for(; scratch_size < max_size; scratch_size+=256) {
2964+
size_t i;
2965+
size_t total_alloc;
2966+
size_t checkpoint;
29492967
scratch = secp256k1_scratch_create(&ctx->error_callback, scratch_size);
29502968
CHECK(scratch != NULL);
2969+
checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch);
29512970
n_points_supported = secp256k1_pippenger_max_points(&ctx->error_callback, scratch);
29522971
if (n_points_supported == 0) {
29532972
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
29542973
continue;
29552974
}
29562975
bucket_window = secp256k1_pippenger_bucket_window(n_points_supported);
2957-
CHECK(secp256k1_scratch_allocate_frame(&ctx->error_callback, scratch, secp256k1_pippenger_scratch_size(n_points_supported, bucket_window), PIPPENGER_SCRATCH_OBJECTS));
2958-
secp256k1_scratch_deallocate_frame(&ctx->error_callback, scratch);
2976+
/* allocate `total_alloc` bytes over `PIPPENGER_SCRATCH_OBJECTS` many allocations */
2977+
total_alloc = secp256k1_pippenger_scratch_size(n_points_supported, bucket_window);
2978+
for (i = 0; i < PIPPENGER_SCRATCH_OBJECTS - 1; i++) {
2979+
CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, 1));
2980+
total_alloc--;
2981+
}
2982+
CHECK(secp256k1_scratch_alloc(&ctx->error_callback, scratch, total_alloc));
2983+
secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint);
29592984
secp256k1_scratch_destroy(&ctx->error_callback, scratch);
29602985
}
29612986
CHECK(bucket_window == PIPPENGER_MAX_BUCKET_WINDOW);

0 commit comments

Comments
 (0)