LCOV - code coverage report
Current view: top level - src/wallet - walletdb.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 47 970 4.8 %
Date: 2023-09-26 12:08:55 Functions: 40 127 31.5 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2022 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <wallet/walletdb.h>
       7             : 
       8             : #include <common/system.h>
       9             : #include <key_io.h>
      10             : #include <protocol.h>
      11             : #include <script/script.h>
      12             : #include <serialize.h>
      13             : #include <sync.h>
      14             : #include <util/bip32.h>
      15             : #include <util/check.h>
      16             : #include <util/fs.h>
      17           2 : #include <util/time.h>
      18           2 : #include <util/translation.h>
      19             : #ifdef USE_BDB
      20             : #include <wallet/bdb.h>
      21             : #endif
      22             : #ifdef USE_SQLITE
      23             : #include <wallet/sqlite.h>
      24             : #endif
      25             : #include <wallet/wallet.h>
      26             : 
      27           2 : #include <atomic>
      28             : #include <optional>
      29             : #include <string>
      30             : 
      31             : namespace wallet {
      32             : namespace DBKeys {
      33           2 : const std::string ACENTRY{"acentry"};
      34           2 : const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
      35           2 : const std::string ACTIVEINTERNALSPK{"activeinternalspk"};
      36           2 : const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"};
      37           2 : const std::string BESTBLOCK{"bestblock"};
      38           2 : const std::string CRYPTED_KEY{"ckey"};
      39           2 : const std::string CSCRIPT{"cscript"};
      40           2 : const std::string DEFAULTKEY{"defaultkey"};
      41           2 : const std::string DESTDATA{"destdata"};
      42           2 : const std::string FLAGS{"flags"};
      43           2 : const std::string HDCHAIN{"hdchain"};
      44           2 : const std::string KEYMETA{"keymeta"};
      45           2 : const std::string KEY{"key"};
      46           2 : const std::string LOCKED_UTXO{"lockedutxo"};
      47           2 : const std::string MASTER_KEY{"mkey"};
      48           2 : const std::string MINVERSION{"minversion"};
      49           2 : const std::string NAME{"name"};
      50           2 : const std::string OLD_KEY{"wkey"};
      51           2 : const std::string ORDERPOSNEXT{"orderposnext"};
      52           2 : const std::string POOL{"pool"};
      53           2 : const std::string PURPOSE{"purpose"};
      54           2 : const std::string SETTINGS{"settings"};
      55           2 : const std::string TX{"tx"};
      56           2 : const std::string VERSION{"version"};
      57           2 : const std::string WALLETDESCRIPTOR{"walletdescriptor"};
      58           2 : const std::string WALLETDESCRIPTORCACHE{"walletdescriptorcache"};
      59           2 : const std::string WALLETDESCRIPTORLHCACHE{"walletdescriptorlhcache"};
      60           2 : const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"};
      61           2 : const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"};
      62           2 : const std::string WATCHMETA{"watchmeta"};
      63           2 : const std::string WATCHS{"watchs"};
      64           2 : const std::unordered_set<std::string> LEGACY_TYPES{CRYPTED_KEY, CSCRIPT, DEFAULTKEY, HDCHAIN, KEYMETA, KEY, OLD_KEY, POOL, WATCHMETA, WATCHS};
      65             : } // namespace DBKeys
      66             : 
      67             : //
      68             : // WalletBatch
      69             : //
      70             : 
      71           0 : bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
      72             : {
      73           0 :     return WriteIC(std::make_pair(DBKeys::NAME, strAddress), strName);
      74           2 : }
      75             : 
      76           0 : bool WalletBatch::EraseName(const std::string& strAddress)
      77             : {
      78             :     // This should only be used for sending addresses, never for receiving addresses,
      79             :     // receiving addresses must always have an address book entry if they're not change return.
      80           0 :     return EraseIC(std::make_pair(DBKeys::NAME, strAddress));
      81           0 : }
      82             : 
      83           0 : bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
      84             : {
      85           0 :     return WriteIC(std::make_pair(DBKeys::PURPOSE, strAddress), strPurpose);
      86           0 : }
      87             : 
      88           0 : bool WalletBatch::ErasePurpose(const std::string& strAddress)
      89             : {
      90           0 :     return EraseIC(std::make_pair(DBKeys::PURPOSE, strAddress));
      91           2 : }
      92             : 
      93           0 : bool WalletBatch::WriteTx(const CWalletTx& wtx)
      94             : {
      95           0 :     return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
      96           0 : }
      97             : 
      98           0 : bool WalletBatch::EraseTx(uint256 hash)
      99           2 : {
     100           0 :     return EraseIC(std::make_pair(DBKeys::TX, hash));
     101           0 : }
     102             : 
     103           0 : bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
     104             : {
     105           0 :     return WriteIC(std::make_pair(DBKeys::KEYMETA, pubkey), meta, overwrite);
     106           0 : }
     107             : 
     108           0 : bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
     109             : {
     110           0 :     if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
     111           0 :         return false;
     112             :     }
     113             : 
     114             :     // hash pubkey/privkey to accelerate wallet load
     115           0 :     std::vector<unsigned char> vchKey;
     116           0 :     vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
     117           0 :     vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
     118           0 :     vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
     119             : 
     120           0 :     return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey)), false);
     121           0 : }
     122             : 
     123           0 : bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
     124             :                                 const std::vector<unsigned char>& vchCryptedSecret,
     125             :                                 const CKeyMetadata &keyMeta)
     126             : {
     127           0 :     if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
     128           0 :         return false;
     129             :     }
     130             : 
     131             :     // Compute a checksum of the encrypted key
     132           0 :     uint256 checksum = Hash(vchCryptedSecret);
     133             : 
     134           0 :     const auto key = std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey);
     135           0 :     if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
     136             :         // It may already exist, so try writing just the checksum
     137           0 :         std::vector<unsigned char> val;
     138           0 :         if (!m_batch->Read(key, val)) {
     139           0 :             return false;
     140             :         }
     141           0 :         if (!WriteIC(key, std::make_pair(val, checksum), true)) {
     142           0 :             return false;
     143             :         }
     144           0 :     }
     145           0 :     EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
     146           0 :     return true;
     147           0 : }
     148             : 
     149           0 : bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
     150             : {
     151           0 :     return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true);
     152           0 : }
     153             : 
     154           0 : bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript)
     155             : {
     156           0 :     return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false);
     157           0 : }
     158             : 
     159           0 : bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
     160             : {
     161           0 :     if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
     162           0 :         return false;
     163           2 :     }
     164           2 :     return WriteIC(std::make_pair(DBKeys::WATCHS, dest), uint8_t{'1'});
     165           2 : }
     166           2 : 
     167           2 : bool WalletBatch::EraseWatchOnly(const CScript &dest)
     168           2 : {
     169           2 :     if (!EraseIC(std::make_pair(DBKeys::WATCHMETA, dest))) {
     170           2 :         return false;
     171             :     }
     172           0 :     return EraseIC(std::make_pair(DBKeys::WATCHS, dest));
     173           0 : }
     174             : 
     175           0 : bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
     176             : {
     177           0 :     WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
     178           0 :     return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
     179           0 : }
     180             : 
     181           0 : bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
     182             : {
     183           0 :     if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
     184           0 :     return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
     185           0 : }
     186             : 
     187           0 : bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
     188             : {
     189           0 :     return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
     190             : }
     191             : 
     192           0 : bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool)
     193             : {
     194           0 :     return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool);
     195           0 : }
     196             : 
     197           0 : bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool)
     198             : {
     199           0 :     return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool);
     200           0 : }
     201             : 
     202           0 : bool WalletBatch::ErasePool(int64_t nPool)
     203             : {
     204           0 :     return EraseIC(std::make_pair(DBKeys::POOL, nPool));
     205           0 : }
     206             : 
     207           0 : bool WalletBatch::WriteMinVersion(int nVersion)
     208             : {
     209           0 :     return WriteIC(DBKeys::MINVERSION, nVersion);
     210             : }
     211             : 
     212           0 : bool WalletBatch::WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal)
     213             : {
     214           0 :     std::string key = internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK;
     215           0 :     return WriteIC(make_pair(key, type), id);
     216           0 : }
     217             : 
     218           0 : bool WalletBatch::EraseActiveScriptPubKeyMan(uint8_t type, bool internal)
     219             : {
     220           0 :     const std::string key{internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK};
     221           0 :     return EraseIC(make_pair(key, type));
     222           0 : }
     223             : 
     224           0 : bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
     225             : {
     226             :     // hash pubkey/privkey to accelerate wallet load
     227           0 :     std::vector<unsigned char> key;
     228           0 :     key.reserve(pubkey.size() + privkey.size());
     229           0 :     key.insert(key.end(), pubkey.begin(), pubkey.end());
     230           0 :     key.insert(key.end(), privkey.begin(), privkey.end());
     231             : 
     232           0 :     return WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)), std::make_pair(privkey, Hash(key)), false);
     233           0 : }
     234             : 
     235           0 : bool WalletBatch::WriteCryptedDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const std::vector<unsigned char>& secret)
     236             : {
     237           0 :     if (!WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORCKEY, std::make_pair(desc_id, pubkey)), secret, false)) {
     238           0 :         return false;
     239             :     }
     240           0 :     EraseIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)));
     241           0 :     return true;
     242           0 : }
     243             : 
     244           0 : bool WalletBatch::WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor)
     245             : {
     246           0 :     return WriteIC(make_pair(DBKeys::WALLETDESCRIPTOR, desc_id), descriptor);
     247           0 : }
     248             : 
     249           0 : bool WalletBatch::WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index)
     250             : {
     251           0 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     252           0 :     xpub.Encode(ser_xpub.data());
     253           0 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), std::make_pair(key_exp_index, der_index)), ser_xpub);
     254           0 : }
     255             : 
     256           0 : bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
     257             : {
     258           0 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     259           0 :     xpub.Encode(ser_xpub.data());
     260           0 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
     261           0 : }
     262             : 
     263           0 : bool WalletBatch::WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
     264             : {
     265           0 :     std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     266           0 :     xpub.Encode(ser_xpub.data());
     267           0 :     return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORLHCACHE, desc_id), key_exp_index), ser_xpub);
     268           0 : }
     269             : 
     270           2 : bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache)
     271             : {
     272           0 :     for (const auto& parent_xpub_pair : cache.GetCachedParentExtPubKeys()) {
     273           0 :         if (!WriteDescriptorParentCache(parent_xpub_pair.second, desc_id, parent_xpub_pair.first)) {
     274           0 :             return false;
     275             :         }
     276             :     }
     277           0 :     for (const auto& derived_xpub_map_pair : cache.GetCachedDerivedExtPubKeys()) {
     278           0 :         for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
     279           0 :             if (!WriteDescriptorDerivedCache(derived_xpub_pair.second, desc_id, derived_xpub_map_pair.first, derived_xpub_pair.first)) {
     280           0 :                 return false;
     281             :             }
     282             :         }
     283             :     }
     284           0 :     for (const auto& lh_xpub_pair : cache.GetCachedLastHardenedExtPubKeys()) {
     285           0 :         if (!WriteDescriptorLastHardenedCache(lh_xpub_pair.second, desc_id, lh_xpub_pair.first)) {
     286           0 :             return false;
     287             :         }
     288             :     }
     289           0 :     return true;
     290           0 : }
     291             : 
     292           0 : bool WalletBatch::WriteLockedUTXO(const COutPoint& output)
     293             : {
     294           0 :     return WriteIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)), uint8_t{'1'});
     295           0 : }
     296             : 
     297           0 : bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
     298             : {
     299           0 :     return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)));
     300           0 : }
     301             : 
     302           0 : bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
     303             : {
     304           0 :     LOCK(pwallet->cs_wallet);
     305             :     try {
     306           0 :         CPubKey vchPubKey;
     307           0 :         ssKey >> vchPubKey;
     308           0 :         if (!vchPubKey.IsValid())
     309             :         {
     310           0 :             strErr = "Error reading wallet database: CPubKey corrupt";
     311           0 :             return false;
     312             :         }
     313           0 :         CKey key;
     314           0 :         CPrivKey pkey;
     315           0 :         uint256 hash;
     316             : 
     317           0 :         ssValue >> pkey;
     318             : 
     319             :         // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
     320             :         // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
     321             :         // using EC operations as a checksum.
     322             :         // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
     323             :         // remaining backwards-compatible.
     324             :         try
     325             :         {
     326           0 :             ssValue >> hash;
     327           0 :         }
     328           0 :         catch (const std::ios_base::failure&) {}
     329             : 
     330           0 :         bool fSkipCheck = false;
     331             : 
     332           0 :         if (!hash.IsNull())
     333             :         {
     334             :             // hash pubkey/privkey to accelerate wallet load
     335           0 :             std::vector<unsigned char> vchKey;
     336           0 :             vchKey.reserve(vchPubKey.size() + pkey.size());
     337           0 :             vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
     338           0 :             vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
     339             : 
     340           0 :             if (Hash(vchKey) != hash)
     341             :             {
     342           0 :                 strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
     343           0 :                 return false;
     344             :             }
     345             : 
     346           0 :             fSkipCheck = true;
     347           0 :         }
     348             : 
     349           0 :         if (!key.Load(pkey, vchPubKey, fSkipCheck))
     350             :         {
     351           0 :             strErr = "Error reading wallet database: CPrivKey corrupt";
     352           0 :             return false;
     353             :         }
     354           0 :         if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKey(key, vchPubKey))
     355             :         {
     356           0 :             strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadKey failed";
     357           0 :             return false;
     358             :         }
     359           0 :     } catch (const std::exception& e) {
     360           0 :         if (strErr.empty()) {
     361           0 :             strErr = e.what();
     362           0 :         }
     363           0 :         return false;
     364           0 :     }
     365           0 :     return true;
     366           0 : }
     367             : 
     368           0 : bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
     369             : {
     370           0 :     LOCK(pwallet->cs_wallet);
     371             :     try {
     372           0 :         CPubKey vchPubKey;
     373           0 :         ssKey >> vchPubKey;
     374           0 :         if (!vchPubKey.IsValid())
     375             :         {
     376           0 :             strErr = "Error reading wallet database: CPubKey corrupt";
     377           0 :             return false;
     378             :         }
     379           0 :         std::vector<unsigned char> vchPrivKey;
     380           0 :         ssValue >> vchPrivKey;
     381             : 
     382             :         // Get the checksum and check it
     383           0 :         bool checksum_valid = false;
     384           0 :         if (!ssValue.eof()) {
     385           0 :             uint256 checksum;
     386           0 :             ssValue >> checksum;
     387           0 :             if (!(checksum_valid = Hash(vchPrivKey) == checksum)) {
     388           0 :                 strErr = "Error reading wallet database: Encrypted key corrupt";
     389           0 :                 return false;
     390             :             }
     391           0 :         }
     392             : 
     393           0 :         if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
     394             :         {
     395           0 :             strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCryptedKey failed";
     396           0 :             return false;
     397             :         }
     398           0 :     } catch (const std::exception& e) {
     399           0 :         if (strErr.empty()) {
     400           0 :             strErr = e.what();
     401           0 :         }
     402           0 :         return false;
     403           0 :     }
     404           0 :     return true;
     405           0 : }
     406             : 
     407           0 : bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
     408             : {
     409           0 :     LOCK(pwallet->cs_wallet);
     410             :     try {
     411             :         // Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans.
     412             :         unsigned int nID;
     413           0 :         ssKey >> nID;
     414           0 :         CMasterKey kMasterKey;
     415           0 :         ssValue >> kMasterKey;
     416           0 :         if(pwallet->mapMasterKeys.count(nID) != 0)
     417             :         {
     418           0 :             strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
     419           0 :             return false;
     420             :         }
     421           0 :         pwallet->mapMasterKeys[nID] = kMasterKey;
     422           0 :         if (pwallet->nMasterKeyMaxID < nID)
     423           0 :             pwallet->nMasterKeyMaxID = nID;
     424             : 
     425           0 :     } catch (const std::exception& e) {
     426           0 :         if (strErr.empty()) {
     427           0 :             strErr = e.what();
     428           0 :         }
     429           0 :         return false;
     430           0 :     }
     431           0 :     return true;
     432           0 : }
     433             : 
     434           0 : bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr)
     435             : {
     436           0 :     LOCK(pwallet->cs_wallet);
     437             :     try {
     438           0 :         CHDChain chain;
     439           0 :         ssValue >> chain;
     440           0 :         pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadHDChain(chain);
     441           0 :     } catch (const std::exception& e) {
     442           0 :         if (strErr.empty()) {
     443           0 :             strErr = e.what();
     444           0 :         }
     445           0 :         return false;
     446           0 :     }
     447           0 :     return true;
     448           0 : }
     449             : 
     450           0 : static DBErrors LoadMinVersion(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
     451             : {
     452           0 :     AssertLockHeld(pwallet->cs_wallet);
     453           0 :     int nMinVersion = 0;
     454           0 :     if (batch.Read(DBKeys::MINVERSION, nMinVersion)) {
     455           0 :         if (nMinVersion > FEATURE_LATEST)
     456           0 :             return DBErrors::TOO_NEW;
     457           0 :         pwallet->LoadMinVersion(nMinVersion);
     458           0 :     }
     459           0 :     return DBErrors::LOAD_OK;
     460           0 : }
     461             : 
     462           0 : static DBErrors LoadWalletFlags(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
     463             : {
     464           0 :     AssertLockHeld(pwallet->cs_wallet);
     465             :     uint64_t flags;
     466           0 :     if (batch.Read(DBKeys::FLAGS, flags)) {
     467           0 :         if (!pwallet->LoadWalletFlags(flags)) {
     468           0 :             pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n");
     469           0 :             return DBErrors::TOO_NEW;
     470             :         }
     471           0 :     }
     472           0 :     return DBErrors::LOAD_OK;
     473           0 : }
     474             : 
     475           0 : struct LoadResult
     476             : {
     477           0 :     DBErrors m_result{DBErrors::LOAD_OK};
     478           0 :     int m_records{0};
     479             : };
     480             : 
     481             : using LoadFunc = std::function<DBErrors(CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err)>;
     482           0 : static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, DataStream& prefix, LoadFunc load_func)
     483             : {
     484           0 :     LoadResult result;
     485           0 :     DataStream ssKey;
     486           0 :     CDataStream ssValue(SER_DISK, CLIENT_VERSION);
     487             : 
     488           0 :     Assume(!prefix.empty());
     489           0 :     std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
     490           0 :     if (!cursor) {
     491           0 :         pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", key);
     492           0 :         result.m_result = DBErrors::CORRUPT;
     493           0 :         return result;
     494             :     }
     495             : 
     496           0 :     while (true) {
     497           0 :         DatabaseCursor::Status status = cursor->Next(ssKey, ssValue);
     498           0 :         if (status == DatabaseCursor::Status::DONE) {
     499           0 :             break;
     500           0 :         } else if (status == DatabaseCursor::Status::FAIL) {
     501           0 :             pwallet->WalletLogPrintf("Error reading next '%s' record for wallet database\n", key);
     502           0 :             result.m_result = DBErrors::CORRUPT;
     503           0 :             return result;
     504             :         }
     505           0 :         std::string type;
     506           0 :         ssKey >> type;
     507           0 :         assert(type == key);
     508           0 :         std::string error;
     509           0 :         DBErrors record_res = load_func(pwallet, ssKey, ssValue, error);
     510           0 :         if (record_res != DBErrors::LOAD_OK) {
     511           0 :             pwallet->WalletLogPrintf("%s\n", error);
     512           0 :         }
     513           0 :         result.m_result = std::max(result.m_result, record_res);
     514           0 :         ++result.m_records;
     515           0 :     }
     516           0 :     return result;
     517           0 : }
     518             : 
     519           0 : static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, LoadFunc load_func)
     520             : {
     521           0 :     DataStream prefix;
     522           0 :     prefix << key;
     523           0 :     return LoadRecords(pwallet, batch, key, prefix, load_func);
     524           0 : }
     525             : 
     526           0 : static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
     527             : {
     528           0 :     AssertLockHeld(pwallet->cs_wallet);
     529           0 :     DBErrors result = DBErrors::LOAD_OK;
     530             : 
     531             :     // Make sure descriptor wallets don't have any legacy records
     532           0 :     if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
     533           0 :         for (const auto& type : DBKeys::LEGACY_TYPES) {
     534           0 :             DataStream key;
     535           0 :             CDataStream value(SER_DISK, CLIENT_VERSION);
     536             : 
     537           0 :             DataStream prefix;
     538           0 :             prefix << type;
     539           0 :             std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
     540           0 :             if (!cursor) {
     541           0 :                 pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", type);
     542           0 :                 return DBErrors::CORRUPT;
     543             :             }
     544             : 
     545           0 :             DatabaseCursor::Status status = cursor->Next(key, value);
     546           0 :             if (status != DatabaseCursor::Status::DONE) {
     547           0 :                 pwallet->WalletLogPrintf("Error: Unexpected legacy entry found in descriptor wallet %s. The wallet might have been tampered with or created with malicious intent.\n", pwallet->GetName());
     548           0 :                 return DBErrors::UNEXPECTED_LEGACY_ENTRY;
     549             :             }
     550           0 :         }
     551             : 
     552           0 :         return DBErrors::LOAD_OK;
     553             :     }
     554             : 
     555             :     // Load HD Chain
     556             :     // Note: There should only be one HDCHAIN record with no data following the type
     557           0 :     LoadResult hd_chain_res = LoadRecords(pwallet, batch, DBKeys::HDCHAIN,
     558           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     559           0 :         return LoadHDChain(pwallet, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT;
     560             :     });
     561           0 :     result = std::max(result, hd_chain_res.m_result);
     562             : 
     563             :     // Load unencrypted keys
     564           0 :     LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::KEY,
     565           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     566           0 :         return LoadKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT;
     567             :     });
     568           0 :     result = std::max(result, key_res.m_result);
     569             : 
     570             :     // Load encrypted keys
     571           0 :     LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::CRYPTED_KEY,
     572           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     573           0 :         return LoadCryptedKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT;
     574             :     });
     575           0 :     result = std::max(result, ckey_res.m_result);
     576             : 
     577             :     // Load scripts
     578           0 :     LoadResult script_res = LoadRecords(pwallet, batch, DBKeys::CSCRIPT,
     579           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) {
     580           0 :         uint160 hash;
     581           0 :         key >> hash;
     582           0 :         CScript script;
     583           0 :         value >> script;
     584           0 :         if (!pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadCScript(script))
     585             :         {
     586           0 :             strErr = "Error reading wallet database: LegacyScriptPubKeyMan::LoadCScript failed";
     587           0 :             return DBErrors::NONCRITICAL_ERROR;
     588             :         }
     589           0 :         return DBErrors::LOAD_OK;
     590           0 :     });
     591           0 :     result = std::max(result, script_res.m_result);
     592             : 
     593             :     // Check whether rewrite is needed
     594           0 :     if (ckey_res.m_records > 0) {
     595             :         // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
     596           0 :         if (last_client == 40000 || last_client == 50000) result = std::max(result, DBErrors::NEED_REWRITE);
     597           0 :     }
     598             : 
     599             :     // Load keymeta
     600           0 :     std::map<uint160, CHDChain> hd_chains;
     601           0 :     LoadResult keymeta_res = LoadRecords(pwallet, batch, DBKeys::KEYMETA,
     602           0 :         [&hd_chains] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) {
     603           0 :         CPubKey vchPubKey;
     604           0 :         key >> vchPubKey;
     605           0 :         CKeyMetadata keyMeta;
     606           0 :         value >> keyMeta;
     607           0 :         pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
     608             : 
     609             :         // Extract some CHDChain info from this metadata if it has any
     610           0 :         if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
     611             :             // Get the path from the key origin or from the path string
     612             :             // Not applicable when path is "s" or "m" as those indicate a seed
     613             :             // See https://github.com/bitcoin/bitcoin/pull/12924
     614           0 :             bool internal = false;
     615           0 :             uint32_t index = 0;
     616           0 :             if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") {
     617           0 :                 std::vector<uint32_t> path;
     618           0 :                 if (keyMeta.has_key_origin) {
     619             :                     // We have a key origin, so pull it from its path vector
     620           0 :                     path = keyMeta.key_origin.path;
     621           0 :                 } else {
     622             :                     // No key origin, have to parse the string
     623           0 :                     if (!ParseHDKeypath(keyMeta.hdKeypath, path)) {
     624           0 :                         strErr = "Error reading wallet database: keymeta with invalid HD keypath";
     625           0 :                         return DBErrors::NONCRITICAL_ERROR;
     626             :                     }
     627             :                 }
     628             : 
     629             :                 // Extract the index and internal from the path
     630             :                 // Path string is m/0'/k'/i'
     631             :                 // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit
     632             :                 // k == 0 for external, 1 for internal. i is the index
     633           0 :                 if (path.size() != 3) {
     634           0 :                     strErr = "Error reading wallet database: keymeta found with unexpected path";
     635           0 :                     return DBErrors::NONCRITICAL_ERROR;
     636             :                 }
     637           0 :                 if (path[0] != 0x80000000) {
     638           0 :                     strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]);
     639           0 :                     return DBErrors::NONCRITICAL_ERROR;
     640             :                 }
     641           0 :                 if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) {
     642           0 :                     strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]);
     643           0 :                     return DBErrors::NONCRITICAL_ERROR;
     644             :                 }
     645           0 :                 if ((path[2] & 0x80000000) == 0) {
     646           0 :                     strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]);
     647           0 :                     return DBErrors::NONCRITICAL_ERROR;
     648             :                 }
     649           0 :                 internal = path[1] == (1 | 0x80000000);
     650           0 :                 index = path[2] & ~0x80000000;
     651           0 :             }
     652             : 
     653             :             // Insert a new CHDChain, or get the one that already exists
     654           0 :             auto [ins, inserted] = hd_chains.emplace(keyMeta.hd_seed_id, CHDChain());
     655           0 :             CHDChain& chain = ins->second;
     656           0 :             if (inserted) {
     657             :                 // For new chains, we want to default to VERSION_HD_BASE until we see an internal
     658           0 :                 chain.nVersion = CHDChain::VERSION_HD_BASE;
     659           0 :                 chain.seed_id = keyMeta.hd_seed_id;
     660           0 :             }
     661           0 :             if (internal) {
     662           0 :                 chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT;
     663           0 :                 chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1);
     664           0 :             } else {
     665           0 :                 chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1);
     666             :             }
     667           0 :         }
     668           0 :         return DBErrors::LOAD_OK;
     669           0 :     });
     670           0 :     result = std::max(result, keymeta_res.m_result);
     671             : 
     672             :     // Set inactive chains
     673           0 :     if (!hd_chains.empty()) {
     674           0 :         LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan();
     675           0 :         if (legacy_spkm) {
     676           0 :             for (const auto& [hd_seed_id, chain] : hd_chains) {
     677           0 :                 if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) {
     678           0 :                     legacy_spkm->AddInactiveHDChain(chain);
     679           0 :                 }
     680             :             }
     681           0 :         } else {
     682           0 :             pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n");
     683           0 :             result = DBErrors::CORRUPT;
     684             :         }
     685           0 :     }
     686             : 
     687             :     // Load watchonly scripts
     688           0 :     LoadResult watch_script_res = LoadRecords(pwallet, batch, DBKeys::WATCHS,
     689           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     690           0 :         CScript script;
     691           0 :         key >> script;
     692             :         uint8_t fYes;
     693           0 :         value >> fYes;
     694           0 :         if (fYes == '1') {
     695           0 :             pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadWatchOnly(script);
     696           0 :         }
     697             :         return DBErrors::LOAD_OK;
     698           0 :     });
     699           0 :     result = std::max(result, watch_script_res.m_result);
     700             : 
     701             :     // Load watchonly meta
     702           0 :     LoadResult watch_meta_res = LoadRecords(pwallet, batch, DBKeys::WATCHMETA,
     703           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     704           0 :         CScript script;
     705           0 :         key >> script;
     706           0 :         CKeyMetadata keyMeta;
     707           0 :         value >> keyMeta;
     708           0 :         pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadScriptMetadata(CScriptID(script), keyMeta);
     709             :         return DBErrors::LOAD_OK;
     710           0 :     });
     711           0 :     result = std::max(result, watch_meta_res.m_result);
     712             : 
     713             :     // Load keypool
     714           0 :     LoadResult pool_res = LoadRecords(pwallet, batch, DBKeys::POOL,
     715           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     716             :         int64_t nIndex;
     717           0 :         key >> nIndex;
     718           0 :         CKeyPool keypool;
     719           0 :         value >> keypool;
     720           0 :         pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyPool(nIndex, keypool);
     721           0 :         return DBErrors::LOAD_OK;
     722             :     });
     723           0 :     result = std::max(result, pool_res.m_result);
     724             : 
     725             :     // Deal with old "wkey" and "defaultkey" records.
     726             :     // These are not actually loaded, but we need to check for them
     727             : 
     728             :     // We don't want or need the default key, but if there is one set,
     729             :     // we want to make sure that it is valid so that we can detect corruption
     730             :     // Note: There should only be one DEFAULTKEY with nothing trailing the type
     731           0 :     LoadResult default_key_res = LoadRecords(pwallet, batch, DBKeys::DEFAULTKEY,
     732           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     733           0 :         CPubKey default_pubkey;
     734             :         try {
     735           0 :             value >> default_pubkey;
     736           0 :         } catch (const std::exception& e) {
     737           0 :             err = e.what();
     738           0 :             return DBErrors::CORRUPT;
     739           0 :         }
     740           0 :         if (!default_pubkey.IsValid()) {
     741           0 :             err = "Error reading wallet database: Default Key corrupt";
     742           0 :             return DBErrors::CORRUPT;
     743             :         }
     744           0 :         return DBErrors::LOAD_OK;
     745           0 :     });
     746           0 :     result = std::max(result, default_key_res.m_result);
     747             : 
     748             :     // "wkey" records are unsupported, if we see any, throw an error
     749           0 :     LoadResult wkey_res = LoadRecords(pwallet, batch, DBKeys::OLD_KEY,
     750           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     751           0 :         err = "Found unsupported 'wkey' record, try loading with version 0.18";
     752           0 :         return DBErrors::LOAD_FAIL;
     753             :     });
     754           0 :     result = std::max(result, wkey_res.m_result);
     755             : 
     756           0 :     if (result <= DBErrors::NONCRITICAL_ERROR) {
     757             :         // Only do logging and time first key update if there were no critical errors
     758           0 :         pwallet->WalletLogPrintf("Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n",
     759           0 :                key_res.m_records, ckey_res.m_records, keymeta_res.m_records, key_res.m_records + ckey_res.m_records);
     760             : 
     761             :         // nTimeFirstKey is only reliable if all keys have metadata
     762           0 :         if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) {
     763           0 :             auto spk_man = pwallet->GetOrCreateLegacyScriptPubKeyMan();
     764           0 :             if (spk_man) {
     765           0 :                 LOCK(spk_man->cs_KeyStore);
     766           0 :                 spk_man->UpdateTimeFirstKey(1);
     767           0 :             }
     768           0 :         }
     769           0 :     }
     770             : 
     771           0 :     return result;
     772           0 : }
     773             : 
     774             : template<typename... Args>
     775           0 : static DataStream PrefixStream(const Args&... args)
     776             : {
     777           0 :     DataStream prefix;
     778           0 :     SerializeMany(prefix, args...);
     779           0 :     return prefix;
     780           0 : }
     781             : 
     782           0 : static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
     783             : {
     784           0 :     AssertLockHeld(pwallet->cs_wallet);
     785             : 
     786             :     // Load descriptor record
     787           0 :     int num_keys = 0;
     788           0 :     int num_ckeys= 0;
     789           0 :     LoadResult desc_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTOR,
     790           0 :         [&batch, &num_keys, &num_ckeys, &last_client] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) {
     791           0 :         DBErrors result = DBErrors::LOAD_OK;
     792             : 
     793           0 :         uint256 id;
     794           0 :         key >> id;
     795           0 :         WalletDescriptor desc;
     796             :         try {
     797           0 :             value >> desc;
     798           0 :         } catch (const std::ios_base::failure& e) {
     799           0 :             strErr = strprintf("Error: Unrecognized descriptor found in wallet %s. ", pwallet->GetName());
     800           0 :             strErr += (last_client > CLIENT_VERSION) ? "The wallet might had been created on a newer version. " :
     801             :                     "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. ";
     802           0 :             strErr += "Please try running the latest software version";
     803             :             // Also include error details
     804           0 :             strErr = strprintf("%s\nDetails: %s", strErr, e.what());
     805           0 :             return DBErrors::UNKNOWN_DESCRIPTOR;
     806           0 :         }
     807           0 :         pwallet->LoadDescriptorScriptPubKeyMan(id, desc);
     808             : 
     809             :         // Prior to doing anything with this spkm, verify ID compatibility
     810           0 :         if (id != pwallet->GetDescriptorScriptPubKeyMan(desc)->GetID()) {
     811           0 :             strErr = "The descriptor ID calculated by the wallet differs from the one in DB";
     812           0 :             return DBErrors::CORRUPT;
     813             :         }
     814             : 
     815           0 :         DescriptorCache cache;
     816             : 
     817             :         // Get key cache for this descriptor
     818           0 :         DataStream prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCACHE, id);
     819           0 :         LoadResult key_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCACHE, prefix,
     820           0 :             [&id, &cache] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     821           0 :             bool parent = true;
     822           0 :             uint256 desc_id;
     823             :             uint32_t key_exp_index;
     824             :             uint32_t der_index;
     825           0 :             key >> desc_id;
     826           0 :             assert(desc_id == id);
     827           0 :             key >> key_exp_index;
     828             : 
     829             :             // if the der_index exists, it's a derived xpub
     830             :             try
     831             :             {
     832           0 :                 key >> der_index;
     833           0 :                 parent = false;
     834           0 :             }
     835           0 :             catch (...) {}
     836             : 
     837           0 :             std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     838           0 :             value >> ser_xpub;
     839           0 :             CExtPubKey xpub;
     840           0 :             xpub.Decode(ser_xpub.data());
     841           0 :             if (parent) {
     842           0 :                 cache.CacheParentExtPubKey(key_exp_index, xpub);
     843           0 :             } else {
     844           0 :                 cache.CacheDerivedExtPubKey(key_exp_index, der_index, xpub);
     845             :             }
     846             :             return DBErrors::LOAD_OK;
     847           0 :         });
     848           0 :         result = std::max(result, key_cache_res.m_result);
     849             : 
     850             :         // Get last hardened cache for this descriptor
     851           0 :         prefix = PrefixStream(DBKeys::WALLETDESCRIPTORLHCACHE, id);
     852           0 :         LoadResult lh_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORLHCACHE, prefix,
     853           0 :             [&id, &cache] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     854           0 :             uint256 desc_id;
     855             :             uint32_t key_exp_index;
     856           0 :             key >> desc_id;
     857           0 :             assert(desc_id == id);
     858           0 :             key >> key_exp_index;
     859             : 
     860           0 :             std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
     861           0 :             value >> ser_xpub;
     862           0 :             CExtPubKey xpub;
     863           0 :             xpub.Decode(ser_xpub.data());
     864           0 :             cache.CacheLastHardenedExtPubKey(key_exp_index, xpub);
     865             :             return DBErrors::LOAD_OK;
     866           0 :         });
     867           0 :         result = std::max(result, lh_cache_res.m_result);
     868             : 
     869             :         // Set the cache for this descriptor
     870           0 :         auto spk_man = (DescriptorScriptPubKeyMan*)pwallet->GetScriptPubKeyMan(id);
     871           0 :         assert(spk_man);
     872           0 :         spk_man->SetCache(cache);
     873             : 
     874             :         // Get unencrypted keys
     875           0 :         prefix = PrefixStream(DBKeys::WALLETDESCRIPTORKEY, id);
     876           0 :         LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORKEY, prefix,
     877           0 :             [&id, &spk_man] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) {
     878           0 :             uint256 desc_id;
     879           0 :             CPubKey pubkey;
     880           0 :             key >> desc_id;
     881           0 :             assert(desc_id == id);
     882           0 :             key >> pubkey;
     883           0 :             if (!pubkey.IsValid())
     884             :             {
     885           0 :                 strErr = "Error reading wallet database: descriptor unencrypted key CPubKey corrupt";
     886           0 :                 return DBErrors::CORRUPT;
     887             :             }
     888           0 :             CKey privkey;
     889           0 :             CPrivKey pkey;
     890           0 :             uint256 hash;
     891             : 
     892           0 :             value >> pkey;
     893           0 :             value >> hash;
     894             : 
     895             :             // hash pubkey/privkey to accelerate wallet load
     896           0 :             std::vector<unsigned char> to_hash;
     897           0 :             to_hash.reserve(pubkey.size() + pkey.size());
     898           0 :             to_hash.insert(to_hash.end(), pubkey.begin(), pubkey.end());
     899           0 :             to_hash.insert(to_hash.end(), pkey.begin(), pkey.end());
     900             : 
     901           0 :             if (Hash(to_hash) != hash)
     902             :             {
     903           0 :                 strErr = "Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt";
     904           0 :                 return DBErrors::CORRUPT;
     905             :             }
     906             : 
     907           0 :             if (!privkey.Load(pkey, pubkey, true))
     908             :             {
     909           0 :                 strErr = "Error reading wallet database: descriptor unencrypted key CPrivKey corrupt";
     910           0 :                 return DBErrors::CORRUPT;
     911             :             }
     912           0 :             spk_man->AddKey(pubkey.GetID(), privkey);
     913           0 :             return DBErrors::LOAD_OK;
     914           0 :         });
     915           0 :         result = std::max(result, key_res.m_result);
     916           0 :         num_keys = key_res.m_records;
     917             : 
     918             :         // Get encrypted keys
     919           0 :         prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCKEY, id);
     920           0 :         LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCKEY, prefix,
     921           0 :             [&id, &spk_man] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
     922           0 :             uint256 desc_id;
     923           0 :             CPubKey pubkey;
     924           0 :             key >> desc_id;
     925           0 :             assert(desc_id == id);
     926           0 :             key >> pubkey;
     927           0 :             if (!pubkey.IsValid())
     928             :             {
     929           0 :                 err = "Error reading wallet database: descriptor encrypted key CPubKey corrupt";
     930           0 :                 return DBErrors::CORRUPT;
     931             :             }
     932           0 :             std::vector<unsigned char> privkey;
     933           0 :             value >> privkey;
     934             : 
     935           0 :             spk_man->AddCryptedKey(pubkey.GetID(), pubkey, privkey);
     936           0 :             return DBErrors::LOAD_OK;
     937           0 :         });
     938           0 :         result = std::max(result, ckey_res.m_result);
     939           0 :         num_ckeys = ckey_res.m_records;
     940             : 
     941           0 :         return result;
     942           0 :     });
     943             : 
     944           0 :     if (desc_res.m_result <= DBErrors::NONCRITICAL_ERROR) {
     945             :         // Only log if there are no critical errors
     946           0 :         pwallet->WalletLogPrintf("Descriptors: %u, Descriptor Keys: %u plaintext, %u encrypted, %u total.\n",
     947           0 :                desc_res.m_records, num_keys, num_ckeys, num_keys + num_ckeys);
     948           0 :     }
     949             : 
     950           0 :     return desc_res.m_result;
     951           0 : }
     952             : 
     953           0 : static DBErrors LoadAddressBookRecords(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
     954             : {
     955           0 :     AssertLockHeld(pwallet->cs_wallet);
     956           0 :     DBErrors result = DBErrors::LOAD_OK;
     957             : 
     958             :     // Load name record
     959           0 :     LoadResult name_res = LoadRecords(pwallet, batch, DBKeys::NAME,
     960           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
     961           0 :         std::string strAddress;
     962           0 :         key >> strAddress;
     963           0 :         std::string label;
     964           0 :         value >> label;
     965           0 :         pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
     966             :         return DBErrors::LOAD_OK;
     967           0 :     });
     968           0 :     result = std::max(result, name_res.m_result);
     969             : 
     970             :     // Load purpose record
     971           0 :     LoadResult purpose_res = LoadRecords(pwallet, batch, DBKeys::PURPOSE,
     972           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
     973           0 :         std::string strAddress;
     974           0 :         key >> strAddress;
     975           0 :         std::string purpose_str;
     976           0 :         value >> purpose_str;
     977           0 :         std::optional<AddressPurpose> purpose{PurposeFromString(purpose_str)};
     978           0 :         if (!purpose) {
     979           0 :             pwallet->WalletLogPrintf("Warning: nonstandard purpose string '%s' for address '%s'\n", purpose_str, strAddress);
     980           0 :         }
     981           0 :         pwallet->m_address_book[DecodeDestination(strAddress)].purpose = purpose;
     982             :         return DBErrors::LOAD_OK;
     983           0 :     });
     984           0 :     result = std::max(result, purpose_res.m_result);
     985             : 
     986             :     // Load destination data record
     987           0 :     LoadResult dest_res = LoadRecords(pwallet, batch, DBKeys::DESTDATA,
     988           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
     989           0 :         std::string strAddress, strKey, strValue;
     990           0 :         key >> strAddress;
     991           0 :         key >> strKey;
     992           0 :         value >> strValue;
     993           0 :         const CTxDestination& dest{DecodeDestination(strAddress)};
     994           0 :         if (strKey.compare("used") == 0) {
     995             :             // Load "used" key indicating if an IsMine address has
     996             :             // previously been spent from with avoid_reuse option enabled.
     997             :             // The strValue is not used for anything currently, but could
     998             :             // hold more information in the future. Current values are just
     999             :             // "1" or "p" for present (which was written prior to
    1000             :             // f5ba424cd44619d9b9be88b8593d69a7ba96db26).
    1001           0 :             pwallet->LoadAddressPreviouslySpent(dest);
    1002           0 :         } else if (strKey.compare(0, 2, "rr") == 0) {
    1003             :             // Load "rr##" keys where ## is a decimal number, and strValue
    1004             :             // is a serialized RecentRequestEntry object.
    1005           0 :             pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue);
    1006           0 :         }
    1007             :         return DBErrors::LOAD_OK;
    1008           0 :     });
    1009           0 :     result = std::max(result, dest_res.m_result);
    1010             : 
    1011           0 :     return result;
    1012           0 : }
    1013             : 
    1014           0 : static DBErrors LoadTxRecords(CWallet* pwallet, DatabaseBatch& batch, std::vector<uint256>& upgraded_txs, bool& any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
    1015             : {
    1016           0 :     AssertLockHeld(pwallet->cs_wallet);
    1017           0 :     DBErrors result = DBErrors::LOAD_OK;
    1018             : 
    1019             :     // Load tx record
    1020           0 :     any_unordered = false;
    1021           0 :     LoadResult tx_res = LoadRecords(pwallet, batch, DBKeys::TX,
    1022           0 :         [&any_unordered, &upgraded_txs] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
    1023           0 :         DBErrors result = DBErrors::LOAD_OK;
    1024           0 :         uint256 hash;
    1025           0 :         key >> hash;
    1026             :         // LoadToWallet call below creates a new CWalletTx that fill_wtx
    1027             :         // callback fills with transaction metadata.
    1028           0 :         auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
    1029           0 :             if(!new_tx) {
    1030             :                 // There's some corruption here since the tx we just tried to load was already in the wallet.
    1031           0 :                 err = "Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.";
    1032           0 :                 result = DBErrors::CORRUPT;
    1033           0 :                 return false;
    1034             :             }
    1035           0 :             value >> wtx;
    1036           0 :             if (wtx.GetHash() != hash)
    1037           0 :                 return false;
    1038             : 
    1039             :             // Undo serialize changes in 31600
    1040           0 :             if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
    1041             :             {
    1042           0 :                 if (!value.empty())
    1043             :                 {
    1044             :                     uint8_t fTmp;
    1045             :                     uint8_t fUnused;
    1046           0 :                     std::string unused_string;
    1047           0 :                     value >> fTmp >> fUnused >> unused_string;
    1048           0 :                     pwallet->WalletLogPrintf("LoadWallet() upgrading tx ver=%d %d %s\n",
    1049           0 :                                        wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
    1050           0 :                     wtx.fTimeReceivedIsTxTime = fTmp;
    1051           0 :                 }
    1052             :                 else
    1053             :                 {
    1054           0 :                     pwallet->WalletLogPrintf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString());
    1055           0 :                     wtx.fTimeReceivedIsTxTime = 0;
    1056             :                 }
    1057           0 :                 upgraded_txs.push_back(hash);
    1058           0 :             }
    1059             : 
    1060           0 :             if (wtx.nOrderPos == -1)
    1061           0 :                 any_unordered = true;
    1062             : 
    1063           0 :             return true;
    1064           0 :         };
    1065           0 :         if (!pwallet->LoadToWallet(hash, fill_wtx)) {
    1066             :             // Use std::max as fill_wtx may have already set result to CORRUPT
    1067           0 :             result = std::max(result, DBErrors::NEED_RESCAN);
    1068           0 :         }
    1069           0 :         return result;
    1070           0 :     });
    1071           0 :     result = std::max(result, tx_res.m_result);
    1072             : 
    1073             :     // Load locked utxo record
    1074           0 :     LoadResult locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO,
    1075           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
    1076           0 :         uint256 hash;
    1077             :         uint32_t n;
    1078           0 :         key >> hash;
    1079           0 :         key >> n;
    1080           0 :         pwallet->LockCoin(COutPoint(hash, n));
    1081           0 :         return DBErrors::LOAD_OK;
    1082             :     });
    1083           0 :     result = std::max(result, locked_utxo_res.m_result);
    1084             : 
    1085             :     // Load orderposnext record
    1086             :     // Note: There should only be one ORDERPOSNEXT record with nothing trailing the type
    1087           0 :     LoadResult order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT,
    1088           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
    1089             :         try {
    1090           0 :             value >> pwallet->nOrderPosNext;
    1091           0 :         } catch (const std::exception& e) {
    1092           0 :             err = e.what();
    1093           0 :             return DBErrors::NONCRITICAL_ERROR;
    1094           0 :         }
    1095           0 :         return DBErrors::LOAD_OK;
    1096           0 :     });
    1097           0 :     result = std::max(result, order_pos_res.m_result);
    1098             : 
    1099           0 :     return result;
    1100           0 : }
    1101             : 
    1102           0 : static DBErrors LoadActiveSPKMs(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
    1103             : {
    1104           0 :     AssertLockHeld(pwallet->cs_wallet);
    1105           0 :     DBErrors result = DBErrors::LOAD_OK;
    1106             : 
    1107             :     // Load spk records
    1108           0 :     std::set<std::pair<OutputType, bool>> seen_spks;
    1109           0 :     for (const auto& spk_key : {DBKeys::ACTIVEEXTERNALSPK, DBKeys::ACTIVEINTERNALSPK}) {
    1110           0 :         LoadResult spkm_res = LoadRecords(pwallet, batch, spk_key,
    1111           0 :             [&seen_spks, &spk_key] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& strErr) {
    1112             :             uint8_t output_type;
    1113           0 :             key >> output_type;
    1114           0 :             uint256 id;
    1115           0 :             value >> id;
    1116             : 
    1117           0 :             bool internal = spk_key == DBKeys::ACTIVEINTERNALSPK;
    1118           0 :             auto [it, insert] = seen_spks.emplace(static_cast<OutputType>(output_type), internal);
    1119           0 :             if (!insert) {
    1120           0 :                 strErr = "Multiple ScriptpubKeyMans specified for a single type";
    1121           0 :                 return DBErrors::CORRUPT;
    1122             :             }
    1123           0 :             pwallet->LoadActiveScriptPubKeyMan(id, static_cast<OutputType>(output_type), /*internal=*/internal);
    1124           0 :             return DBErrors::LOAD_OK;
    1125           0 :         });
    1126           0 :         result = std::max(result, spkm_res.m_result);
    1127             :     }
    1128           0 :     return result;
    1129           0 : }
    1130             : 
    1131           0 : static DBErrors LoadDecryptionKeys(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
    1132             : {
    1133           0 :     AssertLockHeld(pwallet->cs_wallet);
    1134             : 
    1135             :     // Load decryption key (mkey) records
    1136           0 :     LoadResult mkey_res = LoadRecords(pwallet, batch, DBKeys::MASTER_KEY,
    1137           0 :         [] (CWallet* pwallet, DataStream& key, CDataStream& value, std::string& err) {
    1138           0 :         if (!LoadEncryptionKey(pwallet, key, value, err)) {
    1139           0 :             return DBErrors::CORRUPT;
    1140             :         }
    1141           0 :         return DBErrors::LOAD_OK;
    1142           0 :     });
    1143           0 :     return mkey_res.m_result;
    1144           0 : }
    1145             : 
    1146           0 : DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
    1147             : {
    1148           0 :     DBErrors result = DBErrors::LOAD_OK;
    1149           0 :     bool any_unordered = false;
    1150           0 :     std::vector<uint256> upgraded_txs;
    1151             : 
    1152           0 :     LOCK(pwallet->cs_wallet);
    1153             : 
    1154             :     // Last client version to open this wallet
    1155           0 :     int last_client = CLIENT_VERSION;
    1156           0 :     bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);
    1157           0 :     pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client);
    1158             : 
    1159             :     try {
    1160           0 :         if ((result = LoadMinVersion(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result;
    1161             : 
    1162             :         // Load wallet flags, so they are known when processing other records.
    1163             :         // The FLAGS key is absent during wallet creation.
    1164           0 :         if ((result = LoadWalletFlags(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result;
    1165             : 
    1166             : #ifndef ENABLE_EXTERNAL_SIGNER
    1167           0 :         if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
    1168           0 :             pwallet->WalletLogPrintf("Error: External signer wallet being loaded without external signer support compiled\n");
    1169           0 :             return DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED;
    1170             :         }
    1171             : #endif
    1172             : 
    1173             :         // Load legacy wallet keys
    1174           0 :         result = std::max(LoadLegacyWalletRecords(pwallet, *m_batch, last_client), result);
    1175             : 
    1176             :         // Load descriptors
    1177           0 :         result = std::max(LoadDescriptorWalletRecords(pwallet, *m_batch, last_client), result);
    1178             :         // Early return if there are unknown descriptors. Later loading of ACTIVEINTERNALSPK and ACTIVEEXTERNALEXPK
    1179             :         // may reference the unknown descriptor's ID which can result in a misleading corruption error
    1180             :         // when in reality the wallet is simply too new.
    1181           0 :         if (result == DBErrors::UNKNOWN_DESCRIPTOR) return result;
    1182             : 
    1183             :         // Load address book
    1184           0 :         result = std::max(LoadAddressBookRecords(pwallet, *m_batch), result);
    1185             : 
    1186             :         // Load tx records
    1187           0 :         result = std::max(LoadTxRecords(pwallet, *m_batch, upgraded_txs, any_unordered), result);
    1188             : 
    1189             :         // Load SPKMs
    1190           0 :         result = std::max(LoadActiveSPKMs(pwallet, *m_batch), result);
    1191             : 
    1192             :         // Load decryption keys
    1193           0 :         result = std::max(LoadDecryptionKeys(pwallet, *m_batch), result);
    1194           0 :     } catch (...) {
    1195             :         // Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions.
    1196             :         // Any uncaught exceptions will be caught here and treated as critical.
    1197           0 :         result = DBErrors::CORRUPT;
    1198           0 :     }
    1199             : 
    1200             :     // Any wallet corruption at all: skip any rewriting or
    1201             :     // upgrading, we don't want to make it worse.
    1202           0 :     if (result != DBErrors::LOAD_OK)
    1203           0 :         return result;
    1204             : 
    1205           0 :     for (const uint256& hash : upgraded_txs)
    1206           0 :         WriteTx(pwallet->mapWallet.at(hash));
    1207             : 
    1208           0 :     if (!has_last_client || last_client != CLIENT_VERSION) // Update
    1209           0 :         m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
    1210             : 
    1211           0 :     if (any_unordered)
    1212           0 :         result = pwallet->ReorderTransactions();
    1213             : 
    1214             :     // Upgrade all of the wallet keymetadata to have the hd master key id
    1215             :     // This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software
    1216             :     try {
    1217           0 :         pwallet->UpgradeKeyMetadata();
    1218           0 :     } catch (...) {
    1219           0 :         result = DBErrors::CORRUPT;
    1220           0 :     }
    1221             : 
    1222             :     // Upgrade all of the descriptor caches to cache the last hardened xpub
    1223             :     // This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible
    1224             :     try {
    1225           0 :         pwallet->UpgradeDescriptorCache();
    1226           0 :     } catch (...) {
    1227           0 :         result = DBErrors::CORRUPT;
    1228           0 :     }
    1229             : 
    1230           0 :     return result;
    1231           0 : }
    1232             : 
    1233           0 : DBErrors WalletBatch::FindWalletTxHashes(std::vector<uint256>& tx_hashes)
    1234             : {
    1235           0 :     DBErrors result = DBErrors::LOAD_OK;
    1236             : 
    1237             :     try {
    1238           0 :         int nMinVersion = 0;
    1239           0 :         if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) {
    1240           0 :             if (nMinVersion > FEATURE_LATEST)
    1241           0 :                 return DBErrors::TOO_NEW;
    1242           0 :         }
    1243             : 
    1244             :         // Get cursor
    1245           0 :         std::unique_ptr<DatabaseCursor> cursor = m_batch->GetNewCursor();
    1246           0 :         if (!cursor)
    1247             :         {
    1248           0 :             LogPrintf("Error getting wallet database cursor\n");
    1249           0 :             return DBErrors::CORRUPT;
    1250             :         }
    1251             : 
    1252           0 :         while (true)
    1253             :         {
    1254             :             // Read next record
    1255           0 :             DataStream ssKey{};
    1256           0 :             DataStream ssValue{};
    1257           0 :             DatabaseCursor::Status status = cursor->Next(ssKey, ssValue);
    1258           0 :             if (status == DatabaseCursor::Status::DONE) {
    1259           0 :                 break;
    1260           0 :             } else if (status == DatabaseCursor::Status::FAIL) {
    1261           0 :                 LogPrintf("Error reading next record from wallet database\n");
    1262           0 :                 return DBErrors::CORRUPT;
    1263             :             }
    1264             : 
    1265           0 :             std::string strType;
    1266           0 :             ssKey >> strType;
    1267           0 :             if (strType == DBKeys::TX) {
    1268           0 :                 uint256 hash;
    1269           0 :                 ssKey >> hash;
    1270           0 :                 tx_hashes.push_back(hash);
    1271           0 :             }
    1272           0 :         }
    1273           0 :     } catch (...) {
    1274           0 :         result = DBErrors::CORRUPT;
    1275           0 :     }
    1276             : 
    1277           0 :     return result;
    1278           0 : }
    1279             : 
    1280           0 : DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<uint256>& vTxHashOut)
    1281             : {
    1282             :     // build list of wallet TX hashes
    1283           0 :     std::vector<uint256> vTxHash;
    1284           0 :     DBErrors err = FindWalletTxHashes(vTxHash);
    1285           0 :     if (err != DBErrors::LOAD_OK) {
    1286           0 :         return err;
    1287             :     }
    1288             : 
    1289           0 :     std::sort(vTxHash.begin(), vTxHash.end());
    1290           0 :     std::sort(vTxHashIn.begin(), vTxHashIn.end());
    1291             : 
    1292             :     // erase each matching wallet TX
    1293           0 :     bool delerror = false;
    1294           0 :     std::vector<uint256>::iterator it = vTxHashIn.begin();
    1295           0 :     for (const uint256& hash : vTxHash) {
    1296           0 :         while (it < vTxHashIn.end() && (*it) < hash) {
    1297           0 :             it++;
    1298             :         }
    1299           0 :         if (it == vTxHashIn.end()) {
    1300           0 :             break;
    1301             :         }
    1302           0 :         else if ((*it) == hash) {
    1303           0 :             if(!EraseTx(hash)) {
    1304           0 :                 LogPrint(BCLog::WALLETDB, "Transaction was found for deletion but returned database error: %s\n", hash.GetHex());
    1305           0 :                 delerror = true;
    1306           0 :             }
    1307           0 :             vTxHashOut.push_back(hash);
    1308           0 :         }
    1309             :     }
    1310             : 
    1311           0 :     if (delerror) {
    1312           0 :         return DBErrors::CORRUPT;
    1313             :     }
    1314           0 :     return DBErrors::LOAD_OK;
    1315           0 : }
    1316             : 
    1317           0 : void MaybeCompactWalletDB(WalletContext& context)
    1318             : {
    1319             :     static std::atomic<bool> fOneThread(false);
    1320           0 :     if (fOneThread.exchange(true)) {
    1321           0 :         return;
    1322             :     }
    1323             : 
    1324           0 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
    1325           0 :         WalletDatabase& dbh = pwallet->GetDatabase();
    1326             : 
    1327           0 :         unsigned int nUpdateCounter = dbh.nUpdateCounter;
    1328             : 
    1329           0 :         if (dbh.nLastSeen != nUpdateCounter) {
    1330           0 :             dbh.nLastSeen = nUpdateCounter;
    1331           0 :             dbh.nLastWalletUpdate = GetTime();
    1332           0 :         }
    1333             : 
    1334           0 :         if (dbh.nLastFlushed != nUpdateCounter && GetTime() - dbh.nLastWalletUpdate >= 2) {
    1335           0 :             if (dbh.PeriodicFlush()) {
    1336           0 :                 dbh.nLastFlushed = nUpdateCounter;
    1337           0 :             }
    1338           0 :         }
    1339             :     }
    1340             : 
    1341           0 :     fOneThread = false;
    1342           0 : }
    1343             : 
    1344           0 : bool WalletBatch::WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent)
    1345             : {
    1346           0 :     auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), std::string("used")))};
    1347           0 :     return previously_spent ? WriteIC(key, std::string("1")) : EraseIC(key);
    1348           0 : }
    1349             : 
    1350           0 : bool WalletBatch::WriteAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& receive_request)
    1351             : {
    1352           0 :     return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)), receive_request);
    1353           0 : }
    1354             : 
    1355           0 : bool WalletBatch::EraseAddressReceiveRequest(const CTxDestination& dest, const std::string& id)
    1356             : {
    1357           0 :     return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)));
    1358           0 : }
    1359             : 
    1360           0 : bool WalletBatch::EraseAddressData(const CTxDestination& dest)
    1361             : {
    1362           0 :     DataStream prefix;
    1363           0 :     prefix << DBKeys::DESTDATA << EncodeDestination(dest);
    1364           0 :     return m_batch->ErasePrefix(prefix);
    1365           0 : }
    1366             : 
    1367           0 : bool WalletBatch::WriteHDChain(const CHDChain& chain)
    1368             : {
    1369           0 :     return WriteIC(DBKeys::HDCHAIN, chain);
    1370             : }
    1371             : 
    1372           0 : bool WalletBatch::WriteWalletFlags(const uint64_t flags)
    1373             : {
    1374           0 :     return WriteIC(DBKeys::FLAGS, flags);
    1375             : }
    1376             : 
    1377           0 : bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types)
    1378             : {
    1379             :     // Begin db txn
    1380           0 :     if (!m_batch->TxnBegin()) return false;
    1381             : 
    1382             :     // Get cursor
    1383           0 :     std::unique_ptr<DatabaseCursor> cursor = m_batch->GetNewCursor();
    1384           0 :     if (!cursor)
    1385             :     {
    1386           0 :         return false;
    1387             :     }
    1388             : 
    1389             :     // Iterate the DB and look for any records that have the type prefixes
    1390           0 :     while (true) {
    1391             :         // Read next record
    1392           0 :         DataStream key{};
    1393           0 :         DataStream value{};
    1394           0 :         DatabaseCursor::Status status = cursor->Next(key, value);
    1395           0 :         if (status == DatabaseCursor::Status::DONE) {
    1396           0 :             break;
    1397           0 :         } else if (status == DatabaseCursor::Status::FAIL) {
    1398           0 :             cursor.reset(nullptr);
    1399           0 :             m_batch->TxnAbort(); // abort db txn
    1400           0 :             return false;
    1401             :         }
    1402             : 
    1403             :         // Make a copy of key to avoid data being deleted by the following read of the type
    1404           0 :         Span key_data{key};
    1405             : 
    1406           0 :         std::string type;
    1407           0 :         key >> type;
    1408             : 
    1409           0 :         if (types.count(type) > 0) {
    1410           0 :             if (!m_batch->Erase(key_data)) {
    1411           0 :                 cursor.reset(nullptr);
    1412           0 :                 m_batch->TxnAbort();
    1413           0 :                 return false; // erase failed
    1414             :             }
    1415           0 :         }
    1416           0 :     }
    1417             :     // Finish db txn
    1418           0 :     cursor.reset(nullptr);
    1419           0 :     return m_batch->TxnCommit();
    1420           0 : }
    1421             : 
    1422           0 : bool WalletBatch::TxnBegin()
    1423             : {
    1424           0 :     return m_batch->TxnBegin();
    1425             : }
    1426             : 
    1427           0 : bool WalletBatch::TxnCommit()
    1428             : {
    1429           0 :     return m_batch->TxnCommit();
    1430             : }
    1431             : 
    1432           0 : bool WalletBatch::TxnAbort()
    1433             : {
    1434           0 :     return m_batch->TxnAbort();
    1435             : }
    1436             : 
    1437           0 : std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
    1438             : {
    1439             :     bool exists;
    1440             :     try {
    1441           0 :         exists = fs::symlink_status(path).type() != fs::file_type::not_found;
    1442           0 :     } catch (const fs::filesystem_error& e) {
    1443           0 :         error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e)));
    1444           0 :         status = DatabaseStatus::FAILED_BAD_PATH;
    1445           0 :         return nullptr;
    1446           0 :     }
    1447             : 
    1448           0 :     std::optional<DatabaseFormat> format;
    1449           0 :     if (exists) {
    1450           0 :         if (IsBDBFile(BDBDataFile(path))) {
    1451           0 :             format = DatabaseFormat::BERKELEY;
    1452           0 :         }
    1453           0 :         if (IsSQLiteFile(SQLiteDataFile(path))) {
    1454           0 :             if (format) {
    1455           0 :                 error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", fs::PathToString(path)));
    1456           0 :                 status = DatabaseStatus::FAILED_BAD_FORMAT;
    1457           0 :                 return nullptr;
    1458             :             }
    1459           0 :             format = DatabaseFormat::SQLITE;
    1460           0 :         }
    1461           0 :     } else if (options.require_existing) {
    1462           0 :         error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", fs::PathToString(path)));
    1463           0 :         status = DatabaseStatus::FAILED_NOT_FOUND;
    1464           0 :         return nullptr;
    1465             :     }
    1466             : 
    1467           0 :     if (!format && options.require_existing) {
    1468           0 :         error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", fs::PathToString(path)));
    1469           0 :         status = DatabaseStatus::FAILED_BAD_FORMAT;
    1470           0 :         return nullptr;
    1471             :     }
    1472             : 
    1473           0 :     if (format && options.require_create) {
    1474           0 :         error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(path)));
    1475           0 :         status = DatabaseStatus::FAILED_ALREADY_EXISTS;
    1476           0 :         return nullptr;
    1477             :     }
    1478             : 
    1479             :     // A db already exists so format is set, but options also specifies the format, so make sure they agree
    1480           0 :     if (format && options.require_format && format != options.require_format) {
    1481           0 :         error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", fs::PathToString(path)));
    1482           0 :         status = DatabaseStatus::FAILED_BAD_FORMAT;
    1483           0 :         return nullptr;
    1484             :     }
    1485             : 
    1486             :     // Format is not set when a db doesn't already exist, so use the format specified by the options if it is set.
    1487           0 :     if (!format && options.require_format) format = options.require_format;
    1488             : 
    1489             :     // If the format is not specified or detected, choose the default format based on what is available. We prefer BDB over SQLite for now.
    1490           0 :     if (!format) {
    1491             : #ifdef USE_SQLITE
    1492           0 :         format = DatabaseFormat::SQLITE;
    1493             : #endif
    1494             : #ifdef USE_BDB
    1495             :         format = DatabaseFormat::BERKELEY;
    1496             : #endif
    1497           0 :     }
    1498             : 
    1499           0 :     if (format == DatabaseFormat::SQLITE) {
    1500             : #ifdef USE_SQLITE
    1501           0 :         return MakeSQLiteDatabase(path, options, status, error);
    1502             : #endif
    1503             :         error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support SQLite database format.", fs::PathToString(path)));
    1504             :         status = DatabaseStatus::FAILED_BAD_FORMAT;
    1505             :         return nullptr;
    1506             :     }
    1507             : 
    1508             : #ifdef USE_BDB
    1509             :     return MakeBerkeleyDatabase(path, options, status, error);
    1510             : #endif
    1511           0 :     error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", fs::PathToString(path)));
    1512           0 :     status = DatabaseStatus::FAILED_BAD_FORMAT;
    1513           0 :     return nullptr;
    1514           0 : }
    1515             : } // namespace wallet

Generated by: LCOV version 1.14