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-10-25 05:06:34 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                 :         539 : static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)
      11                 :             : {
      12                 :             :     // Parse the pubkeys
      13                 :         539 :     std::vector<secp256k1_pubkey> secp_pubkeys;
      14                 :         539 :     std::vector<const secp256k1_pubkey*> pubkey_ptrs;
      15         [ +  + ]:        2071 :     for (const CPubKey& pubkey : pubkeys) {
      16   [ +  -  +  -  :        1532 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) {
                   +  - ]
      17                 :             :             return false;
      18                 :             :         }
      19                 :             :     }
      20   [ -  +  +  - ]:         539 :     pubkey_ptrs.reserve(secp_pubkeys.size());
      21         [ +  + ]:        2071 :     for (const secp256k1_pubkey& p : secp_pubkeys) {
      22         [ +  - ]:        1532 :         pubkey_ptrs.push_back(&p);
      23                 :             :     }
      24                 :             : 
      25                 :             :     // Aggregate the pubkey
      26   [ -  +  +  -  :         539 :     if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) {
                   -  + ]
      27                 :           0 :         return false;
      28                 :             :     }
      29                 :             :     return true;
      30                 :         539 : }
      31                 :             : 
      32                 :         539 : static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)
      33                 :             : {
      34                 :             :     // Get the plain aggregated pubkey
      35                 :         539 :     secp256k1_pubkey agg_pubkey;
      36         [ -  + ]:         539 :     if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) {
      37                 :           0 :         return std::nullopt;
      38                 :             :     }
      39                 :             : 
      40                 :             :     // Turn into CPubKey
      41                 :         539 :     unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE];
      42                 :         539 :     size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE;
      43                 :         539 :     secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED);
      44                 :         539 :     return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);
      45                 :             : }
      46                 :             : 
      47                 :         539 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate)
      48                 :             : {
      49         [ -  + ]:         539 :     if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {
      50                 :           0 :         return std::nullopt;
      51                 :             :     }
      52                 :         539 :     std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);
      53         [ -  + ]:         539 :     if (!agg_key.has_value()) return std::nullopt;
      54   [ +  +  -  + ]:         539 :     if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt;
      55                 :         539 :     return agg_key;
      56                 :             : }
      57                 :             : 
      58                 :         396 : std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)
      59                 :             : {
      60                 :         396 :     secp256k1_musig_keyagg_cache keyagg_cache;
      61                 :         396 :     return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt);
      62                 :             : }
      63                 :             : 
      64                 :        1082 : CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey)
      65                 :             : {
      66                 :        1082 :     CExtPubKey extpub;
      67                 :        1082 :     extpub.nDepth = 0;
      68                 :        1082 :     std::memset(extpub.vchFingerprint, 0, 4);
      69                 :        1082 :     extpub.nChild = 0;
      70                 :        1082 :     extpub.chaincode = MUSIG_CHAINCODE;
      71                 :        1082 :     extpub.pubkey = pubkey;
      72                 :        1082 :     return extpub;
      73                 :             : }
      74                 :             : 
      75                 :          56 : class MuSig2SecNonceImpl
      76                 :             : {
      77                 :             : private:
      78                 :             :     //! The actual secnonce itself
      79                 :             :     secure_unique_ptr<secp256k1_musig_secnonce> m_nonce;
      80                 :             : 
      81                 :             : public:
      82         [ +  - ]:          56 :     MuSig2SecNonceImpl() : m_nonce{make_secure_unique<secp256k1_musig_secnonce>()} {}
      83                 :             : 
      84                 :             :     // Delete copy constructors
      85                 :             :     MuSig2SecNonceImpl(const MuSig2SecNonceImpl&) = delete;
      86                 :             :     MuSig2SecNonceImpl& operator=(const MuSig2SecNonceImpl&) = delete;
      87                 :             : 
      88                 :         109 :     secp256k1_musig_secnonce* Get() const { return m_nonce.get(); }
      89                 :          53 :     void Invalidate() { m_nonce.reset(); }
      90                 :         106 :     bool IsValid() { return m_nonce != nullptr; }
      91                 :             : };
      92                 :             : 
      93                 :          56 : MuSig2SecNonce::MuSig2SecNonce() : m_impl{std::make_unique<MuSig2SecNonceImpl>()} {}
      94                 :             : 
      95                 :          56 : MuSig2SecNonce::MuSig2SecNonce(MuSig2SecNonce&&) noexcept = default;
      96                 :           0 : MuSig2SecNonce& MuSig2SecNonce::operator=(MuSig2SecNonce&&) noexcept = default;
      97                 :             : 
      98                 :         112 : MuSig2SecNonce::~MuSig2SecNonce() = default;
      99                 :             : 
     100                 :         109 : secp256k1_musig_secnonce* MuSig2SecNonce::Get() const
     101                 :             : {
     102                 :         109 :     return m_impl->Get();
     103                 :             : }
     104                 :             : 
     105                 :          53 : void MuSig2SecNonce::Invalidate()
     106                 :             : {
     107                 :          53 :     return m_impl->Invalidate();
     108                 :             : }
     109                 :             : 
     110                 :         106 : bool MuSig2SecNonce::IsValid()
     111                 :             : {
     112                 :         106 :     return m_impl->IsValid();
     113                 :             : }
     114                 :             : 
     115                 :         150 : uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash)
     116                 :             : {
     117                 :         150 :     HashWriter hasher;
     118                 :         150 :     hasher << script_pubkey << part_pubkey << sighash;
     119                 :         150 :     return hasher.GetSHA256();
     120                 :             : }
     121                 :             : 
     122                 :          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)
     123                 :             : {
     124   [ -  +  -  + ]:          34 :     if (!part_pubkeys.size()) return std::nullopt;
     125                 :             : 
     126                 :             :     // Get the keyagg cache and aggregate pubkey
     127                 :          34 :     secp256k1_musig_keyagg_cache keyagg_cache;
     128         [ -  + ]:          34 :     if (!MuSig2AggregatePubkeys(part_pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt;
     129                 :             : 
     130                 :             :     // Check if enough pubnonces and partial sigs
     131   [ -  +  -  + ]:          34 :     if (pubnonces.size() != part_pubkeys.size()) return std::nullopt;
     132         [ -  + ]:          34 :     if (partial_sigs.size() != part_pubkeys.size()) return std::nullopt;
     133                 :             : 
     134                 :             :     // Parse the pubnonces and partial sigs
     135                 :          34 :     std::vector<std::tuple<secp256k1_pubkey, secp256k1_musig_pubnonce, secp256k1_musig_partial_sig>> signers_data;
     136                 :          34 :     std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;
     137                 :          34 :     std::vector<const secp256k1_musig_partial_sig*> partial_sig_ptrs;
     138         [ +  + ]:         132 :     for (const CPubKey& part_pk : part_pubkeys) {
     139                 :          98 :         const auto& pn_it = pubnonces.find(part_pk);
     140         [ -  + ]:          98 :         if (pn_it == pubnonces.end()) return std::nullopt;
     141         [ +  - ]:          98 :         const std::vector<uint8_t> pubnonce = pn_it->second;
     142   [ -  +  -  + ]:          98 :         if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;
     143                 :          98 :         const auto& it = partial_sigs.find(part_pk);
     144         [ -  + ]:          98 :         if (it == partial_sigs.end()) return std::nullopt;
     145         [ +  - ]:          98 :         const uint256& partial_sig = it->second;
     146                 :             : 
     147   [ +  -  +  - ]:          98 :         auto& [secp_pk, secp_pn, secp_ps] = signers_data.emplace_back();
     148                 :             : 
     149   [ +  -  -  + ]:          98 :         if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) {
     150                 :           0 :             return std::nullopt;
     151                 :             :         }
     152                 :             : 
     153   [ +  -  -  + ]:          98 :         if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {
     154                 :           0 :             return std::nullopt;
     155                 :             :         }
     156                 :             : 
     157   [ +  -  -  + ]:          98 :         if (!secp256k1_musig_partial_sig_parse(secp256k1_context_static, &secp_ps, partial_sig.data())) {
     158                 :           0 :             return std::nullopt;
     159                 :             :         }
     160                 :          98 :     }
     161   [ -  +  +  - ]:          34 :     pubnonce_ptrs.reserve(signers_data.size());
     162   [ -  +  +  - ]:          34 :     partial_sig_ptrs.reserve(signers_data.size());
     163   [ +  -  +  + ]:         132 :     for (auto& [_, pn, ps] : signers_data) {
     164         [ +  - ]:          98 :         pubnonce_ptrs.push_back(&pn);
     165         [ +  - ]:          98 :         partial_sig_ptrs.push_back(&ps);
     166                 :             :     }
     167                 :             : 
     168                 :             :     // Aggregate nonces
     169                 :          34 :     secp256k1_musig_aggnonce aggnonce;
     170   [ -  +  +  -  :          34 :     if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) {
                   -  + ]
     171                 :           0 :         return std::nullopt;
     172                 :             :     }
     173                 :             : 
     174                 :             :     // Apply tweaks
     175   [ +  +  +  + ]:          82 :     for (const auto& [tweak, xonly] : tweaks) {
     176         [ +  + ]:          48 :         if (xonly) {
     177   [ +  -  -  + ]:          12 :             if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     178                 :           0 :                 return std::nullopt;
     179                 :             :             }
     180   [ +  -  -  + ]:          36 :         } else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {
     181                 :           0 :             return std::nullopt;
     182                 :             :         }
     183                 :             :     }
     184                 :             : 
     185                 :             :     // Create musig_session
     186                 :          34 :     secp256k1_musig_session session;
     187   [ +  -  -  + ]:          34 :     if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) {
     188                 :           0 :         return std::nullopt;
     189                 :             :     }
     190                 :             : 
     191                 :             :     // Verify partial sigs
     192   [ +  -  +  + ]:         132 :     for (const auto& [pk, pb, ps] : signers_data) {
     193   [ +  -  -  + ]:          98 :         if (!secp256k1_musig_partial_sig_verify(secp256k1_context_static, &ps, &pb, &pk, &keyagg_cache, &session)) {
     194                 :           0 :             return std::nullopt;
     195                 :             :         }
     196                 :             :     }
     197                 :             : 
     198                 :             :     // Aggregate partial sigs
     199                 :          34 :     std::vector<uint8_t> sig;
     200         [ +  - ]:          34 :     sig.resize(64);
     201   [ -  +  +  -  :          34 :     if (!secp256k1_musig_partial_sig_agg(secp256k1_context_static, sig.data(), &session, partial_sig_ptrs.data(), partial_sig_ptrs.size())) {
                   -  + ]
     202                 :           0 :         return std::nullopt;
     203                 :             :     }
     204                 :             : 
     205                 :          34 :     return sig;
     206                 :          68 : }
        

Generated by: LCOV version 2.0-1