LCOV - code coverage report
Current view: top level - src/wallet - walletdb.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 406 803 50.6 %
Date: 2026-06-25 07:23:51 Functions: 50 65 76.9 %

          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         146 : 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         146 : 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       11762 : bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
      78             : {
      79       11762 :     return WriteIC(std::make_pair(DBKeys::NAME, strAddress), strName);
      80           0 : }
      81             : 
      82           0 : 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           0 :     return EraseIC(std::make_pair(DBKeys::NAME, strAddress));
      87           0 : }
      88             : 
      89       11762 : bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
      90             : {
      91       11762 :     return WriteIC(std::make_pair(DBKeys::PURPOSE, strAddress), strPurpose);
      92           0 : }
      93             : 
      94           0 : bool WalletBatch::ErasePurpose(const std::string& strAddress)
      95             : {
      96           0 :     return EraseIC(std::make_pair(DBKeys::PURPOSE, strAddress));
      97           0 : }
      98             : 
      99         746 : bool WalletBatch::WriteTx(const CWalletTx& wtx)
     100             : {
     101         746 :     return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
     102           0 : }
     103             : 
     104           1 : bool WalletBatch::EraseTx(uint256 hash)
     105             : {
     106           1 :     return EraseIC(std::make_pair(DBKeys::TX, hash));
     107           0 : }
     108             : 
     109        9406 : bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& keyMeta, const CPubKey& vchPubKey, const bool overwrite)
     110             : {
     111        9406 :     return WriteIC(std::make_pair(DBKeys::KEYMETA, vchPubKey), keyMeta, overwrite);
     112           0 : }
     113             : 
     114        9406 : bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
     115             : {
     116        9406 :     if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
     117           0 :         return false;
     118             :     }
     119             : 
     120             :     // hash pubkey/privkey to accelerate wallet load
     121        9406 :     std::vector<unsigned char> vchKey;
     122        9406 :     vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
     123        9406 :     vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
     124        9406 :     vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
     125             : 
     126        9406 :     return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey)), false);
     127        9406 : }
     128             : 
     129           0 : bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
     130             :                                 const std::vector<unsigned char>& vchCryptedSecret,
     131             :                                 const CKeyMetadata &keyMeta)
     132             : {
     133           0 :     if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
     134           0 :         return false;
     135             :     }
     136             : 
     137             :     // Compute a checksum of the encrypted key
     138           0 :     uint256 checksum = Hash(vchCryptedSecret);
     139             : 
     140           0 :     const auto key = std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey);
     141           0 :     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           0 :     EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
     152           0 :     return true;
     153           0 : }
     154             : 
     155           0 : bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
     156             : {
     157           0 :     return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true);
     158           0 : }
     159             : 
     160           9 : bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
     161             : {
     162           9 :     return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false);
     163           0 : }
     164             : 
     165           2 : bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
     166             : {
     167           2 :     if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
     168           0 :         return false;
     169             :     }
     170           2 :     return WriteIC(std::make_pair(DBKeys::WATCHS, dest), uint8_t{'1'});
     171           2 : }
     172             : 
     173           5 : bool WalletBatch::EraseWatchOnly(const CScript &dest)
     174             : {
     175           5 :     if (!EraseIC(std::make_pair(DBKeys::WATCHMETA, dest))) {
     176           0 :         return false;
     177             :     }
     178           5 :     return EraseIC(std::make_pair(DBKeys::WATCHS, dest));
     179           5 : }
     180             : 
     181           5 : bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
     182             : {
     183           5 :     WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
     184           5 :     return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
     185           0 : }
     186             : 
     187          10 : bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
     188             : {
     189          10 :     if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
     190          10 :     return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
     191          10 : }
     192             : 
     193         744 : bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
     194             : {
     195         744 :     return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
     196             : }
     197             : 
     198        6460 : bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
     199             : {
     200        6460 :     return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
     201           0 : }
     202             : 
     203        9389 : bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
     204             : {
     205        9389 :     return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool);
     206           0 : }
     207             : 
     208        6391 : bool WalletBatch::ErasePool(int64_t nPool)
     209             : {
     210        6391 :     return EraseIC(std::make_pair(DBKeys::POOL, nPool));
     211           0 : }
     212             : 
     213           5 : bool WalletBatch::WriteMinVersion(int nVersion)
     214             : {
     215           5 :     return WriteIC(DBKeys::MINVERSION, nVersion);
     216             : }
     217             : 
     218          84 : bool WalletBatch::ReadCoinJoinSalt(uint256& salt, bool fLegacy)
     219             : {
     220             :     // TODO: Remove legacy checks after few major releases
     221          84 :     return m_batch->Read(std::string(fLegacy ? DBKeys::PRIVATESEND_SALT : DBKeys::COINJOIN_SALT), salt);
     222           0 : }
     223             : 
     224          39 : bool WalletBatch::WriteCoinJoinSalt(const uint256& salt)
     225             : {
     226          39 :     return WriteIC(DBKeys::COINJOIN_SALT, salt);
     227             : }
     228             : 
     229           0 : bool WalletBatch::WriteGovernanceObject(const Governance::Object& obj)
     230             : {
     231           0 :     return WriteIC(std::make_pair(DBKeys::G_OBJECT, obj.GetHash()), obj, false);
     232           0 : }
     233             : 
     234          44 : bool WalletBatch::WriteActiveScriptPubKeyMan(const uint256& id, bool internal)
     235             : {
     236          44 :     std::string key = internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK;
     237          44 :     return WriteIC(key, id);
     238          44 : }
     239             : 
     240           0 : bool WalletBatch::EraseActiveScriptPubKeyMan(bool internal)
     241             : {
     242           0 :     const std::string key{internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK};
     243           0 :     return EraseIC(key);
     244           0 : }
     245             : 
     246          80 : 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          80 :     std::vector<unsigned char> key;
     250          80 :     key.reserve(pubkey.size() + privkey.size());
     251          80 :     key.insert(key.end(), pubkey.begin(), pubkey.end());
     252          80 :     key.insert(key.end(), privkey.begin(), privkey.end());
     253             : 
     254          80 :     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          80 : }
     256             : 
     257           0 : 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           0 :     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           0 :     EraseIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)));
     263           0 :     return true;
     264           0 : }
     265             : 
     266       11686 : bool WalletBatch::WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor/*, const SecureString& mnemonic, const SecureString& mnemonic_passphrase*/)
     267             : {
     268       11686 :     return WriteIC(make_pair(DBKeys::WALLETDESCRIPTOR, desc_id), descriptor);
     269           0 : }
     270             : 
     271        1000 : bool WalletBatch::WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index)
     272             : {
     273        1000 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     274        1000 :     xpub.Encode(ser_xpub.data());
     275        1000 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), std::make_pair(key_exp_index, der_index)), ser_xpub);
     276        1000 : }
     277             : 
     278          70 : bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
     279             : {
     280          70 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     281          70 :     xpub.Encode(ser_xpub.data());
     282          70 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
     283          70 : }
     284             : 
     285          70 : bool WalletBatch::WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
     286             : {
     287          70 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     288          70 :     xpub.Encode(ser_xpub.data());
     289          70 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORLHCACHE, desc_id), key_exp_index), ser_xpub);
     290          70 : }
     291             : 
     292       72552 : bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache)
     293             : {
     294       72622 :     for (const auto& parent_xpub_pair : cache.GetCachedParentExtPubKeys()) {
     295          70 :         if (!WriteDescriptorParentCache(parent_xpub_pair.second, desc_id, parent_xpub_pair.first)) {
     296           0 :             return false;
     297             :         }
     298             :     }
     299       73552 :     for (const auto& derived_xpub_map_pair : cache.GetCachedDerivedExtPubKeys()) {
     300        2000 :         for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
     301        1000 :             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       72622 :     for (const auto& lh_xpub_pair : cache.GetCachedLastHardenedExtPubKeys()) {
     307          70 :         if (!WriteDescriptorLastHardenedCache(lh_xpub_pair.second, desc_id, lh_xpub_pair.first)) {
     308           0 :             return false;
     309             :         }
     310             :     }
     311       72552 :     return true;
     312       72552 : }
     313             : 
     314           0 : bool WalletBatch::WriteLockedUTXO(const COutPoint& output)
     315             : {
     316           0 :     return WriteIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)), uint8_t{'1'});
     317           0 : }
     318             : 
     319          41 : bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
     320             : {
     321          41 :     return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)));
     322           0 : }
     323             : 
     324             : class CWalletScanState {
     325             : public:
     326          45 :     unsigned int nKeys{0};
     327          45 :     unsigned int nCKeys{0};
     328          45 :     unsigned int nWatchKeys{0};
     329          45 :     unsigned int nHDPubKeys{0};
     330          45 :     unsigned int nKeyMeta{0};
     331          45 :     unsigned int m_unknown_records{0};
     332          45 :     bool fIsEncrypted{false};
     333          45 :     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          45 :     bool tx_corrupt{false};
     343             : 
     344         180 :     CWalletScanState() = default;
     345             : };
     346             : 
     347             : static bool
     348          73 : 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          73 :         ssKey >> strType;
     356             :         // If we have a filter, check if this matches the filter
     357          73 :         if (filter_fn && !filter_fn(strType)) {
     358           0 :             return true;
     359             :         }
     360          73 :         if (strType == DBKeys::NAME) {
     361           4 :             std::string strAddress;
     362           4 :             ssKey >> strAddress;
     363           4 :             std::string label;
     364           4 :             ssValue >> label;
     365           4 :             pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
     366          73 :         } else if (strType == DBKeys::PURPOSE) {
     367           4 :             std::string strAddress;
     368           4 :             ssKey >> strAddress;
     369           4 :             ssValue >> pwallet->m_address_book[DecodeDestination(strAddress)].purpose;
     370          69 :         } else if (strType == DBKeys::TX) {
     371           2 :             uint256 hash;
     372           2 :             ssKey >> hash;
     373             :             // LoadToWallet call below creates a new CWalletTx that fill_wtx
     374             :             // callback fills with transaction metadata.
     375           4 :             auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
     376           2 :                 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           2 :                 ssValue >> wtx;
     384           2 :                 if (wtx.GetHash() != hash)
     385           0 :                     return false;
     386             : 
     387             :                 // Undo serialize changes in 31600
     388           2 :                 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           2 :                 if (wtx.nOrderPos == -1)
     409           0 :                     wss.fAnyUnordered = true;
     410             : 
     411           2 :                 return true;
     412           2 :             };
     413           2 :             if (!pwallet->LoadToWallet(hash, fill_wtx)) {
     414           0 :                 return false;
     415             :             }
     416          65 :         } else if (strType == DBKeys::WATCHS) {
     417           0 :             wss.nWatchKeys++;
     418           0 :             CScript script;
     419           0 :             ssKey >> script;
     420             :             uint8_t fYes;
     421           0 :             ssValue >> fYes;
     422           0 :             if (fYes == '1') {
     423           0 :                 pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script);
     424           0 :             }
     425          63 :         } else if (strType == DBKeys::KEY) {
     426           0 :             CPubKey vchPubKey;
     427           0 :             ssKey >> vchPubKey;
     428           0 :             if (!vchPubKey.IsValid())
     429             :             {
     430           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     431           0 :                 return false;
     432             :             }
     433           0 :             CKey key;
     434           0 :             CPrivKey pkey;
     435           0 :             uint256 hash;
     436             : 
     437           0 :             wss.nKeys++;
     438           0 :             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           0 :                 ssValue >> hash;
     448           0 :             }
     449           0 :             catch (const std::ios_base::failure&) {}
     450             : 
     451           0 :             bool fSkipCheck = false;
     452             : 
     453           0 :             if (!hash.IsNull())
     454             :             {
     455             :                 // hash pubkey/privkey to accelerate wallet load
     456           0 :                 std::vector<unsigned char> vchKey;
     457           0 :                 vchKey.reserve(vchPubKey.size() + pkey.size());
     458           0 :                 vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
     459           0 :                 vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
     460             : 
     461           0 :                 if (Hash(vchKey) != hash)
     462             :                 {
     463           0 :                     strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
     464           0 :                     return false;
     465             :                 }
     466             : 
     467           0 :                 fSkipCheck = true;
     468           0 :             }
     469             : 
     470           0 :             if (!key.Load(pkey, vchPubKey, fSkipCheck))
     471             :             {
     472           0 :                 strErr = "Error reading wallet database: CPrivKey corrupt";
     473           0 :                 return false;
     474             :             }
     475           0 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey))
     476             :             {
     477           0 :                 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed";
     478           0 :                 return false;
     479             :             }
     480          63 :         } 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           0 :             ssKey >> nID;
     484           0 :             CMasterKey kMasterKey;
     485           0 :             ssValue >> kMasterKey;
     486           0 :             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           0 :             pwallet->mapMasterKeys[nID] = kMasterKey;
     492           0 :             if (pwallet->nMasterKeyMaxID < nID)
     493           0 :                 pwallet->nMasterKeyMaxID = nID;
     494          63 :         } else if (strType == DBKeys::CRYPTED_KEY) {
     495           0 :             CPubKey vchPubKey;
     496           0 :             ssKey >> vchPubKey;
     497           0 :             if (!vchPubKey.IsValid())
     498             :             {
     499           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     500           0 :                 return false;
     501             :             }
     502           0 :             std::vector<unsigned char> vchPrivKey;
     503           0 :             ssValue >> vchPrivKey;
     504             : 
     505             :             // Get the checksum and check it
     506           0 :             bool checksum_valid = false;
     507           0 :             if (!ssValue.eof()) {
     508           0 :                 uint256 checksum;
     509           0 :                 ssValue >> checksum;
     510           0 :                 if (!(checksum_valid = Hash(vchPrivKey) == checksum)) {
     511           0 :                     strErr = "Error reading wallet database: Encrypted key corrupt";
     512           0 :                     return false;
     513             :                 }
     514           0 :             }
     515             : 
     516           0 :             wss.nCKeys++;
     517             : 
     518           0 :             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           0 :             wss.fIsEncrypted = true;
     524          63 :         } else if (strType == DBKeys::KEYMETA) {
     525           0 :             CPubKey vchPubKey;
     526           0 :             ssKey >> vchPubKey;
     527           0 :             CKeyMetadata keyMeta;
     528           0 :             ssValue >> keyMeta;
     529           0 :             wss.nKeyMeta++;
     530           0 :             pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
     531          63 :         } else if (strType == DBKeys::WATCHMETA) {
     532           0 :             CScript script;
     533           0 :             ssKey >> script;
     534           0 :             CKeyMetadata keyMeta;
     535           0 :             ssValue >> keyMeta;
     536           0 :             wss.nKeyMeta++;
     537           0 :             pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta);
     538          63 :         } 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          63 :         } else if (strType == DBKeys::POOL) {
     548             :             int64_t nIndex;
     549           0 :             ssKey >> nIndex;
     550           0 :             CKeyPool keypool;
     551           0 :             ssValue >> keypool;
     552             : 
     553           0 :             pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool);
     554          63 :         } else if (strType == DBKeys::CSCRIPT) {
     555           0 :             uint160 hash;
     556           0 :             ssKey >> hash;
     557           0 :             CScript script;
     558           0 :             ssValue >> script;
     559           0 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script))
     560             :             {
     561           0 :                 strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed";
     562           0 :                 return false;
     563             :             }
     564          63 :         } else if (strType == DBKeys::ORDERPOSNEXT) {
     565           1 :             ssValue >> pwallet->nOrderPosNext;
     566          63 :         } 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          62 :         } else if (strType == DBKeys::HDCHAIN || strType == DBKeys::CRYPTED_HDCHAIN) {
     586           0 :             CHDChain chain;
     587           0 :             ssValue >> chain;
     588           0 :             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           0 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain, /*skip_encryption_check=*/true)) {
     592           0 :                 strErr = "Error reading wallet database: LoadHDChain failed";
     593           0 :                 return false;
     594             :             }
     595          52 :         } else if (strType == DBKeys::HDPUBKEY) {
     596           0 :             wss.nHDPubKeys++;
     597           0 :             CPubKey vchPubKey;
     598           0 :             ssKey >> vchPubKey;
     599             : 
     600           0 :             CHDPubKey hdPubKey;
     601           0 :             ssValue >> hdPubKey;
     602             : 
     603           0 :             if(vchPubKey != hdPubKey.extPubKey.pubkey)
     604             :             {
     605           0 :                 strErr = "Error reading wallet database: CHDPubKey corrupt";
     606           0 :                 return false;
     607             :             }
     608           0 :             if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDPubKey(hdPubKey))
     609             :             {
     610           0 :                 strErr = "Error reading wallet database: LoadHDPubKey failed";
     611           0 :                 return false;
     612             :             }
     613          52 :         } else if (strType == DBKeys::G_OBJECT) {
     614           0 :             uint256 nObjectHash;
     615           0 :             Governance::Object obj;
     616           0 :             ssKey >> nObjectHash;
     617           0 :             ssValue >> obj;
     618             : 
     619           0 :             if (obj.GetHash() != nObjectHash) {
     620           0 :                 strErr = "Invalid governance object: Hash mismatch";
     621           0 :                 return false;
     622             :             }
     623             : 
     624           0 :             if (!pwallet->LoadGovernanceObject(obj)) {
     625           0 :                 strErr = "Invalid governance object: LoadGovernanceObject";
     626           0 :                 return false;
     627             :             }
     628          52 :         } else if (strType == DBKeys::OLD_KEY) {
     629           0 :             strErr = "Found unsupported 'wkey' record, try loading with version 0.17";
     630           0 :             return false;
     631          52 :         } else if (strType == DBKeys::ACTIVEEXTERNALSPK || strType == DBKeys::ACTIVEINTERNALSPK) {
     632           4 :             uint256 id;
     633           4 :             ssValue >> id;
     634             : 
     635           4 :             bool internal = strType == DBKeys::ACTIVEINTERNALSPK;
     636           4 :             auto& spk_mans = internal ? wss.m_active_internal_spks : wss.m_active_external_spks;
     637           4 :             const OutputType type = OutputType::LEGACY;
     638           4 :             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           4 :             spk_mans[static_cast<OutputType>(type)] = id;
     643          52 :         } else if (strType == DBKeys::WALLETDESCRIPTOR) {
     644           8 :             uint256 id;
     645           8 :             ssKey >> id;
     646           8 :             WalletDescriptor desc;
     647           8 :             ssValue >> desc;
     648           8 :             if (wss.m_descriptor_caches.count(id) == 0) {
     649           8 :                 wss.m_descriptor_caches[id] = DescriptorCache();
     650           8 :             }
     651           8 :             pwallet->LoadDescriptorScriptPubKeyMan(id, desc);
     652          48 :         } else if (strType == DBKeys::WALLETDESCRIPTORCACHE) {
     653           6 :             bool parent = true;
     654           6 :             uint256 desc_id;
     655             :             uint32_t key_exp_index;
     656             :             uint32_t der_index;
     657           6 :             ssKey >> desc_id;
     658           6 :             ssKey >> key_exp_index;
     659             : 
     660             :             // if the der_index exists, it's a derived xpub
     661             :             try
     662             :             {
     663           6 :                 ssKey >> der_index;
     664           0 :                 parent = false;
     665           6 :             }
     666           6 :             catch (...) {}
     667             : 
     668           6 :             std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     669           6 :             ssValue >> ser_xpub;
     670           6 :             CExtPubKey xpub;
     671           6 :             xpub.Decode(ser_xpub.data());
     672           6 :             if (parent) {
     673           6 :                 wss.m_descriptor_caches[desc_id].CacheParentExtPubKey(key_exp_index, xpub);
     674           6 :             } else {
     675           0 :                 wss.m_descriptor_caches[desc_id].CacheDerivedExtPubKey(key_exp_index, der_index, xpub);
     676             :             }
     677          40 :         } else if (strType == DBKeys::WALLETDESCRIPTORLHCACHE) {
     678           6 :             uint256 desc_id;
     679             :             uint32_t key_exp_index;
     680           6 :             ssKey >> desc_id;
     681           6 :             ssKey >> key_exp_index;
     682             : 
     683           6 :             std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     684           6 :             ssValue >> ser_xpub;
     685           6 :             CExtPubKey xpub;
     686           6 :             xpub.Decode(ser_xpub.data());
     687           6 :             wss.m_descriptor_caches[desc_id].CacheLastHardenedExtPubKey(key_exp_index, xpub);
     688          34 :         } else if (strType == DBKeys::WALLETDESCRIPTORKEY) {
     689           8 :             uint256 desc_id;
     690           8 :             CPubKey pubkey;
     691           8 :             ssKey >> desc_id;
     692           8 :             ssKey >> pubkey;
     693           8 :             if (!pubkey.IsValid())
     694             :             {
     695           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     696           0 :                 return false;
     697             :             }
     698           8 :             CKey key;
     699           8 :             CPrivKey pkey;
     700           8 :             uint256 hash;
     701             : 
     702           8 :             wss.nKeys++;
     703           8 :             ssValue >> pkey;
     704           8 :             ssValue >> hash;
     705             : 
     706             :             // hash pubkey/privkey to accelerate wallet load
     707           8 :             std::vector<unsigned char> to_hash;
     708           8 :             to_hash.reserve(pubkey.size() + pkey.size());
     709           8 :             to_hash.insert(to_hash.end(), pubkey.begin(), pubkey.end());
     710           8 :             to_hash.insert(to_hash.end(), pkey.begin(), pkey.end());
     711             : 
     712           8 :             if (Hash(to_hash) != hash)
     713             :             {
     714           0 :                 strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
     715           0 :                 return false;
     716             :             }
     717             : 
     718           8 :             if (!key.Load(pkey, pubkey, true))
     719             :             {
     720           0 :                 strErr = "Error reading wallet database: CPrivKey corrupt";
     721           0 :                 return false;
     722             :             }
     723           8 :             wss.m_descriptor_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), key));
     724             : 
     725           8 :             SecureString mnemonic;
     726           8 :             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           8 :                 ssValue >> mnemonic;
     732           8 :                 ssValue >> mnemonic_passphrase;
     733           8 :             }
     734           0 :             catch (const std::ios_base::failure&) {}
     735             : 
     736           8 :             if (!mnemonic.empty()) {
     737           6 :                 wss.mnemonics.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(mnemonic, mnemonic_passphrase)));
     738           6 :             }
     739          28 :         } else if (strType == DBKeys::WALLETDESCRIPTORCKEY) {
     740           0 :             uint256 desc_id;
     741           0 :             CPubKey pubkey;
     742           0 :             ssKey >> desc_id;
     743           0 :             ssKey >> pubkey;
     744           0 :             if (!pubkey.IsValid())
     745             :             {
     746           0 :                 strErr = "Error reading wallet database: CPubKey corrupt";
     747           0 :                 return false;
     748             :             }
     749           0 :             std::vector<unsigned char> privkey;
     750           0 :             ssValue >> privkey;
     751           0 :             wss.nCKeys++;
     752             : 
     753           0 :             wss.m_descriptor_crypt_keys.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(pubkey, privkey)));
     754           0 :             wss.fIsEncrypted = true;
     755             : 
     756             :             // TODO : remove copy-paste with plain-text key
     757           0 :             std::vector<unsigned char> mnemonic;
     758           0 :             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           0 :                 ssValue >> mnemonic;
     764           0 :                 ssValue >> mnemonic_passphrase;
     765           0 :             }
     766           0 :             catch (const std::ios_base::failure&) {}
     767           0 :             if (!mnemonic.empty()) {
     768           0 :                 wss.crypted_mnemonics.insert(std::make_pair(std::make_pair(desc_id, pubkey.GetID()), std::make_pair(mnemonic, mnemonic_passphrase)));
     769           0 :             }
     770             : 
     771          20 :         } else if (strType == DBKeys::LOCKED_UTXO) {
     772           0 :             uint256 hash;
     773             :             uint32_t n;
     774           0 :             ssKey >> hash;
     775           0 :             ssKey >> n;
     776           0 :             pwallet->LockCoin(COutPoint(hash, n));
     777          22 :         } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE &&
     778          16 :                    strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY &&
     779          14 :                    strType != DBKeys::VERSION && strType != DBKeys::SETTINGS &&
     780           8 :                    strType != DBKeys::PRIVATESEND_SALT && strType != DBKeys::COINJOIN_SALT &&
     781           2 :                    strType != DBKeys::FLAGS) {
     782           0 :             wss.m_unknown_records++;
     783           0 :         }
     784          73 :     } 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          73 :     return true;
     796          79 : }
     797             : 
     798           0 : bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, std::string& strType, std::string& strErr, const KeyFilterFn& filter_fn)
     799             : {
     800           0 :     CWalletScanState dummy_wss;
     801           0 :     LOCK(pwallet->cs_wallet);
     802           0 :     return ReadKeyValue(pwallet, ssKey, ssValue, dummy_wss, strType, strErr, filter_fn);
     803           0 : }
     804             : 
     805           0 : bool WalletBatch::IsKeyType(const std::string& strType)
     806             : {
     807           0 :     return (strType == DBKeys::KEY ||
     808           0 :             strType == DBKeys::MASTER_KEY || strType == DBKeys::CRYPTED_KEY ||
     809           0 :             strType == DBKeys::HDCHAIN || strType == DBKeys::CRYPTED_HDCHAIN);
     810             : }
     811             : 
     812          45 : DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
     813             : {
     814          45 :     CWalletScanState wss;
     815          45 :     bool fNoncriticalErrors = false;
     816          45 :     DBErrors result = DBErrors::LOAD_OK;
     817             : 
     818          45 :     LOCK(pwallet->cs_wallet);
     819             :     try {
     820          45 :         int nMinVersion = 0;
     821          45 :         if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
     822           2 :             if (nMinVersion > FEATURE_LATEST)
     823           0 :                 return DBErrors::TOO_NEW;
     824           2 :             pwallet->LoadMinVersion(nMinVersion);
     825           2 :         }
     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          45 :         if (m_batch->Read(DBKeys::FLAGS, flags)) {
     831           2 :             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           2 :         }
     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          45 :         if (!m_batch->StartCursor())
     846             :         {
     847           0 :             pwallet->WalletLogPrintf("Error getting wallet database cursor\n");
     848           0 :             return DBErrors::CORRUPT;
     849             :         }
     850             : 
     851         118 :         while (true)
     852             :         {
     853             :             // Read next record
     854         118 :             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     855         118 :             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
     856             :             bool complete;
     857         118 :             bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
     858         118 :             if (complete) {
     859          45 :                 break;
     860             :             }
     861          73 :             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          73 :             std::string strType, strErr;
     870          73 :             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          73 :             if (!strErr.empty())
     893           0 :                 pwallet->WalletLogPrintf("%s\n", strErr);
     894         118 :         }
     895          45 :     } catch (...) {
     896           0 :         result = DBErrors::CORRUPT;
     897           0 :     }
     898          45 :     m_batch->CloseCursor();
     899             : 
     900             :     // Validate HD chain encryption consistency now that all data is loaded
     901          45 :     if (auto spk_man = pwallet->GetLegacyScriptPubKeyMan()) {
     902           2 :         CHDChain hdChain;
     903           2 :         if (spk_man->GetHDChain(hdChain)) {
     904             :             // If HD chain exists, validate encryption consistency
     905           0 :             bool fHasMasterKeys = pwallet->HasEncryptionKeys();
     906           0 :             bool fChainCrypted = hdChain.IsCrypted();
     907             : 
     908           0 :             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           0 :         }
     915           2 :     }
     916             : 
     917             :     // Set the active ScriptPubKeyMans
     918          47 :     for (auto spk_man : wss.m_active_external_spks) {
     919           2 :         pwallet->LoadActiveScriptPubKeyMan(spk_man.second, /*internal=*/false);
     920             :     }
     921          47 :     for (auto spk_man : wss.m_active_internal_spks) {
     922           2 :         pwallet->LoadActiveScriptPubKeyMan(spk_man.second, /*internal=*/true);
     923             :     }
     924             : 
     925             :     // Set the descriptor caches
     926          53 :     for (const auto& desc_cache_pair : wss.m_descriptor_caches) {
     927           8 :         auto spk_man = pwallet->GetScriptPubKeyMan(desc_cache_pair.first);
     928           8 :         assert(spk_man);
     929           8 :         ((DescriptorScriptPubKeyMan*)spk_man)->SetCache(desc_cache_pair.second);
     930             :     }
     931             : 
     932             :     // Set the descriptor keys
     933          53 :     for (const auto& desc_key_pair : wss.m_descriptor_keys) {
     934           8 :         auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first);
     935           8 :         auto it = wss.mnemonics.find(desc_key_pair.first);
     936           8 :         if (it == wss.mnemonics.end()) {
     937           2 :             ((DescriptorScriptPubKeyMan*)spk_man)->AddKey(desc_key_pair.first.second, desc_key_pair.second, "", "");
     938           2 :         } else {
     939           6 :             ((DescriptorScriptPubKeyMan*)spk_man)->AddKey(desc_key_pair.first.second, desc_key_pair.second, it->second.first, it->second.second);
     940             :         }
     941             :     }
     942             : 
     943          45 :     for (const auto& desc_key_pair : wss.m_descriptor_crypt_keys) {
     944           0 :         auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first);
     945           0 :         auto it = wss.crypted_mnemonics.find(desc_key_pair.first);
     946           0 :         if (it == wss.crypted_mnemonics.end()) {
     947           0 :             ((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second, {}, {});
     948           0 :         } else {
     949           0 :             ((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          45 :     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          45 :     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          45 :     pwallet->nKeysLeftSinceAutoBackup = pwallet->KeypoolCountExternalKeys();
     963          45 :     pwallet->WalletLogPrintf("nKeysLeftSinceAutoBackup: %d\n", pwallet->nKeysLeftSinceAutoBackup);
     964             : 
     965             :     // Last client version to open this wallet
     966          45 :     int last_client = CLIENT_VERSION;
     967          45 :     bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);
     968          45 :     pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client);
     969             : 
     970          45 :     pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u total; Watch scripts: %u; HD PubKeys: %u; Metadata: %u; Unknown wallet records: %u\n",
     971          45 :            wss.nKeys, wss.nCKeys, wss.nKeys + wss.nCKeys,
     972          45 :            wss.nWatchKeys, wss.nHDPubKeys, wss.nKeyMeta, wss.m_unknown_records);
     973             : 
     974             :     // nTimeFirstKey is only reliable if all keys have metadata
     975          45 :     if (pwallet->IsLegacy() && (wss.nKeys + wss.nCKeys + wss.nWatchKeys + wss.nHDPubKeys) != wss.nKeyMeta) {
     976           0 :         auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
     977           0 :         if (spk_man) {
     978           0 :             LOCK(spk_man->cs_KeyStore);
     979           0 :             spk_man->UpdateTimeFirstKey(1);
     980           0 :         }
     981           0 :     }
     982             : 
     983          45 :     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          45 :     if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000))
     988           0 :         return DBErrors::NEED_REWRITE;
     989             : 
     990          45 :     if (!has_last_client || last_client != CLIENT_VERSION) // Update
     991          39 :         m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
     992             : 
     993          45 :     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          45 :         pwallet->UpgradeKeyMetadata();
    1000          45 :     } 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          45 :         pwallet->UpgradeDescriptorCache();
    1008          45 :     } catch (...) {
    1009           0 :         result = DBErrors::CORRUPT;
    1010           0 :     }
    1011             : 
    1012          45 :     return result;
    1013          45 : }
    1014             : 
    1015           1 : DBErrors WalletBatch::FindWalletTxHashes(std::vector<uint256>& tx_hashes)
    1016             : {
    1017           1 :     DBErrors result = DBErrors::LOAD_OK;
    1018             : 
    1019             :     try {
    1020           1 :         int nMinVersion = 0;
    1021           1 :         if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
    1022           1 :             if (nMinVersion > FEATURE_LATEST)
    1023           0 :                 return DBErrors::TOO_NEW;
    1024           1 :         }
    1025             : 
    1026             :         // Get cursor
    1027           1 :         if (!m_batch->StartCursor())
    1028             :         {
    1029           0 :             LogPrintf("Error getting wallet database cursor\n");
    1030           0 :             return DBErrors::CORRUPT;
    1031             :         }
    1032             : 
    1033          29 :         while (true)
    1034             :         {
    1035             :             // Read next record
    1036          29 :             CDataStream ssKey(SER_DISK, CLIENT_VERSION);
    1037          29 :             CDataStream ssValue(SER_DISK, CLIENT_VERSION);
    1038             :             bool complete;
    1039          29 :             bool ret = m_batch->ReadAtCursor(ssKey, ssValue, complete);
    1040          29 :             if (complete) {
    1041           1 :                 break;
    1042          28 :             } 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          28 :             std::string strType;
    1049          28 :             ssKey >> strType;
    1050          28 :             if (strType == DBKeys::TX) {
    1051           1 :                 uint256 hash;
    1052           1 :                 ssKey >> hash;
    1053           1 :                 tx_hashes.push_back(hash);
    1054           1 :             }
    1055          29 :         }
    1056           1 :     } catch (...) {
    1057           0 :         result = DBErrors::CORRUPT;
    1058           0 :     }
    1059           1 :     m_batch->CloseCursor();
    1060             : 
    1061           1 :     return result;
    1062           1 : }
    1063             : 
    1064           1 : DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut)
    1065             : {
    1066             :     // build list of wallet TX hashes
    1067           1 :     std::vector<uint256> vTxHash;
    1068           1 :     DBErrors err = FindWalletTxHashes(vTxHash);
    1069           1 :     if (err != DBErrors::LOAD_OK) {
    1070           0 :         return err;
    1071             :     }
    1072             : 
    1073           1 :     std::sort(vTxHash.begin(), vTxHash.end());
    1074           1 :     std::sort(vTxHashIn.begin(), vTxHashIn.end());
    1075             : 
    1076             :     // erase each matching wallet TX
    1077           1 :     bool delerror = false;
    1078           1 :     std::vector<uint256>::iterator it = vTxHashIn.begin();
    1079           2 :     for (const uint256& hash : vTxHash) {
    1080           2 :         while (it < vTxHashIn.end() && (*it) < hash) {
    1081           0 :             it++;
    1082             :         }
    1083           1 :         if (it == vTxHashIn.end()) {
    1084           0 :             break;
    1085             :         }
    1086           1 :         else if ((*it) == hash) {
    1087           1 :             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           1 :             vTxHashOut.push_back(hash);
    1092           1 :         }
    1093             :     }
    1094             : 
    1095           1 :     if (delerror) {
    1096           0 :         return DBErrors::CORRUPT;
    1097             :     }
    1098           1 :     return DBErrors::LOAD_OK;
    1099           1 : }
    1100             : 
    1101           0 : void MaybeCompactWalletDB(WalletContext& context)
    1102             : {
    1103             :     static std::atomic<bool> fOneThread(false);
    1104           0 :     if (fOneThread.exchange(true)) {
    1105           0 :         return;
    1106             :     }
    1107             : 
    1108           0 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
    1109           0 :         WalletDatabase& dbh = pwallet->GetDatabase();
    1110             : 
    1111           0 :         unsigned int nUpdateCounter = dbh.nUpdateCounter;
    1112             : 
    1113           0 :         if (dbh.nLastSeen != nUpdateCounter) {
    1114           0 :             dbh.nLastSeen = nUpdateCounter;
    1115           0 :             dbh.nLastWalletUpdate = GetTime();
    1116           0 :         }
    1117             : 
    1118           0 :         if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
    1119           0 :             if (dbh.PeriodicFlush()) {
    1120           0 :                 dbh.nLastFlushed = nUpdateCounter;
    1121           0 :             }
    1122           0 :         }
    1123             :     }
    1124             : 
    1125           0 :     fOneThread = false;
    1126           0 : }
    1127             : 
    1128           6 : bool WalletBatch::WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent)
    1129             : {
    1130           6 :     auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), std::string("used")))};
    1131           6 :     return previously_spent ? WriteIC(key, std::string("1")) : EraseIC(key);
    1132           6 : }
    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           2 : bool WalletBatch::EraseAddressData(const CTxDestination& dest)
    1145             : {
    1146           2 :     CDataStream prefix(SER_DISK, CLIENT_VERSION);
    1147           2 :     prefix << DBKeys::DESTDATA << EncodeDestination(dest);
    1148           2 :     return m_batch->ErasePrefix({prefix.data(), prefix.size()});
    1149           2 : }
    1150             : 
    1151           0 : bool WalletBatch::WriteHDChain(const CHDChain& chain)
    1152             : {
    1153           0 :     if (chain.IsCrypted()) {
    1154           0 :         if (!WriteIC(DBKeys::CRYPTED_HDCHAIN, chain))
    1155           0 :             return false;
    1156             : 
    1157           0 :         EraseIC(DBKeys::HDCHAIN);
    1158             : 
    1159           0 :         return true;
    1160             :     }
    1161           0 :     return WriteIC(DBKeys::HDCHAIN, chain);
    1162           0 : }
    1163             : 
    1164           0 : bool WalletBatch::WriteHDPubKey(const CHDPubKey& hdPubKey, const CKeyMetadata& keyMeta)
    1165             : {
    1166           0 :     if (!WriteIC(std::make_pair(DBKeys::KEYMETA, hdPubKey.extPubKey.pubkey), keyMeta, false))
    1167           0 :         return false;
    1168             : 
    1169           0 :     return WriteIC(std::make_pair(DBKeys::HDPUBKEY, hdPubKey.extPubKey.pubkey), hdPubKey, false);
    1170           0 : }
    1171             : 
    1172         110 : bool WalletBatch::WriteWalletFlags(const uint64_t flags)
    1173             : {
    1174         110 :     return WriteIC(DBKeys::FLAGS, flags);
    1175             : }
    1176             : 
    1177           0 : bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types)
    1178             : {
    1179             :     // Get cursor
    1180           0 :     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           0 :     while (true)
    1187             :     {
    1188             :         // Read next record
    1189           0 :         CDataStream key(SER_DISK, CLIENT_VERSION);
    1190           0 :         CDataStream value(SER_DISK, CLIENT_VERSION);
    1191             :         bool complete;
    1192           0 :         bool ret = m_batch->ReadAtCursor(key, value, complete);
    1193           0 :         if (complete) {
    1194           0 :             break;
    1195             :         }
    1196           0 :         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           0 :         Span<const unsigned char> key_data = MakeUCharSpan(key);
    1204             : 
    1205           0 :         std::string type;
    1206           0 :         key >> type;
    1207             : 
    1208           0 :         if (types.count(type) > 0) {
    1209           0 :             m_batch->Erase(key_data);
    1210           0 :         }
    1211           0 :     }
    1212           0 :     m_batch->CloseCursor();
    1213           0 :     return true;
    1214           0 : }
    1215             : 
    1216           2 : bool WalletBatch::TxnBegin()
    1217             : {
    1218           2 :     return m_batch->TxnBegin();
    1219             : }
    1220             : 
    1221           0 : bool WalletBatch::TxnCommit()
    1222             : {
    1223           0 :     return m_batch->TxnCommit();
    1224             : }
    1225             : 
    1226           2 : bool WalletBatch::TxnAbort()
    1227             : {
    1228           2 :     return m_batch->TxnAbort();
    1229             : }
    1230             : 
    1231          15 : std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
    1232             : {
    1233             :     bool exists;
    1234             :     try {
    1235          15 :         exists = fs::symlink_status(path).type() != fs::file_type::not_found;
    1236          15 :     } 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          15 :     std::optional<DatabaseFormat> format;
    1243          15 :     if (exists) {
    1244          13 :         if (IsBDBFile(BDBDataFile(path))) {
    1245           4 :             format = DatabaseFormat::BERKELEY;
    1246           4 :         }
    1247          13 :         if (IsSQLiteFile(SQLiteDataFile(path))) {
    1248           2 :             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           2 :             format = DatabaseFormat::SQLITE;
    1254           2 :         }
    1255          15 :     } else if (options.require_existing) {
    1256           0 :         error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", fs::PathToString(path)));
    1257           0 :         status = DatabaseStatus::FAILED_NOT_FOUND;
    1258           0 :         return nullptr;
    1259             :     }
    1260             : 
    1261          15 :     if (!format && options.require_existing) {
    1262           4 :         error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", fs::PathToString(path)));
    1263           4 :         status = DatabaseStatus::FAILED_BAD_FORMAT;
    1264           4 :         return nullptr;
    1265             :     }
    1266             : 
    1267          11 :     if (format && options.require_create) {
    1268           0 :         error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(path)));
    1269           0 :         status = DatabaseStatus::FAILED_ALREADY_EXISTS;
    1270           0 :         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          11 :     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          11 :     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          11 :     if (!format) {
    1285             : #ifdef USE_SQLITE
    1286           3 :         format = DatabaseFormat::SQLITE;
    1287             : #endif
    1288             : #ifdef USE_BDB
    1289           3 :         format = DatabaseFormat::BERKELEY;
    1290             : #endif
    1291           3 :     }
    1292             : 
    1293          11 :     if (format == DatabaseFormat::SQLITE) {
    1294             : #ifdef USE_SQLITE
    1295             :         if constexpr (true) {
    1296           3 :             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           8 :         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          15 : }
    1317             : 
    1318             : /** Return object for accessing dummy database with no read/write capabilities. */
    1319          20 : std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
    1320             : {
    1321          20 :     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