Bitcoin Core Fuzz Coverage Report for wallet_tx_can_be_bumped

Coverage Report

Created: 2025-11-19 11:20

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