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.
1001throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt.");1002}1003if (keyFail || !keyPass) {1004return false;1005}1006m_decryption_thoroughly_checked = true;1007return true;1008}1010bool DescriptorScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch)1011{1012LOCK(cs_desc_man);1013if (!m_map_crypted_keys.empty()) {1014return false;1015}1017for (const KeyMap::value_type& key_in : m_map_keys)1018{1019const CKey &key = key_in.second;1020CPubKey pubkey = key.GetPubKey();1021CKeyingMaterial secret{UCharCast(key.begin()), UCharCast(key.end())};1022std::vector<unsigned char> crypted_secret;1023if (!EncryptSecret(master_key, secret, pubkey.GetHash(), crypted_secret)) {1024return false;1025}1026m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret);1027batch->WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret);1028}1029m_map_keys.clear();1030return true;1031}1033util::Result<CTxDestination> DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index)1034{1035LOCK(cs_desc_man);1036auto op_dest = GetNewDestination(type);1037index = m_wallet_descriptor.GetNext() - 1;1038return op_dest;1039}1041void DescriptorScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, const CTxDestination& addr)1042{1043LOCK(cs_desc_man);1044// Only return when the index was the most recent1045if (m_wallet_descriptor.GetNext() - 1 == index) {1046DecIndex();1047}1048WalletBatch(m_storage.GetDatabase()).WriteDescriptor(GetID(), m_wallet_descriptor);1049}1051std::map<CKeyID, CKey> DescriptorScriptPubKeyMan::GetKeys() const1052{1053AssertLockHeld(cs_desc_man);1054if (m_storage.HasEncryptionKeys() && !m_storage.IsLocked()) {1055KeyMap keys;1056for (const auto& key_pair : m_map_crypted_keys) {1057const CPubKey& pubkey = key_pair.second.first;1058const std::vector<unsigned char>& crypted_secret = key_pair.second.second;1059CKey key;1060m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {1061return DecryptKey(encryption_key, crypted_secret, pubkey, key);1062});1063keys[pubkey.GetID()] = key;1064}1065return keys;1066}1067return m_map_keys;1068}1070bool DescriptorScriptPubKeyMan::HasPrivKey(const CKeyID& keyid) const1071{1072AssertLockHeld(cs_desc_man);1073return m_map_keys.contains(keyid) || m_map_crypted_keys.contains(keyid);1074}1076std::optional<CKey> DescriptorScriptPubKeyMan::GetKey(const CKeyID& keyid) const1077{1078AssertLockHeld(cs_desc_man);1079if (m_storage.HasEncryptionKeys() && !m_storage.IsLocked()) {1080const auto& it = m_map_crypted_keys.find(keyid);1081if (it == m_map_crypted_keys.end()) {1082return std::nullopt;1083}1084const std::vector<unsigned char>& crypted_secret = it->second.second;1085CKey key;1086if (!Assume(m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {1087return DecryptKey(encryption_key, crypted_secret, it->second.first, key);1088}))) {1089return std::nullopt;1090}1091return key;1092}1093const auto& it = m_map_keys.find(keyid);1094if (it == m_map_keys.end()) {1095return std::nullopt;1096}1097return it->second;1098}1100bool DescriptorScriptPubKeyMan::TopUp(unsigned int size)1101{1102WalletBatch batch(m_storage.GetDatabase());1103if (!batch.TxnBegin()) return false;1104bool res = TopUpWithDB(batch, size);1105if (!batch.TxnCommit()) throw std::runtime_error(strprintf("Error during descriptors keypool top up. Cannot commit changes for wallet [%s]", m_storage.LogName()));1106return res;1107}1109bool DescriptorScriptPubKeyMan::TopUpWithDB(WalletBatch& batch, unsigned int size)1110{1111LOCK(cs_desc_man);1112std::set<CScript> new_spks;1113unsigned int target_size;1114if (size > 0) {1115target_size = size;1116} else {1117target_size = m_keypool_size;1118}1120// Calculate the new range_end1121int32_t new_range_end = std::max(m_wallet_descriptor.GetNext() + (int32_t)target_size, m_wallet_descriptor.GetEnd());1123// If the descriptor is not ranged, we actually just want to fill the first cache item1124if (!m_wallet_descriptor.descriptor->IsRange()) {1125new_range_end = 1;1126}1128FlatSigningProvider provider;1129provider.keys = GetKeys();1131uint256 id = GetID();1132for (int32_t i = m_max_cached_index + 1; i < new_range_end; ++i) {1133FlatSigningProvider out_keys;1134std::vector<CScript> scripts_temp;1135DescriptorCache temp_cache;1136// Maybe we have a cached xpub and we can expand from the cache first1137if (!m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys)) {1138if (!m_wallet_descriptor.descriptor->Expand(i, provider, scripts_temp, out_keys, &temp_cache)) return false;1139}1140// Add all of the scriptPubKeys to the scriptPubKey set1141new_spks.insert(scripts_temp.begin(), scripts_temp.end());1142for (const CScript& script : scripts_temp) {1143m_map_script_pub_keys[script] = i;1144}1145for (const auto& pk_pair : out_keys.pubkeys) {1146const CPubKey& pubkey = pk_pair.second;1147if (m_map_pubkeys.contains(pubkey)) {1148// We don't need to give an error here.1149// 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 key1150continue;1151}1152m_map_pubkeys[pubkey] = i;1153}1154// Merge and write the cache1155DescriptorCache new_items = m_wallet_descriptor.cache.MergeAndDiff(temp_cache);1156if (!batch.WriteDescriptorCacheItems(id, new_items)) {1157throw std::runtime_error(std::string(__func__) + ": writing cache items failed");1158}1159m_max_cached_index++;1160}1161SetRangeEnd(new_range_end);1162batch.WriteDescriptor(GetID(), m_wallet_descriptor);1164// By this point, the cache size should be the size of the entire range1165assert(m_wallet_descriptor.GetEnd() - 1 == m_max_cached_index);1167m_storage.TopUpCallback(new_spks, this);1168return true;1169}1171std::vector<WalletDestination> DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script)1172{1173LOCK(cs_desc_man);1174std::vector<WalletDestination> result;1175if (IsMine(script)) {1176int32_t index = m_map_script_pub_keys[script];1177if (index >= m_wallet_descriptor.GetNext()) {1178WalletLogPrintf("%s: Detected a used keypool item at index %d, mark all keypool items up to this item as used\n", __func__, index);1179auto out_keys = std::make_unique<FlatSigningProvider>();1180std::vector<CScript> scripts_temp;1181while (index >= m_wallet_descriptor.GetNext()) {1182if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.GetNext(), m_wallet_descriptor.cache, scripts_temp, *out_keys)) {1183throw std::runtime_error(std::string(__func__) + ": Unable to expand descriptor from cache");1184}1185CTxDestination dest;1186ExtractDestination(scripts_temp[0], dest);1187result.push_back({dest, std::nullopt});1188IncIndex();1189}1190}1191if (!TopUp()) {1192WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__);1193}1194}1196return result;1197}1199bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey)1200{1201AssertLockHeld(cs_desc_man);1202assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));1204// Check if provided key already exists1205if (m_map_keys.contains(pubkey.GetID()) ||1206m_map_crypted_keys.contains(pubkey.GetID())) {1207return true;1208}1210if (m_storage.HasEncryptionKeys()) {1211if (m_storage.IsLocked()) {1212return false;1213}1215std::vector<unsigned char> crypted_secret;1216CKeyingMaterial secret{UCharCast(key.begin()), UCharCast(key.end())};1217if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {1218return EncryptSecret(encryption_key, secret, pubkey.GetHash(), crypted_secret);1219})) {1220return false;1221}1223m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret);1224return batch.WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret);1225} else {1226m_map_keys[pubkey.GetID()] = key;1227return batch.WriteDescriptorKey(GetID(), pubkey, key.GetPrivKey());1228}1229}1231bool DescriptorScriptPubKeyMan::IsHDEnabled() const1232{1233LOCK(cs_desc_man);1234return m_wallet_descriptor.descriptor->IsRange();1235}1237bool DescriptorScriptPubKeyMan::CanGetAddresses(bool internal) const1238{1239// We can only give out addresses from descriptors that are single type (not combo), ranged,1240// and either have cached keys or can generate more keys (ignoring encryption)1241LOCK(cs_desc_man);1242return m_wallet_descriptor.descriptor->IsSingleType() &&1243m_wallet_descriptor.descriptor->IsRange() &&1244(HavePrivateKeys() || m_wallet_descriptor.GetNext() < m_wallet_descriptor.GetEnd() || m_wallet_descriptor.descriptor->CanSelfExpand());1245}1247bool DescriptorScriptPubKeyMan::HavePrivateKeys() const1248{1249LOCK(cs_desc_man);1250return m_map_keys.size() > 0 || m_map_crypted_keys.size() > 0;