@@ -383,6 +383,95 @@ std::vector<uint8_t> CKey::CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uin
383383 return out;
384384}
385385
386+ std::optional<uint256> CKey::CreateMuSig2PartialSig (const uint256& sighash, const CPubKey& aggregate_pubkey, const std::vector<CPubKey>& pubkeys, const std::map<CPubKey, std::vector<uint8_t >>& pubnonces, MuSig2SecNonce& secnonce, const std::vector<std::pair<uint256, bool >>& tweaks)
387+ {
388+ secp256k1_keypair keypair;
389+ if (!secp256k1_keypair_create (secp256k1_context_sign, &keypair, UCharCast (begin ()))) return std::nullopt ;
390+
391+ // Get the keyagg cache and aggregate pubkey
392+ secp256k1_musig_keyagg_cache keyagg_cache;
393+ if (!MuSig2AggregatePubkeys (pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt ;
394+
395+ // Check that there are enough pubnonces
396+ if (pubnonces.size () != pubkeys.size ()) return std::nullopt ;
397+
398+ // Parse the pubnonces
399+ std::vector<std::pair<secp256k1_pubkey, secp256k1_musig_pubnonce>> signers_data;
400+ std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
401+ std::optional<size_t > our_pubkey_idx;
402+ CPubKey our_pubkey = GetPubKey ();
403+ for (const CPubKey& part_pk : pubkeys) {
404+ const auto & pn_it = pubnonces.find (part_pk);
405+ if (pn_it == pubnonces.end ()) return std::nullopt ;
406+ const std::vector<uint8_t > pubnonce = pn_it->second ;
407+ if (pubnonce.size () != MUSIG2_PUBNONCE_SIZE) return std::nullopt ;
408+ if (part_pk == our_pubkey) {
409+ our_pubkey_idx = signers_data.size ();
410+ }
411+
412+ auto & [secp_pk, secp_pn] = signers_data.emplace_back ();
413+
414+ if (!secp256k1_ec_pubkey_parse (secp256k1_context_static, &secp_pk, part_pk.data (), part_pk.size ())) {
415+ return std::nullopt ;
416+ }
417+
418+ if (!secp256k1_musig_pubnonce_parse (secp256k1_context_static, &secp_pn, pubnonce.data ())) {
419+ return std::nullopt ;
420+ }
421+ }
422+ if (our_pubkey_idx == std::nullopt ) {
423+ return std::nullopt ;
424+ }
425+ pubnonce_ptrs.reserve (signers_data.size ());
426+ for (auto & [_, pn] : signers_data) {
427+ pubnonce_ptrs.push_back (&pn);
428+ }
429+
430+ // Aggregate nonces
431+ secp256k1_musig_aggnonce aggnonce;
432+ if (!secp256k1_musig_nonce_agg (secp256k1_context_static, &aggnonce, pubnonce_ptrs.data (), pubnonce_ptrs.size ())) {
433+ return std::nullopt ;
434+ }
435+
436+ // Apply tweaks
437+ for (const auto & [tweak, xonly] : tweaks) {
438+ if (xonly) {
439+ if (!secp256k1_musig_pubkey_xonly_tweak_add (secp256k1_context_static, nullptr , &keyagg_cache, tweak.data ())) {
440+ return std::nullopt ;
441+ }
442+ } else if (!secp256k1_musig_pubkey_ec_tweak_add (secp256k1_context_static, nullptr , &keyagg_cache, tweak.data ())) {
443+ return std::nullopt ;
444+ }
445+ }
446+
447+ // Create musig_session
448+ secp256k1_musig_session session;
449+ if (!secp256k1_musig_nonce_process (secp256k1_context_static, &session, &aggnonce, sighash.data (), &keyagg_cache)) {
450+ return std::nullopt ;
451+ }
452+
453+ // Create partial signature
454+ secp256k1_musig_partial_sig psig;
455+ if (!secp256k1_musig_partial_sign (secp256k1_context_static, &psig, secnonce.Get (), &keypair, &keyagg_cache, &session)) {
456+ return std::nullopt ;
457+ }
458+ // The secnonce must be deleted after signing to prevent nonce reuse.
459+ secnonce.Invalidate ();
460+
461+ // Verify partial signature
462+ if (!secp256k1_musig_partial_sig_verify (secp256k1_context_static, &psig, &(signers_data.at (*our_pubkey_idx).second ), &(signers_data.at (*our_pubkey_idx).first ), &keyagg_cache, &session)) {
463+ return std::nullopt ;
464+ }
465+
466+ // Serialize
467+ uint256 sig;
468+ if (!secp256k1_musig_partial_sig_serialize (secp256k1_context_static, sig.data (), &psig)) {
469+ return std::nullopt ;
470+ }
471+
472+ return sig;
473+ }
474+
386475CKey GenerateRandomKey (bool compressed) noexcept
387476{
388477 CKey key;
0 commit comments