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.
1501FlatSigningProvider out_keys;1502std::vector<CScript> scripts_temp;1503if (!m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys)) {1504throw std::runtime_error("Error: Unable to expand wallet descriptor from cache");1505}1506// Add all of the scriptPubKeys to the scriptPubKey set1507new_spks.insert(scripts_temp.begin(), scripts_temp.end());1508for (const CScript& script : scripts_temp) {1509if (m_map_script_pub_keys.contains(script)) {1510throw std::runtime_error(strprintf("Error: Already loaded script at index %d as being at index %d", i, m_map_script_pub_keys[script]));1511}1512m_map_script_pub_keys[script] = i;1513}1514for (const auto& pk_pair : out_keys.pubkeys) {1515const CPubKey& pubkey = pk_pair.second;1516if (m_map_pubkeys.contains(pubkey)) {1517// We don't need to give an error here.1518// It doesn't matter which of many valid indexes the pubkey has, we just need an index where we can derive it and its private key1519continue;1520}1521m_map_pubkeys[pubkey] = i;1522}1523m_max_cached_index++;1524}1525// Make sure the wallet knows about our new spks1526m_storage.TopUpCallback(new_spks, this);1527}1529bool DescriptorScriptPubKeyMan::HasWalletDescriptor(const WalletDescriptor& desc) const1530{1531LOCK(cs_desc_man);1532return m_wallet_descriptor.IsCanonicallyEquivalent(desc);1533}1535void DescriptorScriptPubKeyMan::WriteDescriptor()1536{1537LOCK(cs_desc_man);1538WalletBatch batch(m_storage.GetDatabase());1539if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) {1540throw std::runtime_error(std::string(__func__) + ": writing descriptor failed");1541}1542}1544WalletDescriptor DescriptorScriptPubKeyMan::GetWalletDescriptor() const1545{1546return m_wallet_descriptor;1547}1549std::unordered_set<CScript, SaltedSipHasher> DescriptorScriptPubKeyMan::GetScriptPubKeys() const1550{1551return GetScriptPubKeys(0);1552}1554std::unordered_set<CScript, SaltedSipHasher> DescriptorScriptPubKeyMan::GetScriptPubKeys(int32_t minimum_index) const1555{1556LOCK(cs_desc_man);1557std::unordered_set<CScript, SaltedSipHasher> script_pub_keys;1558script_pub_keys.reserve(m_map_script_pub_keys.size());1560for (auto const& [script_pub_key, index] : m_map_script_pub_keys) {1561if (index >= minimum_index) script_pub_keys.insert(script_pub_key);1562}1563return script_pub_keys;1564}1566int32_t DescriptorScriptPubKeyMan::GetEndRange() const1567{1568return m_max_cached_index + 1;1569}1571bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, const bool priv) const1572{1573LOCK(cs_desc_man);1575FlatSigningProvider provider;1576provider.keys = GetKeys();1578if (priv) {1579// For the private version, always return the master key to avoid1580// exposing child private keys. The risk implications of exposing child1581// private keys together with the parent xpub may be non-obvious for users.1582return m_wallet_descriptor.descriptor->ToPrivateString(provider, out);1583}1585return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, &m_wallet_descriptor.cache);1586}1588void DescriptorScriptPubKeyMan::UpgradeDescriptorCache()1589{1590LOCK(cs_desc_man);1591if (m_storage.IsLocked() || m_storage.IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) {1592return;1593}1595// Skip if we have the last hardened xpub cache1596if (m_wallet_descriptor.cache.GetCachedLastHardenedExtPubKeys().size() > 0) {1597return;1598}1600// Expand the descriptor1601FlatSigningProvider provider;1602provider.keys = GetKeys();1603FlatSigningProvider out_keys;1604std::vector<CScript> scripts_temp;1605DescriptorCache temp_cache;1606if (!m_wallet_descriptor.descriptor->Expand(0, provider, scripts_temp, out_keys, &temp_cache)){1607throw std::runtime_error("Unable to expand descriptor");1608}1610// Cache the last hardened xpubs1611DescriptorCache diff = m_wallet_descriptor.cache.MergeAndDiff(temp_cache);1612if (!WalletBatch(m_storage.GetDatabase()).WriteDescriptorCacheItems(GetID(), diff)) {1613throw std::runtime_error(std::string(__func__) + ": writing cache items failed");1614}1615}1617util::Result<void> DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider)1618{1619LOCK(cs_desc_man);1620std::string error;1621if (!CanUpdateToWalletDescriptor(descriptor, error)) {1622return util::Error{Untranslated(std::move(error))};1623}1625m_map_pubkeys.clear();1626m_map_script_pub_keys.clear();1627m_max_cached_index = -1;1628m_wallet_descriptor.UpdateFrom(descriptor);1630WalletBatch batch(m_storage.GetDatabase());1631UpdateWithSigningProvider(batch, provider);1632NotifyFirstKeyTimeChanged(this, m_wallet_descriptor.creation_time);1633return {};1634}1636void DescriptorScriptPubKeyMan::UpdateWithSigningProvider(WalletBatch& batch, const FlatSigningProvider& signing_provider)1637{1638AssertLockHeld(cs_desc_man);1639// Add the private keys to the descriptor1640for (const auto& entry : signing_provider.keys) {1641const CKey& key = entry.second;1642if (!AddDescriptorKeyWithDB(batch, key, key.GetPubKey())) {1643throw std::runtime_error(std::string(__func__) + ": writing descriptor private key failed");1644}1645}1647// Top up key pool, to generate scriptPubKeys1648if (!TopUpWithDB(batch)) {1649throw std::runtime_error("Could not top up scriptPubKeys");1650}1651}1653bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error)1654{1655LOCK(cs_desc_man);1656if (!HasWalletDescriptor(descriptor)) {1657error = "can only update matching descriptor";1658return false;1659}1661if (!descriptor.descriptor->IsRange()) {1662// Skip range check for non-range descriptors1663return true;1664}1666if (descriptor.GetStart() > m_wallet_descriptor.GetStart() ||1667descriptor.GetEnd() < m_wallet_descriptor.GetEnd()) {1668// Use inclusive range for error1669error = strprintf("new range must include current range = [%d,%d]",1670m_wallet_descriptor.GetStart(),1671m_wallet_descriptor.GetEnd() - 1);1672return false;1673}1675return true;1676}1677} // namespace wallet