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.
251virtual bool CanSelfExpand() const = 0;253protected:254static bool DetermineApostropheUse(StringType type, bool normalized, bool public_apostrophe)255{256bool use_apostrophe{false};257switch (type) {258case StringType::COMPAT:259// COMPAT always uses apostrophe to stay compatible with previous versions260use_apostrophe = true;261break;262case StringType::CANONICAL:263// CANONICAL always uses h264use_apostrophe = false;265break;266case StringType::PUBLIC:267use_apostrophe = !normalized && public_apostrophe;268break;269} // no default case, so the compiler can warn about missing cases270return use_apostrophe;271}272};274class OriginPubkeyProvider final : public PubkeyProvider275{276KeyOriginInfo m_origin;277std::unique_ptr<PubkeyProvider> m_provider;278bool m_apostrophe;280std::string OriginString(StringType type, bool normalized=false) const281{282bool use_apostrophe{DetermineApostropheUse(type, normalized, m_apostrophe)};283return HexStr(m_origin.fingerprint) + FormatHDKeypath(m_origin.path, use_apostrophe);284}286public:287OriginPubkeyProvider(uint32_t exp_index, KeyOriginInfo info, std::unique_ptr<PubkeyProvider> provider, bool apostrophe) : PubkeyProvider(exp_index), m_origin(std::move(info)), m_provider(std::move(provider)), m_apostrophe(apostrophe) {}288std::optional<CPubKey> GetPubKey(int pos, const SigningProvider& arg, FlatSigningProvider& out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override289{290// Derive into a temporary provider. Another key expression may have already put this291// key into out with its origin prefixed, and prefixing that entry would double it up.292FlatSigningProvider subprovider;293std::optional<CPubKey> pub = m_provider->GetPubKey(pos, arg, subprovider, read_cache, write_cache);294if (!pub) return std::nullopt;295const CKeyID keyid{pub->GetID()};296Assert(subprovider.pubkeys.contains(keyid));297auto& [pubkey, suborigin] = subprovider.origins[keyid];298Assert(pubkey == *pub); // m_provider must have a valid origin by this point.299suborigin.fingerprint = m_origin.fingerprint;300suborigin.path.insert(suborigin.path.begin(), m_origin.path.begin(), m_origin.path.end());301auto origin{subprovider.origins.extract(keyid)};302out.Merge(std::move(subprovider));303// An explicit origin takes precedence over an implicit one for the same key.304out.origins.insert_or_assign(keyid, std::move(origin.mapped()));305return pub;306}307bool IsRange() const override { return m_provider->IsRange(); }308size_t GetSize() const override { return m_provider->GetSize(); }309bool IsBIP32() const override { return m_provider->IsBIP32(); }310std::string ToString(StringType type) const override { return "[" + OriginString(type) + "]" + m_provider->ToString(type); }311bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override312{313std::string sub;314bool has_priv_key{m_provider->ToPrivateString(arg, sub)};315ret = "[" + OriginString(StringType::PUBLIC) + "]" + std::move(sub);316return has_priv_key;317}318bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override319{320std::string sub;321if (!m_provider->ToNormalizedString(arg, sub, cache)) return false;322// If m_provider is a BIP32PubkeyProvider, we may get a string formatted like a OriginPubkeyProvider323// In that case, we need to strip out the leading square bracket and fingerprint from the substring,324// and append that to our own origin string.325if (sub[0] == '[') {326sub = sub.substr(9);327ret = "[" + OriginString(StringType::PUBLIC, /*normalized=*/true) + std::move(sub);328} else {329ret = "[" + OriginString(StringType::PUBLIC, /*normalized=*/true) + "]" + std::move(sub);330}331return true;332}333void GetPrivKey(int pos, const SigningProvider& arg, FlatSigningProvider& out) const override334{335m_provider->GetPrivKey(pos, arg, out);336}337std::optional<CPubKey> GetRootPubKey() const override338{339return m_provider->GetRootPubKey();340}341std::optional<CExtPubKey> GetRootExtPubKey() const override342{343return m_provider->GetRootExtPubKey();344}345std::unique_ptr<PubkeyProvider> Clone() const override346{347return std::make_unique<OriginPubkeyProvider>(m_expr_index, m_origin, m_provider->Clone(), m_apostrophe);348}349bool CanSelfExpand() const override { return m_provider->CanSelfExpand(); }350};352/** An object representing a parsed constant public key in a descriptor. */353class ConstPubkeyProvider final : public PubkeyProvider354{355CPubKey m_pubkey;356bool m_xonly;358std::optional<CKey> GetPrivKey(const SigningProvider& arg) const359{360CKey key;361if (!(m_xonly ? arg.GetKeyByXOnly(XOnlyPubKey(m_pubkey), key) :362arg.GetKey(m_pubkey.GetID(), key))) return std::nullopt;363return key;364}366public:367ConstPubkeyProvider(uint32_t exp_index, const CPubKey& pubkey, bool xonly) : PubkeyProvider(exp_index), m_pubkey(pubkey), m_xonly(xonly) {}368std::optional<CPubKey> GetPubKey(int pos, const SigningProvider&, FlatSigningProvider& out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override369{370KeyOriginInfo info;371CKeyID keyid = m_pubkey.GetID();372info.fingerprint = keyid.fingerprint();373out.origins.emplace(keyid, std::make_pair(m_pubkey, info));374out.pubkeys.emplace(keyid, m_pubkey);375return m_pubkey;376}377bool IsRange() const override { return false; }378size_t GetSize() const override { return m_pubkey.size(); }379bool IsBIP32() const override { return false; }380std::string ToString(StringType type) const override { return m_xonly ? HexStr(m_pubkey).substr(2) : HexStr(m_pubkey); }381bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override382{383std::optional<CKey> key = GetPrivKey(arg);384if (!key) {385ret = ToString(StringType::PUBLIC);386return false;387}388ret = EncodeSecret(*key);389return true;390}391bool ToNormalizedString(const SigningProvider& arg, std::string& ret, const DescriptorCache* cache) const override392{393ret = ToString(StringType::PUBLIC);394return true;395}396void GetPrivKey(int pos, const SigningProvider& arg, FlatSigningProvider& out) const override397{398std::optional<CKey> key = GetPrivKey(arg);399if (!key) return;400out.keys.emplace(key->GetPubKey().GetID(), *key);401}402std::optional<CPubKey> GetRootPubKey() const override403{404return m_pubkey;405}406std::optional<CExtPubKey> GetRootExtPubKey() const override407{408return std::nullopt;409}410std::unique_ptr<PubkeyProvider> Clone() const override411{412return std::make_unique<ConstPubkeyProvider>(m_expr_index, m_pubkey, m_xonly);413}414bool CanSelfExpand() const final { return true; }415};417enum class DeriveType {418NON_RANGED,419UNHARDENED_RANGED,420HARDENED_RANGED,421};423/** An object representing a parsed extended public key in a descriptor. */424class BIP32PubkeyProvider final : public PubkeyProvider425{426// Root xpub, path, and final derivation step type being used, if any427CExtPubKey m_root_extkey;428KeyPath m_path;429DeriveType m_derive;430// Whether ' or h is used in harded derivation431bool m_apostrophe;433bool GetExtKey(const SigningProvider& arg, CExtKey& ret) const434{435CKey key;436if (!arg.GetKey(m_root_extkey.pubkey.GetID(), key)) return false;437ret.nDepth = m_root_extkey.nDepth;438ret.fingerprint = m_root_extkey.fingerprint;439ret.nChild = m_root_extkey.nChild;440ret.chaincode = m_root_extkey.chaincode;441ret.key = key;442return true;443}445// Derives the last xprv446bool GetDerivedExtKey(const SigningProvider& arg, CExtKey& xprv, CExtKey& last_hardened) const447{448if (!GetExtKey(arg, xprv)) return false;449for (auto entry : m_path) {450if (!xprv.Derive(xprv, entry)) return false;451if (entry >> 31) {452last_hardened = xprv;453}454}455return true;456}458bool IsHardened() const459{460if (m_derive == DeriveType::HARDENED_RANGED) return true;461return HasHardenedDerivation(m_path);462}464public:465BIP32PubkeyProvider(uint32_t exp_index, const CExtPubKey& extkey, KeyPath path, DeriveType derive, bool apostrophe) : PubkeyProvider(exp_index), m_root_extkey(extkey), m_path(std::move(path)), m_derive(derive), m_apostrophe(apostrophe) {}466bool IsRange() const override { return m_derive != DeriveType::NON_RANGED; }467size_t GetSize() const override { return 33; }468bool IsBIP32() const override { return true; }469std::optional<CPubKey> GetPubKey(int pos, const SigningProvider& arg, FlatSigningProvider& out, const DescriptorCache* read_cache = nullptr, DescriptorCache* write_cache = nullptr) const override470{471KeyOriginInfo info;472info.fingerprint = m_root_extkey.id_key_fingerprint();473info.path = m_path;474if (m_derive == DeriveType::UNHARDENED_RANGED) info.path.push_back((uint32_t)pos);475if (m_derive == DeriveType::HARDENED_RANGED) info.path.push_back(((uint32_t)pos) | BIP32_HARDENED_FLAG);477// Derive keys or fetch them from cache478CExtPubKey final_extkey = m_root_extkey;479CExtPubKey parent_extkey = m_root_extkey;480CExtPubKey last_hardened_extkey;481bool der = true;482if (read_cache) {483if (!read_cache->GetCachedDerivedExtPubKey(m_expr_index, pos, final_extkey)) {484if (m_derive == DeriveType::HARDENED_RANGED) return std::nullopt;485// Try to get the derivation parent486if (!read_cache->GetCachedParentExtPubKey(m_expr_index, parent_extkey)) return std::nullopt;487final_extkey = parent_extkey;488if (m_derive == DeriveType::UNHARDENED_RANGED) der = parent_extkey.Derive(final_extkey, pos);489}490} else if (IsHardened()) {491CExtKey xprv;492CExtKey lh_xprv;493if (!GetDerivedExtKey(arg, xprv, lh_xprv)) return std::nullopt;494parent_extkey = xprv.Neuter();495if (m_derive == DeriveType::UNHARDENED_RANGED) der = xprv.Derive(xprv, pos);496if (m_derive == DeriveType::HARDENED_RANGED) der = xprv.Derive(xprv, pos | BIP32_HARDENED_FLAG);497final_extkey = xprv.Neuter();498if (lh_xprv.key.IsValid()) {499last_hardened_extkey = lh_xprv.Neuter();500}