Add support for musig2 (#93)
* Use Jonas Nick's musig2 branch * Reformat c code (no functional changes) * Implement musig2 * Add documentation to musig2 functions (#97) Usage of the Musig2 functions isn't intuitive at all, especially with the key aggregation cache and session data. It's important to provide accurate documentation to help users understand how to correctly produce musig2 signatures. We also change argument names to match Kotlin best practices instead of using the same argument names as C functions. * Add musig2 reference tests (no functional changes) --------- Co-authored-by: Bastien Teinturier <31281497+t-bast@users.noreply.github.com>
This commit is contained in:
@@ -55,7 +55,7 @@ public interface Secp256k1 {
|
||||
*/
|
||||
public fun signSchnorr(data: ByteArray, sec: ByteArray, auxrand32: ByteArray?): ByteArray
|
||||
|
||||
/**
|
||||
/**
|
||||
* Convert an ECDSA signature to a normalized lower-S form (bitcoin standardness rule).
|
||||
* Returns the normalized signature and a boolean set to true if the input signature was not normalized.
|
||||
*
|
||||
@@ -149,10 +149,108 @@ public interface Secp256k1 {
|
||||
compressed[0] = if (pubkey.last() % 2 == 0) 2.toByte() else 3.toByte()
|
||||
compressed
|
||||
}
|
||||
|
||||
else -> throw Secp256k1Exception("invalid public key")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a secret nonce to be used in a musig2 signing session.
|
||||
* This nonce must never be persisted or reused across signing sessions.
|
||||
* All optional arguments exist to enrich the quality of the randomness used, which is critical for security.
|
||||
*
|
||||
* @param sessionId32 unique 32-byte session ID.
|
||||
* @param privkey (optional) signer's private key.
|
||||
* @param aggpubkey aggregated public key of all participants in the signing session.
|
||||
* @param msg32 (optional) 32-byte message that will be signed, if already known.
|
||||
* @param keyaggCache (optional) key aggregation cache data from the signing session.
|
||||
* @param extraInput32 (optional) additional 32-byte random data.
|
||||
* @return serialized version of the secret nonce and the corresponding public nonce.
|
||||
*/
|
||||
public fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray
|
||||
|
||||
/**
|
||||
* Aggregate public nonces from all participants of a signing session.
|
||||
*
|
||||
* @param pubnonces public nonces (one per participant).
|
||||
* @return 66-byte aggregate public nonce (two public keys) or throws an exception is a nonce is invalid.
|
||||
*/
|
||||
public fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray
|
||||
|
||||
/**
|
||||
* Aggregate public keys from all participants of a signing session.
|
||||
*
|
||||
* @param pubkeys public keys of all participants in the signing session.
|
||||
* @param keyaggCache (optional) key aggregation cache data from the signing session. If an empty byte array is
|
||||
* provided, it will be filled with key aggregation data that can be used for the next steps of the signing process.
|
||||
* @return 32-byte x-only public key.
|
||||
*/
|
||||
public fun musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray
|
||||
|
||||
/**
|
||||
* Tweak the aggregated public key of a signing session.
|
||||
*
|
||||
* @param keyaggCache key aggregation cache filled by [musigPubkeyAgg].
|
||||
* @param tweak32 private key tweak to apply.
|
||||
* @return P + tweak32 * G (where P is the aggregated public key from [keyaggCache]). The key aggregation cache will
|
||||
* be updated with the tweaked public key.
|
||||
*/
|
||||
public fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Tweak the aggregated public key of a signing session, treating it as an x-only public key (e.g. when using taproot).
|
||||
*
|
||||
* @param keyaggCache key aggregation cache filled by [musigPubkeyAgg].
|
||||
* @param tweak32 private key tweak to apply.
|
||||
* @return with_even_y(P) + tweak32 * G (where P is the aggregated public key from [keyaggCache]). The key aggregation
|
||||
* cache will be updated with the tweaked public key.
|
||||
*/
|
||||
public fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Create a signing session context based on the public information from all participants.
|
||||
*
|
||||
* @param aggnonce aggregated public nonce (see [musigNonceAgg]).
|
||||
* @param msg32 32-byte message that will be signed.
|
||||
* @param keyaggCache aggregated public key cache filled by calling [musigPubkeyAgg] with the public keys of all participants.
|
||||
* @return signing session context that can be used to create partial signatures and aggregate them.
|
||||
*/
|
||||
public fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Create a partial signature.
|
||||
*
|
||||
* @param secnonce signer's secret nonce (see [musigNonceGen]).
|
||||
* @param privkey signer's private key.
|
||||
* @param keyaggCache aggregated public key cache filled by calling [musigPubkeyAgg] with the public keys of all participants.
|
||||
* @param session signing session context (see [musigNonceProcess]).
|
||||
* @return 32-byte partial signature.
|
||||
*/
|
||||
public fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray
|
||||
|
||||
/**
|
||||
* Verify the partial signature from one of the signing session's participants.
|
||||
*
|
||||
* @param psig 32-byte partial signature.
|
||||
* @param pubnonce individual public nonce of the signing participant.
|
||||
* @param pubkey individual public key of the signing participant.
|
||||
* @param keyaggCache aggregated public key cache filled by calling [musigPubkeyAgg] with the public keys of all participants.
|
||||
* @param session signing session context (see [musigNonceProcess]).
|
||||
* @return result code (1 if the partial signature is valid, 0 otherwise).
|
||||
*/
|
||||
public fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): Int
|
||||
|
||||
/**
|
||||
* Aggregate partial signatures from all participants into a single schnorr signature. If some of the partial
|
||||
* signatures are invalid, this function will return an invalid aggregated signature without raising an error.
|
||||
* It is recommended to use [musigPartialSigVerify] to verify partial signatures first.
|
||||
*
|
||||
* @param session signing session context (see [musigNonceProcess]).
|
||||
* @param psigs list of 32-byte partial signatures.
|
||||
* @return 64-byte aggregated schnorr signature.
|
||||
*/
|
||||
public fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray
|
||||
|
||||
/**
|
||||
* Delete the secp256k1 context from dynamic memory.
|
||||
*/
|
||||
@@ -161,6 +259,13 @@ public interface Secp256k1 {
|
||||
public companion object : Secp256k1 by getSecpk256k1() {
|
||||
@JvmStatic
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package = secp256k1
|
||||
|
||||
headers = secp256k1.h secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h
|
||||
headerFilter = secp256k1/** secp256k1_ecdh.h secp256k1_recovery.h secp256k1_extrakeys.h secp256k1_schnorrsig.h secp256k1.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_musig.h secp256k1.h
|
||||
|
||||
staticLibraries.linux = libsecp256k1.a
|
||||
libraryPaths.linux = c/secp256k1/build/linux/ native/build/linux/ native/build/darwin/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fr.acinq.secp256k1
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import platform.posix.memcpy
|
||||
import platform.posix.size_tVar
|
||||
import secp256k1.*
|
||||
|
||||
@@ -40,6 +41,20 @@ public object Secp256k1Native : Secp256k1 {
|
||||
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 {
|
||||
val serialized = allocArray<UByteVar>(65)
|
||||
val outputLen = alloc<size_tVar>()
|
||||
@@ -48,6 +63,24 @@ public object Secp256k1Native : Secp256k1 {
|
||||
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> {
|
||||
val ubytes = bytes.asUByteArray()
|
||||
val pinned = ubytes.pin()
|
||||
@@ -257,12 +290,163 @@ public object Secp256k1Native : Secp256k1 {
|
||||
return nSig.readBytes(64)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray {
|
||||
require(sessionId32.size == 32)
|
||||
privkey?.let { require(it.size == 32) }
|
||||
msg32?.let { require(it.size == 32) }
|
||||
keyaggCache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
|
||||
extraInput32?.let { require(it.size == 32) }
|
||||
|
||||
val nonce = memScoped {
|
||||
val secnonce = alloc<secp256k1_musig_secnonce>()
|
||||
val pubnonce = alloc<secp256k1_musig_pubnonce>()
|
||||
val nPubkey = allocPublicKey(aggpubkey)
|
||||
val nKeyAggCache = keyaggCache?.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, secnonce.ptr, pubnonce.ptr, toNat(sessionId32), privkey?.let { toNat(it) }, nPubkey.ptr, msg32?.let { toNat(it) },nKeyAggCache?.ptr, extraInput32?.let { toNat(it) }).requireSuccess("secp256k1_musig_nonce_gen() failed")
|
||||
val nPubnonce = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||
secp256k1_musig_pubnonce_serialize(ctx, nPubnonce, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize failed")
|
||||
secnonce.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 musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray {
|
||||
require(pubkeys.isNotEmpty())
|
||||
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
|
||||
keyaggCache?.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 = keyaggCache?.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)
|
||||
keyaggCache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
|
||||
return agg
|
||||
}
|
||||
}
|
||||
|
||||
override fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
require(tweak32.size == 32)
|
||||
memScoped {
|
||||
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
|
||||
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), 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(keyaggCache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
|
||||
return serializePubkey(nPubkey)
|
||||
}
|
||||
}
|
||||
|
||||
override fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
|
||||
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
require(tweak32.size == 32)
|
||||
memScoped {
|
||||
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
|
||||
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), 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(keyaggCache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
|
||||
return serializePubkey(nPubkey)
|
||||
}
|
||||
}
|
||||
|
||||
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray {
|
||||
require(aggnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
|
||||
require(msg32.size == 32)
|
||||
memScoped {
|
||||
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
|
||||
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), 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, keyaggCache: ByteArray, session: ByteArray): ByteArray {
|
||||
require(secnonce.size == Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
|
||||
require(privkey.size == 32)
|
||||
require(keyaggCache.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(keyaggCache), 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, keyaggCache: ByteArray, session: ByteArray): Int {
|
||||
require(psig.size == 32)
|
||||
require(pubnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
|
||||
require(pubkey.size == 33 || pubkey.size == 65)
|
||||
require(keyaggCache.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(keyaggCache), 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() {
|
||||
secp256k1_context_destroy(ctx)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native
|
||||
|
||||
Reference in New Issue
Block a user