Implement musig2

This commit is contained in:
sstone 2023-12-12 10:02:27 +01:00
parent 3a983128c2
commit 951c843e27
No known key found for this signature in database
GPG Key ID: 7A73FE77DE2C4027
12 changed files with 1029 additions and 9 deletions

View File

@ -22,7 +22,7 @@ buildscript {
allprojects { allprojects {
group = "fr.acinq.secp256k1" group = "fr.acinq.secp256k1"
version = "0.13.0-SNAPSHOT" version = "0.13.0-MUSIG2-SNAPSHOT"
repositories { repositories {
google() google()

View File

@ -7,6 +7,34 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_CONTEXT
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_CONTEXT 1L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_COMPRESSION
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_TYPE_COMPRESSION 2L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_VERIFY
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_VERIFY 256L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_SIGN
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_CONTEXT_SIGN 512L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_COMPRESSION
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_FLAGS_BIT_COMPRESSION 256L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_VERIFY
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_VERIFY 257L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_SIGN
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_SIGN 513L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_NONE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_CONTEXT_NONE 1L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_COMPRESSED
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_COMPRESSED 258L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_UNCOMPRESSED
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_EC_UNCOMPRESSED 2L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE 66L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE 132L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE 197L
#undef fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE
#define fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE 133L
/* /*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions * Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_context_create * Method: secp256k1_context_create
@ -167,6 +195,78 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1verify JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1schnorrsig_1verify
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray); (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_gen
* Signature: (J[B[B[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1gen
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_agg
* Signature: (J[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1agg
(JNIEnv *, jclass, jlong, jobjectArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_agg
* Signature: (J[[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1agg
(JNIEnv *, jclass, jlong, jobjectArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_ec_tweak_add
* Signature: (J[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1ec_1tweak_1add
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_xonly_tweak_add
* Signature: (J[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1xonly_1tweak_1add
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_process
* Signature: (J[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1process
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sign
* Signature: (J[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sign
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_verify
* Signature: (J[B[B[B[B[B)I
*/
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1verify
(JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_agg
* Signature: (J[B[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1agg
(JNIEnv *, jclass, jlong, jbyteArray, jobjectArray);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -8,6 +8,7 @@
#include "include/secp256k1_ecdh.h" #include "include/secp256k1_ecdh.h"
#include "include/secp256k1_recovery.h" #include "include/secp256k1_recovery.h"
#include "include/secp256k1_schnorrsig.h" #include "include/secp256k1_schnorrsig.h"
#include "include/secp256k1_musig.h"
#include "fr_acinq_secp256k1_Secp256k1CFunctions.h" #include "fr_acinq_secp256k1_Secp256k1CFunctions.h"
#define SIG_FORMAT_UNKNOWN 0 #define SIG_FORMAT_UNKNOWN 0
@ -803,3 +804,537 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1sc
(*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0); (*penv)->ReleaseByteArrayElements(penv, jmsg, msg, 0);
return result; return result;
} }
static void copy_bytes_from_java(JNIEnv *penv, jbyteArray source, size_t size, unsigned char *dest)
{
jbyte *ptr = NULL;
if (source == NULL)
return; // nothing to do
ptr = (*penv)->GetByteArrayElements(penv, source, 0);
memcpy(dest, ptr, size);
(*penv)->ReleaseByteArrayElements(penv, source, ptr, 0);
}
static void copy_bytes_to_java(JNIEnv *penv, jbyteArray dest, size_t size, unsigned char *source)
{
jbyte *ptr = (*penv)->GetByteArrayElements(penv, dest, 0);
memcpy(ptr, source, size);
(*penv)->ReleaseByteArrayElements(penv, dest, ptr, 0);
}
// session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_gen
* Signature: (J[B[B[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1gen(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsession_id32, jbyteArray jseckey, jbyteArray jpubkey, jbyteArray jmsg32, jbyteArray jkeyaggcache, jbyteArray jextra_input32)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
int result = 0;
size_t size;
secp256k1_musig_pubnonce pubnonce;
secp256k1_musig_secnonce secnonce;
unsigned char session_id32[32];
jbyte *pubkey_ptr;
secp256k1_pubkey pubkey;
unsigned char seckey[32];
unsigned char msg32[32];
secp256k1_musig_keyagg_cache keyaggcache;
unsigned char extra_input32[32];
jbyteArray jnonce;
jbyte *nonce_ptr = NULL;
unsigned char nonce[fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE + fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE];
if (jctx == 0)
return NULL;
if (jsession_id32 == 0)
return NULL;
size = (*penv)->GetArrayLength(penv, jsession_id32);
CHECKRESULT(size != 32, "invalid session_id size");
copy_bytes_from_java(penv, jsession_id32, size, session_id32);
if (jseckey != NULL)
{
size = (*penv)->GetArrayLength(penv, jseckey);
CHECKRESULT(size != 32, "invalid session_id size");
copy_bytes_from_java(penv, jseckey, size, seckey);
}
if (jpubkey == NULL)
return NULL;
size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT((size != 33) && (size != 65), "invalid public key size");
pubkey_ptr = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, (unsigned char *)pubkey_ptr, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pubkey_ptr, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed");
if (jmsg32 != NULL)
{
size = (*penv)->GetArrayLength(penv, jmsg32);
CHECKRESULT(size != 32, "invalid message size");
copy_bytes_from_java(penv, jmsg32, size, msg32);
}
if (jkeyaggcache != NULL)
{
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
}
if (jextra_input32 != NULL)
{
size = (*penv)->GetArrayLength(penv, jextra_input32);
CHECKRESULT(size != 32, "invalid extra input size");
copy_bytes_from_java(penv, jextra_input32, size, extra_input32);
}
result = secp256k1_musig_nonce_gen(ctx, &secnonce, &pubnonce, session_id32,
jseckey == NULL ? NULL : seckey, &pubkey,
jmsg32 == NULL ? NULL : msg32, jkeyaggcache == NULL ? NULL : &keyaggcache, jextra_input32 == NULL ? NULL : extra_input32);
CHECKRESULT(!result, "secp256k1_musig_nonce_gen failed");
memcpy(nonce, secnonce.data, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE);
result = secp256k1_musig_pubnonce_serialize(ctx, nonce + fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE, &pubnonce);
CHECKRESULT(!result, "secp256k1_musig_pubnonce_serialize failed");
jnonce = (*penv)->NewByteArray(penv, sizeof(nonce));
nonce_ptr = (*penv)->GetByteArrayElements(penv, jnonce, 0);
memcpy(nonce_ptr, nonce, sizeof(nonce));
(*penv)->ReleaseByteArrayElements(penv, jnonce, nonce_ptr, 0);
return jnonce;
}
void free_nonces(secp256k1_musig_pubnonce **nonces, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
{
if (nonces[i] != NULL)
free(nonces[i]);
}
free(nonces);
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_agg
* Signature: (J[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1agg(JNIEnv *penv, jclass clazz, jlong jctx, jobjectArray jnonces)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *in66;
secp256k1_musig_pubnonce **pubnonces;
secp256k1_musig_aggnonce combined;
jbyteArray jnonce;
size_t size, count;
size_t i;
int result = 0;
if (jctx == 0)
return NULL;
if (jnonces == NULL)
return NULL;
count = (*penv)->GetArrayLength(penv, jnonces);
CHECKRESULT(count <= 0, "public nonces count cannot be 0");
pubnonces = calloc(count, sizeof(secp256k1_musig_pubnonce *));
for (i = 0; i < count; i++)
{
pubnonces[i] = calloc(1, sizeof(secp256k1_musig_pubnonce));
jnonce = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jnonces, i);
size = (*penv)->GetArrayLength(penv, jnonce);
CHECKRESULT1(size != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE, "invalid public nonce size", free_nonces(pubnonces, count));
in66 = (*penv)->GetByteArrayElements(penv, jnonce, 0);
result = secp256k1_musig_pubnonce_parse(ctx, pubnonces[i], (unsigned char *)in66);
(*penv)->ReleaseByteArrayElements(penv, jnonce, in66, 0);
CHECKRESULT1(!result, "secp256k1_musig_pubnonce_parse failed", free_nonces(pubnonces, count));
}
result = secp256k1_musig_nonce_agg(ctx, &combined, (const secp256k1_musig_pubnonce *const *)pubnonces, count);
free_nonces(pubnonces, count);
CHECKRESULT(!result, "secp256k1_musig_nonce_agg failed");
jnonce = (*penv)->NewByteArray(penv, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE);
in66 = (*penv)->GetByteArrayElements(penv, jnonce, 0);
result = secp256k1_musig_aggnonce_serialize(ctx, (unsigned char *)in66, &combined);
(*penv)->ReleaseByteArrayElements(penv, jnonce, in66, 0);
CHECKRESULT(!result, "secp256k1_musig_aggnonce_serialize failed");
return jnonce;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_agg
* Signature: (J[[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1agg(JNIEnv *penv, jclass clazz, jlong jctx, jobjectArray jpubkeys, jbyteArray jkeyaggcache)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *pub;
secp256k1_pubkey **pubkeys;
secp256k1_xonly_pubkey combined;
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpubkey;
size_t size, count;
size_t i;
int result = 0;
if (jctx == 0)
return NULL;
if (jpubkeys == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jpubkeys) <= 0, "pubkeys count cannot be 0");
if (jkeyaggcache != NULL)
{
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
}
count = (*penv)->GetArrayLength(penv, jpubkeys);
pubkeys = calloc(count, sizeof(secp256k1_pubkey *));
for (i = 0; i < count; i++)
{
pubkeys[i] = calloc(1, sizeof(secp256k1_pubkey));
jpubkey = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jpubkeys, i);
size = (*penv)->GetArrayLength(penv, jpubkey);
CHECKRESULT1((size != 33) && (size != 65), "invalid public key size", free_pubkeys(pubkeys, count));
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, pubkeys[i], (unsigned char *)pub, size);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT1(!result, "secp256k1_ec_pubkey_parse failed", free_pubkeys(pubkeys, count));
}
result = secp256k1_musig_pubkey_agg(ctx, &combined, jkeyaggcache == NULL ? NULL : &keyaggcache, (const secp256k1_pubkey *const *)pubkeys, count);
free_pubkeys(pubkeys, count);
CHECKRESULT(!result, "secp256k1_musig_pubkey_agg failed");
size = 32;
jpubkey = (*penv)->NewByteArray(penv, 32);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_xonly_pubkey_serialize(ctx, (unsigned char *)pub, &combined);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_xonly_pubkey_serialize failed");
if (jkeyaggcache != NULL)
{
pub = (*penv)->GetByteArrayElements(penv, jkeyaggcache, 0);
memcpy(pub, keyaggcache.data, sizeof(secp256k1_musig_keyagg_cache));
(*penv)->ReleaseByteArrayElements(penv, jkeyaggcache, pub, 0);
}
return jpubkey;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_ec_tweak_add
* Signature: (J[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1ec_1tweak_1add(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jkeyaggcache, jbyteArray jtweak32)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *tweak32, *pub;
secp256k1_pubkey pubkey;
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpubkey;
size_t size;
int result = 0;
if (jctx == 0)
return NULL;
if (jkeyaggcache == NULL)
return NULL;
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
if (jtweak32 == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak32) != 32, "tweak must be 32 bytes");
tweak32 = (*penv)->GetByteArrayElements(penv, jtweak32, 0);
result = secp256k1_musig_pubkey_ec_tweak_add(ctx, &pubkey, &keyaggcache, tweak32);
(*penv)->ReleaseByteArrayElements(penv, jtweak32, tweak32, 0);
CHECKRESULT(!result, "secp256k1_musig_pubkey_ec_tweak_add failed");
jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
size = 65;
result = secp256k1_ec_pubkey_serialize(ctx, pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
pub = (*penv)->GetByteArrayElements(penv, jkeyaggcache, 0);
memcpy(pub, keyaggcache.data, sizeof(secp256k1_musig_keyagg_cache));
(*penv)->ReleaseByteArrayElements(penv, jkeyaggcache, pub, 0);
return jpubkey;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_pubkey_xonly_tweak_add
* Signature: (J[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1pubkey_1xonly_1tweak_1add(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jkeyaggcache, jbyteArray jtweak32)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
jbyte *tweak32, *pub;
secp256k1_pubkey pubkey;
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpubkey;
size_t size;
int result = 0;
if (jctx == 0)
return NULL;
if (jkeyaggcache == NULL)
return NULL;
size = (*penv)->GetArrayLength(penv, jkeyaggcache);
CHECKRESULT(size != sizeof(secp256k1_musig_keyagg_cache), "invalid keyagg cache size");
copy_bytes_from_java(penv, jkeyaggcache, size, keyaggcache.data);
if (jtweak32 == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jtweak32) != 32, "tweak must be 32 bytes");
tweak32 = (*penv)->GetByteArrayElements(penv, jtweak32, 0);
result = secp256k1_musig_pubkey_xonly_tweak_add(ctx, &pubkey, &keyaggcache, tweak32);
(*penv)->ReleaseByteArrayElements(penv, jtweak32, tweak32, 0);
CHECKRESULT(!result, "secp256k1_musig_pubkey_xonly_tweak_add failed");
jpubkey = (*penv)->NewByteArray(penv, 65);
pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
size = 65;
result = secp256k1_ec_pubkey_serialize(ctx, pub, &size, &pubkey, SECP256K1_EC_UNCOMPRESSED);
(*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0);
CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed");
pub = (*penv)->GetByteArrayElements(penv, jkeyaggcache, 0);
memcpy(pub, keyaggcache.data, sizeof(secp256k1_musig_keyagg_cache));
(*penv)->ReleaseByteArrayElements(penv, jkeyaggcache, pub, 0);
return jpubkey;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_nonce_process
* Signature: (J[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1nonce_1process(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jaggnonce, jbyteArray jmsg32, jbyteArray jkeyaggcache, jbyteArray jadaptor)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_keyagg_cache keyaggcache;
secp256k1_musig_aggnonce aggnonce;
secp256k1_musig_session session;
unsigned char msg32[32];
jbyteArray jsession;
jbyte *ptr;
size_t size;
int result = 0;
if (jctx == 0)
return NULL;
if (jaggnonce == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jaggnonce) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE, "invalid nonce size");
if (jmsg32 == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jmsg32) != 32, "invalid message size");
if (jkeyaggcache == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jkeyaggcache) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, "invalid nonce size");
ptr = (*penv)->GetByteArrayElements(penv, jaggnonce, 0);
result = secp256k1_musig_aggnonce_parse(ctx, &aggnonce, ptr);
(*penv)->ReleaseByteArrayElements(penv, jaggnonce, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_aggnonce_parse failed");
copy_bytes_from_java(penv, jmsg32, 32, msg32);
copy_bytes_from_java(penv, jkeyaggcache, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, keyaggcache.data);
result = secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg32, &keyaggcache);
CHECKRESULT(!result, "secp256k1_musig_nonce_process failed");
jsession = (*penv)->NewByteArray(penv, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE);
copy_bytes_to_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
return jsession;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sign
* Signature: (J[B[B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sign(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsecnonce, jbyteArray jprivkey, jbyteArray jkeyaggcache, jbyteArray jsession)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_partial_sig psig;
secp256k1_musig_secnonce secnonce;
unsigned char seckey[32];
secp256k1_keypair keypair;
secp256k1_musig_keyagg_cache keyaggcache;
secp256k1_musig_session session;
jbyteArray jpsig;
jbyte *ptr;
int result = 0;
if (jctx == 0)
return NULL;
if (jsecnonce == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jsecnonce) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE, "invalid secret nonce size");
if (jprivkey == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jprivkey) != 32, "invalid private key size");
if (jkeyaggcache == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jkeyaggcache) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, "invalid cache size");
if (jsession == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jsession) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, "invalid session size");
copy_bytes_from_java(penv, jsecnonce, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SECRET_NONCE_SIZE, secnonce.data);
copy_bytes_from_java(penv, jprivkey, 32, seckey);
result = secp256k1_keypair_create(ctx, &keypair, seckey);
CHECKRESULT(!result, "secp256k1_keypair_create failed");
copy_bytes_from_java(penv, jkeyaggcache, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, keyaggcache.data);
copy_bytes_from_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
result = secp256k1_musig_partial_sign(ctx, &psig, &secnonce, &keypair, &keyaggcache, &session);
CHECKRESULT(!result, "secp256k1_musig_partial_sign failed");
result = secp256k1_musig_partial_sig_serialize(ctx, seckey, &psig);
CHECKRESULT(!result, "secp256k1_musig_partial_sig_serialize failed");
jpsig = (*penv)->NewByteArray(penv, 32);
copy_bytes_to_java(penv, jpsig, 32, seckey);
return jpsig;
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_verify
* Signature: (J[B[B[B[B[B)I
*/
JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1verify(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpsig, jbyteArray jpubnonce, jbyteArray jpubkey, jbyteArray jkeyaggcache, jbyteArray jsession)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_partial_sig psig;
secp256k1_musig_pubnonce pubnonce;
secp256k1_pubkey pubkey;
secp256k1_musig_keyagg_cache keyaggcache;
secp256k1_musig_session session;
jbyte *ptr;
int result = 0;
if (jctx == 0)
return 0;
if (jpsig == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jpsig) != 32, "invalid partial signature size");
if (jpubnonce == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jpubnonce) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_PUBLIC_NONCE_SIZE, "invalid public nonce size");
if (jpubkey == NULL)
return 0;
CHECKRESULT(((*penv)->GetArrayLength(penv, jpubkey) != 33) && ((*penv)->GetArrayLength(penv, jpubkey) != 65), "invalid public key size");
if (jkeyaggcache == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jkeyaggcache) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, "invalid cache size");
if (jsession == NULL)
return 0;
CHECKRESULT((*penv)->GetArrayLength(penv, jsession) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, "invalid session size");
ptr = (*penv)->GetByteArrayElements(penv, jpsig, 0);
result = secp256k1_musig_partial_sig_parse(ctx, &psig, ptr);
(*penv)->ReleaseByteArrayElements(penv, jpsig, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_partial_sig_parse failed");
ptr = (*penv)->GetByteArrayElements(penv, jpubnonce, 0);
result = secp256k1_musig_pubnonce_parse(ctx, &pubnonce, ptr);
(*penv)->ReleaseByteArrayElements(penv, jpubnonce, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_pubnonce_parse failed");
ptr = (*penv)->GetByteArrayElements(penv, jpubkey, 0);
result = secp256k1_ec_pubkey_parse(ctx, &pubkey, ptr, (*penv)->GetArrayLength(penv, jpubkey));
(*penv)->ReleaseByteArrayElements(penv, jpubkey, ptr, 0);
CHECKRESULT(!result, "secp256k1_musig_pubkey_parse failed");
copy_bytes_from_java(penv, jkeyaggcache, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_KEYAGG_CACHE_SIZE, keyaggcache.data);
copy_bytes_from_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
result = secp256k1_musig_partial_sig_verify(ctx, &psig, &pubnonce, &pubkey, &keyaggcache, &session);
return result;
}
void free_partial_sigs(secp256k1_musig_partial_sig **psigs, size_t count)
{
size_t i;
for (i = 0; i < count; i++)
{
if (psigs[i] != NULL)
free(psigs[i]);
}
free(psigs);
}
/*
* Class: fr_acinq_secp256k1_Secp256k1CFunctions
* Method: secp256k1_musig_partial_sig_agg
* Signature: (J[B[[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1musig_1partial_1sig_1agg(JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsession, jobjectArray jpsigs)
{
secp256k1_context *ctx = (secp256k1_context *)jctx;
secp256k1_musig_session session;
secp256k1_musig_partial_sig **psigs;
unsigned char sig64[64];
secp256k1_musig_keyagg_cache keyaggcache;
jbyteArray jpsig;
jbyte *ptr;
size_t size, count;
size_t i;
int result = 0;
if (jctx == 0)
return NULL;
if (jsession == NULL)
return NULL;
CHECKRESULT((*penv)->GetArrayLength(penv, jsession) != fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, "invalid session size");
copy_bytes_from_java(penv, jsession, fr_acinq_secp256k1_Secp256k1CFunctions_SECP256K1_MUSIG_SESSION_SIZE, session.data);
if (jpsigs == NULL)
return NULL;
count = (*penv)->GetArrayLength(penv, jpsigs);
CHECKRESULT(count <= 0, "partial sigs count cannot be 0");
psigs = calloc(count, sizeof(secp256k1_musig_partial_sig *));
for (i = 0; i < count; i++)
{
psigs[i] = calloc(1, sizeof(secp256k1_musig_partial_sig));
jpsig = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jpsigs, i);
size = (*penv)->GetArrayLength(penv, jpsig);
CHECKRESULT1(size != 32, "invalid partial signature size", free_partial_sigs(psigs, count));
ptr = (*penv)->GetByteArrayElements(penv, jpsig, 0);
result = secp256k1_musig_partial_sig_parse(ctx, psigs[i], (unsigned char *)ptr);
(*penv)->ReleaseByteArrayElements(penv, jpsig, ptr, 0);
CHECKRESULT1(!result, "secp256k1_musig_partial_sig_parse failed", free_partial_sigs(psigs, count));
}
result = secp256k1_musig_partial_sig_agg(ctx, sig64, &session, (const secp256k1_musig_partial_sig *const *)psigs, count);
free_partial_sigs(psigs, count);
CHECKRESULT(!result, "secp256k1_musig_pubkey_agg failed");
jpsig = (*penv)->NewByteArray(penv, 64);
copy_bytes_to_java(penv, jpsig, 64, sig64);
return jpsig;
}

View File

@ -29,6 +29,14 @@ public class Secp256k1CFunctions {
public static final int SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION); public static final int SECP256K1_EC_COMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION);
public static final int SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION); public static final int SECP256K1_EC_UNCOMPRESSED = (SECP256K1_FLAGS_TYPE_COMPRESSION);
public static final int SECP256K1_MUSIG_PUBLIC_NONCE_SIZE = 66;
public static final int SECP256K1_MUSIG_SECRET_NONCE_SIZE = 132;
public static final int SECP256K1_MUSIG_KEYAGG_CACHE_SIZE = 197;
public static final int SECP256K1_MUSIG_SESSION_SIZE = 133;
public static native long secp256k1_context_create(int flags); public static native long secp256k1_context_create(int flags);
public static native void secp256k1_context_destroy(long ctx); public static native void secp256k1_context_destroy(long ctx);
@ -68,4 +76,22 @@ public class Secp256k1CFunctions {
public static native byte[] secp256k1_schnorrsig_sign(long ctx, byte[] msg, byte[] seckey, byte[] aux_rand32); public static native byte[] secp256k1_schnorrsig_sign(long ctx, byte[] msg, byte[] seckey, byte[] aux_rand32);
public static native int secp256k1_schnorrsig_verify(long ctx, byte[] sig, byte[] msg, byte[] pubkey); public static native int secp256k1_schnorrsig_verify(long ctx, byte[] sig, byte[] msg, byte[] pubkey);
public static native byte[] secp256k1_musig_nonce_gen(long ctx, byte[] session_id32, byte[] seckey, byte[] pubkey, byte[] msg32, byte[] keyagg_cache, byte[] extra_input32);
public static native byte[] secp256k1_musig_nonce_agg(long ctx, byte[][] nonces);
public static native byte[] secp256k1_musig_pubkey_agg(long ctx, byte[][] pubkeys, byte[] keyagg_cache);
public static native byte[] secp256k1_musig_pubkey_ec_tweak_add(long ctx, byte[] keyagg_cache, byte[] tweak32);
public static native byte[] secp256k1_musig_pubkey_xonly_tweak_add(long ctx, byte[] keyagg_cache, byte[] tweak32);
public static native byte[] secp256k1_musig_nonce_process(long ctx, byte[] aggnonce, byte[] msg32, byte[] keyagg_cache, byte[] adaptor);
public static native byte[] secp256k1_musig_partial_sign(long ctx, byte[] secnonce, byte[] privkey, byte[] keyagg_cache, byte[] session);
public static native int secp256k1_musig_partial_sig_verify(long ctx, byte[] psig, byte[] pubnonce, byte[] pubkey, byte[] keyagg_cache, byte[] session);
public static native byte[] secp256k1_musig_partial_sig_agg(long ctx, byte[] session, byte[][] psigs);
} }

View File

@ -92,6 +92,42 @@ public object NativeSecp256k1 : Secp256k1 {
return Secp256k1CFunctions.secp256k1_schnorrsig_sign(Secp256k1Context.getContext(), data, sec, auxrand32) return Secp256k1CFunctions.secp256k1_schnorrsig_sign(Secp256k1Context.getContext(), data, sec, auxrand32)
} }
override fun musigNonceGen(session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_gen(Secp256k1Context.getContext(), session_id32, seckey, pubkey, msg32, keyagg_cache, extra_input32)
}
override fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_agg(Secp256k1Context.getContext(), pubnonces)
}
override fun musigPubkeyAdd(pubkeys: Array<ByteArray>, keyagg_cache: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_agg(Secp256k1Context.getContext(), pubkeys, keyagg_cache)
}
override fun musigPubkeyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_ec_tweak_add(Secp256k1Context.getContext(), keyagg_cache, tweak32)
}
override fun musigPubkeyXonlyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_pubkey_xonly_tweak_add(Secp256k1Context.getContext(), keyagg_cache, tweak32)
}
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyagg_cache: ByteArray, adaptor: ByteArray?): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_nonce_process(Secp256k1Context.getContext(), aggnonce, msg32, keyagg_cache, adaptor)
}
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_partial_sign(Secp256k1Context.getContext(), secnonce, privkey, keyagg_cache, session)
}
override fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): Int {
return Secp256k1CFunctions.secp256k1_musig_partial_sig_verify(Secp256k1Context.getContext(), psig, pubnonce, pubkey, keyagg_cache, session)
}
override fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray {
return Secp256k1CFunctions.secp256k1_musig_partial_sig_agg(Secp256k1Context.getContext(), session, psigs)
}
override fun cleanup() { override fun cleanup() {
return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext()) return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext())
} }

View File

@ -33,7 +33,7 @@ export STRIP=$ANDROID_NDK/toolchains/llvm/prebuilt/$TOOLCHAIN/bin/llvm-strip
cd secp256k1 cd secp256k1
./autogen.sh ./autogen.sh
./configure CFLAGS=-fpic --host=$TARGET --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no ./configure CFLAGS=-fpic --host=$TARGET --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
make clean make clean
make make

View File

@ -6,7 +6,7 @@ cp xconfigure.sh secp256k1
cd secp256k1 cd secp256k1
./autogen.sh ./autogen.sh
sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no sh xconfigure.sh --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
mkdir -p ../build/ios mkdir -p ../build/ios
cp -v _build/universal/ios/* ../build/ios/ cp -v _build/universal/ios/* ../build/ios/

View File

@ -23,7 +23,7 @@ else
fi fi
./autogen.sh ./autogen.sh
./configure $CONF_OPTS --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no ./configure $CONF_OPTS --enable-experimental --enable-module_ecdh --enable-module-recovery --enable-module-schnorrsig --enable-module-musig --enable-benchmark=no --enable-shared=no --enable-exhaustive-tests=no --enable-tests=no
make clean make clean
make make

View File

@ -153,6 +153,25 @@ public interface Secp256k1 {
} }
} }
public fun musigNonceGen(session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?): ByteArray
public fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray
public fun musigPubkeyAdd(pubkeys: Array<ByteArray>, keyagg_cache: ByteArray?): ByteArray
public fun musigPubkeyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray
public fun musigPubkeyXonlyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray
public fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyagg_cache: ByteArray, adaptor: ByteArray?): ByteArray
public fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): ByteArray
public fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): Int
public fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray
/** /**
* Delete the secp256k1 context from dynamic memory. * Delete the secp256k1 context from dynamic memory.
*/ */
@ -161,6 +180,13 @@ public interface Secp256k1 {
public companion object : Secp256k1 by getSecpk256k1() { public companion object : Secp256k1 by getSecpk256k1() {
@JvmStatic @JvmStatic
public fun get(): Secp256k1 = this public fun get(): Secp256k1 = this
// @formatter:off
public const val MUSIG2_SECRET_NONCE_SIZE: Int = 132
public const val MUSIG2_PUBLIC_NONCE_SIZE: Int = 66
public const val MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE: Int = 197
public const val MUSIG2_PUBLIC_SESSION_SIZE: Int = 133
// @formatter:on
} }
} }

View File

@ -1,7 +1,7 @@
package = secp256k1 package = secp256k1
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1.h headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1_musig.h secp256k1.h
staticLibraries.linux = libsecp256k1.a staticLibraries.linux = libsecp256k1.a
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/ libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/

View File

@ -1,6 +1,7 @@
package fr.acinq.secp256k1 package fr.acinq.secp256k1
import kotlinx.cinterop.* import kotlinx.cinterop.*
import platform.posix.memcpy
import platform.posix.size_tVar import platform.posix.size_tVar
import secp256k1.* import secp256k1.*
@ -40,6 +41,20 @@ public object Secp256k1Native : Secp256k1 {
return pub return pub
} }
private fun MemScope.allocPublicNonce(pubnonce: ByteArray): secp256k1_musig_pubnonce {
val nat = toNat(pubnonce)
val nPubnonce = alloc<secp256k1_musig_pubnonce>()
secp256k1_musig_pubnonce_parse(ctx, nPubnonce.ptr, nat).requireSuccess("secp256k1_musig_pubnonce_parse() failed")
return nPubnonce
}
private fun MemScope.allocPartialSig(psig: ByteArray): secp256k1_musig_partial_sig {
val nat = toNat(psig)
val nPsig = alloc<secp256k1_musig_partial_sig>()
secp256k1_musig_partial_sig_parse(ctx, nPsig.ptr, nat).requireSuccess("secp256k1_musig_partial_sig_parse() failed")
return nPsig
}
private fun MemScope.serializePubkey(pubkey: secp256k1_pubkey): ByteArray { private fun MemScope.serializePubkey(pubkey: secp256k1_pubkey): ByteArray {
val serialized = allocArray<UByteVar>(65) val serialized = allocArray<UByteVar>(65)
val outputLen = alloc<size_tVar>() val outputLen = alloc<size_tVar>()
@ -48,6 +63,24 @@ public object Secp256k1Native : Secp256k1 {
return serialized.readBytes(outputLen.value.convert()) return serialized.readBytes(outputLen.value.convert())
} }
private fun MemScope.serializeXonlyPubkey(pubkey: secp256k1_xonly_pubkey): ByteArray {
val serialized = allocArray<UByteVar>(32)
secp256k1_xonly_pubkey_serialize(ctx, serialized, pubkey.ptr).requireSuccess("secp256k1_xonly_pubkey_serialize() failed")
return serialized.readBytes(32)
}
private fun MemScope.serializePubnonce(pubnonce: secp256k1_musig_pubnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_pubnonce_serialize(ctx, serialized, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun MemScope.serializeAggnonce(aggnonce: secp256k1_musig_aggnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_aggnonce_serialize(ctx, serialized, aggnonce.ptr).requireSuccess("secp256k1_musig_aggnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> { private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
val ubytes = bytes.asUByteArray() val ubytes = bytes.asUByteArray()
val pinned = ubytes.pin() val pinned = ubytes.pin()
@ -258,11 +291,162 @@ public object Secp256k1Native : Secp256k1 {
} }
} }
override fun musigNonceGen(session_id32: ByteArray, seckey: ByteArray?, pubkey: ByteArray, msg32: ByteArray?, keyagg_cache: ByteArray?, extra_input32: ByteArray?): ByteArray {
require(session_id32.size == 32)
seckey?.let { require(it.size == 32) }
msg32?.let { require(it.size == 32) }
keyagg_cache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
extra_input32?.let { require(it.size == 32) }
val nonce = memScoped {
val secret_nonce = alloc<secp256k1_musig_secnonce>()
val public_nonce = alloc<secp256k1_musig_pubnonce>()
val nPubkey = allocPublicKey(pubkey)
val nKeyAggCache = keyagg_cache?.let {
val n = alloc<secp256k1_musig_keyagg_cache>()
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
n
}
secp256k1_musig_nonce_gen(ctx, secret_nonce.ptr, public_nonce.ptr, toNat(session_id32), seckey?.let { toNat(it) }, nPubkey.ptr, msg32?.let { toNat(it) },nKeyAggCache?.ptr, extra_input32?.let { toNat(it) }).requireSuccess("secp256k1_musig_nonce_gen() failed")
val nPubnonce = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_pubnonce_serialize(ctx, nPubnonce, public_nonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize failed")
secret_nonce.ptr.readBytes(Secp256k1.MUSIG2_SECRET_NONCE_SIZE) + nPubnonce.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
return nonce
}
override fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray {
require(pubnonces.isNotEmpty())
pubnonces.forEach { require(it.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE) }
memScoped {
val nPubnonces = pubnonces.map { allocPublicNonce(it).ptr }
val combined = alloc<secp256k1_musig_aggnonce>()
secp256k1_musig_nonce_agg(ctx, combined.ptr, nPubnonces.toCValues(), pubnonces.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
return serializeAggnonce(combined)
}
}
override fun musigPubkeyAdd(pubkeys: Array<ByteArray>, keyagg_cache: ByteArray?): ByteArray {
require(pubkeys.isNotEmpty())
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
keyagg_cache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
memScoped {
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
val combined = alloc<secp256k1_xonly_pubkey>()
val nKeyAggCache = keyagg_cache?.let {
val n = alloc<secp256k1_musig_keyagg_cache>()
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
n
}
secp256k1_musig_pubkey_agg(ctx, combined.ptr, nKeyAggCache?.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
val agg = serializeXonlyPubkey(combined)
keyagg_cache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
return agg
}
}
override fun musigPubkeyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(tweak32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_musig_pubkey_ec_tweak_add(ctx, nPubkey.ptr, nKeyAggCache.ptr, toNat(tweak32)).requireSuccess("secp256k1_musig_pubkey_ec_tweak_add() failed")
memcpy(toNat(keyagg_cache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
return serializePubkey(nPubkey)
}
}
override fun musigPubkeyXonlyTweakAdd(keyagg_cache: ByteArray, tweak32: ByteArray): ByteArray {
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(tweak32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_musig_pubkey_xonly_tweak_add(ctx, nPubkey.ptr, nKeyAggCache.ptr, toNat(tweak32)).requireSuccess("secp256k1_musig_pubkey_xonly_tweak_add() failed")
memcpy(toNat(keyagg_cache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
return serializePubkey(nPubkey)
}
}
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyagg_cache: ByteArray, adaptor: ByteArray?): ByteArray {
require(aggnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(msg32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
val nAggnonce = alloc<secp256k1_musig_aggnonce>()
secp256k1_musig_aggnonce_parse(ctx, nAggnonce.ptr, toNat(aggnonce)).requireSuccess("secp256k1_musig_aggnonce_parse() failed")
secp256k1_musig_nonce_process(ctx, nSession.ptr, nAggnonce.ptr, toNat(msg32), nKeyAggCache.ptr).requireSuccess("secp256k1_musig_nonce_process() failed")
val session = ByteArray(Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memcpy(toNat(session), nSession.ptr, Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
return session
}
}
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): ByteArray {
require(secnonce.size == Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
require(privkey.size == 32)
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memScoped {
val nSecnonce = alloc<secp256k1_musig_secnonce>()
memcpy(nSecnonce.ptr, toNat(secnonce), Secp256k1.MUSIG2_SECRET_NONCE_SIZE.toULong())
val nKeypair = alloc<secp256k1_keypair>()
secp256k1_keypair_create(ctx, nKeypair.ptr, toNat(privkey))
val nPsig = alloc<secp256k1_musig_partial_sig>()
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
secp256k1_musig_partial_sign(ctx, nPsig.ptr, nSecnonce.ptr, nKeypair.ptr, nKeyAggCache.ptr, nSession.ptr).requireSuccess("secp256k1_musig_partial_sign failed")
val psig = ByteArray(32)
secp256k1_musig_partial_sig_serialize(ctx, toNat(psig), nPsig.ptr).requireSuccess("secp256k1_musig_partial_sig_serialize() failed")
return psig
}
}
override fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyagg_cache: ByteArray, session: ByteArray): Int {
require(psig.size == 32)
require(pubnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
require(pubkey.size == 33 || pubkey.size == 65)
require(keyagg_cache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memScoped {
val nPSig = allocPartialSig(psig)
val nPubnonce = allocPublicNonce(pubnonce)
val nPubkey = allocPublicKey(pubkey)
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyagg_cache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
return secp256k1_musig_partial_sig_verify(ctx, nPSig.ptr, nPubnonce.ptr, nPubkey.ptr, nKeyAggCache.ptr, nSession.ptr)
}
}
override fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray {
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
require(psigs.isNotEmpty())
psigs.forEach { require(it.size == 32) }
memScoped {
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
val nPsigs = psigs.map { allocPartialSig(it).ptr }
val sig64 = ByteArray(64)
secp256k1_musig_partial_sig_agg(ctx, toNat(sig64), nSession.ptr, nPsigs.toCValues(), psigs.size.convert()).requireSuccess("secp256k1_musig_partial_sig_agg() failed")
return sig64
}
}
public override fun cleanup() { public override fun cleanup() {
secp256k1_context_destroy(ctx) secp256k1_context_destroy(ctx)
} }
} }
internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native

View File

@ -352,6 +352,119 @@ class Secp256k1Test {
} }
} }
@Test
fun testMusig2GenerateNonce() {
val pubkey = Hex.decode("02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9")
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
val nonce = Secp256k1.musigNonceGen(sessionId, null, pubkey, null, null, null)
val pubnonce = Hex.encode(nonce.copyOfRange(132, 132 + 66)).uppercase()
assertEquals("02C96E7CB1E8AA5DAC64D872947914198F607D90ECDE5200DE52978AD5DED63C000299EC5117C2D29EDEE8A2092587C3909BE694D5CFF0667D6C02EA4059F7CD9786", pubnonce)
}
@Test
fun testMusig2AggregateNonce() {
val nonces = listOf(
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E66603BA47FBC1834437B3212E89A84D8425E7BF12E0245D98262268EBDCB385D50641",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"020151C80F435648DF67A22B749CD798CE54E0321D034B92B709B567D60A42E6660279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60379BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",
"04FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B833",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A60248C264CDD57D3C24D79990B0F865674EB62A0F9018277A95011B41BFC193B831",
"03FF406FFD8ADB9CD29877E4985014F66A59F6CD01C0E88CAA8E5F3166B1F676A602FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"
).map { Hex.decode(it) }
val agg1 = Secp256k1.musigNonceAgg(arrayOf(nonces[0], nonces[1]))
assertEquals("035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B024725377345BDE0E9C33AF3C43C0A29A9249F2F2956FA8CFEB55C8573D0262DC8", Hex.encode(agg1).uppercase())
val agg2 = Secp256k1.musigNonceAgg(arrayOf(nonces[2], nonces[3]))
assertEquals("035FE1873B4F2967F52FEA4A06AD5A8ECCBE9D0FD73068012C894E2E87CCB5804B000000000000000000000000000000000000000000000000000000000000000000", Hex.encode(agg2).uppercase())
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[0], nonces[4]))
}
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[5], nonces[1]))
}
assertFails {
Secp256k1.musigNonceAgg(arrayOf(nonces[6], nonces[1]))
}
}
@Test
fun testMusig2AggregatePubkey() {
val pubkeys = listOf(
"02F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659",
"023590A94E768F8E1815C2F24B4D80A8E3149316C3518CE7B7AD338368D038CA66",
"020000000000000000000000000000000000000000000000000000000000000005",
"02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30",
"04F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9",
"03935F972DA013F80AE011890FA89B67A27B7BE6CCB24D3274D18B2D4067F261A9"
).map { Hex.decode(it) }
val agg1 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), null)
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg1).uppercase())
val cache = ByteArray(197)
val agg2 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[1], pubkeys[2]), cache)
assertEquals("90539EEDE565F5D054F32CC0C220126889ED1E5D193BAF15AEF344FE59D4610C", Hex.encode(agg2).uppercase())
val agg3 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[2], pubkeys[1], pubkeys[0]), null)
assertEquals("6204DE8B083426DC6EAF9502D27024D53FC826BF7D2012148A0575435DF54B2B", Hex.encode(agg3).uppercase())
val agg4 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[0], pubkeys[0]), null)
assertEquals("B436E3BAD62B8CD409969A224731C193D051162D8C5AE8B109306127DA3AA935", Hex.encode(agg4).uppercase())
val agg5 = Secp256k1.musigPubkeyAdd(arrayOf(pubkeys[0], pubkeys[0], pubkeys[1], pubkeys[1]), null)
assertEquals("69BC22BFA5D106306E48A20679DE1D7389386124D07571D0D872686028C26A3E", Hex.encode(agg5).uppercase())
}
@Test
fun testMusig2TweakPubkeys() {
val pubkeys = listOf(
"031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
"024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766",
"02531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe337"
).map { Hex.decode(it) }.toTypedArray()
val cache = ByteArray(197)
val agg1 = Secp256k1.musigPubkeyAdd(pubkeys, cache)
assertEquals("b6d830642403fc82511aca5ff98a5e76fcef0f89bffc1aadbe78ee74cd5a5716", Hex.encode(agg1))
val agg2 = Secp256k1.musigPubkeyTweakAdd(cache, Hex.decode("7468697320636f756c64206265206120424950333220747765616b2e2e2e2e00"))
assertEquals("04791e4f22a21f19bd9798eceab92ad2ccc18f2d6660e91ae4c0709aaebf1aa9023701f468b0eddf8973495a5327f2169d9c6a50eb6a0f87c0fbee90a4067eb230", Hex.encode(agg2))
val agg3 = Secp256k1.musigPubkeyXonlyTweakAdd(cache, Hex.decode("7468697320636f756c64206265206120746170726f6f7420747765616b2e2e00"))
assertEquals("04537a081a8d32ff700ca86aaa77a423e9b8d1480938076b645c68ee39d263c93948026928799b2d942cb5851db397015b26b1759de1b9ab2c691ced64a2eef836", Hex.encode(agg3))
}
@Test
fun testMusig2SigningSession() {
val privkeys = listOf(
"0101010101010101010101010101010101010101010101010101010101010101",
"0202020202020202020202020202020202020202020202020202020202020202",
).map { Hex.decode(it) }.toTypedArray()
val pubkeys = privkeys.map { Secp256k1.pubkeyCreate(it) }
val sessionId = Hex.decode("0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F")
val nonces = pubkeys.map { Secp256k1.musigNonceGen(sessionId, null, it, null, null, null) }
val secnonces = nonces.map { it.copyOfRange(0, 132) }
val pubnonces = nonces.map { it.copyOfRange(132, 132 + 66) }
val aggnonce = Secp256k1.musigNonceAgg(pubnonces.toTypedArray())
val caches = (0 until 2).map { ByteArray(197) }
val aggpubkey = Secp256k1.musigPubkeyAdd(pubkeys.toTypedArray(), caches[0])
Secp256k1.musigPubkeyAdd(pubkeys.toTypedArray(), caches[1])
val msg32 = Hex.decode("0303030303030303030303030303030303030303030303030303030303030303")
val sessions = (0 until 2).map { Secp256k1.musigNonceProcess(aggnonce, msg32, caches[it], null) }
val psigs = (0 until 2).map {
val psig = Secp256k1.musigPartialSign(secnonces[it], privkeys[it], caches[it], sessions[it])
val check = Secp256k1.musigPartialSigVerify(psig, pubnonces[it], pubkeys[it], caches[it], sessions[it])
assertEquals(1, check)
psig
}
val sig = Secp256k1.musigPartialSigAgg(sessions[0], psigs.toTypedArray())
val check = Secp256k1.verifySchnorr(sig, msg32, aggpubkey)
assertTrue(check)
}
@Test @Test
fun testInvalidArguments() { fun testInvalidArguments() {
assertFails { assertFails {