LCOV - code coverage report
Current view: top level - src - musig.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 87.9 % 107 94
Test Date: 2025-12-25 05:18:09 Functions: 92.9 % 14 13
Branches: 56.8 % 132 75

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2024-present The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :             : 
       5                 :             : #include <musig.h>
       6                 :             : #include <support/allocators/secure.h>
       7                 :             : 
       8                 :             : #include <secp256k1_musig.h>
       9                 :             : 
      10                 :             : //! MuSig2 chaincode as defined by BIP 328
      11                 :             : using namespace util::hex_literals;
      12                 :             : constexpr uint256 MUSIG_CHAINCODE{
      13                 :             :     // Use immediate lambda to work around GCC-14 bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117966
      14                 :             :     []() consteval { return uint256{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8}; }(),
      15                 :             : };
      16                 :             : 
      17                 :         613 : static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
      18                 :             : {
      19                 :             :     // Parse the pubkeys
      20                 :         613 :     std::vector<secp256k1_pubkey> secp_pubkeys;
      21                 :         613 :     std::vector<const secp256k1_pubkey*> pubkey_ptrs;
      22         [ +  + ]:        2348 :     for (const CPubKey& pubkey : pubkeys) {
      23   [ +  -  +  -  :        1735 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) {
                   +  - ]
      24                 :             :             return false;
      25                 :             :         }
      26                 :             :     }
      27   [ -  +  +  - ]:         613 :     pubkey_ptrs.reserve(secp_pubkeys.size());
      28         [ +  + ]:        2348 :     for (const secp256k1_pubkey& p : secp_pubkeys) {
      29         [ +  - ]:        1735 :         pubkey_ptrs.push_back(&p);
      30                 :             :     }
      31                 :             : 
      32                 :             :     // Aggregate the pubkey
      33   [ -  +  +  -  :         613 :     if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) {
                   -  + ]
      34                 :           0 :         return false;
      35                 :             :     }
      36                 :             :     return true;
      37                 :         613 : }
      38                 :             : 
      39                 :         613 : static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
      40                 :             : {
      41                 :             :     // Get the plain aggregated pubkey
      42                 :         613 :     secp256k1_pubkey agg_pubkey;
      43         [ -  + ]:         613 :     if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) {
      44                 :           0 :         return std::nullopt;
      45                 :             :     }
      46                 :             : 
      47                 :             :     // Turn into CPubKey
      48                 :         613 :     unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE];
      49                 :         613 :     size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE;
      50                 :         613 :     secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED);
      51                 :         613 :     return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);
      52                 :             : }
      53                 :             : 
      54                 :         613 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate)
      55                 :             : {
      56         [ -  + ]:         613 :     if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {
      57                 :           0 :         return std::nullopt;
      58                 :             :     }
      59                 :         613 :     std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
      60         [ -  + ]:         613 :     if (!agg_key.has_value()) return std::nullopt;
      61   [ +  +  -  + ]:         613 :     if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt;
      62                 :         613 :     return agg_key;
      63                 :             : }
      64                 :             : 
      65                 :         461 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
      66                 :             : {
      67                 :         461 :     secp256k1_musig_keyagg_cache keyagg_cache;
      68                 :         461 :     return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt);
      69                 :             : }
      70                 :             : 
      71                 :        1068 : CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey)
      72                 :             : {
      73                 :        1068 :     CExtPubKey extpub;
      74                 :        1068 :     extpub.nDepth = 0;
      75                 :        1068 :     std::memset(extpub.vchFingerprint, 0, 4);
      76                 :        1068 :     extpub.nChild = 0;
      77                 :        1068 :     extpub.chaincode = MUSIG_CHAINCODE;
      78                 :        1068 :     extpub.pubkey = pubkey;
      79                 :        1068 :     return extpub;
      80                 :             : }
      81                 :             : 
      82                 :          63 : class MuSig2SecNonceImpl
      83                 :             : {
      84                 :             : private:
      85                 :             :     //! The actual secnonce itself
      86                 :             :     secure_unique_ptr<secp256k1_musig_secnonce> m_nonce;
      87                 :             : 
      88                 :             : public:
      89         [ +  - ]:          63 :     MuSig2SecNonceImpl() : m_nonce{make_secure_unique<secp256k1_musig_secnonce>()} {}
      90                 :             : 
      91                 :             :     // Delete copy constructors
      92                 :             :     MuSig2SecNonceImpl(const MuSig2SecNonceImpl&) = delete;
      93                 :             :     MuSig2SecNonceImpl& operator=(const MuSig2SecNonceImpl&) = delete;
      94                 :             : 
      95                 :         118 :     secp256k1_musig_secnonce* Get() const { return m_nonce.get(); }
      96                 :          55 :     void Invalidate() { m_nonce.reset(); }
      97                 :         110 :     bool IsValid() { return m_nonce != nullptr; }
      98                 :             : };
      99                 :             : 
     100                 :          63 : MuSig2SecNonce::MuSig2SecNonce() : m_impl{std::make_unique<MuSig2SecNonceImpl>()} {}
     101                 :             : 
     102                 :          63 : MuSig2SecNonce::MuSig2SecNonce(MuSig2SecNonce&&) noexcept = default;
     103                 :           0 : MuSig2SecNonce& MuSig2SecNonce::operator=(MuSig2SecNonce&&) noexcept = default;
     104                 :             : 
     105                 :         126 : MuSig2SecNonce::~MuSig2SecNonce() = default;
     106                 :             : 
     107                 :         118 : secp256k1_musig_secnonce* MuSig2SecNonce::Get() const
     108                 :             : {
     109                 :         118 :     return m_impl->Get();
     110                 :             : }
     111                 :             : 
     112                 :          55 : void MuSig2SecNonce::Invalidate()
     113                 :             : {
     114                 :          55 :     return m_impl->Invalidate();
     115                 :             : }
     116                 :             : 
     117                 :         110 : bool MuSig2SecNonce::IsValid()
     118                 :             : {
     119                 :         110 :     return m_impl->IsValid();
     120                 :             : }
     121                 :             : 
     122                 :         161 : uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash)
     123                 :             : {
     124                 :         161 :     HashWriter hasher;
     125                 :         161 :     hasher << script_pubkey << part_pubkey << sighash;
     126                 :         161 :     return hasher.GetSHA256();
     127                 :             : }
     128                 :             : 
     129                 :          34 : std::optional<std::vector<uint8_t>> CreateMuSig2AggregateSig(const std::vector<CPubKey>& part_pubkeys, const CPubKey& aggregate_pubkey, const std::vector<std::pair<uint256, bool>>& tweaks, const uint256& sighash, const std::map<CPubKey, std::vector<uint8_t>>& pubnonces, const std::map<CPubKey, uint256>& partial_sigs)
     130                 :             : {
     131   [ -  +  -  + ]:          34 :     if (!part_pubkeys.size()) return std::nullopt;
     132                 :             : 
     133                 :             :     // Get the keyagg cache and aggregate pubkey
     134                 :          34 :     secp256k1_musig_keyagg_cache keyagg_cache;
     135         [ -  + ]:          34 :     if (!MuSig2AggregatePubkeys(part_pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt;
     136                 :             : 
     137                 :             :     // Check if enough pubnonces and partial sigs
     138   [ -  +  -  + ]:          34 :     if (pubnonces.size() != part_pubkeys.size()) return std::nullopt;
     139         [ -  + ]:          34 :     if (partial_sigs.size() != part_pubkeys.size()) return std::nullopt;
     140                 :             : 
     141                 :             :     // Parse the pubnonces and partial sigs
     142                 :          34 :     std::vector<std::tuple<secp256k1_pubkey, secp256k1_musig_pubnonce, secp256k1_musig_partial_sig>> signers_data;
     143                 :          34 :     std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
     144                 :          34 :     std::vector<const secp256k1_musig_partial_sig*> partial_sig_ptrs;
     145         [ +  + ]:         132 :     for (const CPubKey& part_pk : part_pubkeys) {
     146                 :          98 :         const auto& pn_it = pubnonces.find(part_pk);
     147         [ -  + ]:          98 :         if (pn_it == pubnonces.end()) return std::nullopt;
     148         [ +  - ]:          98 :         const std::vector<uint8_t> pubnonce = pn_it->second;
     149   [ -  +  -  + ]:          98 :         if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
     150                 :          98 :         const auto& it = partial_sigs.find(part_pk);
     151         [ -  + ]:          98 :         if (it == partial_sigs.end()) return std::nullopt;
     152         [ +  - ]:          98 :         const uint256& partial_sig = it->second;
     153                 :             : 
     154   [ +  -  +  - ]:          98 :         auto& [secp_pk, secp_pn, secp_ps] = signers_data.emplace_back();
     155                 :             : 
     156   [ +  -  -  + ]:          98 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) {
     157                 :           0 :             return std::nullopt;
     158                 :             :         }
     159                 :             : 
     160   [ +  -  -  + ]:          98 :         if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
     161                 :           0 :             return std::nullopt;
     162                 :             :         }
     163                 :             : 
     164   [ +  -  -  + ]:          98 :         if (!secp256k1_musig_partial_sig_parse(secp256k1_context_static, &secp_ps, partial_sig.data())) {
     165                 :           0 :             return std::nullopt;
     166                 :             :         }
     167                 :          98 :     }
     168   [ -  +  +  - ]:          34 :     pubnonce_ptrs.reserve(signers_data.size());
     169   [ -  +  +  - ]:          34 :     partial_sig_ptrs.reserve(signers_data.size());
     170   [ +  -  +  + ]:         132 :     for (auto& [_, pn, ps] : signers_data) {
     171         [ +  - ]:          98 :         pubnonce_ptrs.push_back(&pn);
     172         [ +  - ]:          98 :         partial_sig_ptrs.push_back(&ps);
     173                 :             :     }
     174                 :             : 
     175                 :             :     // Aggregate nonces
     176                 :          34 :     secp256k1_musig_aggnonce aggnonce;
     177   [ -  +  +  -  :          34 :     if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) {
                   -  + ]
     178                 :           0 :         return std::nullopt;
     179                 :             :     }
     180                 :             : 
     181                 :             :     // Apply tweaks
     182   [ +  +  +  + ]:          82 :     for (const auto& [tweak, xonly] : tweaks) {
     183         [ +  + ]:          48 :         if (xonly) {
     184   [ +  -  -  + ]:          12 :             if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     185                 :           0 :                 return std::nullopt;
     186                 :             :             }
     187   [ +  -  -  + ]:          36 :         } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     188                 :           0 :             return std::nullopt;
     189                 :             :         }
     190                 :             :     }
     191                 :             : 
     192                 :             :     // Create musig_session
     193                 :          34 :     secp256k1_musig_session session;
     194   [ +  -  -  + ]:          34 :     if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) {
     195                 :           0 :         return std::nullopt;
     196                 :             :     }
     197                 :             : 
     198                 :             :     // Verify partial sigs
     199   [ +  -  +  + ]:         132 :     for (const auto& [pk, pb, ps] : signers_data) {
     200   [ +  -  -  + ]:          98 :         if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &ps, &pb, &pk, &keyagg_cache, &session)) {
     201                 :           0 :             return std::nullopt;
     202                 :             :         }
     203                 :             :     }
     204                 :             : 
     205                 :             :     // Aggregate partial sigs
     206                 :          34 :     std::vector<uint8_t> sig;
     207         [ +  - ]:          34 :     sig.resize(64);
     208   [ -  +  +  -  :          34 :     if (!secp256k1_musig_partial_sig_agg(secp256k1_context_static, sig.data(), &session, partial_sig_ptrs.data(), partial_sig_ptrs.size())) {
                   -  + ]
     209                 :           0 :         return std::nullopt;
     210                 :             :     }
     211                 :             : 
     212                 :          34 :     return sig;
     213                 :          68 : }
        

Generated by: LCOV version 2.0-1