Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2024-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <musig.h>6#include <key.h>7#include <random.h>8#include <support/allocators/secure.h>10#include <secp256k1_musig.h>12//! MuSig2 chaincode as defined by BIP 32813using namespace util::hex_literals;14const ChainCode MUSIG_CHAINCODE{"868087ca02a6f974c4598924c36b57762d32cb45717167e300622c7167e38965"_hex_u8};16static bool GetMuSig2KeyAggCache(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache)17{18if (pubkeys.empty()) {19return false;20}22// Parse the pubkeys23std::vector<secp256k1_pubkey> secp_pubkeys;24std::vector<const secp256k1_pubkey*> pubkey_ptrs;25for (const CPubKey& pubkey : pubkeys) {26if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pubkeys.emplace_back(), pubkey.data(), pubkey.size())) {27return false;28}29}30pubkey_ptrs.reserve(secp_pubkeys.size());31for (const secp256k1_pubkey& p : secp_pubkeys) {32pubkey_ptrs.push_back(&p);33}35// Aggregate the pubkey36if (!secp256k1_musig_pubkey_agg(secp256k1_context_static, nullptr, &keyagg_cache, pubkey_ptrs.data(), pubkey_ptrs.size())) {37return false;38}39return true;40}42static std::optional<CPubKey> GetCPubKeyFromMuSig2KeyAggCache(secp256k1_musig_keyagg_cache& keyagg_cache)43{44// Get the plain aggregated pubkey45secp256k1_pubkey agg_pubkey;46if (!secp256k1_musig_pubkey_get(secp256k1_context_static, &agg_pubkey, &keyagg_cache)) {47return std::nullopt;48}50// Turn into CPubKey51unsigned char ser_agg_pubkey[CPubKey::COMPRESSED_SIZE];52size_t ser_agg_pubkey_len = CPubKey::COMPRESSED_SIZE;53secp256k1_ec_pubkey_serialize(secp256k1_context_static, ser_agg_pubkey, &ser_agg_pubkey_len, &agg_pubkey, SECP256K1_EC_COMPRESSED);54return CPubKey(ser_agg_pubkey, ser_agg_pubkey + ser_agg_pubkey_len);55}57std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys, secp256k1_musig_keyagg_cache& keyagg_cache, const std::optional<CPubKey>& expected_aggregate)58{59if (!GetMuSig2KeyAggCache(pubkeys, keyagg_cache)) {60return std::nullopt;61}62std::optional<CPubKey> agg_key = GetCPubKeyFromMuSig2KeyAggCache(keyagg_cache);63if (!agg_key.has_value()) return std::nullopt;64if (expected_aggregate.has_value() && expected_aggregate != agg_key) return std::nullopt;65return agg_key;66}68std::optional<CPubKey> MuSig2AggregatePubkeys(const std::vector<CPubKey>& pubkeys)69{70secp256k1_musig_keyagg_cache keyagg_cache;71return MuSig2AggregatePubkeys(pubkeys, keyagg_cache, std::nullopt);72}74CExtPubKey CreateMuSig2SyntheticXpub(const CPubKey& pubkey)75{76CExtPubKey extpub;77extpub.nDepth = 0;78extpub.fingerprint.fill(0);79extpub.nChild = 0;80extpub.chaincode = MUSIG_CHAINCODE;81extpub.pubkey = pubkey;82return extpub;83}85class MuSig2SecNonceImpl86{87private:88//! The actual secnonce itself89secure_unique_ptr<secp256k1_musig_secnonce> m_nonce;91public:92MuSig2SecNonceImpl() : m_nonce{make_secure_unique<secp256k1_musig_secnonce>()} {}94// Delete copy constructors95MuSig2SecNonceImpl(const MuSig2SecNonceImpl&) = delete;96MuSig2SecNonceImpl& operator=(const MuSig2SecNonceImpl&) = delete;98secp256k1_musig_secnonce* Get() const { return m_nonce.get(); }99void Invalidate() { m_nonce.reset(); }100bool IsValid() { return m_nonce != nullptr; }101};103MuSig2SecNonce::MuSig2SecNonce() : m_impl{std::make_unique<MuSig2SecNonceImpl>()} {}105MuSig2SecNonce::MuSig2SecNonce(MuSig2SecNonce&&) noexcept = default;106MuSig2SecNonce& MuSig2SecNonce::operator=(MuSig2SecNonce&&) noexcept = default;108MuSig2SecNonce::~MuSig2SecNonce() = default;110secp256k1_musig_secnonce* MuSig2SecNonce::Get() const111{112return m_impl->Get();113}115void MuSig2SecNonce::Invalidate()116{117return m_impl->Invalidate();118}120bool MuSig2SecNonce::IsValid()121{122return m_impl->IsValid();123}125uint256 MuSig2SessionID(const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256& sighash, const std::vector<uint8_t>& pubnonce)126{127HashWriter hasher;128hasher << script_pubkey << part_pubkey << sighash << pubnonce;129return hasher.GetSHA256();130}132std::vector<uint8_t> CreateMuSig2Nonce(MuSig2SecNonce& secnonce, const uint256& sighash, const CKey& our_seckey, const CPubKey& aggregate_pubkey, const std::vector<CPubKey>& pubkeys)133{134// Get the keyagg cache and aggregate pubkey135secp256k1_musig_keyagg_cache keyagg_cache;136if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return {};138// Parse participant pubkey139CPubKey our_pubkey = our_seckey.GetPubKey();140secp256k1_pubkey pubkey;141if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &pubkey, our_pubkey.data(), our_pubkey.size())) {142return {};143}145// Generate randomness for nonce146uint256 rand;147GetStrongRandBytes(rand);149// Generate nonce150secp256k1_musig_pubnonce pubnonce;151if (!secp256k1_musig_nonce_gen(GetSecp256k1SignContext(), secnonce.Get(), &pubnonce, rand.data(), UCharCast(our_seckey.begin()), &pubkey, sighash.data(), &keyagg_cache, nullptr)) {152return {};153}155// Serialize pubnonce156std::vector<uint8_t> out;157out.resize(MUSIG2_PUBNONCE_SIZE);158if (!secp256k1_musig_pubnonce_serialize(secp256k1_context_static, out.data(), &pubnonce)) {159return {};160}162return out;163}165std::optional<uint256> CreateMuSig2PartialSig(const uint256& sighash, const CKey& our_seckey, 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)166{167secp256k1_keypair keypair;168if (!secp256k1_keypair_create(GetSecp256k1SignContext(), &keypair, UCharCast(our_seckey.begin()))) return std::nullopt;170// Get the keyagg cache and aggregate pubkey171secp256k1_musig_keyagg_cache keyagg_cache;172if (!MuSig2AggregatePubkeys(pubkeys, keyagg_cache, aggregate_pubkey)) return std::nullopt;174// Check that there are enough pubnonces175if (pubnonces.size() != pubkeys.size()) return std::nullopt;177// Parse the pubnonces178std::vector<std::pair<secp256k1_pubkey, secp256k1_musig_pubnonce>> signers_data;179std::vector<const secp256k1_musig_pubnonce*> pubnonce_ptrs;180std::optional<size_t> our_pubkey_idx;181CPubKey our_pubkey = our_seckey.GetPubKey();182for (const CPubKey& part_pk : pubkeys) {183const auto& pn_it = pubnonces.find(part_pk);184if (pn_it == pubnonces.end()) return std::nullopt;185const std::vector<uint8_t> pubnonce = pn_it->second;186if (pubnonce.size() != MUSIG2_PUBNONCE_SIZE) return std::nullopt;187if (part_pk == our_pubkey) {188our_pubkey_idx = signers_data.size();189}191auto& [secp_pk, secp_pn] = signers_data.emplace_back();193if (!secp256k1_ec_pubkey_parse(secp256k1_context_static, &secp_pk, part_pk.data(), part_pk.size())) {194return std::nullopt;195}197if (!secp256k1_musig_pubnonce_parse(secp256k1_context_static, &secp_pn, pubnonce.data())) {198return std::nullopt;199}200}201if (our_pubkey_idx == std::nullopt) {202return std::nullopt;203}204pubnonce_ptrs.reserve(signers_data.size());205for (auto& [_, pn] : signers_data) {206pubnonce_ptrs.push_back(&pn);207}209// Aggregate nonces210secp256k1_musig_aggnonce aggnonce;211if (!secp256k1_musig_nonce_agg(secp256k1_context_static, &aggnonce, pubnonce_ptrs.data(), pubnonce_ptrs.size())) {212return std::nullopt;213}215// Apply tweaks216for (const auto& [tweak, xonly] : tweaks) {217if (xonly) {218if (!secp256k1_musig_pubkey_xonly_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {219return std::nullopt;220}221} else if (!secp256k1_musig_pubkey_ec_tweak_add(secp256k1_context_static, nullptr, &keyagg_cache, tweak.data())) {222return std::nullopt;223}224}226// Create musig_session227secp256k1_musig_session session;228if (!secp256k1_musig_nonce_process(secp256k1_context_static, &session, &aggnonce, sighash.data(), &keyagg_cache)) {229return std::nullopt;230}232// Create partial signature233secp256k1_musig_partial_sig psig;234if (!secp256k1_musig_partial_sign(secp256k1_context_static, &psig, secnonce.Get(), &keypair, &keyagg_cache, &session)) {235return std::nullopt;236}237// The secnonce must be deleted after signing to prevent nonce reuse.238secnonce.Invalidate();240// Verify partial signature241if (!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)) {242return std::nullopt;243}245// Serialize246uint256 sig;247if (!secp256k1_musig_partial_sig_serialize(secp256k1_context_static, sig.data(), &psig)) {248return std::nullopt;249}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.