-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial PublicKey Combine API #439
Conversation
PR Summary
This pull request is designed to enhance our existing functionality by instituting improved key management and facilitating easier and more efficient public key combinations. |
First of all, thank you so much for putting this together! Super useful for what I am trying to build. When I try to test combining two public keys, whenever it does work, it returns a different result than the python wrapper for secp256k1, although I was careful to make sure of the right order and the fact that the python implementation only combines the keys within the list/array and not the key the function was called on. This is how I tested in Swift: func test() {
let publicKey = hashToCurve(message: "test1")
let publicKey2 = hashToCurve(message: "test2")
print("1: " + bytesToHexString(bytes: publicKey.dataRepresentation))
print("2: " + bytesToHexString(bytes: publicKey2.dataRepresentation))
let new:secp256k1.Signing.PublicKey
do {
new = try publicKey.combine([publicKey2], format: .compressed)
print("RESULT " + bytesToHexString(bytes: new.dataRepresentation))
} catch {
print(error)
}
} 1: 021b4f0e9851971998e732078544c96b36c3d01cedf7caa332359d6f1d83567014
2: 0260303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752
RESULT 034662b7e2fd30907601f3b16439ddb636792df7166ff0fe88275965eba37c5406 And this is the Python version: point1 = hash_to_curve("test1".encode("utf-8"))
point2 = hash_to_curve("test2".encode("utf-8"))
print("1: " + point1.serialize().hex())
print("2: " + point2.serialize().hex())
new = PublicKey()
new.combine([point1.public_key, point2.public_key])
print("RESULT " + new.serialize().hex()) 1: 021b4f0e9851971998e732078544c96b36c3d01cedf7caa332359d6f1d83567014
2: 0260303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752
RESULT 03d6a3a9d62c7650fcac18f9ee68c7a004ebad71b7581b683062213ad9f37ddb28 Notice how the public keys 1 & 2 are equal, but the result does not match. |
This is great feedback, thank you! 😊 I'll push up some small changes that should help figure out what's going wrong with the current implementation. |
@zeugmaster I was able to re-create the issue you described, my first thought is the |
@csjones I've tried a few things in the meantime to narrow down the potential causes and would love to hear what you think about them:
func simpleCombine(format: secp256k1.Format = .compressed) {
let context = secp256k1.Context.rawRepresentation
var publicKey = secp256k1_pubkey()
var pubKeyLen = format.length
var pubBytes = [UInt8](repeating: 0, count: pubKeyLen)
let publicKey1 = try! secp256k1.Signing.PublicKey(dataRepresentation: "021b4f0e9851971998e732078544c96b36c3d01cedf7caa332359d6f1d83567014".bytes, format: .compressed)
let publicKey2 = try! secp256k1.Signing.PublicKey(dataRepresentation: "0260303ae22b998861bce3b28f33eec1be758a213c86c93c076dbe9f558c11c752".bytes, format: .compressed)
print(publicKey1)
print(publicKey2)
var newPubKey1 = secp256k1_pubkey()
publicKey1.dataRepresentation.copyToUnsafeMutableBytes(of: &newPubKey1.data)
let pointerKey1: UnsafePointer<secp256k1_pubkey>? = withUnsafePointer(to: &newPubKey1) { $0 }
var newPubKey2 = secp256k1_pubkey()
publicKey2.dataRepresentation.copyToUnsafeMutableBytes(of: &newPubKey2.data)
let pointerKey2: UnsafePointer<secp256k1_pubkey>? = withUnsafePointer(to: &newPubKey2) { $0 }
let keys = [pointerKey1, pointerKey2]
print(keys)
let ret1 = secp256k1_ec_pubkey_combine(context, &publicKey, keys, 2)
print(ret1) // prints 1, according to docs the return value for valid key combination
print(publicKey) // prints valid secp256k1_pubkey but does not match what the python implementation produces
let ret2 = secp256k1_ec_pubkey_serialize(context, &pubBytes, &pubKeyLen, &publicKey, format.rawValue)
print(ret2) // prints 1
print(pubBytes)
}
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "/opt/homebrew/Cellar/secp256k1/0.4.0/include/secp256k1.h"
int main() {
// Initialize the secp256k1 context
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
// Two static secp256k1 public keys in serialized form (compressed format, 33 bytes)
unsigned char pubKey1[] = {
0x02, 0x1b, 0x4f, 0x0e, 0x98, 0x51, 0x97, 0x19, 0x98, 0xe7, 0x32, 0x07, 0x85, 0x44, 0xc9, 0x6b,
0x36, 0xc3, 0xd0, 0x1c, 0xed, 0xf7, 0xca, 0xa3, 0x32, 0x35, 0x9d, 0x6f, 0x1d, 0x83, 0x56, 0x70, 0x14
};
unsigned char pubKey2[] = {
0x02, 0x60, 0x30, 0x3a, 0xe2, 0x2b, 0x99, 0x88, 0x61, 0xbc, 0xe3, 0xb2, 0x8f, 0x33, 0xee, 0xc1,
0xbe, 0x75, 0x8a, 0x21, 0x3c, 0x86, 0xc9, 0x3c, 0x07, 0x6d, 0xbe, 0x9f, 0x55, 0x8c, 0x11, 0xc7, 0x52
};
// Parse the public keys
secp256k1_pubkey pubkey1, pubkey2;
int ret1 = secp256k1_ec_pubkey_parse(ctx, &pubkey1, pubKey1, sizeof(pubKey1));
int ret2 = secp256k1_ec_pubkey_parse(ctx, &pubkey2, pubKey2, sizeof(pubKey2));
assert(ret1 == 1);
assert(ret2 == 1);
// Combine the two public keys
const secp256k1_pubkey *pubkeys[] = { &pubkey1, &pubkey2 };
secp256k1_pubkey combined_pubkey;
int ret_combine = secp256k1_ec_pubkey_combine(ctx, &combined_pubkey, pubkeys, 2);
assert(ret_combine == 1);
// Serialize the combined public key
unsigned char combined_serialized_pubkey[33];
size_t combined_pubkey_len = sizeof(combined_serialized_pubkey);
int ret_serialize = secp256k1_ec_pubkey_serialize(ctx, combined_serialized_pubkey, &combined_pubkey_len, &combined_pubkey, SECP256K1_EC_COMPRESSED);
assert(ret_serialize == 1);
// Do something with the combined public key, like printing it out
printf("Combined Public Key: ");
for (size_t i = 0; i < combined_pubkey_len; i++) {
printf("%02x", combined_serialized_pubkey[i]);
}
printf("\n");
// Destroy the secp256k1 context
secp256k1_context_destroy(ctx);
return 0;
} After many hours of tinkering and staring at results at byte-level 🤦♂️ I am once again at my wits' end. 😅 |
Hey @zeugmaster, this information was super helpful, thank you! I've spent time recently playing with the code you shared and confirmed it's not the Now, I am confident this code ( var keys = allPubKeys.map {
var newPubKey = secp256k1_pubkey()
$0.dataRepresentation.copyToUnsafeMutableBytes(of: &newPubKey.data)
let pointerKey: UnsafePointer<secp256k1_pubkey>? = withUnsafePointer(to: &newPubKey) { $0 }
return pointerKey
} I pushed a new unit test just using the binding but replacing |
Hey @zeugmaster, pushed an update were the Combine API works! Let me know what your thoughts are 😁 |
@csjones Works perfecty now! |
You're welcome, I am thrilled to hear that the API is working perfectly for you now! 🎉 No need to worry about owing me a beer or coffee, but if we do cross paths, it would be great to connect and talk Bitcoin! 😄 I appreciate the great feedback you gave, keep up the great work! 👏🚀 |
Adding Swift APIs for based on
secp256k1_ec_pubkey_combine
#397TODO:
Swift API Usage: