LCOV - code coverage report
Current view: top level - src/wallet - walletdb.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 624 803 77.7 %
Date: 2026-06-25 07:23:43 Functions: 65 65 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Copyright (c) 2014-2025 The Dash Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include <wallet/walletdb.h>
       8             : 
       9             : #include <key_io.h>
      10             : #include <fs.h>
      11             : #include <governance/common.h>
      12             : #include <protocol.h>
      13             : #include <serialize.h>
      14             : #include <sync.h>
      15             : #include <util/system.h>
      16             : #include <util/time.h>
      17             : #include <util/translation.h>
      18             : #ifdef USE_BDB
      19             : #include <wallet/bdb.h>
      20             : #endif
      21             : #ifdef USE_SQLITE
      22             : #include <wallet/sqlite.h>
      23             : #endif
      24             : #include <wallet/hdchain.h>
      25             : #include <wallet/wallet.h>
      26             : #include <validation.h>
      27             : 
      28             : #include <atomic>
      29             : #include <optional>
      30             : #include <string>
      31             : 
      32             : namespace wallet {
      33             : namespace DBKeys {
      34             : const std::string ACENTRY{"acentry"};
      35             : const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
      36             : const std::string ACTIVEINTERNALSPK{"activeinternalspk"};
      37             : const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"};
      38             : const std::string BESTBLOCK{"bestblock"};
      39             : const std::string CRYPTED_KEY{"ckey"};
      40             : const std::string CRYPTED_HDCHAIN{"chdchain"};
      41             : const std::string COINJOIN_SALT{"cj_salt"};
      42             : const std::string CSCRIPT{"cscript"};
      43             : const std::string DEFAULTKEY{"defaultkey"};
      44             : const std::string DESTDATA{"destdata"};
      45             : const std::string FLAGS{"flags"};
      46             : const std::string G_OBJECT{"g_object"};
      47             : const std::string HDCHAIN{"hdchain"};
      48             : const std::string HDPUBKEY{"hdpubkey"};
      49             : const std::string KEYMETA{"keymeta"};
      50             : const std::string KEY{"key"};
      51             : const std::string LOCKED_UTXO{"lockedutxo"};
      52             : const std::string MASTER_KEY{"mkey"};
      53             : const std::string MINVERSION{"minversion"};
      54             : const std::string NAME{"name"};
      55             : const std::string OLD_KEY{"wkey"};
      56             : const std::string ORDERPOSNEXT{"orderposnext"};
      57             : const std::string POOL{"pool"};
      58             : const std::string PURPOSE{"purpose"};
      59             : const std::string PRIVATESEND_SALT{"ps_salt"};
      60             : const std::string SETTINGS{"settings"};
      61             : const std::string TX{"tx"};
      62             : const std::string VERSION{"version"};
      63             : const std::string WALLETDESCRIPTOR{"walletdescriptor"};
      64             : const std::string WALLETDESCRIPTORCACHE{"walletdescriptorcache"};
      65        3444 : const std::string WALLETDESCRIPTORLHCACHE{"walletdescriptorlhcache"};
      66             : const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"};
      67             : const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"};
      68             : const std::string WATCHMETA{"watchmeta"};
      69             : const std::string WATCHS{"watchs"};
      70        3444 : const std::unordered_set<std::string> LEGACY_TYPES{CRYPTED_KEY, CRYPTED_HDCHAIN, CSCRIPT, DEFAULTKEY, HDCHAIN, HDPUBKEY, KEYMETA, KEY, OLD_KEY, POOL, PRIVATESEND_SALT, WATCHMETA, WATCHS};
      71             : } // namespace DBKeys
      72             : 
      73             : //
      74             : // WalletBatch
      75             : //
      76             : 
      77       35106 : bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
      78             : {
      79       35106 :     return WriteIC(std::make_pair(DBKeys::NAME, strAddress), strName);
      80           0 : }
      81             : 
      82           6 : bool WalletBatch::EraseName(const std::string& strAddress)
      83             : {
      84             :     // This should only be used for sending addresses, never for receiving addresses,
      85             :     // receiving addresses must always have an address book entry if they're not change return.
      86           6 :     return EraseIC(std::make_pair(DBKeys::NAME, strAddress));
      87           0 : }
      88             : 
      89       35114 : bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
      90             : {
      91       35114 :     return WriteIC(std::make_pair(DBKeys::PURPOSE, strAddress), strPurpose);
      92           0 : }
      93             : 
      94           6 : bool WalletBatch::ErasePurpose(const std::string& strAddress)
      95             : {
      96           6 :     return EraseIC(std::make_pair(DBKeys::PURPOSE, strAddress));
      97           0 : }
      98             : 
      99       80991 : bool WalletBatch::WriteTx(const CWalletTx& wtx)
     100             : {
     101       80991 :     return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
     102           0 : }
     103             : 
     104         227 : bool WalletBatch::EraseTx(uint256 hash)
     105             : {
     106         227 :     return EraseIC(std::make_pair(DBKeys::TX, hash));
     107           0 : }
     108             : 
     109       14934 : bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& keyMeta, const CPubKey& vchPubKey, const bool overwrite)
     110             : {
     111       14934 :     return WriteIC(std::make_pair(DBKeys::KEYMETA, vchPubKey), keyMeta, overwrite);
     112           0 : }
     113             : 
     114       13806 : bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
     115             : {
     116       13806 :     if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
     117           0 :         return false;
     118             :     }
     119             : 
     120             :     // hash pubkey/privkey to accelerate wallet load
     121       13806 :     std::vector<unsigned char> vchKey;
     122       13806 :     vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
     123       13806 :     vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
     124       13806 :     vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
     125             : 
     126       13806 :     return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey)), false);
     127       13806 : }
     128             : 
     129         426 : bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
     130             :                                 const std::vector<unsigned char>& vchCryptedSecret,
     131             :                                 const CKeyMetadata &keyMeta)
     132             : {
     133         426 :     if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
     134           0 :         return false;
     135             :     }
     136             : 
     137             :     // Compute a checksum of the encrypted key
     138         426 :     uint256 checksum = Hash(vchCryptedSecret);
     139             : 
     140         426 :     const auto key = std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey);
     141         426 :     if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
     142             :         // It may already exist, so try writing just the checksum
     143           0 :         std::vector<unsigned char> val;
     144           0 :         if (!m_batch->Read(key, val)) {
     145           0 :             return false;
     146             :         }
     147           0 :         if (!WriteIC(key, std::make_pair(val, checksum), true)) {
     148           0 :             return false;
     149             :         }
     150           0 :     }
     151         426 :     EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
     152         426 :     return true;
     153         426 : }
     154             : 
     155          92 : bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
     156             : {
     157          92 :     return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true);
     158           0 : }
     159             : 
     160         167 : bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
     161             : {
     162         167 :     return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false);
     163           0 : }
     164             : 
     165        1617 : bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
     166             : {
     167        1617 :     if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
     168           0 :         return false;
     169             :     }
     170        1617 :     return WriteIC(std::make_pair(DBKeys::WATCHS, dest), uint8_t{'1'});
     171        1617 : }
     172             : 
     173          18 : bool WalletBatch::EraseWatchOnly(const CScript &dest)
     174             : {
     175          18 :     if (!EraseIC(std::make_pair(DBKeys::WATCHMETA, dest))) {
     176           0 :         return false;
     177             :     }
     178          18 :     return EraseIC(std::make_pair(DBKeys::WATCHS, dest));
     179          18 : }
     180             : 
     181        3318 : bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
     182             : {
     183        3318 :     WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
     184        3318 :     return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
     185           0 : }
     186             : 
     187        4324 : bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
     188             : {
     189        4324 :     if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
     190        4324 :     return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
     191        4324 : }
     192             : 
     193       67355 : bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
     194             : {
     195       67355 :     return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
     196             : }
     197             : 
     198       22624 : bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
     199             : {
     200       22624 :     return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
     201           0 : }
     202             : 
     203       43513 : bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
     204             : {
     205       43513 :     return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool);
     206           0 : }
     207             : 
     208       20885 : bool WalletBatch::ErasePool(int64_t nPool)
     209             : {
     210       20885 :     return EraseIC(std::make_pair(DBKeys::POOL, nPool));
     211           0 : }
     212             : 
     213        1185 : bool WalletBatch::WriteMinVersion(int nVersion)
     214             : {
     215        1185 :     return WriteIC(DBKeys::MINVERSION, nVersion);
     216             : }
     217             : 
     218        3581 : bool WalletBatch::ReadCoinJoinSalt(uint256& salt, bool fLegacy)
     219             : {
     220             :     // TODO: Remove legacy checks after few major releases
     221        3581 :     return m_batch->Read(std::string(fLegacy ? DBKeys::PRIVATESEND_SALT : DBKeys::COINJOIN_SALT), salt);
     222           0 : }
     223             : 
     224        1255 : bool WalletBatch::WriteCoinJoinSalt(const uint256& salt)
     225             : {
     226        1255 :     return WriteIC(DBKeys::COINJOIN_SALT, salt);
     227             : }
     228             : 
     229          42 : bool WalletBatch::WriteGovernanceObject(const Governance::Object& obj)
     230             : {
     231          42 :     return WriteIC(std::make_pair(DBKeys::G_OBJECT, obj.GetHash()), obj, false);
     232           0 : }
     233             : 
     234         895 : bool WalletBatch::WriteActiveScriptPubKeyMan(const uint256& id, bool internal)
     235             : {
     236         895 :     std::string key = internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK;
     237         895 :     return WriteIC(key, id);
     238         895 : }
     239             : 
     240           2 : bool WalletBatch::EraseActiveScriptPubKeyMan(bool internal)
     241             : {
     242           2 :     const std::string key{internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK};
     243           2 :     return EraseIC(key);
     244           2 : }
     245             : 
     246        1497 : bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey, const SecureString& mnemonic, const SecureString& mnemonic_passphrase)
     247             : {
     248             :     // hash pubkey/privkey to accelerate wallet load
     249        1497 :     std::vector<unsigned char> key;
     250        1497 :     key.reserve(pubkey.size() + privkey.size());
     251        1497 :     key.insert(key.end(), pubkey.begin(), pubkey.end());
     252        1497 :     key.insert(key.end(), privkey.begin(), privkey.end());
     253             : 
     254        1497 :     return WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)), std::make_pair(std::make_pair(privkey, Hash(key)), std::make_pair(mnemonic, mnemonic_passphrase)), false);
     255        1497 : }
     256             : 
     257         134 : bool WalletBatch::WriteCryptedDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const std::vector<unsigned char>& secret, const std::vector<unsigned char>& crypted_mnemonic, const std::vector<unsigned char>& crypted_mnemonic_passphrase)
     258             : {
     259         134 :     if (!WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORCKEY, std::make_pair(desc_id, pubkey)), std::make_pair(secret, std::make_pair(crypted_mnemonic, crypted_mnemonic_passphrase)), false)) {
     260           0 :         return false;
     261             :     }
     262         134 :     EraseIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)));
     263         134 :     return true;
     264         134 : }
     265             : 
     266       90265 : bool WalletBatch::WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor/*, const SecureString& mnemonic, const SecureString& mnemonic_passphrase*/)
     267             : {
     268       90265 :     return WriteIC(make_pair(DBKeys::WALLETDESCRIPTOR, desc_id), descriptor);
     269           0 : }
     270             : 
     271        1788 : bool WalletBatch::WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index)
     272             : {
     273        1788 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     274        1788 :     xpub.Encode(ser_xpub.data());
     275        1788 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), std::make_pair(key_exp_index, der_index)), ser_xpub);
     276        1788 : }
     277             : 
     278        1458 : bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
     279             : {
     280        1458 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     281        1458 :     xpub.Encode(ser_xpub.data());
     282        1458 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
     283        1458 : }
     284             : 
     285        1267 : bool WalletBatch::WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
     286             : {
     287        1267 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     288        1267 :     xpub.Encode(ser_xpub.data());
     289        1267 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORLHCACHE, desc_id), key_exp_index), ser_xpub);
     290        1267 : }
     291             : 
     292      154632 : bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache)
     293             : {
     294      156090 :     for (const auto& parent_xpub_pair : cache.GetCachedParentExtPubKeys()) {
     295        1458 :         if (!WriteDescriptorParentCache(parent_xpub_pair.second, desc_id, parent_xpub_pair.first)) {
     296           0 :             return false;
     297             :         }
     298             :     }
     299      156420 :     for (const auto& derived_xpub_map_pair : cache.GetCachedDerivedExtPubKeys()) {
     300        3576 :         for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
     301        1788 :             if (!WriteDescriptorDerivedCache(derived_xpub_pair.second, desc_id, derived_xpub_map_pair.first, derived_xpub_pair.first)) {
     302           0 :                 return false;
     303             :             }
     304             :         }
     305             :     }
     306      155899 :     for (const auto& lh_xpub_pair : cache.GetCachedLastHardenedExtPubKeys()) {
     307        1267 :         if (!WriteDescriptorLastHardenedCache(lh_xpub_pair.second, desc_id, lh_xpub_pair.first)) {
     308           0 :             return false;
     309             :         }
     310             :     }
     311      154632 :     return true;
     312      154632 : }
     313             : 
     314        1039 : bool WalletBatch::WriteLockedUTXO(const COutPoint& output)
     315             : {
     316        1039 :     return WriteIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)), uint8_t{'1'});
     317           0 : }
     318             : 
     319         654 : bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
     320             : {
     321         654 :     return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)));
     322           0 : }
     323             : 
     324             : class CWalletScanState {
     325             : public:
     326        2360 :     unsigned int nKeys{0};
     327        2360 :     unsigned int nCKeys{0};
     328        2360 :     unsigned int nWatchKeys{0};
     329        2360 :     unsigned int nHDPubKeys{0};
     330        2360 :     unsigned int nKeyMeta{0};
     331        2360 :     unsigned int m_unknown_records{0};
     332        2360 :     bool fIsEncrypted{false};
     333        2360 :     bool fAnyUnordered{false};
     334             :     std::vector<uint256> vWalletUpgrade;
     335             :     std::map<OutputType, uint256> m_active_external_spks;
     336             :     std::map<OutputType, uint256> m_active_internal_spks;
     337             :     std::map<uint256, DescriptorCache> m_descriptor_caches;
     338             :     std::map<std::pair<uint256, CKeyID>, CKey> m_descriptor_keys;
     339             :     std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys;
     340             :     std::map<std::pair<uint256, CKeyID>, std::pair<SecureString, SecureString>> mnemonics;
     341             :     std::map<std::pair<uint256, CKeyID>, std::pair<std::vector<unsigned char>, std::vector<unsigned char>>> crypted_mnemonics;
     342        2360 :     bool tx_corrupt{false};
     343             : 
     344        9440 :     CWalletScanState() = default;
     345             : };
     346             : 
     347             : static bool
     348      155289 : ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
     349             :              CWalletScanState &wss, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn = nullptr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
     350             : {
     351             :     try {
     352             :         // Unserialize
     353             :         // Taking advantage of the fact that pair serialization
     354             :         // is just the two items serialized one after the other
     355      155289 :         ssKey >> strType;
     356             :         // If we have a filter, check if this matches the filter
     357      155289 :         if (filter_fn && !filter_fn(strType)) {
     358          24 :             return true;
     359             :         }
     360      155265 :         if (strType == DBKeys::NAME) {
     361       12052 :             std::string strAddress;
     362       12052 :             ssKey >> strAddress;
     363       12052 :             std::string label;
     364       12052 :             ssValue >> label;
     365       12052 :             pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
     366      155265 :         } else if (strType == DBKeys::PURPOSE) {
     367       12052 :             std::string strAddress;
     368       12052 :             ssKey >> strAddress;
     369       12052 :             ssValue >> pwallet->m_address_book[DecodeDestination(strAddress)].purpose;
     370      143213 :         } else if (strType == DBKeys::TX) {
     371       46370 :             uint256 hash;
     372       46370 :             ssKey >> hash;
     373             :             // LoadToWallet call below creates a new CWalletTx that fill_wtx
     374             :             // callback fills with transaction metadata.
     375       92740 :             auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
     376       46370 :                 if(!new_tx) {
     377             :                     // There's some corruption here since the tx we just tried to load was already in the wallet.
     378             :                     // We don't consider this type of corruption critical, and can fix it by removing tx data and
     379             :                     // rescanning.
     380           0 :                     wss.tx_corrupt = true;
     381           0 :                     return false;
     382             :                 }
     383       46370 :                 ssValue >> wtx;
     384       46370 :                 if (wtx.GetHash() != hash)
     385           0 :                     return false;
     386             : 
     387             :                 // Undo serialize changes in 31600
     388       46370 :                 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
     389             :                 {
     390           0 :                     if (!ssValue.empty())
     391             :                     {
     392             :                         uint8_t fTmp;
     393             :                         uint8_t fUnused;
     394           0 :                         std::string unused_string;
     395           0 :                         ssValue >> fTmp >> fUnused >> unused_string;
     396           0 :                         strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s",
     397           0 :                                            wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
     398           0 :                         wtx.fTimeReceivedIsTxTime = fTmp;
     399           0 :                     }
     400             :                     else
     401             :                     {
     402           0 :                         strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString());
     403           0 :                         wtx.fTimeReceivedIsTxTime = 0;
     404             :                     }
     405           0 :                     wss.vWalletUpgrade.push_back(hash);
     406           0 :                 }
     407             : 
     408       46370 :                 if (wtx.nOrderPos == -1)
     409           0 :                     wss.fAnyUnordered = true;
     410             : 
     411       46370 :                 return true;
     412       46370 :             };
     413       46370 :             if (!pwallet->LoadToWallet(hash, fill_wtx)) {
     414           0 :                 return false;
     415             :             }
     416      131161 :         } else if (strType == DBKeys::WATCHS) {
     417        1328 :             wss.nWatchKeys++;
     418        1328 :             CScript script;
     419        1328 :             ssKey >> script;
     420             :             uint8_t fYes;
     421        1328 :             ssValue >> fYes;
     422        1328 :             if (fYes == '1') {
     423        1328 :                 pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script);
     424        1328 :             }
     425       84791 :         } else if (strType == DBKeys::KEY) {
     426        1668 :             CPubKey vchPubKey;
     427        1668 :             ssKey >> vchPubKey;
     428        1668 :             if (!vchPubKey.IsValid())
     429             :             {
     430           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     431           0 :                 return false;
     432             :             }
     433        1668 :             CKey key;
     434        1668 :             CPrivKey pkey;
     435        1668 :             uint256 hash;
     436             : 
     437        1668 :             wss.nKeys++;
     438        1668 :             ssValue >> pkey;
     439             : 
     440             :             // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
     441             :             // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
     442             :             // using EC operations as a checksum.
     443             :             // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
     444             :             // remaining backwards-compatible.
     445             :             try
     446             :             {
     447        1668 :                 ssValue >> hash;
     448        1668 :             }
     449           0 :             catch (const std::ios_base::failure&) {}
     450             : 
     451        1668 :             bool fSkipCheck = false;
     452             : 
     453        1668 :             if (!hash.IsNull())
     454             :             {
     455             :                 // hash pubkey/privkey to accelerate wallet load
     456        1668 :                 std::vector<unsigned char> vchKey;
     457        1668 :                 vchKey.reserve(vchPubKey.size() + pkey.size());
     458        1668 :                 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
     459        1668 :                 vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
     460             : 
     461        1668 :                 if (Hash(vchKey) != hash)
     462             :                 {
     463           0 :                     strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
     464           0 :                     return false;
     465             :                 }
     466             : 
     467        1668 :                 fSkipCheck = true;
     468        1668 :             }
     469             : 
     470        1668 :             if (!key.Load(pkey, vchPubKey, fSkipCheck))
     471             :             {
     472           0 :                 strErr = "Error reading wallet database: CPrivKey corrupt";
     473           0 :                 return false;
     474             :             }
     475        1668 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey))
     476             :             {
     477           0 :                 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed";
     478           0 :                 return false;
     479             :             }
     480       83463 :         } else if (strType == DBKeys::MASTER_KEY) {
     481             :             // Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans.
     482             :             unsigned int nID;
     483          58 :             ssKey >> nID;
     484          58 :             CMasterKey kMasterKey;
     485          58 :             ssValue >> kMasterKey;
     486          58 :             if(pwallet->mapMasterKeys.count(nID) != 0)
     487             :             {
     488           0 :                 strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
     489           0 :                 return false;
     490             :             }
     491          58 :             pwallet->mapMasterKeys[nID] = kMasterKey;
     492          58 :             if (pwallet->nMasterKeyMaxID < nID)
     493          58 :                 pwallet->nMasterKeyMaxID = nID;
     494       81795 :         } else if (strType == DBKeys::CRYPTED_KEY) {
     495          18 :             CPubKey vchPubKey;
     496          18 :             ssKey >> vchPubKey;
     497          18 :             if (!vchPubKey.IsValid())
     498             :             {
     499           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     500           0 :                 return false;
     501             :             }
     502          18 :             std::vector<unsigned char> vchPrivKey;
     503          18 :             ssValue >> vchPrivKey;
     504             : 
     505             :             // Get the checksum and check it
     506          18 :             bool checksum_valid = false;
     507          18 :             if (!ssValue.eof()) {
     508          18 :                 uint256 checksum;
     509          18 :                 ssValue >> checksum;
     510          18 :                 if (!(checksum_valid = Hash(vchPrivKey) == checksum)) {
     511           0 :                     strErr = "Error reading wallet database: Encrypted key corrupt";
     512           0 :                     return false;
     513             :                 }
     514          18 :             }
     515             : 
     516          18 :             wss.nCKeys++;
     517             : 
     518          18 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
     519             :             {
     520           0 :                 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed";
     521           0 :                 return false;
     522             :             }
     523          18 :             wss.fIsEncrypted = true;
     524       81737 :         } else if (strType == DBKeys::KEYMETA) {
     525       26247 :             CPubKey vchPubKey;
     526       26247 :             ssKey >> vchPubKey;
     527       26247 :             CKeyMetadata keyMeta;
     528       26247 :             ssValue >> keyMeta;
     529       26247 :             wss.nKeyMeta++;
     530       26247 :             pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
     531       81719 :         } else if (strType == DBKeys::WATCHMETA) {
     532        1328 :             CScript script;
     533        1328 :             ssKey >> script;
     534        1328 :             CKeyMetadata keyMeta;
     535        1328 :             ssValue >> keyMeta;
     536        1328 :             wss.nKeyMeta++;
     537        1328 :             pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta);
     538       55472 :         } else if (strType == DBKeys::DEFAULTKEY) {
     539             :             // We don't want or need the default key, but if there is one set,
     540             :             // we want to make sure that it is valid so that we can detect corruption
     541           0 :             CPubKey vchPubKey;
     542           0 :             ssValue >> vchPubKey;
     543           0 :             if (!vchPubKey.IsValid()) {
     544           0 :                 strErr = "Error reading wallet database: Default Key corrupt";
     545           0 :                 return false;
     546             :             }
     547       54144 :         } else if (strType == DBKeys::POOL) {
     548             :             int64_t nIndex;
     549       16735 :             ssKey >> nIndex;
     550       16735 :             CKeyPool keypool;
     551       16735 :             ssValue >> keypool;
     552             : 
     553       16735 :             pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool);
     554       54144 :         } else if (strType == DBKeys::CSCRIPT) {
     555          70 :             uint160 hash;
     556          70 :             ssKey >> hash;
     557          70 :             CScript script;
     558          70 :             ssValue >> script;
     559          70 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script))
     560             :             {
     561           0 :                 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed";
     562           0 :                 return false;
     563             :             }
     564       37409 :         } else if (strType == DBKeys::ORDERPOSNEXT) {
     565         678 :             ssValue >> pwallet->nOrderPosNext;
     566       37339 :         } else if (strType == DBKeys::DESTDATA) {
     567          10 :             std::string strAddress, strKey, strValue;
     568          10 :             ssKey >> strAddress;
     569          10 :             ssKey >> strKey;
     570          10 :             ssValue >> strValue;
     571          10 :             const CTxDestination& dest{DecodeDestination(strAddress)};
     572          10 :             if (strKey.compare("used") == 0) {
     573             :                 // Load "used" key indicating if an IsMine address has
     574             :                 // previously been spent from with avoid_reuse option enabled.
     575             :                 // The strValue is not used for anything currently, but could
     576             :                 // hold more information in the future. Current values are just
     577             :                 // "1" or "p" for present (which was written prior to
     578             :                 // f5ba424cd44619d9b9be88b8593d69a7ba96db26).
     579           4 :                 pwallet->LoadAddressPreviouslySpent(dest);
     580          10 :             } else if (strKey.compare(0, 2, "rr") == 0) {
     581             :                 // Load "rr##" keys where ## is a decimal number, and strValue
     582             :                 // is a serialized RecentRequestEntry object.
     583           6 :                 pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue);
     584           6 :             }
     585       36661 :         } else if (strType == DBKeys::HDCHAIN || strType == DBKeys::CRYPTED_HDCHAIN) {
     586         638 :             CHDChain chain;
     587         638 :             ssValue >> chain;
     588         638 :             assert ((strType == DBKeys::CRYPTED_HDCHAIN) == chain.IsCrypted());
     589             :             // Skip encryption check during loading as MASTER_KEY records may not be loaded yet.
     590             :             // Consistency will be validated after all records are loaded.
     591         638 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain, /*skip_encryption_check=*/true)) {
     592           0 :                 strErr = "Error reading wallet database: LoadHDChain failed";
     593           0 :                 return false;
     594             :             }
     595       36651 :         } else if (strType == DBKeys::HDPUBKEY) {
     596       23941 :             wss.nHDPubKeys++;
     597       23941 :             CPubKey vchPubKey;
     598       23941 :             ssKey >> vchPubKey;
     599             : 
     600       23941 :             CHDPubKey hdPubKey;
     601       23941 :             ssValue >> hdPubKey;
     602             : 
     603       23941 :             if(vchPubKey != hdPubKey.extPubKey.pubkey)
     604             :             {
     605           0 :                 strErr = "Error reading wallet database: CHDPubKey corrupt";
     606           0 :                 return false;
     607             :             }
     608       23941 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDPubKey(hdPubKey))
     609             :             {
     610           0 :                 strErr = "Error reading wallet database: LoadHDPubKey failed";
     611           0 :                 return false;
     612             :             }
     613       36013 :         } else if (strType == DBKeys::G_OBJECT) {
     614          14 :             uint256 nObjectHash;
     615          14 :             Governance::Object obj;
     616          14 :             ssKey >> nObjectHash;
     617          14 :             ssValue >> obj;
     618             : 
     619          14 :             if (obj.GetHash() != nObjectHash) {
     620           0 :                 strErr = "Invalid governance object: Hash mismatch";
     621           0 :                 return false;
     622             :             }
     623             : 
     624          14 :             if (!pwallet->LoadGovernanceObject(obj)) {
     625           0 :                 strErr = "Invalid governance object: LoadGovernanceObject";
     626           0 :                 return false;
     627             :             }
     628       12072 :         } else if (strType == DBKeys::OLD_KEY) {
     629           0 :             strErr = "Found unsupported 'wkey' record, try loading with version 0.17";
     630           0 :             return false;
     631       12058 :         } else if (strType == DBKeys::ACTIVEEXTERNALSPK || strType == DBKeys::ACTIVEINTERNALSPK) {
     632         651 :             uint256 id;
     633         651 :             ssValue >> id;
     634             : 
     635         651 :             bool internal = strType == DBKeys::ACTIVEINTERNALSPK;
     636         651 :             auto& spk_mans = internal ? wss.m_active_internal_spks : wss.m_active_external_spks;
     637         651 :             const OutputType type = OutputType::LEGACY;
     638         651 :             if (spk_mans.count(static_cast<OutputType>(type)) > 0) {
     639           0 :                 strErr = "Multiple ScriptPubKeyMans specified for a single type";
     640           0 :                 return false;
     641             :             }
     642         651 :             spk_mans[static_cast<OutputType>(type)] = id;
     643       12058 :         } else if (strType == DBKeys::WALLETDESCRIPTOR) {
     644        1296 :             uint256 id;
     645        1296 :             ssKey >> id;
     646        1296 :             WalletDescriptor desc;
     647        1296 :             ssValue >> desc;
     648        1296 :             if (wss.m_descriptor_caches.count(id) == 0) {
     649         270 :                 wss.m_descriptor_caches[id] = DescriptorCache();
     650         270 :             }
     651        1296 :             pwallet->LoadDescriptorScriptPubKeyMan(id, desc);
     652       11407 :         } else if (strType == DBKeys::WALLETDESCRIPTORCACHE) {
     653        1040 :             bool parent = true;
     654        1040 :             uint256 desc_id;
     655             :             uint32_t key_exp_index;
     656             :             uint32_t der_index;
     657        1040 :             ssKey >> desc_id;
     658        1040 :             ssKey >> key_exp_index;
     659             : 
     660             :             // if the der_index exists, it's a derived xpub
     661             :             try
     662             :             {
     663        1040 :                 ssKey >> der_index;
     664           0 :                 parent = false;
     665        1040 :             }
     666        1040 :             catch (...) {}
     667             : 
     668        1040 :             std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     669        1040 :             ssValue >> ser_xpub;
     670        1040 :             CExtPubKey xpub;
     671        1040 :             xpub.Decode(ser_xpub.data());
     672        1040 :             if (parent) {
     673        1040 :                 wss.m_descriptor_caches[desc_id].CacheParentExtPubKey(key_exp_index, xpub);
     674        1040 :             } else {
     675           0 :                 wss.m_descriptor_caches[desc_id].CacheDerivedExtPubKey(key_exp_index, der_index, xpub);
     676             :             }
     677       10111 :         } else if (strType == DBKeys::WALLETDESCRIPTORLHCACHE) {
     678        1015 :             uint256 desc_id;
     679             :             uint32_t key_exp_index;
     680        1015 :             ssKey >> desc_id;
     681        1015 :             ssKey >> key_exp_index;
     682             : 
     683        1015 :             std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     684        1015 :             ssValue >> ser_xpub;
     685        1015 :             CExtPubKey xpub;
     686        1015 :             xpub.Decode(ser_xpub.data());
     687        1015 :             wss.m_descriptor_caches[desc_id].CacheLastHardenedExtPubKey(key_exp_index, xpub);
     688        9071 :         } else if (strType == DBKeys::WALLETDESCRIPTORKEY) {
     689        1161 :             uint256 desc_id;
     690        1161 :             CPubKey pubkey;
     691        1161 :             ssKey >> desc_id;
     692        1161 :             ssKey >> pubkey;
     693        1161 :             if (!pubkey.IsValid())
     694             :             {
     695           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     696           0 :                 return false;
     697             :             }
     698        1161 :             CKey key;
     699        1161 :             CPrivKey pkey;
     700        1161 :             uint256 hash;
     701             : 
     702        1161 :             wss.nKeys++;
     703        1161 :             ssValue >> pkey;
     704        1161 :             ssValue >> hash;
     705             : 
     706             :             // hash pubkey/privkey to accelerate wallet load
     707        1161 :             std::vector<unsigned char> to_hash;
     708        1161 :             to_hash.reserve(pubkey.size() + pkey.size());
     709        1161 :             to_hash.insert(to_hash.end(), pubkey.begin(), pubkey.end());
     710        1161 :             to_hash.insert(to_hash.end(), pkey.begin(), pkey.end());
     711             : 
     712        1161 :             if (Hash(to_hash) != hash)
     713             :             {
     714           0 :                 strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
     715           0 :                 return false;
     716             :             }
     717             : 
     718        1161 :             if (!key.Load(pkey, pubkey, true))
     719             :             {
     720           0 :                 strErr = "Error reading wallet database: CPrivKey corrupt";
     721           0 :                 return false;
     722             :             }
     723        1161 :             wss.m_descriptor_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), key));
     724             : 
     725        1161 :             SecureString mnemonic;
     726        1161 :             SecureString mnemonic_passphrase;
     727             :             // it's okay if wallet doesn't have mnemonic.
     728             :             // The wallet may be created in an older version of Dash Core or by importing descriptor
     729             :             try
     730             :             {
     731        1161 :                 ssValue >> mnemonic;
     732        1161 :                 ssValue >> mnemonic_passphrase;
     733        1161 :             }
     734           0 :             catch (const std::ios_base::failure&) {}
     735             : 
     736        1161 :             if (!mnemonic.empty()) {
     737         925 :                 wss.mnemonics.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(mnemonic, mnemonic_passphrase)));
     738         925 :             }
     739        8056 :         } else if (strType == DBKeys::WALLETDESCRIPTORCKEY) {
     740          88 :             uint256 desc_id;
     741          88 :             CPubKey pubkey;
     742          88 :             ssKey >> desc_id;
     743          88 :             ssKey >> pubkey;
     744          88 :             if (!pubkey.IsValid())
     745             :             {
     746           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     747           0 :                 return false;
     748             :             }
     749          88 :             std::vector<unsigned char> privkey;
     750          88 :             ssValue >> privkey;
     751          88 :             wss.nCKeys++;
     752             : 
     753          88 :             wss.m_descriptor_crypt_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(pubkey, privkey)));
     754          88 :             wss.fIsEncrypted = true;
     755             : 
     756             :             // TODO : remove copy-paste with plain-text key
     757          88 :             std::vector<unsigned char> mnemonic;
     758          88 :             std::vector<unsigned char> mnemonic_passphrase;
     759             :             // it's okay if wallet doesn't have mnemonic.
     760             :             // The wallet may be created in an older version of Dash Core or by importing descriptor
     761             :             try
     762             :             {
     763          88 :                 ssValue >> mnemonic;
     764          88 :                 ssValue >> mnemonic_passphrase;
     765          88 :             }
     766           0 :             catch (const std::ios_base::failure&) {}
     767          88 :             if (!mnemonic.empty()) {
     768          70 :                 wss.crypted_mnemonics.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(mnemonic, mnemonic_passphrase)));
     769          70 :             }
     770             : 
     771        6895 :         } else if (strType == DBKeys::LOCKED_UTXO) {
     772         309 :             uint256 hash;
     773             :             uint32_t n;
     774         309 :             ssKey >> hash;
     775         309 :             ssKey >> n;
     776         309 :             pwallet->LockCoin(COutPoint(hash, n));
     777        7890 :         } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
     778        4340 :                    strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
     779        3257 :                    strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
     780        2170 :                    strType != DBKeys::PRIVATESEND_SALT && strType != DBKeys::COINJOIN_SALT &&
     781        1083 :                    strType != DBKeys::FLAGS) {
     782           0 :             wss.m_unknown_records++;
     783           0 :         }
     784      155265 :     } catch (const std::exception& e) {
     785           0 :         if (strErr.empty()) {
     786           0 :             strErr = e.what();
     787           0 :         }
     788           0 :         return false;
     789           0 :     } catch (...) {
     790           0 :         if (strErr.empty()) {
     791           0 :             strErr = "Caught unknown exception in ReadKeyValue";
     792           0 :         }
     793           0 :         return false;
     794           0 :     }
     795      155265 :     return true;
     796      156329 : }
     797             : 
     798          26 : bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn)
     799             : {
     800          26 :     CWalletScanState dummy_wss;
     801          26 :     LOCK(pwallet->cs_wallet);
     802          26 :     return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr, filter_fn);
     803          26 : }
     804             : 
     805          52 : bool WalletBatch::IsKeyType(const std::string& strType)
     806             : {
     807         104 :     return (strType == DBKeys::KEY ||
     808          52 :             strType == DBKeys::MASTER_KEY || strType == DBKeys::CRYPTED_KEY ||
     809          52 :             strType == DBKeys::HDCHAIN || strType == DBKeys::CRYPTED_HDCHAIN);
     810             : }
     811             : 
     812        2334 : DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
     813             : {
     814        2334 :     CWalletScanState wss;
     815        2334 :     bool fNoncriticalErrors = false;
     816        2334 :     DBErrors result = DBErrors::LOAD_OK;
     817             : 
     818        2334 :     LOCK(pwallet->cs_wallet);
     819             :     try {
     820        2334 :         int nMinVersion = 0;
     821        2334 :         if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
     822        1083 :             if (nMinVersion > FEATURE_LATEST)
     823           0 :                 return DBErrors::TOO_NEW;
     824        1083 :             pwallet->LoadMinVersion(nMinVersion);
     825        1083 :         }
     826             : 
     827             :         // Load wallet flags, so they are known when processing other records.
     828             :         // The FLAGS key is absent during wallet creation.
     829             :         uint64_t flags;
     830        2334 :         if (m_batch->Read(DBKeys::FLAGS, flags)) {
     831        1083 :             if (!pwallet->LoadWalletFlags(flags)) {
     832           0 :                 pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n");
     833           0 :                 return DBErrors::CORRUPT;
     834             :             }
     835        1083 :         }
     836             : 
     837             : #ifndef ENABLE_EXTERNAL_SIGNER
     838             :         if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
     839             :             pwallet->WalletLogPrintf("Error: External signer wallet being loaded without external signer support compiled\n");
     840             :             return DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED;
     841             :         }
     842             : #endif
     843             : 
     844             :         // Get cursor
     845        2334 :         if (!m_batch->StartCursor())
     846             :         {
     847           0 :             pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
     848           0 :             return DBErrors::CORRUPT;
     849             :         }
     850             : 
     851      157597 :         while (true)
     852             :         {
     853             :             // Read next record
     854      157597 :             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     855      157597 :             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
     856             :             bool complete;
     857      157597 :             bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
     858      157597 :             if (complete) {
     859        2334 :                 break;
     860             :             }
     861      155263 :             else if (!ret)
     862             :             {
     863           0 :                 m_batch->CloseCursor();
     864           0 :                 pwallet->WalletLogPrintf("Error reading next record from wallet database\n");
     865           0 :                 return DBErrors::CORRUPT;
     866             :             }
     867             : 
     868             :             // Try to be tolerant of single corrupt records:
     869      155263 :             std::string strType, strErr;
     870      155263 :             if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr))
     871             :             {
     872             :                 // losing keys is considered a catastrophic error, anything else
     873             :                 // we assume the user can live with:
     874           0 :                 if (IsKeyType(strType) || strType == DBKeys::DEFAULTKEY) {
     875           0 :                     result = DBErrors::CORRUPT;
     876           0 :                 } else if (strType == DBKeys::FLAGS) {
     877             :                     // reading the wallet flags can only fail if unknown flags are present
     878           0 :                     result = DBErrors::TOO_NEW;
     879           0 :                 } else if (wss.tx_corrupt) {
     880           0 :                     pwallet->WalletLogPrintf("Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.\n");
     881             :                     // Set tx_corrupt back to false so that the error is only printed once (per corrupt tx)
     882           0 :                     wss.tx_corrupt = false;
     883           0 :                     result = DBErrors::CORRUPT;
     884           0 :                 } else {
     885             :                     // Leave other errors alone, if we try to fix them we might make things worse.
     886           0 :                     fNoncriticalErrors = true; // ... but do warn the user there is something wrong.
     887           0 :                     if (strType == DBKeys::TX)
     888             :                         // Rescan if there is a bad transaction record:
     889           0 :                         gArgs.SoftSetBoolArg("-rescan", true);
     890             :                 }
     891           0 :             }
     892      155263 :             if (!strErr.empty())
     893           0 :                 pwallet->WalletLogPrintf("%s\n", strErr);
     894      157597 :         }
     895        2334 :     } catch (...) {
     896           0 :         result = DBErrors::CORRUPT;
     897           0 :     }
     898        2334 :     m_batch->CloseCursor();
     899             : 
     900             :     // Validate HD chain encryption consistency now that all data is loaded
     901        2334 :     if (auto spk_man = pwallet->GetLegacyScriptPubKeyMan()) {
     902         713 :         CHDChain hdChain;
     903         713 :         if (spk_man->GetHDChain(hdChain)) {
     904             :             // If HD chain exists, validate encryption consistency
     905         636 :             bool fHasMasterKeys = pwallet->HasEncryptionKeys();
     906         636 :             bool fChainCrypted = hdChain.IsCrypted();
     907             : 
     908         636 :             if (fHasMasterKeys != fChainCrypted) {
     909           0 :                 pwallet->WalletLogPrintf("Error: HD chain encryption state (%s) inconsistent with wallet encryption state (%s)\n",
     910           0 :                     fChainCrypted ? "encrypted" : "not encrypted",
     911           0 :                     fHasMasterKeys ? "encrypted" : "not encrypted");
     912           0 :                 return DBErrors::CORRUPT;
     913             :             }
     914         636 :         }
     915         713 :     }
     916             : 
     917             :     // Set the active ScriptPubKeyMans
     918        2659 :     for (auto spk_man : wss.m_active_external_spks) {
     919         325 :         pwallet->LoadActiveScriptPubKeyMan(spk_man.second, /*internal=*/false);
     920             :     }
     921        2660 :     for (auto spk_man : wss.m_active_internal_spks) {
     922         326 :         pwallet->LoadActiveScriptPubKeyMan(spk_man.second, /*internal=*/true);
     923             :     }
     924             : 
     925             :     // Set the descriptor caches
     926        3630 :     for (const auto& desc_cache_pair : wss.m_descriptor_caches) {
     927        1296 :         auto spk_man = pwallet->GetScriptPubKeyMan(desc_cache_pair.first);
     928        1296 :         assert(spk_man);
     929        1296 :         ((DescriptorScriptPubKeyMan*)spk_man)->SetCache(desc_cache_pair.second);
     930             :     }
     931             : 
     932             :     // Set the descriptor keys
     933        3495 :     for (const auto& desc_key_pair : wss.m_descriptor_keys) {
     934        1161 :         auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first);
     935        1161 :         auto it = wss.mnemonics.find(desc_key_pair.first);
     936        1161 :         if (it == wss.mnemonics.end()) {
     937         236 :             ((DescriptorScriptPubKeyMan*)spk_man)->AddKey(desc_key_pair.first.second, desc_key_pair.second, "", "");
     938         236 :         } else {
     939         925 :             ((DescriptorScriptPubKeyMan*)spk_man)->AddKey(desc_key_pair.first.second, desc_key_pair.second, it->second.first, it->second.second);
     940             :         }
     941             :     }
     942             : 
     943        2422 :     for (const auto& desc_key_pair : wss.m_descriptor_crypt_keys) {
     944          88 :         auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first);
     945          88 :         auto it = wss.crypted_mnemonics.find(desc_key_pair.first);
     946          88 :         if (it == wss.crypted_mnemonics.end()) {
     947          18 :             ((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second, {}, {});
     948          18 :         } else {
     949          70 :             ((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second, it->second.first, it->second.second);
     950             :         }
     951             :     }
     952             : 
     953        2334 :     if (fNoncriticalErrors && result == DBErrors::LOAD_OK)
     954           0 :         result = DBErrors::NONCRITICAL_ERROR;
     955             : 
     956             :     // Any wallet corruption at all: skip any rewriting or
     957             :     // upgrading, we don't want to make it worse.
     958        2334 :     if (result != DBErrors::LOAD_OK)
     959           0 :         return result;
     960             : 
     961             :     // Store initial external keypool size since we mostly use external keys in mixing
     962        2334 :     pwallet->nKeysLeftSinceAutoBackup = pwallet->KeypoolCountExternalKeys();
     963        2334 :     pwallet->WalletLogPrintf("nKeysLeftSinceAutoBackup: %d\n", pwallet->nKeysLeftSinceAutoBackup);
     964             : 
     965             :     // Last client version to open this wallet
     966        2334 :     int last_client = CLIENT_VERSION;
     967        2334 :     bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);
     968        2334 :     pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client);
     969             : 
     970        2334 :     pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u total; Watch scripts: %u; HD PubKeys: %u; Metadata: %u; Unknown wallet records: %u\n",
     971        2334 :            wss.nKeys, wss.nCKeys, wss.nKeys + wss.nCKeys,
     972        2334 :            wss.nWatchKeys, wss.nHDPubKeys, wss.nKeyMeta, wss.m_unknown_records);
     973             : 
     974             :     // nTimeFirstKey is only reliable if all keys have metadata
     975        2334 :     if (pwallet->IsLegacy() && (wss.nKeys + wss.nCKeys + wss.nWatchKeys + wss.nHDPubKeys) != wss.nKeyMeta) {
     976          14 :         auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
     977          14 :         if (spk_man) {
     978          14 :             LOCK(spk_man->cs_KeyStore);
     979          14 :             spk_man->UpdateTimeFirstKey(1);
     980          14 :         }
     981          14 :     }
     982             : 
     983        2334 :     for (const uint256& hash : wss.vWalletUpgrade)
     984           0 :         WriteTx(pwallet->mapWallet.at(hash));
     985             : 
     986             :     // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
     987        2334 :     if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000))
     988           0 :         return DBErrors::NEED_REWRITE;
     989             : 
     990        2334 :     if (!has_last_client || last_client != CLIENT_VERSION) // Update
     991        1247 :         m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
     992             : 
     993        2334 :     if (wss.fAnyUnordered)
     994           0 :         result = pwallet->ReorderTransactions();
     995             : 
     996             :     // Upgrade all of the wallet keymetadata to have the hd master key id
     997             :     // This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software
     998             :     try {
     999        2334 :         pwallet->UpgradeKeyMetadata();
    1000        2334 :     } catch (...) {
    1001           0 :         result = DBErrors::CORRUPT;
    1002           0 :     }
    1003             : 
    1004             :     // Upgrade all of the descriptor caches to cache the last hardened xpub
    1005             :     // This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible
    1006             :     try {
    1007        2334 :         pwallet->UpgradeDescriptorCache();
    1008        2334 :     } catch (...) {
    1009           0 :         result = DBErrors::CORRUPT;
    1010           0 :     }
    1011             : 
    1012        2334 :     return result;
    1013        2334 : }
    1014             : 
    1015          23 : DBErrors WalletBatch::FindWalletTxHashes(std::vector<uint256>& tx_hashes)
    1016             : {
    1017          23 :     DBErrors result = DBErrors::LOAD_OK;
    1018             : 
    1019             :     try {
    1020          23 :         int nMinVersion = 0;
    1021          23 :         if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
    1022          23 :             if (nMinVersion > FEATURE_LATEST)
    1023           0 :                 return DBErrors::TOO_NEW;
    1024          23 :         }
    1025             : 
    1026             :         // Get cursor
    1027          23 :         if (!m_batch->StartCursor())
    1028             :         {
    1029           0 :             LogPrintf("Error getting wallet database cursor\n");
    1030           0 :             return DBErrors::CORRUPT;
    1031             :         }
    1032             : 
    1033        1031 :         while (true)
    1034             :         {
    1035             :             // Read next record
    1036        1031 :             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
    1037        1031 :             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
    1038             :             bool complete;
    1039        1031 :             bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
    1040        1031 :             if (complete) {
    1041          23 :                 break;
    1042        1008 :             } else if (!ret) {
    1043           0 :                 m_batch->CloseCursor();
    1044           0 :                 LogPrintf("Error reading next record from wallet database\n");
    1045           0 :                 return DBErrors::CORRUPT;
    1046             :             }
    1047             : 
    1048        1008 :             std::string strType;
    1049        1008 :             ssKey >> strType;
    1050        1008 :             if (strType == DBKeys::TX) {
    1051         439 :                 uint256 hash;
    1052         439 :                 ssKey >> hash;
    1053         439 :                 tx_hashes.push_back(hash);
    1054         439 :             }
    1055        1031 :         }
    1056          23 :     } catch (...) {
    1057           0 :         result = DBErrors::CORRUPT;
    1058           0 :     }
    1059          23 :     m_batch->CloseCursor();
    1060             : 
    1061          23 :     return result;
    1062          23 : }
    1063             : 
    1064          23 : DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut)
    1065             : {
    1066             :     // build list of wallet TX hashes
    1067          23 :     std::vector<uint256> vTxHash;
    1068          23 :     DBErrors err = FindWalletTxHashes(vTxHash);
    1069          23 :     if (err != DBErrors::LOAD_OK) {
    1070           0 :         return err;
    1071             :     }
    1072             : 
    1073          23 :     std::sort(vTxHash.begin(), vTxHash.end());
    1074          23 :     std::sort(vTxHashIn.begin(), vTxHashIn.end());
    1075             : 
    1076             :     // erase each matching wallet TX
    1077          23 :     bool delerror = false;
    1078          23 :     std::vector<uint256>::iterator it = vTxHashIn.begin();
    1079         417 :     for (const uint256& hash : vTxHash) {
    1080        1008 :         while (it < vTxHashIn.end() && (*it) < hash) {
    1081         212 :             it++;
    1082             :         }
    1083         398 :         if (it == vTxHashIn.end()) {
    1084           4 :             break;
    1085             :         }
    1086         394 :         else if ((*it) == hash) {
    1087         227 :             if(!EraseTx(hash)) {
    1088           0 :                 LogPrint(BCLog::WALLETDB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
    1089           0 :                 delerror = true;
    1090           0 :             }
    1091         227 :             vTxHashOut.push_back(hash);
    1092         227 :         }
    1093             :     }
    1094             : 
    1095          23 :     if (delerror) {
    1096           0 :         return DBErrors::CORRUPT;
    1097             :     }
    1098          23 :     return DBErrors::LOAD_OK;
    1099          23 : }
    1100             : 
    1101       46327 : void MaybeCompactWalletDB(WalletContext& context)
    1102             : {
    1103             :     static std::atomic<bool> fOneThread(false);
    1104       46327 :     if (fOneThread.exchange(true)) {
    1105           0 :         return;
    1106             :     }
    1107             : 
    1108       97696 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
    1109       51369 :         WalletDatabase& dbh = pwallet->GetDatabase();
    1110             : 
    1111       51369 :         unsigned int nUpdateCounter = dbh.nUpdateCounter;
    1112             : 
    1113       51369 :         if (dbh.nLastSeen != nUpdateCounter) {
    1114       16069 :             dbh.nLastSeen = nUpdateCounter;
    1115       16069 :             dbh.nLastWalletUpdate = GetTime();
    1116       16069 :         }
    1117             : 
    1118       51369 :         if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
    1119        4225 :             if (dbh.PeriodicFlush()) {
    1120        1379 :                 dbh.nLastFlushed = nUpdateCounter;
    1121        1379 :             }
    1122        4225 :         }
    1123             :     }
    1124             : 
    1125       46327 :     fOneThread = false;
    1126       46327 : }
    1127             : 
    1128          68 : bool WalletBatch::WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent)
    1129             : {
    1130          68 :     auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), std::string("used")))};
    1131          68 :     return previously_spent ? WriteIC(key, std::string("1")) : EraseIC(key);
    1132          68 : }
    1133             : 
    1134           8 : bool WalletBatch::WriteAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& receive_request)
    1135             : {
    1136           8 :     return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)), receive_request);
    1137           0 : }
    1138             : 
    1139           2 : bool WalletBatch::EraseAddressReceiveRequest(const CTxDestination& dest, const std::string& id)
    1140             : {
    1141           2 :     return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)));
    1142           0 : }
    1143             : 
    1144           8 : bool WalletBatch::EraseAddressData(const CTxDestination& dest)
    1145             : {
    1146           8 :     CDataStream prefix(SER_DISK, CLIENT_VERSION);
    1147           8 :     prefix << DBKeys::DESTDATA << EncodeDestination(dest);
    1148           8 :     return m_batch->ErasePrefix({prefix.data(), prefix.size()});
    1149           8 : }
    1150             : 
    1151       32502 : bool WalletBatch::WriteHDChain(const CHDChain& chain)
    1152             : {
    1153       32502 :     if (chain.IsCrypted()) {
    1154         651 :         if (!WriteIC(DBKeys::CRYPTED_HDCHAIN, chain))
    1155           0 :             return false;
    1156             : 
    1157         651 :         EraseIC(DBKeys::HDCHAIN);
    1158             : 
    1159         651 :         return true;
    1160             :     }
    1161       31851 :     return WriteIC(DBKeys::HDCHAIN, chain);
    1162       32502 : }
    1163             : 
    1164       31845 : bool WalletBatch::WriteHDPubKey(const CHDPubKey& hdPubKey, const CKeyMetadata& keyMeta)
    1165             : {
    1166       31845 :     if (!WriteIC(std::make_pair(DBKeys::KEYMETA, hdPubKey.extPubKey.pubkey), keyMeta, false))
    1167           0 :         return false;
    1168             : 
    1169       31845 :     return WriteIC(std::make_pair(DBKeys::HDPUBKEY, hdPubKey.extPubKey.pubkey), hdPubKey, false);
    1170       31845 : }
    1171             : 
    1172       69453 : bool WalletBatch::WriteWalletFlags(const uint64_t flags)
    1173             : {
    1174       69453 :     return WriteIC(DBKeys::FLAGS, flags);
    1175             : }
    1176             : 
    1177          26 : bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types)
    1178             : {
    1179             :     // Get cursor
    1180          26 :     if (!m_batch->StartCursor())
    1181             :     {
    1182           0 :         return false;
    1183             :     }
    1184             : 
    1185             :     // Iterate the DB and look for any records that have the type prefixes
    1186         918 :     while (true)
    1187             :     {
    1188             :         // Read next record
    1189         918 :         CDataStream key(SER_DISK, CLIENT_VERSION);
    1190         918 :         CDataStream value(SER_DISK, CLIENT_VERSION);
    1191             :         bool complete;
    1192         918 :         bool ret = m_batch->ReadAtCursor(key, value, complete);
    1193         918 :         if (complete) {
    1194          26 :             break;
    1195             :         }
    1196         892 :         else if (!ret)
    1197             :         {
    1198           0 :             m_batch->CloseCursor();
    1199           0 :             return false;
    1200             :         }
    1201             : 
    1202             :         // Make a copy of key to avoid data being deleted by the following read of the type
    1203         892 :         Span<const unsigned char> key_data = MakeUCharSpan(key);
    1204             : 
    1205         892 :         std::string type;
    1206         892 :         key >> type;
    1207             : 
    1208         892 :         if (types.count(type) > 0) {
    1209         328 :             m_batch->Erase(key_data);
    1210         328 :         }
    1211         918 :     }
    1212          26 :     m_batch->CloseCursor();
    1213          26 :     return true;
    1214          26 : }
    1215             : 
    1216         138 : bool WalletBatch::TxnBegin()
    1217             : {
    1218         138 :     return m_batch->TxnBegin();
    1219             : }
    1220             : 
    1221         136 : bool WalletBatch::TxnCommit()
    1222             : {
    1223         136 :     return m_batch->TxnCommit();
    1224             : }
    1225             : 
    1226           2 : bool WalletBatch::TxnAbort()
    1227             : {
    1228           2 :     return m_batch->TxnAbort();
    1229             : }
    1230             : 
    1231        3773 : std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
    1232             : {
    1233             :     bool exists;
    1234             :     try {
    1235        3773 :         exists = fs::symlink_status(path).type() != fs::file_type::not_found;
    1236        3773 :     } catch (const fs::filesystem_error& e) {
    1237           0 :         error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e)));
    1238           0 :         status = DatabaseStatus::FAILED_BAD_PATH;
    1239           0 :         return nullptr;
    1240           0 :     }
    1241             : 
    1242        3773 :     std::optional<DatabaseFormat> format;
    1243        3773 :     if (exists) {
    1244        2966 :         if (IsBDBFile(BDBDataFile(path))) {
    1245        1159 :             format = DatabaseFormat::BERKELEY;
    1246        1159 :         }
    1247        2966 :         if (IsSQLiteFile(SQLiteDataFile(path))) {
    1248         576 :             if (format) {
    1249           0 :                 error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", fs::PathToString(path)));
    1250           0 :                 status = DatabaseStatus::FAILED_BAD_FORMAT;
    1251           0 :                 return nullptr;
    1252             :             }
    1253         576 :             format = DatabaseFormat::SQLITE;
    1254         576 :         }
    1255        3773 :     } else if (options.require_existing) {
    1256          12 :         error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", fs::PathToString(path)));
    1257          12 :         status = DatabaseStatus::FAILED_NOT_FOUND;
    1258          12 :         return nullptr;
    1259             :     }
    1260             : 
    1261        3761 :     if (!format && options.require_existing) {
    1262         781 :         error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", fs::PathToString(path)));
    1263         781 :         status = DatabaseStatus::FAILED_BAD_FORMAT;
    1264         781 :         return nullptr;
    1265             :     }
    1266             : 
    1267        2980 :     if (format && options.require_create) {
    1268          10 :         error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(path)));
    1269          10 :         status = DatabaseStatus::FAILED_ALREADY_EXISTS;
    1270          10 :         return nullptr;
    1271             :     }
    1272             : 
    1273             :     // A db already exists so format is set, but options also specifies the format, so make sure they agree
    1274        2970 :     if (format && options.require_format && format != options.require_format) {
    1275           0 :         error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", fs::PathToString(path)));
    1276           0 :         status = DatabaseStatus::FAILED_BAD_FORMAT;
    1277           0 :         return nullptr;
    1278             :     }
    1279             : 
    1280             :     // Format is not set when a db doesn't already exist, so use the format specified by the options if it is set.
    1281        2970 :     if (!format && options.require_format) format = options.require_format;
    1282             : 
    1283             :     // If the format is not specified or detected, choose the default format based on what is available. We prefer BDB over SQLite for now..
    1284        2970 :     if (!format) {
    1285             : #ifdef USE_SQLITE
    1286         761 :         format = DatabaseFormat::SQLITE;
    1287             : #endif
    1288             : #ifdef USE_BDB
    1289         761 :         format = DatabaseFormat::BERKELEY;
    1290             : #endif
    1291         761 :     }
    1292             : 
    1293        2970 :     if (format == DatabaseFormat::SQLITE) {
    1294             : #ifdef USE_SQLITE
    1295             :         if constexpr (true) {
    1296        1041 :             return MakeSQLiteDatabase(path, options, status, error);
    1297             :         } else
    1298             : #endif
    1299             :         {
    1300             :             error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support SQLite database format.", fs::PathToString(path)));
    1301             :             status = DatabaseStatus::FAILED_BAD_FORMAT;
    1302             :             return nullptr;
    1303             :         }
    1304             :     }
    1305             : 
    1306             : #ifdef USE_BDB
    1307             :     if constexpr (true) {
    1308        1929 :         return MakeBerkeleyDatabase(path, options, status, error);
    1309             :     } else
    1310             : #endif
    1311             :     {
    1312             :         error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", fs::PathToString(path)));
    1313             :         status = DatabaseStatus::FAILED_BAD_FORMAT;
    1314             :         return nullptr;
    1315             :     }
    1316        3773 : }
    1317             : 
    1318             : /** Return object for accessing dummy database with no read/write capabilities. */
    1319          22 : std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
    1320             : {
    1321          22 :     return std::make_unique<DummyDatabase>();
    1322             : }
    1323             : 
    1324             : /** Return object for accessing temporary in-memory database. */
    1325          36 : std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(DatabaseOptions& options)
    1326             : {
    1327             : 
    1328          36 :     std::optional<DatabaseFormat> format;
    1329          36 :     if (options.require_format) format = options.require_format;
    1330          36 :     if (!format) {
    1331             : #ifdef USE_BDB
    1332          36 :         format = DatabaseFormat::BERKELEY;
    1333             : #endif
    1334             : #ifdef USE_SQLITE
    1335          36 :         format = DatabaseFormat::SQLITE;
    1336             : #endif
    1337          36 :     }
    1338             : 
    1339          36 :     if (format == DatabaseFormat::SQLITE) {
    1340             : #ifdef USE_SQLITE
    1341          36 :         return std::make_unique<SQLiteDatabase>(":memory:", "", options, true);
    1342             : #endif
    1343             :         assert(false);
    1344             :     }
    1345             : 
    1346             : #ifdef USE_BDB
    1347           0 :     return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "", options);
    1348             : #endif
    1349             :     assert(false);
    1350          36 : }
    1351             : 
    1352          36 : std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
    1353             : {
    1354          36 :     DatabaseOptions options;
    1355          36 :     return CreateMockWalletDatabase(options);
    1356          36 : }
    1357             : } // namespace wallet

Generated by: LCOV version 1.16