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// P2SH, P2WSH, and P2SH-P2WSH as a precaution.502for (const auto& script : setWatchOnly) {503add_script(script);504}506return candidate_spks;507}509std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys() const510{511// Run IsMine() on each candidate output script. Any script that IsMine is an output512// script to return.513// This both filters out things that are not watched by the wallet, and things that are invalid.514std::unordered_set<CScript, SaltedSipHasher> spks;515for (const CScript& script : GetCandidateScriptPubKeys()) {516if (IsMine(script)) {517spks.insert(script);518}519}521return spks;522}524std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetNotMineScriptPubKeys() const525{526LOCK(cs_KeyStore);527std::unordered_set<CScript, SaltedSipHasher> spks;528for (const CScript& script : setWatchOnly) {529if (!IsMine(script)) spks.insert(script);530}531return spks;532}534std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()535{536LOCK(cs_KeyStore);537if (m_storage.IsLocked()) {538return std::nullopt;539}541MigrationData out;543std::unordered_set<CScript, SaltedSipHasher> spks{GetScriptPubKeys()};545// Get all key ids546std::set<CKeyID> keyids;547for (const auto& key_pair : mapKeys) {548keyids.insert(key_pair.first);549}550for (const auto& key_pair : mapCryptedKeys) {551keyids.insert(key_pair.first);552}554// Get key metadata and figure out which keys don't have a seed555// Note that we do not ignore the seeds themselves because they are considered IsMine!556for (auto keyid_it = keyids.begin(); keyid_it != keyids.end();) {557const CKeyID& keyid = *keyid_it;558const auto& it = mapKeyMetadata.find(keyid);559if (it != mapKeyMetadata.end()) {560const CKeyMetadata& meta = it->second;561if (meta.hdKeypath == "s" || meta.hdKeypath == "m") {562keyid_it++;563continue;564}565if (!meta.hd_seed_id.IsNull() && (m_hd_chain.seed_id == meta.hd_seed_id || m_inactive_hd_chains.contains(meta.hd_seed_id))) {566keyid_it = keyids.erase(keyid_it);567continue;568}569}570keyid_it++;571}573WalletBatch batch(m_storage.GetDatabase());574if (!batch.TxnBegin()) {575LogWarning("Error generating descriptors for migration, cannot initialize db transaction");576return std::nullopt;577}579// keyids is now all non-HD keys. Each key will have its own combo descriptor580for (const CKeyID& keyid : keyids) {581CKey key;582if (!GetKey(keyid, key)) {583assert(false);584}586// Get birthdate from key meta587uint64_t creation_time = 0;588const auto& it = mapKeyMetadata.find(keyid);589if (it != mapKeyMetadata.end()) {590creation_time = it->second.nCreateTime;591}593// Get the key origin594// Maybe this doesn't matter because floating keys here shouldn't have origins595KeyOriginInfo info;596bool has_info = GetKeyOrigin(keyid, info);597std::string origin_str = has_info ? "[" + HexStr(info.fingerprint) + FormatHDKeypath(info.path) + "]" : "";599// Construct the combo descriptor600std::string desc_str = "combo(" + origin_str + HexStr(key.GetPubKey()) + ")";601FlatSigningProvider provider;602std::string error;603std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, provider, error, false);604CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor605WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0);607// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys608provider.keys.emplace(key.GetPubKey().GetID(), key);609auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider);610auto desc_spks = desc_spk_man->GetScriptPubKeys();612// Remove the scriptPubKeys from our current set613for (const CScript& spk : desc_spks) {614size_t erased = spks.erase(spk);615assert(erased == 1);616assert(IsMine(spk));617}619out.desc_spkms.push_back(std::move(desc_spk_man));620}622// Handle HD keys by using the CHDChains623std::set<CHDChain> chains;624chains.insert(m_hd_chain);625for (const auto& chain_pair : m_inactive_hd_chains) {626chains.insert(chain_pair.second);627}629bool can_support_hd_split_feature = m_hd_chain.nVersion >= CHDChain::VERSION_HD_CHAIN_SPLIT;631std::set<CExtPubKey> master_xpubs;632for (const CHDChain& chain : chains) {633if (chain.seed_id.IsNull()) continue;635// Get the master xprv636CKey seed_key;637if (!GetKey(chain.seed_id, seed_key)) {638assert(false);639}640CExtKey master_key;641master_key.SetSeed(seed_key);643// Get the xpub and verify that we haven't already seen this xpub before644CExtPubKey master_xpub = master_key.Neuter();645const auto& [_, inserted] = master_xpubs.insert(master_xpub);646if (!inserted) continue;648for (int i = 0; i < 2; ++i) {649// Skip if doing internal chain and split chain is not supported650if (i == 1 && !can_support_hd_split_feature) {651continue;652}654// Make the combo descriptor655std::string xpub = EncodeExtPubKey(master_key.Neuter());656std::string desc_str = "combo(" + xpub + "/0h/" + ToString(i) + "h/*h)";657FlatSigningProvider provider;658std::string error;659std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, provider, error, false);660CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor661uint32_t chain_counter = std::max((i == 1 ? chain.nInternalChainCounter : chain.nExternalChainCounter), (uint32_t)0);662WalletDescriptor w_desc(std::move(descs.at(0)), 0, 0, chain_counter, 0);664// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys665provider.keys.emplace(master_key.key.GetPubKey().GetID(), master_key.key);666auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider);667auto desc_spks = desc_spk_man->GetScriptPubKeys();669// Remove the scriptPubKeys from our current set670for (const CScript& spk : desc_spks) {671size_t erased = spks.erase(spk);672assert(erased == 1);673assert(IsMine(spk));674}676out.desc_spkms.push_back(std::move(desc_spk_man));677}678}679// Add the current master seed to the migration data680if (!m_hd_chain.seed_id.IsNull()) {681CKey seed_key;682if (!GetKey(m_hd_chain.seed_id, seed_key)) {683assert(false);684}685out.master_key.SetSeed(seed_key);686}688// Handle the rest of the scriptPubKeys which must be imports and may not have all info689for (auto it = spks.begin(); it != spks.end();) {690const CScript& spk = *it;692// Get birthdate from script meta693uint64_t creation_time = 0;694const auto& mit = m_script_metadata.find(CScriptID(spk));695if (mit != m_script_metadata.end()) {696creation_time = mit->second.nCreateTime;697}699// InferDescriptor as that will get us all the solving info if it is there700std::unique_ptr<Descriptor> desc = InferDescriptor(spk, *GetSolvingProvider(spk));702// Past bugs in InferDescriptor have caused it to create descriptors which cannot be re-parsed.703// Re-parse the descriptors to detect that, and skip any that do not parse.704{705std::string desc_str = desc->ToString();706FlatSigningProvider parsed_keys;707std::string parse_error;708std::vector<std::unique_ptr<Descriptor>> parsed_descs = Parse(desc_str, parsed_keys, parse_error);709if (parsed_descs.empty()) {710// Remove this scriptPubKey from the set711it = spks.erase(it);712continue;713}714}716// Get the private keys for this descriptor717std::vector<CScript> scripts;718FlatSigningProvider keys;719if (!desc->Expand(0, DUMMY_SIGNING_PROVIDER, scripts, keys)) {720assert(false);721}722std::set<CKeyID> privkeyids;723for (const auto& key_orig_pair : keys.origins) {724privkeyids.insert(key_orig_pair.first);725}727std::vector<CScript> desc_spks;729// If we can't provide all private keys for this inferred descriptor,730// but this wallet is not watch-only, migrate it to the watch-only wallet.731if (!desc->HavePrivateKeys(*this) && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {732out.watch_descs.emplace_back(desc->ToString(), creation_time);734// Get the scriptPubKeys without writing this to the wallet735FlatSigningProvider provider;736desc->Expand(0, provider, desc_spks, provider);737} else {738// Make the DescriptorScriptPubKeyMan and get the scriptPubKeys739for (const auto& keyid : privkeyids) {740CKey key;741if (!GetKey(keyid, key)) {742continue;743}744keys.keys.emplace(key.GetPubKey().GetID(), key);745}746WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);747auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, keys);748auto desc_spks_set = desc_spk_man->GetScriptPubKeys();749desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end());