Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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.
501} else {502for (auto entry : m_path) {503if (!parent_extkey.Derive(parent_extkey, entry)) return std::nullopt;504}505final_extkey = parent_extkey;506if (m_derive == DeriveType::UNHARDENED_RANGED) der = parent_extkey.Derive(final_extkey, pos);507assert(m_derive != DeriveType::HARDENED_RANGED);508}509if (!der) return std::nullopt;511out.origins.emplace(final_extkey.pubkey.GetID(), std::make_pair(final_extkey.pubkey, info));512out.pubkeys.emplace(final_extkey.pubkey.GetID(), final_extkey.pubkey);514if (write_cache) {515// Only cache parent if there is any unhardened derivation516if (m_derive != DeriveType::HARDENED_RANGED) {517write_cache->CacheParentExtPubKey(m_expr_index, parent_extkey);518// Cache last hardened xpub if we have it519if (last_hardened_extkey.pubkey.IsValid()) {520write_cache->CacheLastHardenedExtPubKey(m_expr_index, last_hardened_extkey);521}522} else if (info.path.size() > 0) {523write_cache->CacheDerivedExtPubKey(m_expr_index, pos, final_extkey);524}525}527return final_extkey.pubkey;528}529std::string ToString(StringType type, bool normalized) const530{531bool use_apostrophe{DetermineApostropheUse(type, normalized, m_apostrophe)};532std::string ret = EncodeExtPubKey(m_root_extkey) + FormatHDKeypath(m_path, /*apostrophe=*/use_apostrophe);533if (IsRange()) {534ret += "/*";535if (m_derive == DeriveType::HARDENED_RANGED) ret += use_apostrophe ? '\'' : 'h';536}537return ret;538}539std::string ToString(StringType type) const override540{541return ToString(type, /*normalized=*/false);542}543bool ToPrivateString(const SigningProvider& arg, std::string& out) const override544{545CExtKey key;546if (!GetExtKey(arg, key)) {547out = ToString(StringType::PUBLIC);548return false;549}550out = EncodeExtKey(key) + FormatHDKeypath(m_path, /*apostrophe=*/m_apostrophe);551if (IsRange()) {552out += "/*";553if (m_derive == DeriveType::HARDENED_RANGED) out += m_apostrophe ? '\'' : 'h';554}555return true;556}557bool ToNormalizedString(const SigningProvider& arg, std::string& out, const DescriptorCache* cache) const override558{559if (m_derive == DeriveType::HARDENED_RANGED) {560out = ToString(StringType::PUBLIC, /*normalized=*/true);562return true;563}564// Step backwards to find the last hardened step in the path565int i = (int)m_path.size() - 1;566for (; i >= 0; --i) {567if (m_path.at(i) >> 31) {568break;569}570}571// Either no derivation or all unhardened derivation572if (i == -1) {573out = ToString(StringType::PUBLIC);574return true;575}576// Get the path to the last hardened stup577KeyOriginInfo origin;578int k = 0;579for (; k <= i; ++k) {580// Add to the path581origin.path.push_back(m_path.at(k));582}583// Build the remaining path584KeyPath end_path;585for (; k < (int)m_path.size(); ++k) {586end_path.push_back(m_path.at(k));587}588origin.fingerprint = m_root_extkey.id_key_fingerprint();590CExtPubKey xpub;591CExtKey lh_xprv;592// If we have the cache, just get the parent xpub593if (cache != nullptr) {594cache->GetCachedLastHardenedExtPubKey(m_expr_index, xpub);595}596if (!xpub.pubkey.IsValid()) {597// Cache miss, or nor cache, or need privkey598CExtKey xprv;599if (!GetDerivedExtKey(arg, xprv, lh_xprv)) return false;600xpub = lh_xprv.Neuter();601}602assert(xpub.pubkey.IsValid());604// Build the string605std::string origin_str = HexStr(origin.fingerprint) + FormatHDKeypath(origin.path);606out = "[" + origin_str + "]" + EncodeExtPubKey(xpub) + FormatHDKeypath(end_path);607if (IsRange()) {608out += "/*";609assert(m_derive == DeriveType::UNHARDENED_RANGED);610}611return true;612}613void GetPrivKey(int pos, const SigningProvider& arg, FlatSigningProvider& out) const override614{615CExtKey extkey;616CExtKey dummy;617if (!GetDerivedExtKey(arg, extkey, dummy)) return;618if (m_derive == DeriveType::UNHARDENED_RANGED && !extkey.Derive(extkey, pos)) return;619if (m_derive == DeriveType::HARDENED_RANGED && !extkey.Derive(extkey, pos | BIP32_HARDENED_FLAG)) return;620out.keys.emplace(extkey.key.GetPubKey().GetID(), extkey.key);621}622std::optional<CPubKey> GetRootPubKey() const override623{624return std::nullopt;625}626std::optional<CExtPubKey> GetRootExtPubKey() const override627{628return m_root_extkey;629}630std::unique_ptr<PubkeyProvider> Clone() const override631{632return std::make_unique<BIP32PubkeyProvider>(m_expr_index, m_root_extkey, m_path, m_derive, m_apostrophe);633}634bool CanSelfExpand() const override { return !IsHardened(); }635};637/** PubkeyProvider for a musig() expression */638class MuSigPubkeyProvider final : public PubkeyProvider639{640private:641//! PubkeyProvider for the participants642const std::vector<std::unique_ptr<PubkeyProvider>> m_participants;643//! Derivation path644const KeyPath m_path;645//! PubkeyProvider for the aggregate pubkey if it can be cached (i.e. participants are not ranged)646mutable std::unique_ptr<PubkeyProvider> m_aggregate_provider;647mutable std::optional<CPubKey> m_aggregate_pubkey;648const DeriveType m_derive;649const bool m_ranged_participants;651bool IsRangedDerivation() const { return m_derive != DeriveType::NON_RANGED; }653public:654MuSigPubkeyProvider(655uint32_t exp_index,656std::vector<std::unique_ptr<PubkeyProvider>> providers,657KeyPath path,658DeriveType derive659)660: PubkeyProvider(exp_index),661m_participants(std::move(providers)),662m_path(std::move(path)),663m_derive(derive),664m_ranged_participants(std::any_of(m_participants.begin(), m_participants.end(), [](const auto& pubkey) { return pubkey->IsRange(); }))665{666if (!Assume(!(m_ranged_participants && IsRangedDerivation()))) {667throw std::runtime_error("musig(): Cannot have both ranged participants and ranged derivation");668}669if (!Assume(m_derive != DeriveType::HARDENED_RANGED)) {670throw std::runtime_error("musig(): Cannot have hardened derivation");671}672}674std::optional<CPubKey> GetPubKey(int pos, const SigningProvider& arg, FlatSigningProvider& out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override675{676FlatSigningProvider dummy;677// If the participants are not ranged, we can compute and cache the aggregate pubkey by creating a PubkeyProvider for it678if (!m_aggregate_provider && !m_ranged_participants) {679// Retrieve the pubkeys from the providers680std::vector<CPubKey> pubkeys;681for (const auto& prov : m_participants) {682std::optional<CPubKey> pubkey = prov->GetPubKey(0, arg, dummy, read_cache, write_cache);683if (!pubkey.has_value()) {684return std::nullopt;685}686pubkeys.push_back(pubkey.value());687}688std::sort(pubkeys.begin(), pubkeys.end());690// Aggregate the pubkey691m_aggregate_pubkey = MuSig2AggregatePubkeys(pubkeys);692if (!Assume(m_aggregate_pubkey.has_value())) return std::nullopt;694// Make our pubkey provider695if (IsRangedDerivation() || !m_path.empty()) {696// Make the synthetic xpub and construct the BIP32PubkeyProvider697CExtPubKey extpub = CreateMuSig2SyntheticXpub(m_aggregate_pubkey.value());698m_aggregate_provider = std::make_unique<BIP32PubkeyProvider>(m_expr_index, extpub, m_path, m_derive, /*apostrophe=*/false);699} else {700m_aggregate_provider = std::make_unique<ConstPubkeyProvider>(m_expr_index, m_aggregate_pubkey.value(), /*xonly=*/false);701}702}704// Retrieve all participant pubkeys705std::vector<CPubKey> pubkeys;706for (const auto& prov : m_participants) {707std::optional<CPubKey> pub = prov->GetPubKey(pos, arg, out, read_cache, write_cache);708if (!pub) return std::nullopt;709pubkeys.emplace_back(*pub);710}711std::sort(pubkeys.begin(), pubkeys.end());713CPubKey pubout;714if (m_aggregate_provider) {715// When we have a cached aggregate key, we are either returning it or deriving from it716// Either way, we can passthrough to its GetPubKey717// Use a dummy signing provider as private keys do not exist for the aggregate pubkey718std::optional<CPubKey> pub = m_aggregate_provider->GetPubKey(pos, dummy, out, read_cache, write_cache);719if (!pub) return std::nullopt;720pubout = *pub;721out.aggregate_pubkeys.emplace(m_aggregate_pubkey.value(), pubkeys);722} else {723if (!Assume(m_ranged_participants) || !Assume(m_path.empty())) return std::nullopt;724// Compute aggregate key from derived participants725std::optional<CPubKey> aggregate_pubkey = MuSig2AggregatePubkeys(pubkeys);726if (!aggregate_pubkey) return std::nullopt;727pubout = *aggregate_pubkey;729std::unique_ptr<ConstPubkeyProvider> this_agg_provider = std::make_unique<ConstPubkeyProvider>(m_expr_index, aggregate_pubkey.value(), /*xonly=*/false);730this_agg_provider->GetPubKey(0, dummy, out, read_cache, write_cache);731out.aggregate_pubkeys.emplace(pubout, pubkeys);732}734if (!Assume(pubout.IsValid())) return std::nullopt;735return pubout;736}737bool IsRange() const override { return IsRangedDerivation() || m_ranged_participants; }738// musig() expressions can only be used in tr() contexts which have 32 byte xonly pubkeys739size_t GetSize() const override { return 32; }741std::string ToString(StringType type) const override742{743std::string out = "musig(";744for (size_t i = 0; i < m_participants.size(); ++i) {745const auto& pubkey = m_participants.at(i);746if (i) out += ",";747out += pubkey->ToString(type);748}749out += ")";750out += FormatHDKeypath(m_path);