Skip to content

Commit d176205

Browse files
Merge #313: Upstream PR 1518
2cb2e31 extrakeys: Migrate to bitcoin-core/secp256k1#1518 secp256k1_ec_pubkey_sort (DarkWindman) 7d2591c Add secp256k1_pubkey_sort (Jonas Nick) Pull request description: Merge bitcoin-core/secp256k1#1518: Add secp256k1_pubkey_sort This PR can be recreated with `./contrib/sync-upstream.sh -b master range bb528cf`. Tip: Use `git show --remerge-diff` to show the changes manually added to the merge commit. ACKs for top commit: real-or-random: ACK 2cb2e31 Tree-SHA512: dbdb6c5df2195d2ece9574367e0f684a651ea199806a80232c85b0ffd0ba6b930b108bd97385d9fab656754a85fa6de223a93b945046b043aab20ad7bb3d1bff
2 parents ca68d08 + 2cb2e31 commit d176205

File tree

12 files changed

+332
-280
lines changed

12 files changed

+332
-280
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010

1111
## [Unreleased]
1212

13+
#### Added
14+
- New function `secp256k1_ec_pubkey_sort` that sorts public keys using lexicographic (of compressed serialization) order.
15+
1316
#### Changed
1417
- The implementation of the point multiplication algorithm used for signing and public key generation was changed, resulting in improved performance for those operations.
1518
- The related configure option `--ecmult-gen-precision` was replaced with `--ecmult-gen-kb` (`ECMULT_GEN_KB` for CMake).

Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ noinst_HEADERS += src/field.h
6666
noinst_HEADERS += src/field_impl.h
6767
noinst_HEADERS += src/bench.h
6868
noinst_HEADERS += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h
69+
noinst_HEADERS += src/hsort.h
70+
noinst_HEADERS += src/hsort_impl.h
6971
noinst_HEADERS += contrib/lax_der_parsing.h
7072
noinst_HEADERS += contrib/lax_der_parsing.c
7173
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h

include/secp256k1.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,20 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp(
474474
const secp256k1_pubkey *pubkey2
475475
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
476476

477+
/** Sort public keys using lexicographic (of compressed serialization) order
478+
*
479+
* Returns: 0 if the arguments are invalid. 1 otherwise.
480+
*
481+
* Args: ctx: pointer to a context object
482+
* In: pubkeys: array of pointers to pubkeys to sort
483+
* n_pubkeys: number of elements in the pubkeys array
484+
*/
485+
SECP256K1_API int secp256k1_ec_pubkey_sort(
486+
const secp256k1_context *ctx,
487+
const secp256k1_pubkey **pubkeys,
488+
size_t n_pubkeys
489+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
490+
477491
/** Parse an ECDSA signature in compact (64 bytes) format.
478492
*
479493
* Returns: 1 when the signature could be parsed, 0 otherwise.

include/secp256k1_extrakeys.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -240,21 +240,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add
240240
const unsigned char *tweak32
241241
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
242242

243-
/** Sort public keys using lexicographic order of their compressed
244-
* serialization.
245-
*
246-
* Returns: 0 if the arguments are invalid. 1 otherwise.
247-
*
248-
* Args: ctx: pointer to a context object
249-
* In: pubkeys: array of pointers to pubkeys to sort
250-
* n_pubkeys: number of elements in the pubkeys array
251-
*/
252-
SECP256K1_API int secp256k1_pubkey_sort(
253-
const secp256k1_context *ctx,
254-
const secp256k1_pubkey **pubkeys,
255-
size_t n_pubkeys
256-
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
257-
258243
#ifdef __cplusplus
259244
}
260245
#endif

include/secp256k1_musig.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_parse(
186186
*
187187
* Different orders of `pubkeys` result in different `agg_pk`s.
188188
*
189-
* Before aggregating, the pubkeys can be sorted with `secp256k1_pubkey_sort`
189+
* Before aggregating, the pubkeys can be sorted with `secp256k1_ec_pubkey_sort`
190190
* which ensures the same `agg_pk` result for the same multiset of pubkeys.
191191
* This is useful to do before `pubkey_agg`, such that the order of pubkeys
192192
* does not affect the aggregate public key.
Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,19 @@
1313
/* In-place, iterative heapsort with an interface matching glibc's qsort_r. This
1414
* is preferred over standard library implementations because they generally
1515
* make no guarantee about being fast for malicious inputs.
16+
* Remember that heapsort is unstable.
1617
*
17-
* See the qsort_r manpage for a description of the interface.
18+
* In/Out: ptr: pointer to the array to sort. The contents of the array are
19+
* sorted in ascending order according to the comparison function.
20+
* In: count: number of elements in the array.
21+
* size: size in bytes of each element.
22+
* cmp: pointer to a comparison function that is called with two
23+
* arguments that point to the objects being compared. The cmp_data
24+
* argument of secp256k1_hsort is passed as third argument. The
25+
* function must return an integer less than, equal to, or greater
26+
* than zero if the first argument is considered to be respectively
27+
* less than, equal to, or greater than the second.
28+
* cmp_data: pointer passed as third argument to cmp.
1829
*/
1930
static void secp256k1_hsort(void *ptr, size_t count, size_t size,
2031
int (*cmp)(const void *, const void *, void *),
Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,42 @@
1313
* compares as less than or equal to the element at index parent(i) = (i-1)/2.
1414
*/
1515

16-
static SECP256K1_INLINE size_t child1(size_t i) {
16+
static SECP256K1_INLINE size_t secp256k1_heap_child1(size_t i) {
1717
VERIFY_CHECK(i <= (SIZE_MAX - 1)/2);
1818
return 2*i + 1;
1919
}
2020

21-
static SECP256K1_INLINE size_t child2(size_t i) {
21+
static SECP256K1_INLINE size_t secp256k1_heap_child2(size_t i) {
2222
VERIFY_CHECK(i <= SIZE_MAX/2 - 1);
23-
return child1(i)+1;
23+
return secp256k1_heap_child1(i)+1;
2424
}
2525

26-
static SECP256K1_INLINE void heap_swap64(unsigned char *a, size_t i, size_t j, size_t stride) {
26+
static SECP256K1_INLINE void secp256k1_heap_swap64(unsigned char *a, unsigned char *b, size_t len) {
2727
unsigned char tmp[64];
28-
VERIFY_CHECK(stride <= 64);
29-
memcpy(tmp, a + i*stride, stride);
30-
memmove(a + i*stride, a + j*stride, stride);
31-
memcpy(a + j*stride, tmp, stride);
28+
VERIFY_CHECK(len <= 64);
29+
memcpy(tmp, a, len);
30+
memmove(a, b, len);
31+
memcpy(b, tmp, len);
3232
}
3333

34-
static SECP256K1_INLINE void heap_swap(unsigned char *a, size_t i, size_t j, size_t stride) {
35-
while (64 < stride) {
36-
heap_swap64(a + (stride - 64), i, j, 64);
37-
stride -= 64;
34+
static SECP256K1_INLINE void secp256k1_heap_swap(unsigned char *arr, size_t i, size_t j, size_t stride) {
35+
unsigned char *a = arr + i*stride;
36+
unsigned char *b = arr + j*stride;
37+
size_t len = stride;
38+
while (64 < len) {
39+
secp256k1_heap_swap64(a + (len - 64), b + (len - 64), 64);
40+
len -= 64;
3841
}
39-
heap_swap64(a, i, j, stride);
42+
secp256k1_heap_swap64(a, b, len);
4043
}
4144

42-
static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_size, size_t stride,
45+
/* This function accepts an array arr containing heap_size elements, each of
46+
* size stride. The elements in the array at indices >i satisfy the max-heap
47+
* property, i.e., for any element at index j (where j > i), all of its children
48+
* are smaller than the element itself. The purpose of the function is to update
49+
* the array so that all elements at indices >=i satisfy the max-heap
50+
* property. */
51+
static SECP256K1_INLINE void secp256k1_heap_down(unsigned char *arr, size_t i, size_t heap_size, size_t stride,
4352
int (*cmp)(const void *, const void *, void *), void *cmp_data) {
4453
while (i < heap_size/2) {
4554
VERIFY_CHECK(i <= SIZE_MAX/2 - 1);
@@ -50,7 +59,7 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
5059
* 2*i <= SIZE_MAX - 2
5160
*/
5261

53-
VERIFY_CHECK(child1(i) < heap_size);
62+
VERIFY_CHECK(secp256k1_heap_child1(i) < heap_size);
5463
/* Proof:
5564
* i < heap_size/2
5665
* i + 1 <= heap_size/2
@@ -59,7 +68,7 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
5968
* child1(i) < heap_size
6069
*/
6170

62-
/* Let [x] be notation for the contents at a[x*stride].
71+
/* Let [x] be notation for the contents at arr[x*stride].
6372
*
6473
* If [child1(i)] > [i] and [child2(i)] > [i],
6574
* swap [i] with the larger child to ensure the new parent is larger
@@ -68,20 +77,20 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
6877
* Else if [child1(i)] > [i], swap [i] with [child1(i)].
6978
* Else if [child2(i)] > [i], swap [i] with [child2(i)].
7079
*/
71-
if (child2(i) < heap_size
72-
&& 0 <= cmp(a + child2(i)*stride, a + child1(i)*stride, cmp_data)) {
73-
if (0 < cmp(a + child2(i)*stride, a + i*stride, cmp_data)) {
74-
heap_swap(a, i, child2(i), stride);
75-
i = child2(i);
80+
if (secp256k1_heap_child2(i) < heap_size
81+
&& 0 <= cmp(arr + secp256k1_heap_child2(i)*stride, arr + secp256k1_heap_child1(i)*stride, cmp_data)) {
82+
if (0 < cmp(arr + secp256k1_heap_child2(i)*stride, arr + i*stride, cmp_data)) {
83+
secp256k1_heap_swap(arr, i, secp256k1_heap_child2(i), stride);
84+
i = secp256k1_heap_child2(i);
7685
} else {
7786
/* At this point we have [child2(i)] >= [child1(i)] and we have
7887
* [child2(i)] <= [i], and thus [child1(i)] <= [i] which means
7988
* that the next comparison can be skipped. */
8089
return;
8190
}
82-
} else if (0 < cmp(a + child1(i)*stride, a + i*stride, cmp_data)) {
83-
heap_swap(a, i, child1(i), stride);
84-
i = child1(i);
91+
} else if (0 < cmp(arr + secp256k1_heap_child1(i)*stride, arr + i*stride, cmp_data)) {
92+
secp256k1_heap_swap(arr, i, secp256k1_heap_child1(i), stride);
93+
i = secp256k1_heap_child1(i);
8594
} else {
8695
return;
8796
}
@@ -98,18 +107,18 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
98107
/* In-place heap sort. */
99108
static void secp256k1_hsort(void *ptr, size_t count, size_t size,
100109
int (*cmp)(const void *, const void *, void *),
101-
void *cmp_data ) {
110+
void *cmp_data) {
102111
size_t i;
103112

104-
for(i = count/2; 0 < i; --i) {
105-
heap_down(ptr, i-1, count, size, cmp, cmp_data);
113+
for (i = count/2; 0 < i; --i) {
114+
secp256k1_heap_down(ptr, i-1, count, size, cmp, cmp_data);
106115
}
107-
for(i = count; 1 < i; --i) {
116+
for (i = count; 1 < i; --i) {
108117
/* Extract the largest value from the heap */
109-
heap_swap(ptr, 0, i-1, size);
118+
secp256k1_heap_swap(ptr, 0, i-1, size);
110119

111120
/* Repair the heap condition */
112-
heap_down(ptr, 0, i-1, size, cmp, cmp_data);
121+
secp256k1_heap_down(ptr, 0, i-1, size, cmp, cmp_data);
113122
}
114123
}
115124

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
include_HEADERS += include/secp256k1_extrakeys.h
22
noinst_HEADERS += src/modules/extrakeys/tests_impl.h
33
noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h
4-
noinst_HEADERS += src/modules/extrakeys/main_impl.h
5-
noinst_HEADERS += src/modules/extrakeys/hsort.h
6-
noinst_HEADERS += src/modules/extrakeys/hsort_impl.h
4+
noinst_HEADERS += src/modules/extrakeys/main_impl.h

src/modules/extrakeys/main_impl.h

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
#include "../../../include/secp256k1.h"
1111
#include "../../../include/secp256k1_extrakeys.h"
12-
#include "hsort_impl.h"
1312
#include "../../util.h"
1413

1514
static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) {
@@ -283,38 +282,4 @@ int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_ke
283282
return ret;
284283
}
285284

286-
/* This struct wraps a const context pointer to satisfy the secp256k1_hsort api
287-
* which expects a non-const cmp_data pointer. */
288-
typedef struct {
289-
const secp256k1_context *ctx;
290-
} secp256k1_pubkey_sort_cmp_data;
291-
292-
static int secp256k1_pubkey_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) {
293-
return secp256k1_ec_pubkey_cmp(((secp256k1_pubkey_sort_cmp_data*)cmp_data)->ctx,
294-
*(secp256k1_pubkey **)pk1,
295-
*(secp256k1_pubkey **)pk2);
296-
}
297-
298-
int secp256k1_pubkey_sort(const secp256k1_context* ctx, const secp256k1_pubkey **pubkeys, size_t n_pubkeys) {
299-
secp256k1_pubkey_sort_cmp_data cmp_data;
300-
VERIFY_CHECK(ctx != NULL);
301-
ARG_CHECK(pubkeys != NULL);
302-
303-
cmp_data.ctx = ctx;
304-
305-
/* Suppress wrong warning (fixed in MSVC 19.33) */
306-
#if defined(_MSC_VER) && (_MSC_VER < 1933)
307-
#pragma warning(push)
308-
#pragma warning(disable: 4090)
309-
#endif
310-
311-
secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_pubkey_sort_cmp, &cmp_data);
312-
313-
#if defined(_MSC_VER) && (_MSC_VER < 1933)
314-
#pragma warning(pop)
315-
#endif
316-
317-
return 1;
318-
}
319-
320285
#endif

0 commit comments

Comments
 (0)