]> Git Repo - secp256k1.git/commitdiff
add `secp256k1_xonly_pubkey_cmp` method
authorAndrew Poelstra <[email protected]>
Mon, 30 Nov 2020 18:42:32 +0000 (18:42 +0000)
committerAndrew Poelstra <[email protected]>
Thu, 6 May 2021 18:36:44 +0000 (18:36 +0000)
include/secp256k1_extrakeys.h
src/modules/extrakeys/main_impl.h
src/modules/extrakeys/tests_impl.h

index 6fc7b290f8cae62afaaf9e81b60ecc29a9341186..0a37fb6b9d31809fed5e674c2e035d80ea2db7d8 100644 (file)
@@ -15,9 +15,9 @@ extern "C" {
  *  The exact representation of data inside is implementation defined and not
  *  guaranteed to be portable between different platforms or versions. It is
  *  however guaranteed to be 64 bytes in size, and can be safely copied/moved.
  *  The exact representation of data inside is implementation defined and not
  *  guaranteed to be portable between different platforms or versions. It is
  *  however guaranteed to be 64 bytes in size, and can be safely copied/moved.
- *  If you need to convert to a format suitable for storage, transmission, or
- *  comparison, use secp256k1_xonly_pubkey_serialize and
- *  secp256k1_xonly_pubkey_parse.
+ *  If you need to convert to a format suitable for storage, transmission, use
+ *  use secp256k1_xonly_pubkey_serialize and secp256k1_xonly_pubkey_parse. To
+ *  compare keys, use secp256k1_xonly_pubkey_cmp.
  */
 typedef struct {
     unsigned char data[64];
  */
 typedef struct {
     unsigned char data[64];
@@ -67,6 +67,21 @@ SECP256K1_API int secp256k1_xonly_pubkey_serialize(
     const secp256k1_xonly_pubkey* pubkey
 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
 
     const secp256k1_xonly_pubkey* pubkey
 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
 
+/** Compare two x-only public keys using lexicographic order
+ *
+ *  Returns: <0 if the first public key is less than the second
+ *           >0 if the first public key is greater than the second
+ *           0 if the two public keys are equal
+ *  Args: ctx:      a secp256k1 context object.
+ *  In:   pubkey1:  first public key to compare
+ *        pubkey2:  second public key to compare
+ */
+SECP256K1_API int secp256k1_xonly_pubkey_cmp(
+    const secp256k1_context* ctx,
+    const secp256k1_xonly_pubkey* pk1,
+    const secp256k1_xonly_pubkey* pk2
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
 /** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
  *
  *  Returns: 1 if the public key was successfully converted
 /** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
  *
  *  Returns: 1 if the public key was successfully converted
index 7390b227182fe33181d111b580be47100d5558eb..c052e29a859492702622525bc307230d4672b2a2 100644 (file)
@@ -55,6 +55,32 @@ int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char
     return 1;
 }
 
     return 1;
 }
 
+int secp256k1_xonly_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_xonly_pubkey* pk0, const secp256k1_xonly_pubkey* pk1) {
+    unsigned char out[2][32];
+    const secp256k1_xonly_pubkey* pk[2];
+    int i;
+
+    VERIFY_CHECK(ctx != NULL);
+    pk[0] = pk0; pk[1] = pk1;
+    for (i = 0; i < 2; i++) {
+        /* If the public key is NULL or invalid, xonly_pubkey_serialize will
+         * call the illegal_callback and return 0. In that case we will
+         * serialize the key as all zeros which is less than any valid public
+         * key. This results in consistent comparisons even if NULL or invalid
+         * pubkeys are involved and prevents edge cases such as sorting
+         * algorithms that use this function and do not terminate as a
+         * result. */
+        if (!secp256k1_xonly_pubkey_serialize(ctx, out[i], pk[i])) {
+            /* Note that xonly_pubkey_serialize should already set the output to
+             * zero in that case, but it's not guaranteed by the API, we can't
+             * test it and writing a VERIFY_CHECK is more complex than
+             * explicitly memsetting (again). */
+            memset(out[i], 0, sizeof(out[i]));
+        }
+    }
+    return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1]));
+}
+
 /** Keeps a group element as is if it has an even Y and otherwise negates it.
  *  y_parity is set to 0 in the former case and to 1 in the latter case.
  *  Requires that the coordinates of r are normalized. */
 /** Keeps a group element as is if it has an even Y and otherwise negates it.
  *  y_parity is set to 0 in the former case and to 1 in the latter case.
  *  Requires that the coordinates of r are normalized. */
index 9473a7dd4852e66f2ad7b30adbc10b41fabb4bb1..25e42f68e15ba0a8a8a8e283bb489fdd3c627965 100644 (file)
@@ -137,6 +137,43 @@ void test_xonly_pubkey(void) {
     secp256k1_context_destroy(verify);
 }
 
     secp256k1_context_destroy(verify);
 }
 
+void test_xonly_pubkey_comparison(void) {
+    unsigned char pk1_ser[32] = {
+        0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11,
+        0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23
+    };
+    const unsigned char pk2_ser[32] = {
+        0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d,
+        0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c
+    };
+    secp256k1_xonly_pubkey pk1;
+    secp256k1_xonly_pubkey pk2;
+    int ecount = 0;
+    secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+
+    CHECK(secp256k1_xonly_pubkey_parse(none, &pk1, pk1_ser) == 1);
+    CHECK(secp256k1_xonly_pubkey_parse(none, &pk2, pk2_ser) == 1);
+
+    CHECK(secp256k1_xonly_pubkey_cmp(none, NULL, &pk2) < 0);
+    CHECK(ecount == 1);
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, NULL) > 0);
+    CHECK(ecount == 2);
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk2) < 0);
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk2, &pk1) > 0);
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk1) == 0);
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk2, &pk2) == 0);
+    CHECK(ecount == 2);
+    memset(&pk1, 0, sizeof(pk1)); /* illegal pubkey */
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk2) < 0);
+    CHECK(ecount == 3);
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk1) == 0);
+    CHECK(ecount == 5);
+    CHECK(secp256k1_xonly_pubkey_cmp(none, &pk2, &pk1) > 0);
+    CHECK(ecount == 6);
+
+    secp256k1_context_destroy(none);
+}
+
 void test_xonly_pubkey_tweak(void) {
     unsigned char zeros64[64] = { 0 };
     unsigned char overflows[32];
 void test_xonly_pubkey_tweak(void) {
     unsigned char zeros64[64] = { 0 };
     unsigned char overflows[32];
@@ -540,6 +577,7 @@ void run_extrakeys_tests(void) {
     test_xonly_pubkey_tweak();
     test_xonly_pubkey_tweak_check();
     test_xonly_pubkey_tweak_recursive();
     test_xonly_pubkey_tweak();
     test_xonly_pubkey_tweak_check();
     test_xonly_pubkey_tweak_recursive();
+    test_xonly_pubkey_comparison();
 
     /* keypair tests */
     test_keypair();
 
     /* keypair tests */
     test_keypair();
This page took 0.030354 seconds and 4 git commands to generate.