diff --git a/src/musig.cpp b/src/musig.cpp index 5cf638d50b7..686ec5e869a 100644 --- a/src/musig.cpp +++ b/src/musig.cpp @@ -118,3 +118,89 @@ uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey hasher << script_pubkey << part_pubkey << sighash; return hasher.GetSHA256(); } + +std::optional> CreateMuSig2AggregateSig(const std::vector& part_pubkeys, const CPubKey& aggregate_pubkey, const std::vector>& tweaks, const uint256& sighash, const std::map>& pubnonces, const std::map& partial_sigs) +{ + if (!part_pubkeys.size()) return std::nullopt; + + // Get the keyagg cache and aggregate pubkey + secp256k1_musig_keyagg_cache keyagg_cache; + if (!MuSig2AggregatePubkeys(part_pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt; + + // Check if enough pubnonces and partial sigs + if (pubnonces.size() != part_pubkeys.size()) return std::nullopt; + if (partial_sigs.size() != part_pubkeys.size()) return std::nullopt; + + // Parse the pubnonces and partial sigs + std::vector> signers_data; + std::vector pubnonce_ptrs; + std::vector partial_sig_ptrs; + for (const CPubKey& part_pk : part_pubkeys) { + const auto& pn_it = pubnonces.find(part_pk); + if (pn_it == pubnonces.end()) return std::nullopt; + const std::vector pubnonce = pn_it->second; + if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt; + const auto& it = partial_sigs.find(part_pk); + if (it == partial_sigs.end()) return std::nullopt; + const uint256& partial_sig = it->second; + + auto& [secp_pk, secp_pn, secp_ps] = signers_data.emplace_back(); + + if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) { + return std::nullopt; + } + + if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) { + return std::nullopt; + } + + if (!secp256k1_musig_partial_sig_parse(secp256k1_context_static, &secp_ps, partial_sig.data())) { + return std::nullopt; + } + } + pubnonce_ptrs.reserve(signers_data.size()); + partial_sig_ptrs.reserve(signers_data.size()); + for (auto& [_, pn, ps] : signers_data) { + pubnonce_ptrs.push_back(&pn); + partial_sig_ptrs.push_back(&ps); + } + + // Aggregate nonces + secp256k1_musig_aggnonce aggnonce; + if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) { + return std::nullopt; + } + + // Apply tweaks + for (const auto& [tweak, xonly] : tweaks) { + if (xonly) { + if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) { + return std::nullopt; + } + } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) { + return std::nullopt; + } + } + + // Create musig_session + secp256k1_musig_session session; + if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) { + return std::nullopt; + } + + // Verify partial sigs + for (const auto& [pk, pb, ps] : signers_data) { + if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &ps, &pb, &pk, &keyagg_cache, &session)) { + return std::nullopt; + } + } + + // Aggregate partial sigs + std::vector sig; + sig.resize(64); + if (!secp256k1_musig_partial_sig_agg(secp256k1_context_static, sig.data(), &session, partial_sig_ptrs.data(), partial_sig_ptrs.size())) { + return std::nullopt; + } + + return sig; +} diff --git a/src/musig.h b/src/musig.h index 03324fc454a..c81b53eddd4 100644 --- a/src/musig.h +++ b/src/musig.h @@ -62,4 +62,6 @@ public: uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash); +std::optional> CreateMuSig2AggregateSig(const std::vector& participants, const CPubKey& aggregate_pubkey, const std::vector>& tweaks, const uint256& sighash, const std::map>& pubnonces, const std::map& partial_sigs); + #endif // BITCOIN_MUSIG_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 8abc82bcb3c..bc40c05a195 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -174,6 +174,36 @@ bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningPro return true; } +bool MutableTransactionSignatureCreator::CreateMuSig2AggregateSig(const std::vector& participants, std::vector& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const +{ + assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT); + if (!participants.size()) return false; + + // Retrieve pubnonces and partial sigs + auto this_leaf_aggkey = std::make_pair(script_pubkey, leaf_hash ? *leaf_hash : uint256()); + auto pubnonce_it = sigdata.musig2_pubnonces.find(this_leaf_aggkey); + if (pubnonce_it == sigdata.musig2_pubnonces.end()) return false; + const std::map>& pubnonces = pubnonce_it->second; + auto partial_sigs_it = sigdata.musig2_partial_sigs.find(this_leaf_aggkey); + if (partial_sigs_it == sigdata.musig2_partial_sigs.end()) return false; + const std::map& partial_sigs = partial_sigs_it->second; + + // Check if enough pubnonces and partial sigs + if (pubnonces.size() != participants.size()) return false; + if (partial_sigs.size() != participants.size()) return false; + + // Compute sighash + std::optional sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion); + if (!sighash.has_value()) return false; + + std::optional> res = ::CreateMuSig2AggregateSig(participants, aggregate_pubkey, tweaks, *sighash, pubnonces, partial_sigs); + if (!res) return false; + sig = res.value(); + if (nHashType) sig.push_back(nHashType); + + return true; +} + static bool GetCScript(const SigningProvider& provider, const SignatureData& sigdata, const CScriptID& scriptid, CScript& script) { if (provider.GetCScript(scriptid, script)) { @@ -840,6 +870,11 @@ public: partial_sig = uint256::ONE; return true; } + bool CreateMuSig2AggregateSig(const std::vector& participants, std::vector& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override + { + sig.assign(64, '\000'); + return true; + } }; } diff --git a/src/script/sign.h b/src/script/sign.h index 0c9263df0b4..dd86a8066a2 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -36,6 +36,7 @@ public: virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0; virtual std::vector CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const =0; virtual bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0; + virtual bool CreateMuSig2AggregateSig(const std::vector& participants, std::vector& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0; }; /** A signature creator for transactions. */ @@ -58,6 +59,7 @@ public: bool CreateSchnorrSig(const SigningProvider& provider, std::vector& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override; std::vector CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const override; bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override; + bool CreateMuSig2AggregateSig(const std::vector& participants, std::vector& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override; }; /** A signature checker that accepts all signatures */