LCOV - code coverage report
Current view: top level - src/wallet - wallet.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 2553 3026 84.4 %
Date: 2026-06-25 07:23:43 Functions: 224 232 96.6 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Copyright (c) 2014-2025 The Dash Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include <wallet/wallet.h>
       8             : 
       9             : #include <blockfilter.h>
      10             : #include <chain.h>
      11             : #include <chainparams.h>
      12             : #include <consensus/amount.h>
      13             : #include <consensus/consensus.h>
      14             : #include <crypto/common.h>
      15             : #include <external_signer.h>
      16             : #include <fs.h>
      17             : #include <interfaces/chain.h>
      18             : #include <interfaces/wallet.h>
      19             : #include <key.h>
      20             : #include <key_io.h>
      21             : #include <policy/fees.h>
      22             : #include <policy/policy.h>
      23             : #include <primitives/block.h>
      24             : #include <primitives/transaction.h>
      25             : #include <psbt.h>
      26             : #include <script/descriptor.h>
      27             : #include <script/script.h>
      28             : #include <script/sign.h>
      29             : #include <script/signingprovider.h>
      30             : #include <support/cleanse.h>
      31             : #include <txmempool.h>
      32             : #include <util/check.h>
      33             : #include <util/error.h>
      34             : #include <util/moneystr.h>
      35             : #include <util/string.h>
      36             : #include <util/time.h>
      37             : #include <util/translation.h>
      38             : #ifdef USE_BDB
      39             : #include <wallet/bdb.h>
      40             : #endif
      41             : #include <wallet/bip39.h> // TODO(refactor): move dependency it to scriptpubkeyman.cpp
      42             : #include <wallet/coincontrol.h>
      43             : #include <wallet/context.h>
      44             : #include <wallet/external_signer_scriptpubkeyman.h>
      45             : #include <warnings.h>
      46             : 
      47             : #include <coinjoin/options.h>
      48             : #include <evo/providertx.h>
      49             : #include <governance/vote.h>
      50             : 
      51             : #include <univalue.h>
      52             : 
      53             : #include <algorithm>
      54             : #include <cassert>
      55             : #include <ranges>
      56             : 
      57             : using interfaces::FoundBlock;
      58             : 
      59             : namespace wallet {
      60          24 : static isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
      61             : {
      62          24 :     AssertLockHeld(wallet.cs_wallet);
      63          24 :     const CWalletTx* prev = wallet.GetWalletTx(txin.prevout.hash);
      64          24 :     if (prev && txin.prevout.n < prev->tx->vout.size()) {
      65           8 :         return wallet.IsMine(prev->tx->vout[txin.prevout.n]);
      66             :     }
      67          16 :     return ISMINE_NO;
      68          24 : }
      69             : 
      70        3444 : const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
      71        3444 :     {WALLET_FLAG_AVOID_REUSE,
      72             :         "You need to rescan the blockchain in order to correctly mark used "
      73             :         "destinations in the past. Until this is done, some destinations may "
      74             :         "be considered unused, even if the opposite is the case."
      75             :     },
      76             : };
      77             : 
      78         620 : bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
      79             : {
      80         620 :     util::SettingsValue setting_value = chain.getRwSetting("wallet");
      81         620 :     if (!setting_value.isArray()) setting_value.setArray();
      82         644 :     for (const util::SettingsValue& value : setting_value.getValues()) {
      83          33 :         if (value.isStr() && value.get_str() == wallet_name) return true;
      84             :     }
      85         611 :     setting_value.push_back(wallet_name);
      86         611 :     return chain.updateRwSetting("wallet", setting_value);
      87         620 : }
      88             : 
      89         582 : bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
      90             : {
      91         582 :     util::SettingsValue setting_value = chain.getRwSetting("wallet");
      92         582 :     if (!setting_value.isArray()) return true;
      93         500 :     util::SettingsValue new_value(util::SettingsValue::VARR);
      94        1050 :     for (const util::SettingsValue& value : setting_value.getValues()) {
      95         550 :         if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value);
      96             :     }
      97         500 :     if (new_value.size() == setting_value.size()) return true;
      98          21 :     return chain.updateRwSetting("wallet", new_value);
      99         582 : }
     100             : 
     101        3827 : static void UpdateWalletSetting(interfaces::Chain& chain,
     102             :                                 const std::string& wallet_name,
     103             :                                 std::optional<bool> load_on_startup,
     104             :                                 std::vector<bilingual_str>& warnings)
     105             : {
     106        3827 :     if (load_on_startup == std::nullopt) return;
     107        1202 :     if (load_on_startup.value() && !AddWalletSetting(chain, wallet_name)) {
     108           0 :         warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
     109        1202 :     } else if (!load_on_startup.value() && !RemoveWalletSetting(chain, wallet_name)) {
     110           0 :         warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup."));
     111           0 :     }
     112        3827 : }
     113             : 
     114             : /**
     115             :  * Refresh mempool status so the wallet is in an internally consistent state and
     116             :  * immediately knows the transaction's status: Whether it can be considered
     117             :  * trusted and is eligible to be abandoned ...
     118             :  */
     119       67055 : static void RefreshMempoolStatus(CWalletTx& tx, interfaces::Chain& chain)
     120             : {
     121       67055 :     if (chain.isInMempool(tx.GetHash())) {
     122        8402 :         tx.m_state = TxStateInMempool();
     123       67055 :     } else if (tx.state<TxStateInMempool>()) {
     124          70 :         tx.m_state = TxStateInactive();
     125          70 :     }
     126       67055 : }
     127             : 
     128        2190 : bool AddWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet)
     129             : {
     130             :     {
     131        2190 :         LOCK(context.wallets_mutex);
     132        2190 :         assert(wallet);
     133        2190 :         std::vector<std::shared_ptr<CWallet>>::const_iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet);
     134        2190 :         if (i != context.wallets.end()) return false;
     135        2190 :         context.wallets.push_back(wallet);
     136        2190 :     }
     137        2190 :     wallet->ConnectScriptPubKeyManNotifiers();
     138        2190 :     wallet->AutoLockMasternodeCollaterals();
     139        2190 :     if (wallet->coinjoin_available()) {
     140        2190 :         wallet->coinjoin_loader().AddWallet(wallet);
     141        2190 :     }
     142        2190 :     wallet->NotifyCanGetAddressesChanged();
     143        2190 :     return true;
     144        2190 : }
     145             : 
     146        2190 : bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
     147             : {
     148        2190 :     assert(wallet);
     149             : 
     150        2190 :     interfaces::Chain& chain = wallet->chain();
     151        2190 :     std::string name = wallet->GetName();
     152             : 
     153             :     // Unregister with the validation interface which also drops shared pointers.
     154        2190 :     wallet->m_chain_notifications_handler.reset();
     155             :     {
     156        2190 :         LOCK(context.wallets_mutex);
     157        2190 :         std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet);
     158        2190 :         if (i == context.wallets.end()) return false;
     159        2190 :         context.wallets.erase(i);
     160        2190 :     }
     161             : 
     162        2190 :     if (wallet->coinjoin_available()) {
     163        2190 :         wallet->coinjoin_loader().RemoveWallet(name);
     164        2190 :     }
     165             : 
     166             :     // Write the wallet setting
     167        2190 :     UpdateWalletSetting(chain, name, load_on_start, warnings);
     168             : 
     169        2190 :     return true;
     170        2190 : }
     171             : 
     172           7 : bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start)
     173             : {
     174           7 :     std::vector<bilingual_str> warnings;
     175           7 :     return RemoveWallet(context, wallet, load_on_start, warnings);
     176           7 : }
     177             : 
     178       61190 : std::vector<std::shared_ptr<CWallet>> GetWallets(WalletContext& context)
     179             : {
     180       61190 :     LOCK(context.wallets_mutex);
     181       61190 :     return context.wallets;
     182       61190 : }
     183             : 
     184       35456 : std::shared_ptr<CWallet> GetDefaultWallet(WalletContext& context, size_t& count)
     185             : {
     186       35456 :     LOCK(context.wallets_mutex);
     187       35456 :     count = context.wallets.size();
     188       35456 :     return count == 1 ? context.wallets[0] : nullptr;
     189       35456 : }
     190             : 
     191       15819 : std::shared_ptr<CWallet> GetWallet(WalletContext& context, const std::string& name)
     192             : {
     193       15819 :     LOCK(context.wallets_mutex);
     194       35086 :     for (const std::shared_ptr<CWallet>& wallet : context.wallets) {
     195       35026 :         if (wallet->GetName() == name) return wallet;
     196             :     }
     197          60 :     return nullptr;
     198       15819 : }
     199             : 
     200           1 : std::unique_ptr<interfaces::Handler> HandleLoadWallet(WalletContext& context, LoadWalletFn load_wallet)
     201             : {
     202           1 :     LOCK(context.wallets_mutex);
     203           1 :     auto it = context.wallet_load_fns.emplace(context.wallet_load_fns.end(), std::move(load_wallet));
     204           2 :     return interfaces::MakeHandler([&context, it] { LOCK(context.wallets_mutex); context.wallet_load_fns.erase(it); });
     205           1 : }
     206             : 
     207           0 : std::unique_ptr<interfaces::Handler> HandleLoadWalletLoading(WalletContext& context, LoadWalletFn load_wallet)
     208             : {
     209           0 :     LOCK(context.wallets_mutex);
     210           0 :     auto it = context.wallet_loading_fns.emplace(context.wallet_loading_fns.end(), std::move(load_wallet));
     211           0 :     return interfaces::MakeHandler([&context, it] { LOCK(context.wallets_mutex); context.wallet_loading_fns.erase(it); });
     212           0 : }
     213             : 
     214        2226 : void NotifyWalletLoading(WalletContext& context, const std::shared_ptr<CWallet>& wallet)
     215             : {
     216        2226 :     LOCK(context.wallets_mutex);
     217        2226 :     for (auto& load_wallet : context.wallet_loading_fns) {
     218           0 :         load_wallet(interfaces::MakeWallet(context, wallet));
     219             :     }
     220        2226 : }
     221             : 
     222        2184 : void NotifyWalletLoaded(WalletContext& context, const std::shared_ptr<CWallet>& wallet)
     223             : {
     224        2184 :     LOCK(context.wallets_mutex);
     225        2185 :     for (auto& load_wallet : context.wallet_load_fns) {
     226           1 :         load_wallet(interfaces::MakeWallet(context, wallet));
     227             :     }
     228        2184 : }
     229             : 
     230             : static GlobalMutex g_loading_wallet_mutex;
     231             : static GlobalMutex g_wallet_release_mutex;
     232             : static std::condition_variable g_wallet_release_cv;
     233        3444 : static std::set<std::string> g_loading_wallet_set GUARDED_BY(g_loading_wallet_mutex);
     234        3444 : static std::set<std::string> g_unloading_wallet_set GUARDED_BY(g_wallet_release_mutex);
     235             : 
     236             : // Custom deleter for shared_ptr<CWallet>.
     237        2260 : static void ReleaseWallet(CWallet* wallet)
     238             : {
     239        2260 :     const std::string name = wallet->GetName();
     240        2260 :     wallet->WalletLogPrintf("Releasing wallet\n");
     241        2260 :     wallet->Flush();
     242        2260 :     delete wallet;
     243             :     // Wallet is now released, notify UnloadWallet, if any.
     244             :     {
     245        2260 :         LOCK(g_wallet_release_mutex);
     246        2260 :         if (g_unloading_wallet_set.erase(name) == 0) {
     247             :             // UnloadWallet was not called for this wallet, all done.
     248          76 :             return;
     249             :         }
     250        2260 :     }
     251        2184 :     g_wallet_release_cv.notify_all();
     252        2260 : }
     253             : 
     254        2184 : void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
     255             : {
     256             :     // Mark wallet for unloading.
     257        2184 :     const std::string name = wallet->GetName();
     258             :     {
     259        2184 :         LOCK(g_wallet_release_mutex);
     260        2184 :         auto it = g_unloading_wallet_set.insert(name);
     261        2184 :         assert(it.second);
     262        2184 :     }
     263             :     // The wallet can be in use so it's not possible to explicitly unload here.
     264             :     // Notify the unload intent so that all remaining shared pointers are
     265             :     // released.
     266        2184 :     wallet->NotifyUnload();
     267             : 
     268             :     // Time to ditch our shared_ptr and wait for ReleaseWallet call.
     269        2184 :     wallet.reset();
     270             :     {
     271        2184 :         WAIT_LOCK(g_wallet_release_mutex, lock);
     272        2184 :         while (g_unloading_wallet_set.count(name) == 1) {
     273           0 :             g_wallet_release_cv.wait(lock);
     274             :         }
     275        2184 :     }
     276        2184 : }
     277             : 
     278             : namespace {
     279         511 : std::shared_ptr<CWallet> LoadWalletInternal(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
     280             : {
     281             :     try {
     282         511 :         std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
     283         511 :         if (!database) {
     284          36 :             error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
     285          36 :             return nullptr;
     286             :         }
     287             : 
     288         475 :         context.chain->initMessage(_("Loading wallet…").translated);
     289         475 :         std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings);
     290         467 :         if (!wallet) {
     291           8 :             error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
     292           8 :             status = DatabaseStatus::FAILED_LOAD;
     293           8 :             return nullptr;
     294             :         }
     295             : 
     296         459 :         NotifyWalletLoaded(context, wallet);
     297         459 :         AddWallet(context, wallet);
     298         459 :         wallet->postInitProcess();
     299             : 
     300             :         // Write the wallet setting
     301         459 :         UpdateWalletSetting(*context.chain, name, load_on_start, warnings);
     302             : 
     303         459 :         return wallet;
     304         511 :     } catch (const std::runtime_error& e) {
     305           8 :         error = Untranslated(e.what());
     306           8 :         status = DatabaseStatus::FAILED_LOAD;
     307           8 :         return nullptr;
     308           8 :     }
     309         519 : }
     310             : 
     311             : class FastWalletRescanFilter
     312             : {
     313             : public:
     314          12 :     FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet)
     315           6 :     {
     316             :         // fast rescanning via block filters is only supported by descriptor wallets right now
     317           6 :         assert(!m_wallet.IsLegacy());
     318             : 
     319             :         // create initial filter with scripts from all ScriptPubKeyMans
     320          26 :         for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) {
     321          20 :             auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
     322          20 :             assert(desc_spkm != nullptr);
     323          20 :             AddScriptPubKeys(desc_spkm);
     324             :             // save each range descriptor's end for possible future filter updates
     325          20 :             if (desc_spkm->IsHDEnabled()) {
     326          18 :                 m_last_range_ends.emplace(desc_spkm->GetID(), desc_spkm->GetEndRange());
     327          18 :             }
     328             :         }
     329          12 :     }
     330             : 
     331         828 :     void UpdateIfNeeded()
     332             :     {
     333             :         // repopulate filter with new scripts if top-up has happened since last iteration
     334        3312 :         for (const auto& [desc_spkm_id, last_range_end] : m_last_range_ends) {
     335        4968 :             auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(m_wallet.GetScriptPubKeyMan(desc_spkm_id))};
     336        2484 :             assert(desc_spkm != nullptr);
     337        2484 :             int32_t current_range_end{desc_spkm->GetEndRange()};
     338        4968 :             if (current_range_end > last_range_end) {
     339         120 :                 AddScriptPubKeys(desc_spkm, last_range_end);
     340          60 :                 m_last_range_ends.at(desc_spkm->GetID()) = current_range_end;
     341          60 :             }
     342             :         }
     343         828 :     }
     344             : 
     345         828 :     std::optional<bool> MatchesBlock(const uint256& block_hash) const
     346             :     {
     347         828 :         return m_wallet.chain().blockFilterMatchesAny(BlockFilterType::BASIC_FILTER, block_hash, m_filter_set);
     348             :     }
     349             : 
     350             : private:
     351             :     const CWallet& m_wallet;
     352             :     /** Map for keeping track of each range descriptor's last seen end range.
     353             :       * This information is used to detect whether new addresses were derived
     354             :       * (that is, if the current end range is larger than the saved end range)
     355             :       * after processing a block and hence a filter set update is needed to
     356             :       * take possible keypool top-ups into account.
     357             :       */
     358             :     std::map<uint256, int32_t> m_last_range_ends;
     359             :     GCSFilter::ElementSet m_filter_set;
     360             : 
     361          80 :     void AddScriptPubKeys(const DescriptorScriptPubKeyMan* desc_spkm, int32_t last_range_end = 0)
     362             :     {
     363        7886 :         for (const auto& script_pub_key : desc_spkm->GetScriptPubKeys(last_range_end)) {
     364        7806 :             m_filter_set.emplace(script_pub_key.begin(), script_pub_key.end());
     365             :         }
     366          80 :     }
     367             : };
     368             : } // namespace
     369             : 
     370         523 : std::shared_ptr<CWallet> LoadWallet(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
     371             : {
     372        1046 :     auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
     373         523 :     if (!result.second) {
     374          12 :         error = Untranslated("Wallet already loading.");
     375          12 :         status = DatabaseStatus::FAILED_LOAD;
     376          12 :         return nullptr;
     377             :     }
     378         511 :     auto wallet = LoadWalletInternal(context, name, load_on_start, options, status, error, warnings);
     379        1022 :     WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));
     380         511 :     return wallet;
     381        1034 : }
     382             : 
     383        1200 : std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
     384             : {
     385        1200 :     ArgsManager& args = *Assert(context.args);
     386             : 
     387        1200 :     uint64_t wallet_creation_flags = options.create_flags;
     388        1200 :     const SecureString& passphrase = options.create_passphrase;
     389             : 
     390        1200 :     if (wallet_creation_flags & WALLET_FLAG_DESCRIPTORS) options.require_format = DatabaseFormat::SQLITE;
     391             : 
     392             :     // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted
     393        1200 :     bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
     394             : 
     395             :     // Born encrypted wallets need to be created blank first.
     396        1200 :     if (!passphrase.empty()) {
     397          28 :         wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
     398          28 :     }
     399             : 
     400             :     // Private keys must be disabled for an external signer wallet
     401        1200 :     if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     402           6 :         error = Untranslated("Private keys must be disabled when using an external signer");
     403           2 :         status = DatabaseStatus::FAILED_CREATE;
     404           2 :         return nullptr;
     405             :     }
     406             : 
     407             :     // Do not allow a passphrase when private keys are disabled
     408        1198 :     if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     409          12 :         error = Untranslated("Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.");
     410          12 :         status = DatabaseStatus::FAILED_CREATE;
     411          12 :         return nullptr;
     412             :     }
     413             : 
     414             :     // Descriptor support must be enabled for an external signer wallet
     415        1186 :     if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS)) {
     416           2 :         error = Untranslated("Descriptor support must be enabled when using an external signer");
     417           2 :         status = DatabaseStatus::FAILED_CREATE;
     418           2 :         return nullptr;
     419             :     }
     420             : 
     421             :     // Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
     422        1184 :     std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
     423        1184 :     if (!database) {
     424           8 :         error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
     425           8 :         status = DatabaseStatus::FAILED_VERIFY;
     426           8 :         return nullptr;
     427             :     }
     428             : 
     429             :     // Make the wallet
     430        1176 :     context.chain->initMessage(_("Loading wallet…").translated);
     431        1176 :     std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings);
     432        1172 :     if (!wallet) {
     433           0 :         error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
     434           0 :         status = DatabaseStatus::FAILED_CREATE;
     435           0 :         return nullptr;
     436             :     }
     437        1172 :     if (args.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) {
     438        1058 :         wallet->WalletLogPrintf("Set HD by default\n");
     439        1058 :         wallet->SetMinVersion(FEATURE_HD);
     440        1058 :     }
     441             : 
     442             :     // Encrypt the wallet
     443        1172 :     if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     444          16 :         if (!wallet->EncryptWallet(passphrase)) {
     445           0 :             error = Untranslated("Error: Wallet created but failed to encrypt.");
     446           0 :             status = DatabaseStatus::FAILED_ENCRYPT;
     447           0 :             return nullptr;
     448             :         }
     449          16 :         if (!create_blank) {
     450             :             // Unlock the wallet
     451           8 :             if (!wallet->Unlock(passphrase)) {
     452           0 :                 error = Untranslated("Error: Wallet was encrypted but could not be unlocked");
     453           0 :                 status = DatabaseStatus::FAILED_ENCRYPT;
     454           0 :                 return nullptr;
     455             :             }
     456             : 
     457             :             // Set a seed for the wallet
     458           8 :             if (wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
     459           4 :                 LOCK(wallet->cs_wallet);
     460           4 :                 wallet->SetupDescriptorScriptPubKeyMans("", "");
     461           4 :             } else {
     462             :                 // TODO: drop this condition after removing option to create non-HD wallets
     463             :                 // related backport bitcoin#11250
     464           4 :                 if (wallet->GetVersion() >= FEATURE_HD) {
     465           4 :                     auto spk_man = wallet->GetLegacyScriptPubKeyMan();
     466           4 :                     if (!spk_man) {
     467           0 :                         error = Untranslated("Error: Legacy ScriptPubKeyMan is not available");
     468           0 :                         status = DatabaseStatus::FAILED_ENCRYPT;
     469           0 :                         return nullptr;
     470             :                     }
     471             : 
     472           8 :                     wallet->WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
     473           4 :                             spk_man->GenerateNewHDChain(/*secureMnemonic=*/"", /*secureMnemonicPassphrase=*/"", encryption_key);
     474           4 :                             return true;
     475           0 :                         });
     476           4 :                 }
     477             :             }
     478             : 
     479             :             // backup the wallet we just encrypted
     480           8 :             if (!wallet->AutoBackupWallet("", error, warnings) && !error.original.empty()) {
     481           0 :                 status = DatabaseStatus::FAILED_ENCRYPT;
     482           0 :                 return nullptr;
     483             :             }
     484             : 
     485             :             // Relock the wallet
     486           8 :             wallet->Lock();
     487           8 :         }
     488          16 :     }
     489             : 
     490        1172 :     NotifyWalletLoaded(context, wallet);
     491        1172 :     AddWallet(context, wallet);
     492        1172 :     wallet->postInitProcess();
     493             : 
     494             :     // Write the wallet settings
     495        1172 :     UpdateWalletSetting(*context.chain, name, load_on_start, warnings);
     496             : 
     497        1172 :     status = DatabaseStatus::SUCCESS;
     498        1172 :     return wallet;
     499        1200 : }
     500             : 
     501          46 : std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
     502             : {
     503          46 :     DatabaseOptions options;
     504          46 :     ReadDatabaseArgs(*context.args, options);
     505          46 :     options.require_existing = true;
     506             : 
     507          46 :     const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name));
     508          46 :     auto wallet_file = wallet_path / "wallet.dat";
     509          46 :     std::shared_ptr<CWallet> wallet;
     510             : 
     511             :     try {
     512          46 :         if (!fs::exists(backup_file)) {
     513           6 :             error = Untranslated("Backup file does not exist");
     514           6 :             status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE;
     515           6 :             return nullptr;
     516             :         }
     517             : 
     518          40 :         if (fs::exists(wallet_path) || !TryCreateDirectories(wallet_path)) {
     519           6 :             error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(wallet_path)));
     520           6 :             status = DatabaseStatus::FAILED_ALREADY_EXISTS;
     521           6 :             return nullptr;
     522             :         }
     523             : 
     524          34 :         fs::copy_file(backup_file, wallet_file, fs::copy_options::none);
     525             : 
     526          34 :         wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings);
     527          34 :     } catch (const std::exception& e) {
     528           0 :         assert(!wallet);
     529           0 :         if (!error.empty()) error += Untranslated("\n");
     530           0 :         error += strprintf(Untranslated("Unexpected exception: %s"), e.what());
     531           0 :     }
     532          34 :     if (!wallet) {
     533          10 :         fs::remove_all(wallet_path);
     534          10 :     }
     535             : 
     536          34 :     return wallet;
     537          46 : }
     538             : 
     539             : /** @defgroup mapWallet
     540             :  *
     541             :  * @{
     542             :  */
     543             : 
     544      311712 : const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
     545             : {
     546      311712 :     AssertLockHeld(cs_wallet);
     547      311712 :     const auto it = mapWallet.find(hash);
     548      311712 :     if (it == mapWallet.end())
     549        2049 :         return nullptr;
     550      309663 :     return &(it->second);
     551      311712 : }
     552             : 
     553        2564 : void CWallet::UpgradeKeyMetadata()
     554             : {
     555        2564 :     if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
     556         502 :         return;
     557             :     }
     558             : 
     559        2062 :     auto spk_man = GetLegacyScriptPubKeyMan();
     560        2062 :     if (!spk_man) {
     561        1667 :         return;
     562             :     }
     563             : 
     564         395 :     spk_man->UpgradeKeyMetadata();
     565         395 :     SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
     566        2564 : }
     567             : 
     568        2564 : void CWallet::UpgradeDescriptorCache()
     569             : {
     570        2564 :     if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) {
     571        2356 :         return;
     572             :     }
     573             : 
     574         878 :     for (ScriptPubKeyMan* spkm : GetAllScriptPubKeyMans()) {
     575         670 :         DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm);
     576         670 :         desc_spkm->UpgradeDescriptorCache();
     577             :     }
     578         208 :     SetWalletFlag(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED);
     579        2564 : }
     580             : 
     581           8 : bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
     582             : {
     583           8 :     bool fWasLocked = IsLocked(true);
     584             : 
     585             :     {
     586           8 :         LOCK(cs_wallet);
     587           8 :         Lock();
     588             : 
     589           8 :         CCrypter crypter;
     590           8 :         CKeyingMaterial _vMasterKey;
     591           8 :         for (MasterKeyMap::value_type& pMasterKey : mapMasterKeys)
     592             :         {
     593           8 :             if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
     594           0 :                 return false;
     595           8 :             if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
     596           0 :                 return false;
     597           8 :             if (Unlock(_vMasterKey))
     598             :             {
     599           8 :                 constexpr MillisecondsDouble target{100};
     600           8 :                 auto start{SteadyClock::now()};
     601           8 :                 crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
     602           8 :                 pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start));
     603             : 
     604           8 :                 start = SteadyClock::now();
     605           8 :                 crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
     606           8 :                 pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start))) / 2;
     607             : 
     608           8 :                 if (pMasterKey.second.nDeriveIterations < 25000)
     609           0 :                     pMasterKey.second.nDeriveIterations = 25000;
     610             : 
     611           8 :                 WalletLogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
     612             : 
     613           8 :                 if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
     614           0 :                     return false;
     615           8 :                 if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey))
     616           0 :                     return false;
     617           8 :                 WalletBatch(GetDatabase()).WriteMasterKey(pMasterKey.first, pMasterKey.second);
     618           8 :                 if (fWasLocked)
     619           8 :                     Lock();
     620             : 
     621           8 :                 return true;
     622             :             }
     623             :         }
     624           8 :     }
     625             : 
     626           0 :     return false;
     627           8 : }
     628             : 
     629        3316 : void CWallet::chainStateFlushed(const CBlockLocator& loc)
     630             : {
     631             :     // Don't update the best block until the chain is attached so that in case of a shutdown,
     632             :     // the rescan will be restarted at next startup.
     633        3316 :     if (m_attaching_chain) {
     634           0 :         return;
     635             :     }
     636        3316 :     WalletBatch batch(GetDatabase());
     637        3316 :     batch.WriteBestBlock(loc);
     638        3316 : }
     639             : 
     640        4902 : void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in)
     641             : {
     642        4902 :     LOCK(cs_wallet);
     643        4902 :     if (nWalletVersion >= nVersion)
     644        3717 :         return;
     645        1185 :     WalletLogPrintf("Setting minversion to %d\n", nVersion);
     646        1185 :     nWalletVersion = nVersion;
     647             : 
     648             :     {
     649        1185 :         WalletBatch* batch = batch_in ? batch_in : new WalletBatch(GetDatabase());
     650        1185 :         if (nWalletVersion > 40000)
     651        1185 :             batch->WriteMinVersion(nWalletVersion);
     652        1185 :         if (!batch_in)
     653        1185 :             delete batch;
     654             :     }
     655        4902 : }
     656             : 
     657        5384 : std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
     658             : {
     659        5384 :     std::set<uint256> result;
     660        5384 :     AssertLockHeld(cs_wallet);
     661             : 
     662        5384 :     const auto it = mapWallet.find(txid);
     663        5384 :     if (it == mapWallet.end())
     664           0 :         return result;
     665        5384 :     const CWalletTx& wtx = it->second;
     666             : 
     667        5384 :     std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
     668             : 
     669       12554 :     for (const CTxIn& txin : wtx.tx->vin)
     670             :     {
     671        7170 :         if (mapTxSpends.count(txin.prevout) <= 1)
     672        7031 :             continue;  // No conflict if zero or one spends
     673         139 :         range = mapTxSpends.equal_range(txin.prevout);
     674         417 :         for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it)
     675         278 :             result.insert(_it->second);
     676             :     }
     677        5384 :     return result;
     678        5384 : }
     679             : 
     680           2 : bool CWallet::HasWalletSpend(const CTransactionRef& tx) const
     681             : {
     682           2 :     AssertLockHeld(cs_wallet);
     683           2 :     const uint256& txid = tx->GetHash();
     684           3 :     for (unsigned int i = 0; i < tx->vout.size(); ++i) {
     685           2 :         if (IsSpent(COutPoint(txid, i))) {
     686           1 :             return true;
     687             :         }
     688           1 :     }
     689           1 :     return false;
     690           2 : }
     691             : 
     692        4051 : void CWallet::Flush()
     693             : {
     694        4051 :     GetDatabase().Flush();
     695        4051 : }
     696             : 
     697        1855 : void CWallet::Close()
     698             : {
     699        1855 :     GetDatabase().Close();
     700        1855 : }
     701             : 
     702       27963 : void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range)
     703             : {
     704             :     // We want all the wallet transactions in range to have the same metadata as
     705             :     // the oldest (smallest nOrderPos).
     706             :     // So: find smallest nOrderPos:
     707             : 
     708       27963 :     int nMinOrderPos = std::numeric_limits<int>::max();
     709       27963 :     const CWalletTx* copyFrom = nullptr;
     710       56014 :     for (TxSpends::iterator it = range.first; it != range.second; ++it) {
     711       28051 :         const CWalletTx* wtx = &mapWallet.at(it->second);
     712       28051 :         if (wtx->nOrderPos < nMinOrderPos) {
     713       27965 :             nMinOrderPos = wtx->nOrderPos;
     714       27965 :             copyFrom = wtx;
     715       27965 :         }
     716       28051 :     }
     717             : 
     718       27963 :     if (!copyFrom) {
     719           0 :         return;
     720             :     }
     721             : 
     722             :     // Now copy data from copyFrom to rest:
     723       56014 :     for (TxSpends::iterator it = range.first; it != range.second; ++it)
     724             :     {
     725       28051 :         const uint256& hash = it->second;
     726       28051 :         CWalletTx* copyTo = &mapWallet.at(hash);
     727       28051 :         if (copyFrom == copyTo) continue;
     728         176 :         assert(copyFrom && "Oldest wallet transaction in range assumed to have been found.");
     729          88 :         if (!copyFrom->IsEquivalentTo(*copyTo)) continue;
     730           6 :         copyTo->mapValue = copyFrom->mapValue;
     731           6 :         copyTo->vOrderForm = copyFrom->vOrderForm;
     732             :         // fTimeReceivedIsTxTime not copied on purpose
     733             :         // nTimeReceived not copied on purpose
     734           6 :         copyTo->nTimeSmart = copyFrom->nTimeSmart;
     735           6 :         copyTo->fFromMe = copyFrom->fFromMe;
     736             :         // nOrderPos not copied on purpose
     737             :         // cached members not copied on purpose
     738           6 :     }
     739       27963 : }
     740             : 
     741             : /**
     742             :  * Outpoint is spent if any non-conflicted transaction
     743             :  * spends it:
     744             :  */
     745     1323520 : bool CWallet::IsSpent(const COutPoint& outpoint) const
     746             : {
     747     1323520 :     std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
     748     1323520 :     range = mapTxSpends.equal_range(outpoint);
     749             : 
     750     1323873 :     for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {
     751       97607 :         const uint256& wtxid = it->second;
     752       97607 :         const auto mit = mapWallet.find(wtxid);
     753       97607 :         if (mit != mapWallet.end()) {
     754       97607 :             int depth = GetTxDepthInMainChain(mit->second);
     755       97607 :             if (depth > 0  || (depth == 0 && !mit->second.isAbandoned()))
     756       97254 :                 return true; // Spent
     757         353 :         }
     758         353 :     }
     759     1226266 :     return false;
     760     1323520 : }
     761             : 
     762       27963 : void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch)
     763             : {
     764       27963 :     mapTxSpends.insert(std::make_pair(outpoint, wtxid));
     765       27963 :     setWalletUTXO.erase(outpoint);
     766             : 
     767       27963 :     if (batch) {
     768       20213 :         UnlockCoin(outpoint, batch);
     769       20213 :     } else {
     770        7750 :         WalletBatch temp_batch(GetDatabase());
     771        7750 :         UnlockCoin(outpoint, &temp_batch);
     772        7750 :     }
     773             : 
     774       27963 :     std::pair<TxSpends::iterator, TxSpends::iterator> range;
     775       27963 :     range = mapTxSpends.equal_range(outpoint);
     776       27963 :     SyncMetaData(range);
     777       27963 : }
     778             : 
     779             : 
     780      113725 : void CWallet::AddToSpends(const CWalletTx& wtx, WalletBatch* batch)
     781             : {
     782      113725 :     if (wtx.IsCoinBase()) // Coinbases don't spend anything!
     783       96763 :         return;
     784             : 
     785       44925 :     for (const CTxIn& txin : wtx.tx->vin)
     786       27963 :         AddToSpends(txin.prevout, wtx.GetHash(), batch);
     787      113725 : }
     788             : 
     789          84 : bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
     790             : {
     791          84 :     if (IsCrypted())
     792           0 :         return false;
     793             : 
     794          84 :     CKeyingMaterial _vMasterKey;
     795             : 
     796          84 :     _vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
     797          84 :     GetStrongRandBytes(_vMasterKey);
     798             : 
     799          84 :     CMasterKey kMasterKey;
     800             : 
     801          84 :     kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
     802          84 :     GetStrongRandBytes(kMasterKey.vchSalt);
     803             : 
     804          84 :     CCrypter crypter;
     805          84 :     constexpr MillisecondsDouble target{100};
     806          84 :     auto start{SteadyClock::now()};
     807          84 :     crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
     808          84 :     kMasterKey.nDeriveIterations = static_cast<unsigned int>(25000 * target / (SteadyClock::now() - start));
     809             : 
     810          84 :     start = SteadyClock::now();
     811          84 :     crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
     812          84 :     kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * target / (SteadyClock::now() - start))) / 2;
     813             : 
     814          84 :     if (kMasterKey.nDeriveIterations < 25000)
     815           0 :         kMasterKey.nDeriveIterations = 25000;
     816             : 
     817          84 :     WalletLogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
     818             : 
     819          84 :     if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
     820           0 :         return false;
     821          84 :     if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey))
     822           0 :         return false;
     823             : 
     824             :     {
     825          84 :         LOCK(cs_wallet);
     826             : 
     827          84 :         if (IsCrypted()) {
     828             :             // verify again now that cs_wallet lock is held
     829           0 :             return false;
     830             :         }
     831             : 
     832          84 :         mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
     833          84 :         WalletBatch* encrypted_batch = new WalletBatch(GetDatabase());
     834          84 :         if (!encrypted_batch->TxnBegin()) {
     835           0 :             delete encrypted_batch;
     836           0 :             encrypted_batch = nullptr;
     837           0 :             return false;
     838             :         }
     839          84 :         encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
     840             : 
     841         223 :         for (const auto& spk_man_pair : m_spk_managers) {
     842         139 :             auto spk_man = spk_man_pair.second.get();
     843             : 
     844         139 :             if (!spk_man->Encrypt(_vMasterKey, encrypted_batch)) {
     845           0 :                 encrypted_batch->TxnAbort();
     846           0 :                 delete encrypted_batch;
     847           0 :                 encrypted_batch = nullptr;
     848             :                 // We now probably have half of our keys encrypted in memory, and half not...
     849             :                 // die and let the user reload the unencrypted wallet.
     850           0 :                 assert(false);
     851             :             }
     852             :         }
     853             : 
     854             : 
     855             :         // Encryption was introduced in version 0.4.0
     856          84 :         SetMinVersion(FEATURE_WALLETCRYPT, encrypted_batch);
     857             : 
     858          84 :         if (!encrypted_batch->TxnCommit()) {
     859           0 :             delete encrypted_batch;
     860           0 :             encrypted_batch = nullptr;
     861             :             // We now have keys encrypted in memory, but not on disk...
     862             :             // die to avoid confusion and let the user reload the unencrypted wallet.
     863           0 :             assert(false);
     864             :         }
     865             : 
     866          84 :         delete encrypted_batch;
     867          84 :         encrypted_batch = nullptr;
     868             : 
     869          84 :         Lock();
     870          84 :         Unlock(strWalletPassphrase);
     871             : 
     872             :         // If we are using descriptors, make new descriptors with a new seed
     873          84 :         if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
     874             :             // Do nothing for descriptor wallets (keep old seed / mnemonic)
     875          84 :         } else if (auto spk_man = GetLegacyScriptPubKeyMan()) {
     876             :             // if we are not using HD, generate new keypool
     877          49 :             if (spk_man->IsHDEnabled()) {
     878             :                 // NOTE: using internal master key which should be populated on Unlock()
     879          25 :                 if (!spk_man->CheckDecryptionKey(vMasterKey)) {
     880           0 :                     return false;
     881             :                 }
     882          25 :                 if (!spk_man->TopUp()) {
     883           0 :                     return false;
     884             :                 }
     885          25 :             }
     886             :             else {
     887          24 :                 spk_man->NewKeyPool();
     888             :             }
     889          49 :         }
     890             : 
     891          84 :         Lock();
     892             : 
     893             :         // Need to completely rewrite the wallet file; if we don't, bdb might keep
     894             :         // bits of the unencrypted private key in slack space in the database file.
     895          84 :         if (!GetDatabase().Rewrite()) {
     896           0 :             WalletLogPrintf("ERROR: Rewrite failed - wallet is in dangerous state!\n");
     897           0 :             return false;
     898             :         }
     899             :         // BDB seems to have a bad habit of writing old data into
     900             :         // slack space in .dat files; that is bad if the old data is
     901             :         // unencrypted private keys. So:
     902          84 :         GetDatabase().ReloadDbEnv();
     903             : 
     904          84 :     }
     905          84 :     NotifyStatusChanged(this);
     906             : 
     907          84 :     return true;
     908          84 : }
     909             : 
     910           0 : DBErrors CWallet::ReorderTransactions()
     911             : {
     912           0 :     LOCK(cs_wallet);
     913           0 :     WalletBatch batch(GetDatabase());
     914             : 
     915             :     // Old wallets didn't have any defined order for transactions
     916             :     // Probably a bad idea to change the output of this
     917             : 
     918             :     // First: get all CWalletTx into a sorted-by-time multimap.
     919             :     typedef std::multimap<int64_t, CWalletTx*> TxItems;
     920           0 :     TxItems txByTime;
     921             : 
     922           0 :     for (auto& entry : mapWallet)
     923             :     {
     924           0 :         CWalletTx* wtx = &entry.second;
     925           0 :         txByTime.insert(std::make_pair(wtx->nTimeReceived, wtx));
     926             :     }
     927             : 
     928           0 :     nOrderPosNext = 0;
     929           0 :     std::vector<int64_t> nOrderPosOffsets;
     930           0 :     for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)
     931             :     {
     932           0 :         CWalletTx *const pwtx = (*it).second;
     933           0 :         int64_t& nOrderPos = pwtx->nOrderPos;
     934             : 
     935           0 :         if (nOrderPos == -1)
     936             :         {
     937           0 :             nOrderPos = nOrderPosNext++;
     938           0 :             nOrderPosOffsets.push_back(nOrderPos);
     939             : 
     940           0 :             if (!batch.WriteTx(*pwtx))
     941           0 :                 return DBErrors::LOAD_FAIL;
     942           0 :         }
     943             :         else
     944             :         {
     945           0 :             int64_t nOrderPosOff = 0;
     946           0 :             for (const int64_t& nOffsetStart : nOrderPosOffsets)
     947             :             {
     948           0 :                 if (nOrderPos >= nOffsetStart)
     949           0 :                     ++nOrderPosOff;
     950             :             }
     951           0 :             nOrderPos += nOrderPosOff;
     952           0 :             nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
     953             : 
     954           0 :             if (!nOrderPosOff)
     955           0 :                 continue;
     956             : 
     957             :             // Since we're changing the order, write it back
     958           0 :             if (!batch.WriteTx(*pwtx))
     959           0 :                 return DBErrors::LOAD_FAIL;
     960             :         }
     961           0 :     }
     962           0 :     batch.WriteOrderPosNext(nOrderPosNext);
     963             : 
     964           0 :     return DBErrors::LOAD_OK;
     965           0 : }
     966             : 
     967       67355 : int64_t CWallet::IncOrderPosNext(WalletBatch* batch)
     968             : {
     969       67355 :     AssertLockHeld(cs_wallet);
     970       67355 :     int64_t nRet = nOrderPosNext++;
     971       67355 :     if (batch) {
     972       67355 :         batch->WriteOrderPosNext(nOrderPosNext);
     973       67355 :     } else {
     974           0 :         WalletBatch(GetDatabase()).WriteOrderPosNext(nOrderPosNext);
     975             :     }
     976       67355 :     return nRet;
     977           0 : }
     978             : 
     979         848 : void CWallet::MarkDirty()
     980             : {
     981             :     {
     982         848 :         LOCK(cs_wallet);
     983       18776 :         for (std::pair<const uint256, CWalletTx>& item : mapWallet)
     984       17928 :             item.second.MarkDirty();
     985         848 :     }
     986             : 
     987         848 :     fAnonymizableTallyCached = false;
     988         848 :     fAnonymizableTallyCachedNonDenom = false;
     989         848 : }
     990             : 
     991        6438 : void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
     992             : {
     993        6438 :     AssertLockHeld(cs_wallet);
     994        6438 :     const CWalletTx* srctx = GetWalletTx(hash);
     995        6438 :     if (!srctx) return;
     996             : 
     997        6164 :     CTxDestination dst;
     998        6164 :     if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) {
     999        6164 :         if (IsMine(dst)) {
    1000        4796 :             if (used != IsAddressPreviouslySpent(dst)) {
    1001          62 :                 if (used) {
    1002          62 :                     tx_destinations.insert(dst);
    1003          62 :                 }
    1004          62 :                 SetAddressPreviouslySpent(batch, dst, used);
    1005          62 :             }
    1006        4796 :         }
    1007        6164 :     }
    1008        6438 : }
    1009             : 
    1010        2250 : bool CWallet::IsSpentKey(const CScript& scriptPubKey) const
    1011             : {
    1012        2250 :     AssertLockHeld(cs_wallet);
    1013        2250 :     CTxDestination dest;
    1014        2250 :     if (!ExtractDestination(scriptPubKey, dest)) {
    1015           0 :         return false;
    1016             :     }
    1017        2250 :     if (IsAddressPreviouslySpent(dest)) {
    1018          36 :         return true;
    1019             :     }
    1020        2214 :     if (IsLegacy()) {
    1021        1120 :         LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
    1022        1120 :         assert(spk_man != nullptr);
    1023        2194 :         for (const auto& keyid : GetAffectedKeys(scriptPubKey, *spk_man)) {
    1024        1074 :             if (IsAddressPreviouslySpent(PKHash(keyid))) {
    1025           0 :                 return true;
    1026             :             }
    1027             :         }
    1028        1120 :     }
    1029        2214 :     return false;
    1030        2250 : }
    1031             : 
    1032      166824 : CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose, bool rescanning_old_block)
    1033             : {
    1034      166824 :     LOCK(cs_wallet);
    1035             : 
    1036      166824 :     WalletBatch batch(GetDatabase(), fFlushOnClose);
    1037             : 
    1038      166824 :     uint256 hash = tx->GetHash();
    1039             : 
    1040      109482 :     if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
    1041             :         // Mark used destinations
    1042        1800 :         std::set<CTxDestination> tx_destinations;
    1043             : 
    1044        8238 :         for (const CTxIn& txin : tx->vin) {
    1045        6438 :             const COutPoint& op = txin.prevout;
    1046        6438 :             SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations);
    1047             :         }
    1048             : 
    1049        1800 :         MarkDestinationsDirty(tx_destinations);
    1050        1800 :     }
    1051             : 
    1052             :     // Inserts only if not already there, returns tx inserted or tx found
    1053      109482 :     auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(tx, state));
    1054      109482 :     CWalletTx& wtx = (*ret.first).second;
    1055      166824 :     bool fInsertedNew = ret.second;
    1056      171985 :     bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew);
    1057      166824 :     std::set<COutPoint> candidates;
    1058      166824 :     if (fInsertedNew) {
    1059       67355 :         wtx.nTimeReceived = GetTime();
    1060       67355 :         wtx.nOrderPos = IncOrderPosNext(&batch);
    1061       67355 :         wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
    1062       67355 :         wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
    1063       67355 :         AddToSpends(wtx, &batch);
    1064       67355 :         candidates = AddWalletUTXOs(wtx.tx, /*ret_dups=*/true);
    1065       67355 :     }
    1066             : 
    1067      166824 :     if (!fInsertedNew)
    1068             :     {
    1069       42127 :         if (state.index() != wtx.m_state.index()) {
    1070       13331 :             wtx.m_state = state;
    1071       13331 :             fUpdated = true;
    1072       13331 :         } else {
    1073       28796 :             assert(TxStateSerializedIndex(wtx.m_state) == TxStateSerializedIndex(state));
    1074       28796 :             assert(TxStateSerializedBlockHash(wtx.m_state) == TxStateSerializedBlockHash(state));
    1075             :         }
    1076       42127 :         candidates = AddWalletUTXOs(wtx.tx, /*ret_dups=*/false);
    1077       42127 :         if (!candidates.empty()) fUpdated = true;
    1078       42127 :     }
    1079             : 
    1080      166824 :     LockProTxCoins(candidates, &batch);
    1081             : 
    1082      109482 :     if (fInsertedNew) {
    1083       67355 :         CheckAndLockDustOutputs(hash, batch);
    1084       67355 :     }
    1085             : 
    1086             :     //// debug print
    1087      109482 :     WalletLogPrintf("AddToWallet %s  %s%s %s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""), TxStateString(state));
    1088             : 
    1089             :     // Write to disk
    1090      109482 :     if (fInsertedNew || fUpdated)
    1091      109482 :         if (!batch.WriteTx(wtx))
    1092           1 :             return nullptr;
    1093             : 
    1094             :     // Break debit/credit balance caches:
    1095       80810 :     wtx.MarkDirty();
    1096             : 
    1097             :     // Notify UI of new or updated transaction
    1098      109481 :     NotifyTransactionChanged(hash, fInsertedNew ? CT_NEW : CT_UPDATED);
    1099             : 
    1100             : #if HAVE_SYSTEM
    1101             :     // notify an external script when a wallet transaction comes in or is updated
    1102      109481 :     std::string strCmd = m_args.GetArg("-walletnotify", "");
    1103             : 
    1104      109481 :     if (!strCmd.empty())
    1105             :     {
    1106          60 :         ReplaceAll(strCmd, "%s", hash.GetHex());
    1107          60 :         if (auto* conf = wtx.state<TxStateConfirmed>())
    1108             :         {
    1109          40 :             ReplaceAll(strCmd, "%b", conf->confirmed_block_hash.GetHex());
    1110          40 :             ReplaceAll(strCmd, "%h", ToString(conf->confirmed_block_height));
    1111          40 :         } else {
    1112          20 :             ReplaceAll(strCmd, "%b", "unconfirmed");
    1113          20 :             ReplaceAll(strCmd, "%h", "-1");
    1114             :         }
    1115             : #ifndef WIN32
    1116             :         // Substituting the wallet name isn't currently supported on windows
    1117             :         // because windows shell escaping has not been implemented yet:
    1118             :         // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875
    1119             :         // A few ways it could be implemented in the future are described in:
    1120             :         // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094
    1121          60 :         ReplaceAll(strCmd, "%w", ShellEscape(GetName()));
    1122             : #endif
    1123          60 :         std::thread t(runCommand, strCmd);
    1124          60 :         t.detach(); // thread runs free
    1125          60 :     }
    1126             : #endif
    1127             : 
    1128      109481 :     fAnonymizableTallyCached = false;
    1129      109481 :     fAnonymizableTallyCachedNonDenom = false;
    1130             : 
    1131      109481 :     return &wtx;
    1132      453534 : }
    1133             : 
    1134       46370 : bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx)
    1135             : {
    1136       46370 :     const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(nullptr, TxStateInactive{}));
    1137       46370 :     CWalletTx& wtx = ins.first->second;
    1138       46370 :     if (!fill_wtx(wtx, ins.second)) {
    1139           0 :         return false;
    1140             :     }
    1141             :     // If wallet doesn't have a chain (e.g when using dash-wallet tool),
    1142             :     // don't bother to update txn.
    1143       46370 :     if (HaveChain()) {
    1144             :         bool active;
    1145       92067 :         auto lookup_block = [&](const uint256& hash, int& height, TxState& state) {
    1146             :             // If tx block (or conflicting block) was reorged out of chain
    1147             :             // while the wallet was shutdown, change tx status to UNCONFIRMED
    1148             :             // and reset block height, hash, and index. ABANDONED tx don't have
    1149             :             // associated blocks and don't need to be updated. The case where a
    1150             :             // transaction was reorged out while online and then reconfirmed
    1151             :             // while offline is covered by the rescan logic.
    1152       45773 :             if (!chain().findBlock(hash, FoundBlock().inActiveChain(active).height(height)) || !active) {
    1153        1619 :                 state = TxStateInactive{};
    1154        1619 :             }
    1155       45773 :         };
    1156       46294 :         if (auto* conf = wtx.state<TxStateConfirmed>()) {
    1157       45763 :             lookup_block(conf->confirmed_block_hash, conf->confirmed_block_height, wtx.m_state);
    1158       46294 :         } else if (auto* conf = wtx.state<TxStateConflicted>()) {
    1159          10 :             lookup_block(conf->conflicting_block_hash, conf->conflicting_block_height, wtx.m_state);
    1160          10 :         }
    1161       46294 :     }
    1162       46370 :     if (/* insertion took place */ ins.second) {
    1163       46370 :         wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
    1164       46370 :     }
    1165       46370 :     AddToSpends(wtx);
    1166       95359 :     for (const CTxIn& txin : wtx.tx->vin) {
    1167       48989 :         auto it = mapWallet.find(txin.prevout.hash);
    1168       48989 :         if (it != mapWallet.end()) {
    1169        4336 :             CWalletTx& prevtx = it->second;
    1170        4336 :             if (auto* prev = prevtx.state<TxStateConflicted>()) {
    1171           0 :                 MarkConflicted(prev->conflicting_block_hash, prev->conflicting_block_height, wtx.GetHash());
    1172           0 :             }
    1173        4336 :         }
    1174             :     }
    1175       46370 :     return true;
    1176       46370 : }
    1177             : 
    1178      196635 : std::set<COutPoint> CWallet::AddWalletUTXOs(CTransactionRef tx, bool ret_dups)
    1179             : {
    1180      196635 :     AssertLockHeld(cs_wallet);
    1181      196635 :     std::set<COutPoint> ret;
    1182      196635 :     uint256 hash{tx->GetHash()};
    1183      647629 :     for (size_t idx = 0; idx < tx->vout.size(); ++idx) {
    1184      450994 :         COutPoint outpoint(hash, idx);
    1185      450994 :         if (IsMine(tx->vout[idx]) && !IsSpent(outpoint)) {
    1186      280421 :             if (auto [_, inserted] = setWalletUTXO.emplace(outpoint); inserted || ret_dups) {
    1187      230483 :                 ret.emplace(outpoint);
    1188      230483 :             }
    1189      280421 :         }
    1190      450994 :     }
    1191      196635 :     return ret;
    1192      196635 : }
    1193             : 
    1194      340691 : bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxState& state, WalletBatch& batch, bool fUpdate, bool rescanning_old_block)
    1195             : {
    1196      340691 :     const CTransaction& tx = *ptx;
    1197             :     {
    1198      340691 :         AssertLockHeld(cs_wallet);
    1199             : 
    1200      340691 :         if (auto* conf = std::get_if<TxStateConfirmed>(&state)) {
    1201      605367 :             for (const CTxIn& txin : tx.vin) {
    1202      281492 :                 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
    1203      298294 :                 while (range.first != range.second) {
    1204       16802 :                     if (range.first->second != tx.GetHash()) {
    1205         117 :                         WalletLogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), conf->confirmed_block_hash.ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);
    1206         117 :                         MarkConflicted(conf->confirmed_block_hash, conf->confirmed_block_height, range.first->second);
    1207         117 :                     }
    1208       16802 :                     range.first++;
    1209             :                 }
    1210             :             }
    1211      323875 :         }
    1212             : 
    1213      340691 :         bool fExisted = mapWallet.count(tx.GetHash()) != 0;
    1214      340691 :         if (fExisted && !fUpdate) return false;
    1215      340590 :         if (fExisted || IsMine(tx) || IsFromMe(tx))
    1216             :         {
    1217             :             /* Check if any keys in the wallet keypool that were supposed to be unused
    1218             :              * have appeared in a new transaction. If so, remove those keys from the keypool.
    1219             :              * This can happen when restoring an old wallet backup that does not contain
    1220             :              * the mostly recently created transactions from newer versions of the wallet.
    1221             :              */
    1222             : 
    1223      104304 :             std::optional<int64_t> block_time;
    1224      104304 :             if (auto* conf = std::get_if<TxStateConfirmed>(&state)) {
    1225             :                 int64_t block_time_tmp;
    1226       93070 :                 bool found_block = chain().findBlock(conf->confirmed_block_hash, FoundBlock().maxTime(block_time_tmp));
    1227       93070 :                 assert(found_block);
    1228       93070 :                 block_time = block_time_tmp;
    1229       93070 :             }
    1230             :             // loop though all outputs
    1231      413838 :             for (const CTxOut& txout: tx.vout) {
    1232      494392 :                 for (const auto& spk_man : GetScriptPubKeyMans(txout.scriptPubKey)) {
    1233      204129 :                     for (auto &dest : spk_man->MarkUnusedAddresses(batch, txout.scriptPubKey, block_time)) {
    1234             :                         // If internal flag is not defined try to infer it from the ScriptPubKeyMan
    1235       19271 :                         if (!dest.internal.has_value()) {
    1236       18774 :                             dest.internal = IsInternalScriptPubKeyMan(spk_man);
    1237       18774 :                         }
    1238             : 
    1239             :                         // skip if can't determine whether it's a receiving address or not
    1240       19271 :                         if (!dest.internal.has_value()) continue;
    1241             : 
    1242             :                         // If this is a receiving address and it's not in the address book yet
    1243             :                         // (e.g. it wasn't generated on this node or we're restoring from backup)
    1244             :                         // add it to the address book for proper transaction accounting
    1245        8216 :                         if (!*dest.internal && !FindAddressBookEntry(dest.dest, /* allow_change= */ false)) {
    1246        4376 :                             SetAddressBook(dest.dest, "", "receive");
    1247        4376 :                         }
    1248             :                     }
    1249             :                 }
    1250             :             }
    1251             : 
    1252             :             // Block disconnection override an abandoned tx as unconfirmed
    1253             :             // which means user may have to call abandontransaction again
    1254      208608 :             TxState tx_state = std::visit([](auto&& s) -> TxState { return s; }, state);
    1255      104304 :             CWalletTx* wtx = AddToWallet(MakeTransactionRef(tx), tx_state, /*update_wtx=*/nullptr, /*fFlushOnClose=*/false, rescanning_old_block);
    1256      104304 :             if (!wtx) {
    1257             :                 // Can only be nullptr if there was a db write error (missing db, read-only db or a db engine internal writing error).
    1258             :                 // As we only store arriving transaction in this process, and we don't want an inconsistent state, let's throw an error.
    1259           1 :                 throw std::runtime_error("DB error adding transaction to wallet, write failed");
    1260             :             }
    1261      104303 :             return true;
    1262             :         }
    1263             :     }
    1264      236286 :     return false;
    1265      340690 : }
    1266             : 
    1267           0 : bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
    1268             : {
    1269           0 :     LOCK(cs_wallet);
    1270           0 :     const CWalletTx* wtx = GetWalletTx(hashTx);
    1271           0 :     return wtx && !wtx->isAbandoned() && GetTxDepthInMainChain(*wtx) == 0 && !wtx->InMempool();
    1272           0 : }
    1273             : 
    1274           0 : bool CWallet::TransactionCanBeResent(const uint256& hashTx) const
    1275             : {
    1276           0 :     LOCK(cs_wallet);
    1277           0 :     const CWalletTx* wtx = GetWalletTx(hashTx);
    1278           0 :     return wtx && CanTxBeResent(*wtx);
    1279           0 : }
    1280             : 
    1281      104483 : void CWallet::MarkInputsDirty(const CTransactionRef& tx)
    1282             : {
    1283      223949 :     for (const CTxIn& txin : tx->vin) {
    1284      119466 :         auto it = mapWallet.find(txin.prevout.hash);
    1285      119466 :         if (it != mapWallet.end()) {
    1286       34744 :             it->second.MarkDirty();
    1287       34744 :         }
    1288             :     }
    1289      104483 : }
    1290             : 
    1291          16 : bool CWallet::AbandonTransaction(const uint256& hashTx)
    1292             : {
    1293          16 :     LOCK(cs_wallet);
    1294             : 
    1295             :     // Can't mark abandoned if confirmed or in mempool
    1296          16 :     auto it = mapWallet.find(hashTx);
    1297          16 :     assert(it != mapWallet.end());
    1298          16 :     const CWalletTx& origtx = it->second;
    1299          16 :     if (GetTxDepthInMainChain(origtx) != 0 || origtx.InMempool() || IsTxLockedByInstantSend(origtx)) {
    1300           4 :         return false;
    1301             :     }
    1302             : 
    1303          32 :     auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1304             :         // If the orig tx was not in block/mempool, none of its spends can be.
    1305          20 :         assert(!wtx.isConfirmed());
    1306          20 :         assert(!wtx.InMempool());
    1307             :         // If already conflicted or abandoned, no need to set abandoned
    1308          20 :         if (!wtx.isConflicted() && !wtx.isAbandoned()) {
    1309          20 :             wtx.m_state = TxStateInactive{/*abandoned=*/true};
    1310          20 :             return TxUpdate::NOTIFY_CHANGED;
    1311             :         }
    1312           0 :         return TxUpdate::UNCHANGED;
    1313          20 :     };
    1314             : 
    1315             :     // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
    1316             : 
    1317          12 :     RecursiveUpdateTxState(hashTx, try_updating_state);
    1318             : 
    1319          12 :     fAnonymizableTallyCached = false;
    1320          12 :     fAnonymizableTallyCachedNonDenom = false;
    1321             : 
    1322          12 :     return true;
    1323          16 : }
    1324             : 
    1325           0 : bool CWallet::ResendTransaction(const uint256& hashTx)
    1326             : {
    1327           0 :     LOCK(cs_wallet);
    1328             : 
    1329           0 :     auto it = mapWallet.find(hashTx);
    1330           0 :     assert(it != mapWallet.end());
    1331           0 :     CWalletTx& wtx = it->second;
    1332             : 
    1333           0 :     bilingual_str unused_err_string;
    1334           0 :     return SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, true);
    1335           0 : }
    1336             : 
    1337         117 : void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx)
    1338             : {
    1339         117 :     LOCK(cs_wallet);
    1340             : 
    1341         117 :     int conflictconfirms = (m_last_block_processed_height - conflicting_height + 1) * -1;
    1342             :     // If number of conflict confirms cannot be determined, this means
    1343             :     // that the block is still unknown or not yet part of the main chain,
    1344             :     // for example when loading the wallet during a reindex. Do nothing in that
    1345             :     // case.
    1346         117 :     if (conflictconfirms >= 0)
    1347           0 :         return;
    1348             : 
    1349         252 :     auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1350         135 :         if (conflictconfirms < GetTxDepthInMainChain(wtx)) {
    1351             :             // Block is 'more conflicted' than current confirm; update.
    1352             :             // Mark transaction as conflicted with this block.
    1353         123 :             wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
    1354         123 :             return TxUpdate::CHANGED;
    1355             :         }
    1356          12 :         return TxUpdate::UNCHANGED;
    1357         135 :     };
    1358             : 
    1359             :     // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too.
    1360         117 :     RecursiveUpdateTxState(hashTx, try_updating_state);
    1361             : 
    1362         117 : }
    1363             : 
    1364         158 : void CWallet::RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1365             :     // Do not flush the wallet here for performance reasons
    1366         158 :     WalletBatch batch(GetDatabase(), false);
    1367             : 
    1368         158 :     std::set<uint256> todo;
    1369         158 :     std::set<uint256> done;
    1370             : 
    1371         158 :     todo.insert(tx_hash);
    1372             : 
    1373         354 :     while (!todo.empty()) {
    1374         196 :         uint256 now = *todo.begin();
    1375         196 :         todo.erase(now);
    1376         196 :         done.insert(now);
    1377         196 :         auto it = mapWallet.find(now);
    1378         196 :         assert(it != mapWallet.end());
    1379         196 :         CWalletTx& wtx = it->second;
    1380             : 
    1381         196 :         TxUpdate update_state = try_updating_state(wtx);
    1382         196 :         if (update_state != TxUpdate::UNCHANGED) {
    1383         180 :             wtx.MarkDirty();
    1384         180 :             batch.WriteTx(wtx);
    1385             :             // Iterate over all its outputs, and update those tx states as well (if applicable)
    1386         506 :             for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
    1387         326 :                 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
    1388         364 :                 for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
    1389          38 :                     if (!done.count(iter->second)) {
    1390          38 :                         todo.insert(iter->second);
    1391          38 :                     }
    1392          38 :                 }
    1393         326 :             }
    1394             : 
    1395         180 :             if (update_state == TxUpdate::NOTIFY_CHANGED) {
    1396          20 :                 NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
    1397          20 :             }
    1398             : 
    1399             :             // If a transaction changes its tx state, that usually changes the balance
    1400             :             // available of the outputs it spends. So force those to be recomputed
    1401         180 :             MarkInputsDirty(wtx.tx);
    1402         180 :         }
    1403             :     }
    1404             : 
    1405         158 :     fAnonymizableTallyCached = false;
    1406         158 :     fAnonymizableTallyCachedNonDenom = false;
    1407         158 : }
    1408             : 
    1409      340690 : void CWallet::SyncTransaction(const CTransactionRef& ptx, const SyncTxState& state, WalletBatch& batch, bool update_tx, bool rescanning_old_block)
    1410             : {
    1411      340690 :     if (!AddToWalletIfInvolvingMe(ptx, state, batch, update_tx, rescanning_old_block))
    1412      236387 :         return; // Not one of ours
    1413             : 
    1414             :     // If a transaction changes 'conflicted' state, that changes the balance
    1415             :     // available of the outputs it spends. So force those to be
    1416             :     // recomputed, also:
    1417      104303 :     MarkInputsDirty(ptx);
    1418             : 
    1419      104303 :     fAnonymizableTallyCached = false;
    1420      104303 :     fAnonymizableTallyCachedNonDenom = false;
    1421      340690 : }
    1422             : 
    1423       11657 : void CWallet::transactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) {
    1424       11657 :     LOCK(cs_wallet);
    1425       11657 :     WalletBatch batch(GetDatabase());
    1426       11657 :     SyncTransaction(tx, TxStateInMempool{}, batch);
    1427             : 
    1428       11656 :     auto it = mapWallet.find(tx->GetHash());
    1429       11656 :     if (it != mapWallet.end()) {
    1430        8457 :         RefreshMempoolStatus(it->second, chain());
    1431        8457 :     }
    1432       11657 : }
    1433             : 
    1434      172441 : void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
    1435      172441 :     if (reason != MemPoolRemovalReason::CONFLICT) {
    1436      172376 :         LOCK(cs_wallet);
    1437      172376 :         auto it = mapWallet.find(tx->GetHash());
    1438      172376 :         if (it != mapWallet.end()) {
    1439       58598 :             RefreshMempoolStatus(it->second, chain());
    1440       58598 :         }
    1441      172376 :     }
    1442             :     // Handle transactions that were removed from the mempool because they
    1443             :     // conflict with transactions in a newly connected block.
    1444      172441 :     if (reason == MemPoolRemovalReason::CONFLICT) {
    1445             :         // Trigger external -walletnotify notifications for these transactions.
    1446             :         // Set Status::UNCONFIRMED instead of Status::CONFLICTED for a few reasons:
    1447             :         //
    1448             :         // 1. The transactionRemovedFromMempool callback does not currently
    1449             :         //    provide the conflicting block's hash and height, and for backwards
    1450             :         //    compatibility reasons it may not be not safe to store conflicted
    1451             :         //    wallet transactions with a null block hash. See
    1452             :         //    https://github.com/bitcoin/bitcoin/pull/18600#discussion_r420195993.
    1453             :         // 2. For most of these transactions, the wallet's internal conflict
    1454             :         //    detection in the blockConnected handler will subsequently call
    1455             :         //    MarkConflicted and update them with CONFLICTED status anyway. This
    1456             :         //    applies to any wallet transaction that has inputs spent in the
    1457             :         //    block, or that has ancestors in the wallet with inputs spent by
    1458             :         //    the block.
    1459             :         // 3. Longstanding behavior since the sync implementation in
    1460             :         //    https://github.com/bitcoin/bitcoin/pull/9371 and the prior sync
    1461             :         //    implementation before that was to mark these transactions
    1462             :         //    unconfirmed rather than conflicted.
    1463             :         //
    1464             :         // Nothing described above should be seen as an unchangeable requirement
    1465             :         // when improving this code in the future. The wallet's heuristics for
    1466             :         // distinguishing between conflicted and unconfirmed transactions are
    1467             :         // imperfect, and could be improved in general, see
    1468             :         // https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking
    1469          65 :         LOCK(cs_wallet);
    1470          65 :         WalletBatch batch(GetDatabase());
    1471          65 :         SyncTransaction(tx, TxStateInactive{}, batch);
    1472          65 :     }
    1473      172441 : }
    1474             : 
    1475      105056 : void CWallet::blockConnected(const CBlock& block, int height)
    1476             : {
    1477      105056 :     const uint256& block_hash = block.GetHash();
    1478      105056 :     LOCK(cs_wallet);
    1479             : 
    1480      105056 :     m_last_block_processed_height = height;
    1481      105056 :     m_last_block_processed = block_hash;
    1482      105056 :     WalletBatch batch(GetDatabase());
    1483      277406 :     for (size_t index = 0; index < block.vtx.size(); index++) {
    1484      172350 :         SyncTransaction(block.vtx[index], TxStateConfirmed{block_hash, height, static_cast<int>(index)}, batch);
    1485      172350 :         transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK);
    1486      172350 :     }
    1487             : 
    1488             :     // reset cache to make sure no longer immature coins are included
    1489      105056 :     fAnonymizableTallyCached = false;
    1490      105056 :     fAnonymizableTallyCachedNonDenom = false;
    1491      105056 : }
    1492             : 
    1493        3039 : void CWallet::blockDisconnected(const CBlock& block, int height)
    1494             : {
    1495        3039 :     LOCK(cs_wallet);
    1496             : 
    1497             :     // At block disconnection, this will change an abandoned transaction to
    1498             :     // be unconfirmed, whether or not the transaction is added back to the mempool.
    1499             :     // User may have to call abandontransaction again. It may be addressed in the
    1500             :     // future with a stickier abandoned state or even removing abandontransaction call.
    1501        3039 :     m_last_block_processed_height = height - 1;
    1502        3039 :     m_last_block_processed = block.hashPrevBlock;
    1503             : 
    1504        3039 :     int disconnect_height = height;
    1505             : 
    1506        3039 :     WalletBatch batch(GetDatabase());
    1507        8133 :     for (const CTransactionRef& ptx : block.vtx) {
    1508        5094 :         SyncTransaction(ptx, TxStateInactive{}, batch);
    1509             : 
    1510        8628 :         for (const CTxIn& tx_in : ptx->vin) {
    1511             :             // No other wallet transactions conflicted with this transaction
    1512        3534 :             if (mapTxSpends.count(tx_in.prevout) < 1) continue;
    1513             : 
    1514         309 :             std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(tx_in.prevout);
    1515             : 
    1516             :             // For all of the spends that conflict with this transaction
    1517         651 :             for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) {
    1518         342 :                 CWalletTx& wtx = mapWallet.find(_it->second)->second;
    1519             : 
    1520         342 :                 if (!wtx.isConflicted()) continue;
    1521             : 
    1522          70 :                 auto try_updating_state = [&](CWalletTx& tx) {
    1523          41 :                     if (!tx.isConflicted()) return TxUpdate::UNCHANGED;
    1524          41 :                     if (tx.state<TxStateConflicted>()->conflicting_block_height >= disconnect_height) {
    1525          37 :                         tx.m_state = TxStateInactive{};
    1526          37 :                         return TxUpdate::CHANGED;
    1527             :                     }
    1528           4 :                     return TxUpdate::UNCHANGED;
    1529          41 :                 };
    1530             : 
    1531          29 :                 RecursiveUpdateTxState(wtx.tx->GetHash(), try_updating_state);
    1532          29 :             }
    1533             :         }
    1534             :     }
    1535             : 
    1536             :     // reset cache to make sure no longer mature coins are excluded
    1537        3039 :     fAnonymizableTallyCached = false;
    1538        3039 :     fAnonymizableTallyCachedNonDenom = false;
    1539        3039 : }
    1540             : 
    1541      104472 : void CWallet::updatedBlockTip()
    1542             : {
    1543      104472 :     m_best_block_time = GetTime();
    1544      104472 : }
    1545             : 
    1546       18559 : void CWallet::BlockUntilSyncedToCurrentChain() const {
    1547       18559 :     AssertLockNotHeld(cs_wallet);
    1548             :     // Skip the queue-draining stuff if we know we're caught up with
    1549             :     // chain().Tip(), otherwise put a callback in the validation interface queue and wait
    1550             :     // for the queue to drain enough to execute it (indicating we are caught up
    1551             :     // at least with the time we entered this function).
    1552       37118 :     uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
    1553       18559 :     chain().waitForNotificationsIfTipChanged(last_block_hash);
    1554       18559 : }
    1555             : 
    1556             : // Note that this function doesn't distinguish between a 0-valued input,
    1557             : // and a not-"is mine" (according to the filter) input.
    1558      250482 : CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
    1559             : {
    1560             :     {
    1561      250482 :         LOCK(cs_wallet);
    1562      250482 :         const auto mi = mapWallet.find(txin.prevout.hash);
    1563      250481 :         if (mi != mapWallet.end())
    1564             :         {
    1565       32231 :             const CWalletTx& prev = (*mi).second;
    1566       32231 :             if (txin.prevout.n < prev.tx->vout.size())
    1567       32231 :                 if (IsMine(prev.tx->vout[txin.prevout.n]) & filter)
    1568       21937 :                     return prev.tx->vout[txin.prevout.n].nValue;
    1569       10294 :         }
    1570      250484 :     }
    1571      228545 :     return 0;
    1572      250484 : }
    1573             : 
    1574     2245436 : isminetype CWallet::IsMine(const CTxOut& txout) const
    1575             : {
    1576     2245436 :     AssertLockHeld(cs_wallet);
    1577     2245436 :     return IsMine(txout.scriptPubKey);
    1578             : }
    1579             : 
    1580       51644 : isminetype CWallet::IsMine(const CTxDestination& dest) const
    1581             : {
    1582       51644 :     AssertLockHeld(cs_wallet);
    1583       51644 :     return IsMine(GetScriptForDestination(dest));
    1584           0 : }
    1585             : 
    1586     2314179 : isminetype CWallet::IsMine(const CScript& script) const
    1587             : {
    1588     2314179 :     AssertLockHeld(cs_wallet);
    1589     2314179 :     isminetype result = ISMINE_NO;
    1590     7120444 :     for (const auto& spk_man_pair : m_spk_managers) {
    1591     4806265 :         result = std::max(result, spk_man_pair.second->IsMine(script));
    1592             :     }
    1593     2314179 :     return result;
    1594             : }
    1595             : 
    1596      298544 : bool CWallet::IsMine(const CTransaction& tx) const
    1597             : {
    1598      298544 :     AssertLockHeld(cs_wallet);
    1599      685543 :     for (const CTxOut& txout : tx.vout)
    1600      448664 :         if (IsMine(txout))
    1601       61665 :             return true;
    1602      236879 :     return false;
    1603      298544 : }
    1604             : 
    1605         685 : isminetype CWallet::IsMine(const COutPoint& outpoint) const
    1606             : {
    1607         685 :     AssertLockHeld(cs_wallet);
    1608         685 :     auto wtx = GetWalletTx(outpoint.hash);
    1609         685 :     if (!wtx) {
    1610         143 :         return ISMINE_NO;
    1611             :     }
    1612         542 :     if (outpoint.n >= wtx->tx->vout.size()) {
    1613           0 :         return ISMINE_NO;
    1614             :     }
    1615         542 :     return IsMine(wtx->tx->vout[outpoint.n]);
    1616         685 : }
    1617             : 
    1618      236875 : bool CWallet::IsFromMe(const CTransaction& tx) const
    1619             : {
    1620      236875 :     return (GetDebit(tx, ISMINE_ALL) > 0);
    1621             : }
    1622             : 
    1623      285518 : CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const
    1624             : {
    1625      285518 :     CAmount nDebit = 0;
    1626      535948 :     for (const CTxIn& txin : tx.vin)
    1627             :     {
    1628      250430 :         nDebit += GetDebit(txin, filter);
    1629      250430 :         if (!MoneyRange(nDebit))
    1630           0 :             throw std::runtime_error(std::string(__func__) + ": value out of range");
    1631             :     }
    1632      285518 :     return nDebit;
    1633           0 : }
    1634             : 
    1635        3540 : bool CWallet::IsHDEnabled() const
    1636             : {
    1637             :     // All Active ScriptPubKeyMans must be HD for this to be true
    1638        3540 :     bool result = false;
    1639        7290 :     for (const auto& spk_man : GetActiveScriptPubKeyMans()) {
    1640        3750 :         if (!spk_man->IsHDEnabled()) return false;
    1641        2788 :         result = true;
    1642             :     }
    1643        2578 :     return result;
    1644        3540 : }
    1645             : 
    1646       23749 : bool CWallet::CanGetAddresses(bool internal) const
    1647             : {
    1648       23749 :     LOCK(cs_wallet);
    1649       23749 :     if (m_spk_managers.empty()) return false;
    1650       23713 :     auto spk_man = GetScriptPubKeyMan(internal);
    1651       23713 :     if (spk_man && spk_man->CanGetAddresses(internal)) {
    1652       23621 :         return true;
    1653             :     }
    1654          92 :     return false;
    1655       23749 : }
    1656             : 
    1657         660 : void CWallet::SetWalletFlag(uint64_t flags)
    1658             : {
    1659         660 :     LOCK(cs_wallet);
    1660         660 :     m_wallet_flags |= flags;
    1661         660 :     if (!WalletBatch(GetDatabase()).WriteWalletFlags(m_wallet_flags))
    1662           0 :         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
    1663         660 : }
    1664             : 
    1665           4 : void CWallet::UnsetWalletFlag(uint64_t flag)
    1666             : {
    1667           4 :     LOCK(cs_wallet);
    1668           4 :     WalletBatch batch(GetDatabase());
    1669           4 :     UnsetWalletFlagWithDB(batch, flag);
    1670           4 : }
    1671             : 
    1672       67610 : void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag)
    1673             : {
    1674       67610 :     LOCK(cs_wallet);
    1675       67610 :     m_wallet_flags &= ~flag;
    1676       67610 :     if (!batch.WriteWalletFlags(m_wallet_flags))
    1677           0 :         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
    1678       67610 : }
    1679             : 
    1680       67606 : void CWallet::UnsetBlankWalletFlag(WalletBatch& batch)
    1681             : {
    1682       67606 :     UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
    1683       67606 : }
    1684             : 
    1685         660 : void CWallet::NewKeyPoolCallback()
    1686             : {
    1687             :     // Note: GetClient(*this) can return nullptr when this wallet is in the middle of its creation.
    1688             :     // Skipping stopMixing() is fine in this case.
    1689         747 :     if (std::unique_ptr<interfaces::CoinJoin::Client> coinjoin_client = coinjoin_available() ? coinjoin_loader().GetClient(GetName()) : nullptr) {
    1690          87 :         coinjoin_client->stopMixing();
    1691          87 :     }
    1692         660 :     nKeysLeftSinceAutoBackup = 0;
    1693         660 : }
    1694             : 
    1695       19980 : void CWallet::KeepDestinationCallback(bool erased)
    1696             : {
    1697       19980 :     if (erased) --nKeysLeftSinceAutoBackup;
    1698       19980 :     if (!nWalletBackups) nKeysLeftSinceAutoBackup = 0;
    1699       19980 : }
    1700             : 
    1701      458981 : bool CWallet::IsWalletFlagSet(uint64_t flag) const
    1702             : {
    1703      458981 :     return (m_wallet_flags & flag);
    1704             : }
    1705             : 
    1706        2266 : bool CWallet::LoadWalletFlags(uint64_t flags)
    1707             : {
    1708        2266 :     LOCK(cs_wallet);
    1709        2266 :     if (((flags & KNOWN_WALLET_FLAGS) >> 32) ^ (flags >> 32)) {
    1710             :         // contains unknown non-tolerable wallet flags
    1711           0 :         return false;
    1712             :     }
    1713        2266 :     m_wallet_flags = flags;
    1714             : 
    1715        2266 :     return true;
    1716        2266 : }
    1717             : 
    1718        1183 : void CWallet::InitWalletFlags(uint64_t flags)
    1719             : {
    1720        1183 :     LOCK(cs_wallet);
    1721             : 
    1722             :     // We should never be writing unknown non-tolerable wallet flags
    1723        1183 :     assert(((flags & KNOWN_WALLET_FLAGS) >> 32) == (flags >> 32));
    1724             :     // This should only be used once, when creating a new wallet - so current flags are expected to be blank
    1725        1183 :     assert(m_wallet_flags == 0);
    1726             : 
    1727        1183 :     if (!WalletBatch(GetDatabase()).WriteWalletFlags(flags)) {
    1728           0 :         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
    1729             :     }
    1730             : 
    1731        1183 :     if (!LoadWalletFlags(flags)) assert(false);
    1732        1183 : }
    1733             : 
    1734             : // Helper for producing a max-sized low-S low-R signature (eg 71 bytes)
    1735             : // or a max-sized low-S signature (e.g. 72 bytes) depending on coin_control
    1736      644400 : bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, const CCoinControl* coin_control)
    1737             : {
    1738             :     // Fill in dummy signatures for fee calculation.
    1739      644400 :     const CScript& scriptPubKey = txout.scriptPubKey;
    1740      644400 :     SignatureData sigdata;
    1741             : 
    1742             :     // Use max sig if watch only inputs were used or if this particular input is an external input
    1743             :     // to ensure a sufficient fee is attained for the requested feerate.
    1744     1228778 :     const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(tx_in.prevout));
    1745      644400 :     if (!ProduceSignature(provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
    1746         893 :         return false;
    1747             :     }
    1748      643507 :     UpdateInput(tx_in, sigdata);
    1749      643507 :     return true;
    1750      644400 : }
    1751             : 
    1752         187 : bool FillInputToWeight(CTxIn& txin, int64_t target_weight)
    1753             : {
    1754         187 :     assert(txin.scriptSig.empty());
    1755             : 
    1756         187 :     int64_t txin_weight = ::GetSerializeSize(txin);
    1757             : 
    1758             :     // Do nothing if the weight that should be added is less than the weight that already exists
    1759         187 :     if (target_weight < txin_weight) {
    1760           3 :         return false;
    1761             :     }
    1762         184 :     if (target_weight == txin_weight) {
    1763           1 :         return true;
    1764             :     }
    1765             : 
    1766             :     // Subtract current txin weight, which should include empty witness stack
    1767         183 :     int64_t add_weight = target_weight - txin_weight;
    1768         183 :     assert(add_weight > 0);
    1769             : 
    1770         183 :     add_weight -= (GetSizeOfCompactSize(add_weight) - 1);
    1771         183 :     txin.scriptSig.insert(txin.scriptSig.end(), add_weight, 0);
    1772             : 
    1773             :     /*
    1774             :     // We will want to subtract the size of the Compact Size UInt that will also be serialized.
    1775             :     // However doing so when the size is near a boundary can result in a problem where it is not
    1776             :     // possible to have a stack element size and combination to exactly equal a target.
    1777             :     // So we allow to fullfill transaction 2 byte less in this situation.
    1778             :     */
    1779         183 :     assert(::GetSerializeSize(txin) <= static_cast<size_t>(target_weight));
    1780         183 :     assert(::GetSerializeSize(txin) >= static_cast<size_t>(target_weight) - 2);
    1781             : 
    1782         183 :     return true;
    1783         187 : }
    1784             : 
    1785             : // Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
    1786       14461 : bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const
    1787             : {
    1788             :     // Fill in dummy signatures for fee calculation.
    1789       14461 :     int nIn = 0;
    1790       75149 :     for (const auto& txout : txouts)
    1791             :     {
    1792       60688 :         CTxIn& txin = txNew.vin[nIn];
    1793             :         // If weight was provided, fill the input to that weight
    1794       60688 :         if (coin_control && coin_control->HasInputWeight(txin.prevout)) {
    1795         174 :             if (!FillInputToWeight(txin, coin_control->GetInputWeight(txin.prevout))) {
    1796           0 :                 return false;
    1797             :             }
    1798         174 :             nIn++;
    1799         174 :             continue;
    1800             :         }
    1801       60514 :         const std::unique_ptr<SigningProvider> provider = GetSolvingProvider(txout.scriptPubKey);
    1802       60514 :         if (!provider || !DummySignInput(*provider, txin, txout, coin_control)) {
    1803          68 :             if (!coin_control || !DummySignInput(coin_control->m_external_provider, txin, txout, coin_control)) {
    1804           0 :                 return false;
    1805             :             }
    1806          68 :         }
    1807             : 
    1808       60514 :         nIn++;
    1809       60514 :     }
    1810       14461 :     return true;
    1811       14461 : }
    1812             : 
    1813         216 : bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp)
    1814             : {
    1815         216 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1816         216 :     if (!spk_man) {
    1817           0 :         return false;
    1818             :     }
    1819         216 :     LOCK(spk_man->cs_KeyStore);
    1820         216 :     return spk_man->ImportScripts(scripts, timestamp);
    1821         216 : }
    1822             : 
    1823        3186 : bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
    1824             : {
    1825        3186 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1826        3186 :     if (!spk_man) {
    1827           0 :         return false;
    1828             :     }
    1829        3186 :     LOCK(spk_man->cs_KeyStore);
    1830        3186 :     return spk_man->ImportPrivKeys(privkey_map, timestamp);
    1831        3186 : }
    1832             : 
    1833         244 : bool CWallet::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp)
    1834             : {
    1835         244 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1836         244 :     if (!spk_man) {
    1837           0 :         return false;
    1838             :     }
    1839         244 :     LOCK(spk_man->cs_KeyStore);
    1840         244 :     return spk_man->ImportPubKeys(ordered_pubkeys, pubkey_map, key_origins, add_keypool, internal, timestamp);
    1841         244 : }
    1842             : 
    1843         323 : bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp)
    1844             : {
    1845         323 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1846         323 :     if (!spk_man) {
    1847           0 :         return false;
    1848             :     }
    1849         323 :     LOCK(spk_man->cs_KeyStore);
    1850         323 :     if (!spk_man->ImportScriptPubKeys(script_pub_keys, have_solving_data, timestamp)) {
    1851           0 :         return false;
    1852             :     }
    1853         323 :     if (apply_label) {
    1854         295 :         WalletBatch batch(GetDatabase());
    1855         606 :         for (const CScript& script : script_pub_keys) {
    1856         311 :             CTxDestination dest;
    1857         311 :             ExtractDestination(script, dest);
    1858         311 :             if (IsValidDestination(dest)) {
    1859         303 :                 SetAddressBookWithDB(batch, dest, label, "receive");
    1860         303 :             }
    1861             :         }
    1862         295 :     }
    1863         323 :     return true;
    1864         323 : }
    1865             : 
    1866             : /**
    1867             :  * Scan active chain for relevant transactions after importing keys. This should
    1868             :  * be called whenever new keys are added to the wallet, with the oldest key
    1869             :  * creation time.
    1870             :  *
    1871             :  * @return Earliest timestamp that could be successfully scanned from. Timestamp
    1872             :  * returned will be higher than startTime if relevant blocks could not be read.
    1873             :  */
    1874        1102 : int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update)
    1875             : {
    1876             :     // Find starting block. May be null if nCreateTime is greater than the
    1877             :     // highest blockchain timestamp, in which case there is nothing that needs
    1878             :     // to be scanned.
    1879        1102 :     int start_height = 0;
    1880        1102 :     uint256 start_block;
    1881        1102 :     bool start = chain().findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, FoundBlock().hash(start_block).height(start_height));
    1882        2204 :     WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - start_height + 1 : 0);
    1883             : 
    1884        1102 :     if (start) {
    1885             :         // TODO: this should take into account failure by ScanResult::USER_ABORT
    1886        1102 :         ScanResult result = ScanForWalletTransactions(start_block, start_height, /*max_height=*/{}, reserver, /*fUpdate=*/update, /*save_progress=*/false);
    1887        1102 :         if (result.status == ScanResult::FAILURE) {
    1888             :             int64_t time_max;
    1889           1 :             CHECK_NONFATAL(chain().findBlock(result.last_failed_block, FoundBlock().maxTime(time_max)));
    1890           1 :             return time_max + TIMESTAMP_WINDOW + 1;
    1891             :         }
    1892        1101 :     }
    1893        1101 :     return startTime;
    1894        1102 : }
    1895             : 
    1896             : /**
    1897             :  * Scan the block chain (starting in start_block) for transactions
    1898             :  * from or to us. If fUpdate is true, found transactions that already
    1899             :  * exist in the wallet will be updated. If max_height is not set, the
    1900             :  * mempool will be scanned as well.
    1901             :  *
    1902             :  * @param[in] start_block Scan starting block. If block is not on the active
    1903             :  *                        chain, the scan will return SUCCESS immediately.
    1904             :  * @param[in] start_height Height of start_block
    1905             :  * @param[in] max_height  Optional max scanning height. If unset there is
    1906             :  *                        no maximum and scanning can continue to the tip
    1907             :  *
    1908             :  * @return ScanResult returning scan information and indicating success or
    1909             :  *         failure. Return status will be set to SUCCESS if scan was
    1910             :  *         successful. FAILURE if a complete rescan was not possible (due to
    1911             :  *         pruning or corruption). USER_ABORT if the rescan was aborted before
    1912             :  *         it could complete.
    1913             :  *
    1914             :  * @pre Caller needs to make sure start_block (and the optional stop_block) are on
    1915             :  * the main chain after to the addition of any new keys you want to detect
    1916             :  * transactions for.
    1917             :  */
    1918        1498 : CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate, const bool save_progress)
    1919             : {
    1920        1498 :     constexpr auto INTERVAL_TIME{60s};
    1921        1498 :     auto current_time{reserver.now()};
    1922        1498 :     auto start_time{reserver.now()};
    1923             : 
    1924        1498 :     assert(reserver.isReserved());
    1925             : 
    1926        1498 :     uint256 block_hash = start_block;
    1927        1498 :     ScanResult result;
    1928             : 
    1929        1498 :     std::unique_ptr<FastWalletRescanFilter> fast_rescan_filter;
    1930        1498 :     if (!IsLegacy() && chain().hasBlockFilterIndex(BlockFilterType::BASIC_FILTER)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this);
    1931             : 
    1932        1498 :     WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(),
    1933        1498 :                     fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks");
    1934             : 
    1935        1498 :     ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
    1936        2996 :     uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
    1937        1498 :     uint256 end_hash = tip_hash;
    1938        1498 :     if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash));
    1939        1498 :     double progress_begin = chain().guessVerificationProgress(block_hash);
    1940        1498 :     double progress_end = chain().guessVerificationProgress(end_hash);
    1941        1498 :     double progress_current = progress_begin;
    1942        1498 :     int block_height = start_height;
    1943        1498 :     WalletBatch batch(GetDatabase());
    1944      139333 :     while (!fAbortRescan && !chain().shutdownRequested()) {
    1945      139333 :         if (progress_end - progress_begin > 0.0) {
    1946           1 :             m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin);
    1947           1 :         } else { // avoid divide-by-zero for single block scan range (i.e. start and stop hashes are equal)
    1948      139332 :             m_scanning_progress = 0;
    1949             :         }
    1950      139333 :         if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
    1951           1 :             ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
    1952           1 :         }
    1953             : 
    1954      139333 :         bool next_interval = reserver.now() >= current_time + INTERVAL_TIME;
    1955      139333 :         if (next_interval) {
    1956           2 :             current_time = reserver.now();
    1957           2 :             WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", block_height, progress_current);
    1958           2 :         }
    1959             : 
    1960      139333 :         bool fetch_block{true};
    1961      139333 :         if (fast_rescan_filter) {
    1962         828 :             fast_rescan_filter->UpdateIfNeeded();
    1963         828 :             auto matches_block{fast_rescan_filter->MatchesBlock(block_hash)};
    1964         828 :             if (matches_block.has_value()) {
    1965         828 :                 if (*matches_block) {
    1966         124 :                     LogPrint(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (filter matched)\n", block_height, block_hash.ToString());
    1967         124 :                 } else {
    1968         704 :                     result.last_scanned_block = block_hash;
    1969         704 :                     result.last_scanned_height = block_height;
    1970         704 :                     fetch_block = false;
    1971             :                 }
    1972         828 :             } else {
    1973           0 :                 LogPrint(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (WARNING: block filter not found!)\n", block_height, block_hash.ToString());
    1974             :             }
    1975         828 :         }
    1976             : 
    1977             :         // Find next block separately from reading data above, because reading
    1978             :         // is slow and there might be a reorg while it is read.
    1979      139333 :         bool block_still_active = false;
    1980      139333 :         bool next_block = false;
    1981      139333 :         uint256 next_block_hash;
    1982      139333 :         chain().findBlock(block_hash, FoundBlock().inActiveChain(block_still_active).nextBlock(FoundBlock().inActiveChain(next_block).hash(next_block_hash)));
    1983             : 
    1984      139333 :         if (fetch_block) {
    1985             :             // Read block data
    1986      138629 :             CBlock block;
    1987      138629 :             chain().findBlock(block_hash, FoundBlock().data(block));
    1988             : 
    1989      138629 :             if (!block.IsNull()) {
    1990      138524 :                 LOCK(cs_wallet);
    1991      138524 :                 if (!block_still_active) {
    1992             :                     // Abort scan if current block is no longer active, to prevent
    1993             :                     // marking transactions as coming from the wrong block.
    1994           0 :                     result.last_failed_block = block_hash;
    1995           0 :                     result.status = ScanResult::FAILURE;
    1996           0 :                     break;
    1997             :                 }
    1998      290049 :                 for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
    1999      151525 :                     SyncTransaction(block.vtx[posInBlock], TxStateConfirmed{block_hash, block_height, static_cast<int>(posInBlock)}, batch, fUpdate, /*rescanning_old_block=*/true);
    2000      151525 :                 }
    2001             :                 // scan succeeded, record block as most recent successfully scanned
    2002      138524 :                 result.last_scanned_block = block_hash;
    2003      138524 :                 result.last_scanned_height = block_height;
    2004             : 
    2005      138524 :                 if (save_progress && next_interval) {
    2006           2 :                     CBlockLocator loc = m_chain->getActiveChainLocator(block_hash);
    2007             : 
    2008           2 :                     if (!loc.IsNull()) {
    2009           2 :                         WalletLogPrintf("Saving scan progress %d.\n", block_height);
    2010           2 :                         WalletBatch batch(GetDatabase());
    2011           2 :                         batch.WriteBestBlock(loc);
    2012           2 :                     }
    2013           2 :                 }
    2014      138524 :             } else {
    2015             :                 // could not scan block, keep scanning but record this block as the most recent failure
    2016         105 :                 result.last_failed_block = block_hash;
    2017         105 :                 result.status = ScanResult::FAILURE;
    2018             :             }
    2019      138629 :         }
    2020      139333 :         if (max_height && block_height >= *max_height) {
    2021           6 :             break;
    2022             :         }
    2023             :         {
    2024      139327 :             if (!next_block) {
    2025             :                 // break successfully when rescan has reached the tip, or
    2026             :                 // previous block is no longer on the chain due to a reorg
    2027        1492 :                 break;
    2028             :             }
    2029             : 
    2030             :             // increment block and verification progress
    2031      137835 :             block_hash = next_block_hash;
    2032      137835 :             ++block_height;
    2033      137835 :             progress_current = chain().guessVerificationProgress(block_hash);
    2034             : 
    2035             :             // handle updated tip hash
    2036      137835 :             const uint256 prev_tip_hash = tip_hash;
    2037      275670 :             tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
    2038      137835 :             if (!max_height && prev_tip_hash != tip_hash) {
    2039             :                 // in case the tip has changed, update progress max
    2040           0 :                 progress_end = chain().guessVerificationProgress(tip_hash);
    2041           0 :             }
    2042             :         }
    2043             :     }
    2044        1498 :     if (!max_height) {
    2045        1492 :         WalletLogPrintf("Scanning current mempool transactions.\n");
    2046        2984 :         WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this));
    2047        1492 :     }
    2048        1498 :     ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 100); // hide progress dialog in GUI
    2049        1498 :     if (block_height && fAbortRescan) {
    2050           0 :         WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height, progress_current);
    2051           0 :         result.status = ScanResult::USER_ABORT;
    2052        1498 :     } else if (block_height && chain().shutdownRequested()) {
    2053           0 :         WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height, progress_current);
    2054           0 :         result.status = ScanResult::USER_ABORT;
    2055           0 :     } else {
    2056        1498 :         WalletLogPrintf("Rescan completed in %15dms\n", Ticks<std::chrono::milliseconds>(reserver.now() - start_time));
    2057             :     }
    2058             :     return result;
    2059        1498 : }
    2060             : 
    2061        2789 : void CWallet::ReacceptWalletTransactions()
    2062             : {
    2063             :     // If transactions aren't being broadcasted, don't let them into local mempool either
    2064        2789 :     if (!fBroadcastTransactions)
    2065          19 :         return;
    2066        2770 :     std::map<int64_t, CWalletTx*> mapSorted;
    2067             : 
    2068             :     // Sort pending wallet transactions based on their initial wallet insertion order
    2069       65819 :     for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
    2070       63049 :         const uint256& wtxid = item.first;
    2071       63049 :         CWalletTx& wtx = item.second;
    2072       63049 :         assert(wtx.GetHash() == wtxid);
    2073             : 
    2074       63049 :         int nDepth = GetTxDepthInMainChain(wtx);
    2075             : 
    2076       63049 :         if (!wtx.IsCoinBase() && (nDepth == 0 && !IsTxLockedByInstantSend(wtx) && !wtx.isAbandoned())) {
    2077         636 :             mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
    2078         636 :         }
    2079             :     }
    2080             : 
    2081             :     // Try to add wallet transactions to memory pool
    2082        3406 :     for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
    2083         636 :         CWalletTx& wtx = *(item.second);
    2084         636 :         bilingual_str unused_err_string;
    2085         636 :         SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, false);
    2086         636 :     }
    2087        2789 : }
    2088             : 
    2089        9890 : bool CWallet::CanTxBeResent(const CWalletTx& wtx) const
    2090             : {
    2091        9890 :     AssertLockHeld(cs_wallet);
    2092             : 
    2093        9890 :     return
    2094             :         // Can't relay if wallet is not broadcasting
    2095        9890 :         GetBroadcastTransactions() &&
    2096             :         // Don't relay abandoned transactions
    2097        9890 :         !wtx.isAbandoned() &&
    2098             :         // Don't try to submit coinbase transactions. These would fail anyway but would
    2099             :         // cause log spam.
    2100        9890 :         !wtx.IsCoinBase() &&
    2101             :         // Don't try to submit conflicted or confirmed transactions.
    2102        6139 :         GetTxDepthInMainChain(wtx) == 0 &&
    2103             :         // Don't try to submit transactions locked via InstantSend.
    2104        5770 :         !IsTxLockedByInstantSend(wtx);
    2105             : }
    2106             : 
    2107        9890 : bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, bilingual_str& err_string, bool relay) const
    2108             : {
    2109        9890 :     AssertLockHeld(cs_wallet);
    2110             : 
    2111        9890 :     if (!CanTxBeResent(wtx)) return false;
    2112             : 
    2113             :     // Submit transaction to mempool for relay
    2114        5770 :     WalletLogPrintf("Submitting wtx %s to mempool for relay\n", wtx.GetHash().ToString());
    2115             :     // We must set TxStateInMempool here. Even though it will also be set later by the
    2116             :     // entered-mempool callback, if we did not there would be a race where a
    2117             :     // user could call sendmoney in a loop and hit spurious out of funds errors
    2118             :     // because we think that this newly generated transaction's change is
    2119             :     // unavailable as we're not yet aware that it is in the mempool.
    2120             :     //
    2121             :     // If broadcast fails for any reason, trying to set wtx.m_state here would be incorrect.
    2122             :     // If transaction was previously in the mempool, it should be updated when
    2123             :     // TransactionRemovedFromMempool fires.
    2124        5770 :     bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, relay, err_string);
    2125        5770 :     if (ret) wtx.m_state = TxStateInMempool{};
    2126        5770 :     return ret;
    2127        9890 : }
    2128             : 
    2129        5383 : std::set<uint256> CWallet::GetTxConflicts(const CWalletTx& wtx) const
    2130             : {
    2131        5383 :     AssertLockHeld(cs_wallet);
    2132             : 
    2133        5383 :     const uint256 myHash{wtx.GetHash()};
    2134        5383 :     std::set<uint256> result{GetConflicts(myHash)};
    2135        5383 :     result.erase(myHash);
    2136        5383 :     return result;
    2137        5383 : }
    2138             : 
    2139             : // Rebroadcast transactions from the wallet. We do this on a random timer
    2140             : // to slightly obfuscate which transactions come from our wallet.
    2141             : //
    2142             : // Ideally, we'd only resend transactions that we think should have been
    2143             : // mined in the most recent block. Any transaction that wasn't in the top
    2144             : // blockweight of transactions in the mempool shouldn't have been mined,
    2145             : // and so is probably just sitting in the mempool waiting to be confirmed.
    2146             : // Rebroadcasting does nothing to speed up confirmation and only damages
    2147             : // privacy.
    2148        1017 : void CWallet::ResendWalletTransactions()
    2149             : {
    2150             :     // During reindex, importing and IBD, old wallet transactions become
    2151             :     // unconfirmed. Don't resend them as that would spam other nodes.
    2152        1017 :     if (!chain().isReadyToBroadcast()) return;
    2153             : 
    2154             :     // Do this infrequently and randomly to avoid giving away
    2155             :     // that these are our transactions.
    2156        1017 :     if (GetTime() < m_next_resend || !fBroadcastTransactions) return;
    2157         293 :     bool fFirst = (m_next_resend == 0);
    2158             :     // resend 1-3 hours from now, ~2 hours on average.
    2159         293 :     m_next_resend = GetTime() + (1 * 60 * 60) + GetRand(2 * 60 * 60);
    2160         293 :     if (fFirst) return;
    2161             : 
    2162          21 :     int submitted_tx_count = 0;
    2163             : 
    2164             :     { // cs_wallet scope
    2165          21 :         LOCK(cs_wallet);
    2166             : 
    2167             :         // Relay transactions
    2168        5301 :         for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
    2169        5280 :             CWalletTx& wtx = item.second;
    2170             :             // Attempt to rebroadcast all txes more than 5 minutes older than
    2171             :             // the last block. SubmitTxMemoryPoolAndRelay() will not rebroadcast
    2172             :             // any confirmed or conflicting txs.
    2173        5280 :             if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
    2174        4128 :             bilingual_str unused_err_string;
    2175        4128 :             if (SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, true)) ++submitted_tx_count;
    2176        4128 :         }
    2177          21 :     } // cs_wallet
    2178             : 
    2179          21 :     if (submitted_tx_count > 0) {
    2180           5 :         WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, submitted_tx_count);
    2181           5 :     }
    2182        1017 : }
    2183             : 
    2184             : /** @} */ // end of mapWallet
    2185             : 
    2186         961 : void MaybeResendWalletTxs(WalletContext& context)
    2187             : {
    2188        1978 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
    2189        1017 :         pwallet->ResendWalletTransactions();
    2190             :     }
    2191         961 : }
    2192             : 
    2193             : 
    2194             : /** @defgroup Actions
    2195             :  *
    2196             :  * @{
    2197             :  */
    2198             : 
    2199       21411 : std::unordered_set<const CWalletTx*, WalletTxHasher> CWallet::GetSpendableTXs() const
    2200             : {
    2201       21411 :     AssertLockHeld(cs_wallet);
    2202             : 
    2203       21411 :     std::unordered_set<const CWalletTx*, WalletTxHasher> ret;
    2204     1910883 :     for (auto it = setWalletUTXO.begin(); it != setWalletUTXO.end(); ) {
    2205     1889472 :         const auto& outpoint = *it;
    2206     1889472 :         const auto jt = mapWallet.find(outpoint.hash);
    2207     1889472 :         if (jt != mapWallet.end()) {
    2208     1889266 :             ret.emplace(&jt->second);
    2209     1889266 :         }
    2210             : 
    2211             :         // setWalletUTXO is sorted by COutPoint, which means that all UTXOs for the same TX are neighbors
    2212             :         // skip entries until we encounter a new TX
    2213     7991343 :         while (it != setWalletUTXO.end() && it->hash == outpoint.hash) {
    2214     2116159 :             ++it;
    2215             :         }
    2216             :     }
    2217       21411 :     return ret;
    2218       21411 : }
    2219             : 
    2220       11782 : bool CWallet::SignTransaction(CMutableTransaction& tx) const
    2221             : {
    2222       11782 :     AssertLockHeld(cs_wallet);
    2223             : 
    2224             :     // Build coins map
    2225       11782 :     std::map<COutPoint, Coin> coins;
    2226       57899 :     for (auto& input : tx.vin) {
    2227       46117 :         const auto mi = mapWallet.find(input.prevout.hash);
    2228       46117 :         if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
    2229           0 :             return false;
    2230             :         }
    2231       46117 :         const CWalletTx& wtx = mi->second;
    2232       46117 :         int prev_height = wtx.state<TxStateConfirmed>() ? wtx.state<TxStateConfirmed>()->confirmed_block_height : 0;
    2233       46117 :         coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], prev_height, wtx.IsCoinBase());
    2234             :     }
    2235       11782 :     std::map<int, bilingual_str> input_errors;
    2236       11782 :     return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
    2237       11782 : }
    2238             : 
    2239       13424 : bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const
    2240             : {
    2241             :     // Try to sign with all ScriptPubKeyMans
    2242       33572 :     for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
    2243             :         // spk_man->SignTransaction will return true if the transaction is complete,
    2244             :         // so we can exit early and return true if that happens
    2245       20148 :         if (spk_man->SignTransaction(tx, coins, sighash, input_errors)) {
    2246       13376 :             return true;
    2247             :         }
    2248             :     }
    2249             : 
    2250             :     // At this point, one input was not fully signed otherwise we would have exited already
    2251          48 :     return false;
    2252       13424 : }
    2253             : 
    2254         956 : TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed, bool finalize) const
    2255             : {
    2256         956 :     if (n_signed) {
    2257           0 :         *n_signed = 0;
    2258           0 :     }
    2259         956 :     const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
    2260         956 :     LOCK(cs_wallet);
    2261             :     // Get all of the previous transactions
    2262        2676 :     for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
    2263        1720 :         const CTxIn& txin = psbtx.tx->vin[i];
    2264        1720 :         PSBTInput& input = psbtx.inputs.at(i);
    2265             : 
    2266        1720 :         if (PSBTInputSigned(input)) {
    2267          44 :             continue;
    2268             :         }
    2269             : 
    2270             :         // If we have no utxo, grab it from the wallet.
    2271        1676 :         if (!input.non_witness_utxo) {
    2272        1004 :             const uint256& txhash = txin.prevout.hash;
    2273        1004 :             const auto it = mapWallet.find(txhash);
    2274        1004 :             if (it != mapWallet.end()) {
    2275         878 :                 const CWalletTx& wtx = it->second;
    2276             :                 // We only need the non_witness_utxo, which is a superset of the witness_utxo.
    2277             :                 //   The signing code will switch to the smaller witness_utxo if this is ok.
    2278         878 :                 input.non_witness_utxo = wtx.tx;
    2279         878 :             }
    2280        1004 :         }
    2281        1676 :     }
    2282             : 
    2283             :     // Fill in information from ScriptPubKeyMans
    2284        3021 :     for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
    2285        2065 :         int n_signed_this_spkm = 0;
    2286        2065 :         TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm, finalize);
    2287        2065 :         if (res != TransactionError::OK) {
    2288           5 :             return res;
    2289             :         }
    2290             : 
    2291        2060 :         if (n_signed) {
    2292           0 :             (*n_signed) += n_signed_this_spkm;
    2293           0 :         }
    2294             :     }
    2295             : 
    2296         951 :     RemoveUnnecessaryTransactions(psbtx, sighash_type);
    2297             : 
    2298             :     // Complete if every input is now signed
    2299         951 :     complete = true;
    2300        2661 :     for (const auto& input : psbtx.inputs) {
    2301        1710 :         complete &= PSBTInputSigned(input);
    2302             :     }
    2303             : 
    2304         951 :     return TransactionError::OK;
    2305         956 : }
    2306             : 
    2307         428 : SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
    2308             : {
    2309         428 :     SignatureData sigdata;
    2310         428 :     CScript script_pub_key = GetScriptForDestination(pkhash);
    2311         559 :     for (const auto& spk_man_pair : m_spk_managers) {
    2312         559 :         if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {
    2313         428 :             LOCK(cs_wallet);  // DescriptorScriptPubKeyMan calls IsLocked which can lock cs_wallet in a deadlocking order
    2314         428 :             return spk_man_pair.second->SignMessage(message, pkhash, str_sig);
    2315         428 :         }
    2316             :     }
    2317           0 :     return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
    2318         428 : }
    2319             : 
    2320           8 : bool CWallet::SignSpecialTxPayload(const uint256& hash, const CKeyID& keyid, std::vector<unsigned char>& vchSig) const
    2321             : {
    2322           8 :     SignatureData sigdata;
    2323           8 :     CScript script_pub_key = GetScriptForDestination(PKHash(keyid));
    2324          10 :     for (const auto& spk_man_pair : m_spk_managers) {
    2325          10 :         if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {
    2326           8 :             LOCK(cs_wallet);  // DescriptorScriptPubKeyMan calls IsLocked which can lock cs_wallet in a deadlocking order
    2327           8 :             return spk_man_pair.second->SignSpecialTxPayload(hash, keyid, vchSig);
    2328           8 :         }
    2329             :     }
    2330           0 :     return false;
    2331           8 : }
    2332             : 
    2333         120 : bool CWallet::SignGovernanceVote(const CKeyID& keyID, CGovernanceVote& vote) const
    2334             : {
    2335             :     // Special implementation for testnet (Harden Spork6 that has not been deployed to other networks)
    2336         120 :     if (Params().NetworkIDString() == CBaseChainParams::TESTNET) {
    2337           0 :         std::vector<unsigned char> signature;
    2338           0 :         if (!SignSpecialTxPayload(vote.GetSignatureHash(), keyID, signature)) {
    2339           0 :             LogPrintf("SignGovernanceVote -- SignHash() failed\n");
    2340           0 :             return false;
    2341             :         }
    2342           0 :         vote.SetSignature(signature);
    2343           0 :         return true;
    2344           0 :     } // end of testnet implementation
    2345             : 
    2346         120 :     std::string strMessage{vote.GetSignatureString()};
    2347         120 :     std::string signature;
    2348         120 :     SigningResult err = SignMessage(strMessage, PKHash{keyID}, signature);
    2349         120 :     if (err != SigningResult::OK) {
    2350           0 :         LogPrintf("SignGovernanceVote failed due to: %s\n", SigningResultString(err));
    2351           0 :         return false;
    2352             :     }
    2353         120 :     const auto opt_decoded = DecodeBase64(signature);
    2354         120 :     CHECK_NONFATAL(opt_decoded.has_value()); // DecodeBase64 should not fail
    2355             : 
    2356         120 :     vote.SetSignature(std::vector<unsigned char>(opt_decoded->data(), opt_decoded->data() + opt_decoded->size()));
    2357         120 :     return true;
    2358         120 : }
    2359             : 
    2360        5155 : void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm)
    2361             : {
    2362        5155 :     LOCK(cs_wallet);
    2363        5155 :     WalletLogPrintf("CommitTransaction:\n%s", tx->ToString()); /* Continued */
    2364             : 
    2365             :     // Add tx to wallet, because if it has change it's also ours,
    2366             :     // otherwise just for transaction history.
    2367       10310 :     CWalletTx* wtx = AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {
    2368        5155 :         CHECK_NONFATAL(wtx.mapValue.empty());
    2369        5155 :         CHECK_NONFATAL(wtx.vOrderForm.empty());
    2370        5155 :         wtx.mapValue = std::move(mapValue);
    2371        5155 :         wtx.vOrderForm = std::move(orderForm);
    2372        5155 :         wtx.fTimeReceivedIsTxTime = true;
    2373        5155 :         wtx.fFromMe = true;
    2374        5155 :         return true;
    2375             :     });
    2376             : 
    2377             :     // wtx can only be null if the db write failed.
    2378        5155 :     if (!wtx) {
    2379           0 :         throw std::runtime_error(std::string(__func__) + ": Wallet db error, transaction commit failed");
    2380             :     }
    2381             : 
    2382             :     // Notify that old coins are spent
    2383        5155 :     std::set<uint256> updated_hahes;
    2384       15432 :     for (const CTxIn& txin : tx->vin){
    2385             :         // notify only once
    2386       10277 :         if(updated_hahes.find(txin.prevout.hash) != updated_hahes.end()) continue;
    2387             : 
    2388        9397 :         CWalletTx &coin = mapWallet.at(txin.prevout.hash);
    2389        9397 :         coin.MarkDirty();
    2390        9397 :         NotifyTransactionChanged(txin.prevout.hash, CT_UPDATED);
    2391        9397 :         updated_hahes.insert(txin.prevout.hash);
    2392             :     }
    2393             : 
    2394        5155 :     if (!fBroadcastTransactions) {
    2395             :         // Don't submit tx to the mempool
    2396          29 :         return;
    2397             :     }
    2398             : 
    2399        5126 :     bilingual_str err_string;
    2400        5126 :     if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, true)) {
    2401          14 :         WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string.original);
    2402             :         // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
    2403          14 :     }
    2404        5155 : }
    2405             : 
    2406        2346 : DBErrors CWallet::LoadWallet()
    2407             : {
    2408        2346 :     LOCK(cs_wallet);
    2409             : 
    2410        2346 :     DBErrors nLoadWalletRet = WalletBatch(GetDatabase()).LoadWallet(this);
    2411        2334 :     if (nLoadWalletRet == DBErrors::NEED_REWRITE)
    2412             :     {
    2413           0 :         if (GetDatabase().Rewrite("\x04pool"))
    2414             :         {
    2415           0 :             for (const auto& spk_man_pair : m_spk_managers) {
    2416           0 :                 spk_man_pair.second->RewriteDB();
    2417             :             }
    2418           0 :             nKeysLeftSinceAutoBackup = 0;
    2419           0 :         }
    2420           0 :     }
    2421             : 
    2422        2334 :     if (m_spk_managers.empty()) {
    2423        1254 :         assert(m_external_spk_managers == nullptr);
    2424        1254 :         assert(m_internal_spk_managers == nullptr);
    2425        1254 :     }
    2426             : 
    2427        2334 :     if (HaveChain()) {
    2428        2247 :         const std::optional<int> tip_height = chain().getHeight();
    2429        2247 :         if (tip_height) {
    2430        2217 :             SetLastBlockProcessed(*tip_height, chain().getBlockHash(*tip_height));
    2431       46903 :             for (auto& pair : mapWallet) {
    2432      108106 :                 for(unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
    2433       63420 :                     COutPoint outpoint(pair.first, i);
    2434       63420 :                     if (IsMine(pair.second.tx->vout[i]) && !IsSpent(outpoint)) {
    2435       48007 :                         setWalletUTXO.insert(outpoint);
    2436       48007 :                     }
    2437       63420 :                 }
    2438             :             }
    2439        2217 :         }
    2440        2247 :     }
    2441             : 
    2442        2334 :     if (nLoadWalletRet != DBErrors::LOAD_OK)
    2443           0 :         return nLoadWalletRet;
    2444             : 
    2445             :     /* If the CoinJoin salt is not set, try to set a new random hash as the salt */
    2446        2334 :     if (GetCoinJoinSalt().IsNull() && !SetCoinJoinSalt(GetRandHash())) {
    2447           0 :         return DBErrors::LOAD_FAIL;
    2448             :     }
    2449             : 
    2450        2334 :     return DBErrors::LOAD_OK;
    2451        2346 : }
    2452             : 
    2453             : // Goes through all wallet transactions and checks if they are masternode collaterals, in which case these are locked
    2454             : // This avoids accidental spending of collaterals. They can still be unlocked manually if a spend is really intended.
    2455        2734 : void CWallet::AutoLockMasternodeCollaterals()
    2456             : {
    2457        2734 :     std::set<COutPoint> candidates;
    2458        2734 :     LOCK(cs_wallet);
    2459       89887 :     for (const auto& [txid, wtx] : mapWallet) {
    2460       87153 :         auto tx_utxos{AddWalletUTXOs(wtx.tx, /*ret_dups=*/true)};
    2461       87153 :         candidates.insert(tx_utxos.begin(), tx_utxos.end());
    2462       87153 :     }
    2463        2734 :     WalletBatch batch(GetDatabase());
    2464        2734 :     LockProTxCoins(candidates, &batch);
    2465        2734 : }
    2466             : 
    2467          23 : DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
    2468             : {
    2469          23 :     AssertLockHeld(cs_wallet);
    2470             : 
    2471          23 :     WalletLogPrintf("ZapSelectTx started for %d transactions...\n", vHashIn.size());
    2472             : 
    2473          23 :     DBErrors nZapSelectTxRet = WalletBatch(GetDatabase()).ZapSelectTx(vHashIn, vHashOut);
    2474         250 :     for (const uint256& hash : vHashOut) {
    2475         227 :         const auto& it = mapWallet.find(hash);
    2476         227 :         wtxOrdered.erase(it->second.m_it_wtxOrdered);
    2477         454 :         for (const auto& txin : it->second.tx->vin)
    2478         227 :             mapTxSpends.erase(txin.prevout);
    2479         227 :         mapWallet.erase(it);
    2480         227 :         NotifyTransactionChanged(hash, CT_DELETED);
    2481             :     }
    2482             : 
    2483          23 :     if (nZapSelectTxRet == DBErrors::NEED_REWRITE)
    2484             :     {
    2485           0 :         if (GetDatabase().Rewrite("\x04pool"))
    2486             :         {
    2487           0 :             for (const auto& spk_man_pair : m_spk_managers) {
    2488           0 :                 spk_man_pair.second->RewriteDB();
    2489             :             }
    2490           0 :         }
    2491           0 :     }
    2492             : 
    2493          23 :     if (nZapSelectTxRet != DBErrors::LOAD_OK)
    2494           0 :         return nZapSelectTxRet;
    2495             : 
    2496          23 :     MarkDirty();
    2497             : 
    2498          23 :     WalletLogPrintf("ZapSelectTx completed for %d transactions.\n", vHashOut.size());
    2499          23 :     return DBErrors::LOAD_OK;
    2500          23 : }
    2501             : 
    2502       35102 : bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
    2503             : {
    2504       35102 :     bool fUpdated = false;
    2505             :     bool is_mine;
    2506             :     {
    2507       35102 :         LOCK(cs_wallet);
    2508       35102 :         std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
    2509       35102 :         fUpdated = (mi != m_address_book.end() && !mi->second.IsChange());
    2510       35102 :         m_address_book[address].SetLabel(strName);
    2511       35102 :         if (!strPurpose.empty()) /* update purpose only if requested */
    2512       35102 :             m_address_book[address].purpose = strPurpose;
    2513       35102 :         is_mine = IsMine(address) != ISMINE_NO;
    2514       35102 :     }
    2515       70204 :     NotifyAddressBookChanged(address, strName, is_mine,
    2516       35102 :                              strPurpose, (fUpdated ? CT_UPDATED : CT_NEW));
    2517       35102 :     if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
    2518           0 :         return false;
    2519       35102 :     return batch.WriteName(EncodeDestination(address), strName);
    2520       35102 : }
    2521             : 
    2522       34799 : bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
    2523             : {
    2524       34799 :     WalletBatch batch(GetDatabase());
    2525       34799 :     return SetAddressBookWithDB(batch, address, strName, strPurpose);
    2526       34799 : }
    2527             : 
    2528           6 : bool CWallet::DelAddressBook(const CTxDestination& address)
    2529             : {
    2530             :     bool is_mine;
    2531           6 :     WalletBatch batch(GetDatabase());
    2532             :     {
    2533           6 :         LOCK(cs_wallet);
    2534             :         // If we want to delete receiving addresses, we should avoid calling EraseAddressData because it will delete the previously_spent value. Could instead just erase the label so it becomes a change address, and keep the data.
    2535             :         // NOTE: This isn't a problem for sending addresses because they don't have any data that needs to be kept.
    2536             :         // When adding new address data, it should be considered here whether to retain or delete it.
    2537           6 :         if (IsMine(address)) {
    2538           0 :             WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
    2539           0 :             return false;
    2540             :         }
    2541             :         // Delete data rows associated with this address
    2542           6 :         batch.EraseAddressData(address);
    2543           6 :         m_address_book.erase(address);
    2544           6 :         is_mine = IsMine(address) != ISMINE_NO;
    2545           6 :     }
    2546             : 
    2547           6 :     NotifyAddressBookChanged(address, "", is_mine, "", CT_DELETED);
    2548             : 
    2549           6 :     batch.ErasePurpose(EncodeDestination(address));
    2550           6 :     return batch.EraseName(EncodeDestination(address));
    2551           6 : }
    2552             : 
    2553        6627 : size_t CWallet::KeypoolCountExternalKeys() const
    2554             : {
    2555        6627 :     AssertLockHeld(cs_wallet);
    2556             : 
    2557        6627 :     auto legacy_spk_man = GetLegacyScriptPubKeyMan();
    2558        6627 :     if (legacy_spk_man) {
    2559        3433 :         return legacy_spk_man->KeypoolCountExternalKeys();
    2560             :     }
    2561             : 
    2562        3194 :     unsigned int count = 0;
    2563        3194 :     if (m_external_spk_managers) {
    2564        1647 :         count += m_external_spk_managers->GetKeyPoolSize();
    2565        1647 :     }
    2566             : 
    2567        3194 :     return count;
    2568        6627 : }
    2569             : 
    2570        4374 : unsigned int CWallet::GetKeyPoolSize() const
    2571             : {
    2572        4374 :     AssertLockHeld(cs_wallet);
    2573             : 
    2574        4374 :     unsigned int count = 0;
    2575        9823 :     for (auto spk_man : GetActiveScriptPubKeyMans()) {
    2576        5449 :         count += spk_man->GetKeyPoolSize();
    2577             :     }
    2578        4374 :     return count;
    2579           0 : }
    2580             : 
    2581        2439 : bool CWallet::TopUpKeyPool(unsigned int kpSize)
    2582             : {
    2583        2439 :     LOCK(cs_wallet);
    2584        2439 :     bool res = true;
    2585        5442 :     for (auto spk_man : GetActiveScriptPubKeyMans()) {
    2586        3003 :         res &= spk_man->TopUp(kpSize);
    2587             :     }
    2588        2439 :     return res;
    2589        2439 : }
    2590             : 
    2591       28898 : util::Result<CTxDestination> CWallet::GetNewDestination(const std::string label)
    2592             : {
    2593       28898 :     LOCK(cs_wallet);
    2594       28898 :     auto spk_man = GetScriptPubKeyMan(false /* internal */);
    2595       28898 :     if (!spk_man) {
    2596           1 :         return util::Error{_("Error: No addresses available.")};
    2597             :     }
    2598             : 
    2599       28897 :     auto op_dest = spk_man->GetNewDestination();
    2600       28897 :     if (op_dest) {
    2601       28880 :         SetAddressBook(*op_dest, label, "receive");
    2602       28880 :     }
    2603             : 
    2604       28897 :     return op_dest;
    2605       57795 : }
    2606             : 
    2607         263 : util::Result<CTxDestination> CWallet::GetNewChangeDestination()
    2608             : {
    2609         263 :     LOCK(cs_wallet);
    2610             : 
    2611         263 :     ReserveDestination reservedest(this);
    2612         263 :     auto op_dest = reservedest.GetReservedDestination(true);
    2613         263 :     if (op_dest) reservedest.KeepDestination();
    2614             : 
    2615         263 :     return op_dest;
    2616         263 : }
    2617             : 
    2618        2072 : std::optional<int64_t> CWallet::GetOldestKeyPoolTime() const
    2619             : {
    2620        2072 :     LOCK(cs_wallet);
    2621        2072 :     if (m_spk_managers.empty()) {
    2622          45 :         return std::nullopt;
    2623             :     }
    2624             : 
    2625        2027 :     std::optional<int64_t> oldest_key{std::numeric_limits<int64_t>::max()};
    2626        5888 :     for (const auto& spk_man_pair : m_spk_managers) {
    2627        3861 :         oldest_key = std::min(oldest_key, spk_man_pair.second->GetOldestKeyPoolTime());
    2628             :     }
    2629        2027 :     return oldest_key;
    2630        2072 : }
    2631             : 
    2632        1800 : void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations) {
    2633      456039 :     for (auto& entry : mapWallet) {
    2634      454239 :         CWalletTx& wtx = entry.second;
    2635      454239 :         if (wtx.m_is_cache_empty) continue;
    2636        2322 :         for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
    2637        2135 :             CTxDestination dst;
    2638        2135 :             if (ExtractDestination(wtx.tx->vout[i].scriptPubKey, dst) && destinations.count(dst)) {
    2639        1858 :                 wtx.MarkDirty();
    2640        1858 :                 break;
    2641             :             }
    2642         277 :         }
    2643             :     }
    2644        1800 : }
    2645             : 
    2646         704 : void CWallet::ForEachAddrBookEntry(const ListAddrBookFunc& func) const
    2647             : {
    2648       10326 :     for (const std::pair<const CTxDestination, CAddressBookData>& item : m_address_book) {
    2649        9622 :         const auto& entry = item.second;
    2650        9622 :         func(item.first, entry.GetLabel(), entry.purpose, entry.IsChange());
    2651             :     }
    2652         704 : }
    2653             : 
    2654         118 : std::vector<CTxDestination> CWallet::ListAddrBookAddresses(const std::optional<AddrBookFilter>& _filter) const
    2655             : {
    2656         118 :     AssertLockHeld(cs_wallet);
    2657         118 :     std::vector<CTxDestination> result;
    2658         118 :     AddrBookFilter filter = _filter ? *_filter : AddrBookFilter();
    2659        1020 :     ForEachAddrBookEntry([&result, &filter](const CTxDestination& dest, const std::string& label, const std::string& purpose, bool is_change) {
    2660             :         // Filter by change
    2661         902 :         if (filter.ignore_change && is_change) return;
    2662             :         // Filter by label
    2663         902 :         if (filter.m_op_label && *filter.m_op_label != label) return;
    2664             :         // All good
    2665         196 :         result.emplace_back(dest);
    2666         902 :     });
    2667         118 :     return result;
    2668         118 : }
    2669             : 
    2670         233 : std::set<std::string> CWallet::ListAddrBookLabels(const std::string& purpose) const
    2671             : {
    2672         233 :     AssertLockHeld(cs_wallet);
    2673         233 :     std::set<std::string> label_set;
    2674        4289 :     ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label,
    2675             :                              const std::string& _purpose, bool _is_change) {
    2676        4056 :         if (_is_change) return;
    2677        4056 :         if (purpose.empty() || _purpose == purpose) {
    2678        3999 :             label_set.insert(_label);
    2679        3999 :         }
    2680        4056 :     });
    2681         233 :     return label_set;
    2682         233 : }
    2683             : 
    2684        7058 : util::Result<CTxDestination> ReserveDestination::GetReservedDestination(bool fInternalIn)
    2685             : {
    2686        7058 :     m_spk_man = pwallet->GetScriptPubKeyMan(fInternalIn);
    2687        7058 :     if (!m_spk_man) {
    2688          22 :         return util::Error{_("Error: No addresses available.")};
    2689             :     }
    2690             : 
    2691        7036 :     if (nIndex == -1) {
    2692        7036 :         CKeyPool keypool;
    2693             :         int64_t index;
    2694        7036 :         auto op_address = m_spk_man->GetReservedDestination(fInternalIn, index, keypool);
    2695        7036 :         if (!op_address) return op_address;
    2696        6984 :         address = *op_address;
    2697        6984 :         nIndex = index;
    2698        6984 :         fInternal = keypool.fInternal;
    2699       14020 :     }
    2700        6984 :     return address;
    2701        7058 : }
    2702             : 
    2703       14228 : void ReserveDestination::KeepDestination()
    2704             : {
    2705       14228 :     if (nIndex != -1) {
    2706        6590 :         m_spk_man->KeepDestination(nIndex);
    2707        6590 :     }
    2708       14228 :     nIndex = -1;
    2709       14228 :     address = CNoDestination();
    2710       14228 : }
    2711             : 
    2712       14668 : void ReserveDestination::ReturnDestination()
    2713             : {
    2714       14668 :     if (nIndex != -1) {
    2715         394 :         m_spk_man->ReturnDestination(nIndex, fInternal, address);
    2716         394 :     }
    2717       14668 :     nIndex = -1;
    2718       14668 :     address = CNoDestination();
    2719       14668 : }
    2720             : 
    2721           4 : bool CWallet::DisplayAddress(const CTxDestination& dest)
    2722             : {
    2723           4 :     CScript scriptPubKey = GetScriptForDestination(dest);
    2724           8 :     for (const auto& spk_man : GetScriptPubKeyMans(scriptPubKey)) {
    2725           4 :         auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan *>(spk_man);
    2726           4 :         if (signer_spk_man == nullptr) {
    2727           0 :             continue;
    2728             :         }
    2729           4 :         ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
    2730           2 :         return signer_spk_man->DisplayAddress(scriptPubKey, signer);
    2731           2 :     }
    2732           0 :     return false;
    2733           6 : }
    2734             : 
    2735        2243 : bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch)
    2736             : {
    2737        2243 :     AssertLockHeld(cs_wallet);
    2738        2243 :     setLockedCoins.insert(output);
    2739        2243 :     RecalculateMixedCredit(output.hash);
    2740        2243 :     if (batch) {
    2741        1039 :         return batch->WriteLockedUTXO(output);
    2742             :     }
    2743        1204 :     return true;
    2744        2243 : }
    2745             : 
    2746       27997 : bool CWallet::UnlockCoin(const COutPoint& output, WalletBatch* batch)
    2747             : {
    2748       27997 :     AssertLockHeld(cs_wallet);
    2749       27997 :     bool was_locked = setLockedCoins.erase(output);
    2750       27997 :     RecalculateMixedCredit(output.hash);
    2751       27997 :     if (batch && was_locked) {
    2752          73 :         return batch->EraseLockedUTXO(output);
    2753             :     }
    2754       27924 :     return true;
    2755       27997 : }
    2756             : 
    2757          29 : bool CWallet::UnlockAllCoins()
    2758             : {
    2759          29 :     AssertLockHeld(cs_wallet);
    2760          29 :     bool success = true;
    2761          29 :     WalletBatch batch(GetDatabase());
    2762         610 :     for (auto it = setLockedCoins.begin(); it != setLockedCoins.end(); ++it) {
    2763         581 :         success &= batch.EraseLockedUTXO(*it);
    2764         581 :     }
    2765          29 :     setLockedCoins.clear();
    2766          29 :     return success;
    2767          29 : }
    2768             : 
    2769      930519 : bool CWallet::IsLockedCoin(const COutPoint& output) const
    2770             : {
    2771      930519 :     AssertLockHeld(cs_wallet);
    2772      930519 :     return setLockedCoins.count(output) > 0;
    2773             : }
    2774             : 
    2775         108 : const std::set<COutPoint>& CWallet::ListLockedCoins() const
    2776             : {
    2777         108 :     AssertLockHeld(cs_wallet);
    2778         108 :     return setLockedCoins;
    2779             : }
    2780             : 
    2781           0 : std::vector<COutPoint> CWallet::ListProTxCoins() const { return ListProTxCoins(setWalletUTXO); }
    2782             : 
    2783      112216 : std::vector<COutPoint> CWallet::ListProTxCoins(const std::set<COutPoint>& utxos) const
    2784             : {
    2785      112216 :     AssertLockHeld(cs_wallet);
    2786             : 
    2787      112216 :     if (!m_chain) return std::vector<COutPoint>();
    2788             : 
    2789      112216 :     std::vector<std::pair<const CTransactionRef&, /*index=*/uint32_t>> candidates;
    2790      342699 :     for (const auto& output : utxos) {
    2791      230483 :         if (auto it = mapWallet.find(output.hash); it != mapWallet.end()) {
    2792      230483 :             const auto& [hash, wtx] = *it;
    2793      230483 :             candidates.emplace_back(wtx.tx, output.n);
    2794      230483 :         }
    2795             :     }
    2796      112216 :     return m_chain->listMNCollaterials(candidates);
    2797      112216 : }
    2798             : 
    2799      112216 : void CWallet::LockProTxCoins(const std::set<COutPoint>& utxos, WalletBatch* batch)
    2800             : {
    2801      112216 :     AssertLockHeld(cs_wallet);
    2802      113229 :     for (const auto& utxo : ListProTxCoins(utxos)) {
    2803        1013 :         LockCoin(utxo, batch);
    2804             :     }
    2805      112216 : }
    2806             : 
    2807          80 : bool CWallet::IsDustProtectionTarget(const CWalletTx& wtx, unsigned int output_index) const
    2808             : {
    2809          80 :     AssertLockHeld(cs_wallet);
    2810             : 
    2811          80 :     if (m_dust_protection_threshold <= 0) return false;
    2812             : 
    2813          80 :     const CTransactionRef& tx = wtx.tx;
    2814          80 :     if (tx->IsCoinBase() || tx->nType != TRANSACTION_NORMAL) return false;
    2815             : 
    2816          80 :     if (output_index >= tx->vout.size()) return false;
    2817          80 :     const CTxOut& txout = tx->vout[output_index];
    2818             : 
    2819          80 :     if (txout.nValue <= 0 || txout.nValue > m_dust_protection_threshold) return false;
    2820          24 :     if (IsMine(txout) == ISMINE_NO) return false;
    2821             : 
    2822             :     // Skip self-sends: if any input is ours, this is not an external dust attack.
    2823          44 :     for (const auto& txin : tx->vin) {
    2824          24 :         if (InputIsMine(*this, txin) != ISMINE_NO) return false;
    2825             :     }
    2826             : 
    2827          20 :     return true;
    2828          80 : }
    2829             : 
    2830       67355 : void CWallet::CheckAndLockDustOutputs(const uint256& txHash, WalletBatch& batch)
    2831             : {
    2832       67355 :     AssertLockHeld(cs_wallet);
    2833             : 
    2834       67355 :     if (m_dust_protection_threshold <= 0) return;
    2835             : 
    2836          28 :     auto it = mapWallet.find(txHash);
    2837          28 :     if (it == mapWallet.end()) return;
    2838             : 
    2839          28 :     const CWalletTx& wtx = it->second;
    2840          84 :     for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
    2841          56 :         if (IsDustProtectionTarget(wtx, i)) {
    2842          12 :             LockCoin(COutPoint(txHash, i), &batch);
    2843          12 :         }
    2844          56 :     }
    2845       67355 : }
    2846             : 
    2847        2218 : void CWallet::LockExistingDustOutputs()
    2848             : {
    2849        2218 :     AssertLockHeld(cs_wallet);
    2850             : 
    2851        2218 :     if (m_dust_protection_threshold <= 0) return;
    2852             : 
    2853          32 :     WalletBatch batch(GetDatabase());
    2854          52 :     for (const auto* pwtx : GetSpendableTXs()) {
    2855          20 :         const CWalletTx& wtx = *pwtx;
    2856             : 
    2857          20 :         if (IsTxImmatureCoinBase(wtx)) continue;
    2858             : 
    2859          20 :         const int depth = GetTxDepthInMainChain(wtx);
    2860          20 :         if (depth < 0) continue;
    2861          20 :         if (depth == 0 && !wtx.InMempool()) continue;
    2862             : 
    2863          60 :         for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
    2864          40 :             const COutPoint outpoint(wtx.GetHash(), i);
    2865          40 :             if (IsLockedCoin(outpoint) || IsSpent(outpoint)) continue;
    2866             : 
    2867          24 :             if (IsDustProtectionTarget(wtx, i)) {
    2868           8 :                 LockCoin(outpoint, &batch);
    2869           8 :             }
    2870          24 :         }
    2871             :     }
    2872        2218 : }
    2873             : 
    2874             : /** @} */ // end of Actions
    2875             : 
    2876          19 : void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
    2877          19 :     AssertLockHeld(cs_wallet);
    2878          19 :     mapKeyBirth.clear();
    2879             : 
    2880             :     // map in which we'll infer heights of other keys
    2881          19 :     std::map<CKeyID, const TxStateConfirmed*> mapKeyFirstBlock;
    2882          19 :     TxStateConfirmed max_confirm{uint256{}, /*height=*/-1, /*index=*/-1};
    2883          19 :     max_confirm.confirmed_block_height = GetLastBlockHeight() > 144 ? GetLastBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin
    2884          19 :     CHECK_NONFATAL(chain().findAncestorByHeight(GetLastBlockHash(), max_confirm.confirmed_block_height, FoundBlock().hash(max_confirm.confirmed_block_hash)));
    2885             : 
    2886             :     {
    2887          19 :         LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
    2888          19 :         assert(spk_man != nullptr);
    2889          19 :         LOCK(spk_man->cs_KeyStore);
    2890             : 
    2891             :         // get birth times for keys with metadata
    2892        3663 :         for (const auto& entry : spk_man->mapKeyMetadata) {
    2893        3644 :             if (entry.second.nCreateTime) {
    2894        3644 :                 mapKeyBirth[entry.first] = entry.second.nCreateTime;
    2895        3644 :             }
    2896             :         }
    2897             : 
    2898             :         // Prepare to infer birth heights for keys without metadata
    2899          32 :         for (const CKeyID &keyid : spk_man->GetKeys()) {
    2900          13 :             if (mapKeyBirth.count(keyid) == 0)
    2901           0 :                 mapKeyFirstBlock[keyid] = &max_confirm;
    2902             :         }
    2903             : 
    2904             :         // if there are no such keys, we're done
    2905          19 :         if (mapKeyFirstBlock.empty())
    2906          19 :             return;
    2907             : 
    2908             :         // find first block that affects those keys, if there are any left
    2909           0 :         for (const auto& entry : mapWallet) {
    2910             :             // iterate over all wallet transactions...
    2911           0 :             const CWalletTx &wtx = entry.second;
    2912           0 :             if (auto* conf = wtx.state<TxStateConfirmed>()) {
    2913             :                 // ... which are already in a block
    2914           0 :                 for (const CTxOut &txout : wtx.tx->vout) {
    2915             :                     // iterate over all their outputs
    2916           0 :                     for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) {
    2917             :                         // ... and all their affected keys
    2918           0 :                         auto rit = mapKeyFirstBlock.find(keyid);
    2919           0 :                         if (rit != mapKeyFirstBlock.end() && conf->confirmed_block_height < rit->second->confirmed_block_height) {
    2920           0 :                             rit->second = conf;
    2921           0 :                         }
    2922             :                     }
    2923             :                 }
    2924           0 :             }
    2925             :         }
    2926          19 :     }
    2927             : 
    2928             :     // Extract block timestamps for those keys
    2929           0 :     for (const auto& entry : mapKeyFirstBlock) {
    2930             :         int64_t block_time;
    2931           0 :         CHECK_NONFATAL(chain().findBlock(entry.second->confirmed_block_hash, FoundBlock().time(block_time)));
    2932           0 :         mapKeyBirth[entry.first] = block_time - TIMESTAMP_WINDOW; // block times can be 2h off
    2933             :     }
    2934          19 : }
    2935             : 
    2936             : /**
    2937             :  * Compute smart timestamp for a transaction being added to the wallet.
    2938             :  *
    2939             :  * Logic:
    2940             :  * - If sending a transaction, assign its timestamp to the current time.
    2941             :  * - If receiving a transaction outside a block, assign its timestamp to the
    2942             :  *   current time.
    2943             :  * - If receiving a transaction during a rescanning process, assign all its
    2944             :  *   (not already known) transactions' timestamps to the block time.
    2945             :  * - If receiving a block with a future timestamp, assign all its (not already
    2946             :  *   known) transactions' timestamps to the current time.
    2947             :  * - If receiving a block with a past timestamp, before the most recent known
    2948             :  *   transaction (that we care about), assign all its (not already known)
    2949             :  *   transactions' timestamps to the same timestamp as that most-recent-known
    2950             :  *   transaction.
    2951             :  * - If receiving a block with a past timestamp, but after the most recent known
    2952             :  *   transaction, assign all its (not already known) transactions' timestamps to
    2953             :  *   the block time.
    2954             :  *
    2955             :  * For more information see CWalletTx::nTimeSmart,
    2956             :  * https://bitcointalk.org/?topic=54527, or
    2957             :  * https://github.com/bitcoin/bitcoin/pull/1393.
    2958             :  */
    2959       67355 : unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const
    2960             : {
    2961       67355 :     std::optional<uint256> block_hash;
    2962       67355 :     if (auto* conf = wtx.state<TxStateConfirmed>()) {
    2963       59498 :         block_hash = conf->confirmed_block_hash;
    2964       67355 :     } else if (auto* conf = wtx.state<TxStateConflicted>()) {
    2965           0 :         block_hash = conf->conflicting_block_hash;
    2966           0 :     }
    2967             : 
    2968       67355 :     unsigned int nTimeSmart = wtx.nTimeReceived;
    2969       67355 :     if (block_hash) {
    2970             :         int64_t blocktime;
    2971             :         int64_t block_max_time;
    2972       59498 :         if (chain().findBlock(*block_hash, FoundBlock().time(blocktime).maxTime(block_max_time))) {
    2973       59498 :             if (rescanning_old_block) {
    2974       11238 :                 nTimeSmart = block_max_time;
    2975       11238 :             } else {
    2976       48260 :                 int64_t latestNow = wtx.nTimeReceived;
    2977       48260 :                 int64_t latestEntry = 0;
    2978             : 
    2979             :                 // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
    2980       48260 :                 int64_t latestTolerated = latestNow + 300;
    2981       48260 :                 const TxItems& txOrdered = wtxOrdered;
    2982       96526 :                 for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
    2983       96080 :                     CWalletTx* const pwtx = it->second;
    2984       96080 :                     if (pwtx == &wtx) {
    2985       48260 :                         continue;
    2986             :                     }
    2987             :                     int64_t nSmartTime;
    2988       47820 :                     nSmartTime = pwtx->nTimeSmart;
    2989       47820 :                     if (!nSmartTime) {
    2990           0 :                         nSmartTime = pwtx->nTimeReceived;
    2991           0 :                     }
    2992       47820 :                     if (nSmartTime <= latestTolerated) {
    2993       47814 :                         latestEntry = nSmartTime;
    2994       47814 :                         if (nSmartTime > latestNow) {
    2995           4 :                             latestNow = nSmartTime;
    2996           4 :                         }
    2997       47814 :                         break;
    2998             :                     }
    2999           6 :                 }
    3000             : 
    3001       48260 :                 nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
    3002             :             }
    3003       59498 :         } else {
    3004           0 :             WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), block_hash->ToString());
    3005             :         }
    3006       59498 :     }
    3007       67355 :     return nTimeSmart;
    3008           0 : }
    3009             : 
    3010          62 : bool CWallet::SetAddressPreviouslySpent(WalletBatch& batch, const CTxDestination& dest, bool used)
    3011             : {
    3012          62 :     if (std::get_if<CNoDestination>(&dest))
    3013           0 :         return false;
    3014             : 
    3015          62 :     if (!used) {
    3016           0 :         if (auto* data{util::FindKey(m_address_book, dest)}) data->previously_spent = false;
    3017           0 :         return batch.WriteAddressPreviouslySpent(dest, false);
    3018             :     }
    3019             : 
    3020          62 :     LoadAddressPreviouslySpent(dest);
    3021          62 :     return batch.WriteAddressPreviouslySpent(dest, true);
    3022          62 : }
    3023             : 
    3024          66 : void CWallet::LoadAddressPreviouslySpent(const CTxDestination& dest)
    3025             : {
    3026          66 :     m_address_book[dest].previously_spent = true;
    3027          66 : }
    3028             : 
    3029           6 : void CWallet::LoadAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& request)
    3030             : {
    3031           6 :     m_address_book[dest].receive_requests[id] = request;
    3032           6 : }
    3033             : 
    3034        8130 : bool CWallet::IsAddressPreviouslySpent(const CTxDestination& dest) const
    3035             : {
    3036        8130 :     if (auto* data{util::FindKey(m_address_book, dest)}) return data->previously_spent;
    3037         240 :     return false;
    3038        8130 : }
    3039             : 
    3040           4 : std::vector<std::string> CWallet::GetAddressReceiveRequests() const
    3041             : {
    3042           4 :     std::vector<std::string> values;
    3043          10 :     for (const auto& [dest, entry] : m_address_book) {
    3044          12 :         for (const auto& [id, request] : entry.receive_requests) {
    3045           6 :             values.emplace_back(request);
    3046             :         }
    3047             :     }
    3048           4 :     return values;
    3049           4 : }
    3050             : 
    3051           8 : bool CWallet::SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value)
    3052             : {
    3053           8 :     if (!batch.WriteAddressReceiveRequest(dest, id, value)) return false;
    3054           8 :     m_address_book[dest].receive_requests[id] = value;
    3055           8 :     return true;
    3056           8 : }
    3057             : 
    3058           2 : bool CWallet::EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id)
    3059             : {
    3060           2 :     if (!batch.EraseAddressReceiveRequest(dest, id)) return false;
    3061           2 :     m_address_book[dest].receive_requests.erase(id);
    3062           2 :     return true;
    3063           2 : }
    3064             : 
    3065        3675 : std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string)
    3066             : {
    3067             :     // Do some checking on wallet path. It should be either a:
    3068             :     //
    3069             :     // 1. Path where a directory can be created.
    3070             :     // 2. Path to an existing directory.
    3071             :     // 3. Path to a symlink to a directory.
    3072             :     // 4. For backwards compatibility, the name of a data file in -walletdir.
    3073        3679 :     const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
    3074        3675 :     fs::file_type path_type = fs::symlink_status(wallet_path).type();
    3075        3775 :     if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory ||
    3076         164 :           (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) ||
    3077          64 :           (path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
    3078          12 :         error_string = Untranslated(strprintf(
    3079             :               "Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and "
    3080             :               "database/log.?????????? files can be stored, a location where such a directory could be created, "
    3081             :               "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)",
    3082          12 :               name, fs::quoted(fs::PathToString(GetWalletDir()))));
    3083          12 :         status = DatabaseStatus::FAILED_BAD_PATH;
    3084          12 :         return nullptr;
    3085             :     }
    3086             : 
    3087        3663 :     return MakeDatabase(wallet_path, options, status, error_string);
    3088        3675 : }
    3089             : 
    3090        2260 : std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings)
    3091             : {
    3092        2260 :     interfaces::Chain* chain = context.chain;
    3093        2260 :     interfaces::CoinJoin::Loader* coinjoin_loader = context.coinjoin_loader;
    3094        2260 :     ArgsManager& args = *Assert(context.args);
    3095        2260 :     const std::string& walletFile = database->Filename();
    3096             : 
    3097        2260 :     const auto start{SteadyClock::now()};
    3098             :     // TODO: Can't use std::make_shared because we need a custom deleter but
    3099             :     // should be possible to use std::allocate_shared.
    3100        2260 :     std::shared_ptr<CWallet> walletInstance(new CWallet(chain, coinjoin_loader, name, args, std::move(database)), ReleaseWallet);
    3101             :     // TODO: refactor this condition: validation of error looks like workaround
    3102        2260 :     if (!walletInstance->AutoBackupWallet(fs::PathFromString(walletFile), error, warnings) && !error.original.empty()) {
    3103           0 :         return nullptr;
    3104             :     }
    3105        2260 :     DBErrors nLoadWalletRet = walletInstance->LoadWallet();
    3106        2248 :     if (nLoadWalletRet != DBErrors::LOAD_OK)
    3107             :     {
    3108           0 :         if (nLoadWalletRet == DBErrors::CORRUPT) {
    3109           0 :             error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);
    3110           0 :             return nullptr;
    3111             :         }
    3112           0 :         else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR)
    3113             :         {
    3114           0 :             warnings.push_back(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
    3115             :                                            " or address book entries might be missing or incorrect."),
    3116           0 :                 walletFile));
    3117           0 :         }
    3118           0 :         else if (nLoadWalletRet == DBErrors::TOO_NEW) {
    3119           0 :             error = strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, PACKAGE_NAME);
    3120           0 :             return nullptr;
    3121             :         }
    3122           0 :         else if (nLoadWalletRet == DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED) {
    3123           0 :             error = strprintf(_("Error loading %s: External signer wallet being loaded without external signer support compiled"), walletFile);
    3124           0 :             return nullptr;
    3125             :         }
    3126           0 :         else if (nLoadWalletRet == DBErrors::NEED_REWRITE)
    3127             :         {
    3128           0 :             error = strprintf(_("Wallet needed to be rewritten: restart %s to complete"), PACKAGE_NAME);
    3129           0 :             return nullptr;
    3130             :         }
    3131             :         else {
    3132           0 :             error = strprintf(_("Error loading %s"), walletFile);
    3133           0 :             return nullptr;
    3134             :         }
    3135           0 :     }
    3136             : 
    3137             :     // This wallet is in its first run if there are no ScriptPubKeyMans and it isn't blank or no privkeys
    3138        3432 :     const bool fFirstRun = walletInstance->m_spk_managers.empty() &&
    3139        1184 :                      !walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) &&
    3140        1184 :                      !walletInstance->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
    3141        2248 :     if (fFirstRun)
    3142             :     {
    3143        1179 :         walletInstance->SetMinVersion(FEATURE_LATEST);
    3144             : 
    3145        1179 :         walletInstance->InitWalletFlags(wallet_creation_flags);
    3146             : 
    3147             :         // Only create LegacyScriptPubKeyMan when not descriptor wallet
    3148        1179 :         if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    3149         752 :             walletInstance->SetupLegacyScriptPubKeyMan();
    3150         752 :         }
    3151             : 
    3152        1179 :         if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) || !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
    3153             :             // Create new HD chain
    3154         960 :             if (args.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
    3155         868 :                 std::string strSeed = args.GetArg("-hdseed", "not hex");
    3156             : 
    3157             :                 // ensure this wallet.dat can only be opened by clients supporting HD
    3158         868 :                 walletInstance->WalletLogPrintf("Upgrading wallet to HD\n");
    3159         868 :                 walletInstance->SetMinVersion(FEATURE_HD);
    3160             : 
    3161         868 :                 if (args.IsArgSet("-hdseed") && IsHex(strSeed)) {
    3162           0 :                     CHDChain newHdChain;
    3163           0 :                     std::vector<unsigned char> vchSeed = ParseHex(strSeed);
    3164           0 :                     if (!newHdChain.SetSeed(SecureVector(vchSeed.begin(), vchSeed.end()), true)) {
    3165           0 :                         error = strprintf(_("%s failed"), "SetSeed");
    3166           0 :                         return nullptr;
    3167             :                     }
    3168           0 :                     LOCK(walletInstance->cs_wallet);
    3169           0 :                     if (auto spk_man = walletInstance->GetLegacyScriptPubKeyMan()) {
    3170           0 :                         if (!spk_man->AddHDChainSingle(newHdChain)) {
    3171           0 :                             error = strprintf(_("%s failed"), "AddHDChainSingle");
    3172           0 :                             return nullptr;
    3173             :                         }
    3174           0 :                     }
    3175             :                     // add default account
    3176           0 :                     newHdChain.AddAccount();
    3177           0 :                 } else {
    3178         868 :                     if (args.IsArgSet("-hdseed") && !IsHex(strSeed)) {
    3179           0 :                         error = strprintf(_("%s -- Incorrect seed, it should be a hex string"), __func__);
    3180           0 :                         return nullptr;
    3181             :                     }
    3182             : 
    3183         868 :                     SecureString mnemonic, mnemonic_passphrase;
    3184         868 :                     mnemonic.reserve(256);
    3185         868 :                     mnemonic_passphrase.reserve(256);
    3186             : 
    3187         868 :                     mnemonic = args.GetArg("-mnemonic", "");
    3188         868 :                     mnemonic_passphrase = args.GetArg("-mnemonicpassphrase", "");
    3189         868 :                     LOCK(walletInstance->cs_wallet);
    3190         868 :                     if (auto spk_man = walletInstance->GetLegacyScriptPubKeyMan()) {
    3191         571 :                         spk_man->GenerateNewHDChain(mnemonic, mnemonic_passphrase);
    3192         571 :                     }
    3193         868 :                 }
    3194             : 
    3195             :                 // clean up
    3196         868 :                 args.ForceRemoveArg("hdseed");
    3197         868 :                 args.ForceRemoveArg("mnemonic");
    3198         868 :                 args.ForceRemoveArg("mnemonicpassphrase");
    3199         868 :             } // Otherwise, do not create a new HD chain
    3200             : 
    3201         960 :             LOCK(walletInstance->cs_wallet);
    3202         960 :             if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    3203         326 :                 SecureString mnemonic, mnemonic_passphrase;
    3204         326 :                 mnemonic.reserve(256);
    3205         326 :                 mnemonic_passphrase.reserve(256);
    3206         326 :                 mnemonic = args.GetArg("-mnemonic", "");
    3207         326 :                 mnemonic_passphrase = args.GetArg("-mnemonicpassphrase", "");
    3208         326 :                 args.ForceRemoveArg("mnemonic");
    3209         326 :                 args.ForceRemoveArg("mnemonicpassphrase");
    3210         326 :                 walletInstance->SetupDescriptorScriptPubKeyMans(mnemonic, mnemonic_passphrase);
    3211             :                 // SetupDescriptorScriptPubKeyMans already calls SetupGeneration for us so we don't need to call SetupGeneration separately
    3212         326 :             } else { // Top up the keypool
    3213             :                 // Legacy wallets need SetupGeneration here.
    3214         634 :                 if (auto spk_man = walletInstance->GetLegacyScriptPubKeyMan()) {
    3215         634 :                     if (spk_man->CanGenerateKeys() && !spk_man->TopUp()) {
    3216           0 :                         error = _("Unable to generate initial keys");
    3217           0 :                         return nullptr;
    3218             :                     }
    3219         634 :                 }
    3220             :             }
    3221         960 :         }
    3222             : 
    3223        1175 :         if (chain) {
    3224        1174 :             walletInstance->chainStateFlushed(chain->getTipLocator());
    3225        1174 :         }
    3226             : 
    3227             :         // Try to create wallet backup right after new wallet was created
    3228        1175 :         bilingual_str strBackupError;
    3229        1175 :         if(!walletInstance->AutoBackupWallet("", strBackupError, warnings)) {
    3230        1172 :             if (!strBackupError.original.empty()) {
    3231           0 :                 error = strBackupError;
    3232           0 :                 return nullptr;
    3233             :             }
    3234        1172 :         }
    3235        2244 :     } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
    3236             :         // Make it impossible to disable private keys after creation
    3237           0 :         error = strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile);
    3238           0 :         return nullptr;
    3239        1069 :     } else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
    3240          73 :         for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) {
    3241          31 :             if (spk_man->HavePrivateKeys()) {
    3242           0 :                 warnings.push_back(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
    3243           0 :             }
    3244             :         }
    3245          42 :     }
    3246        1027 :     else if (args.IsArgSet("-usehd")) {
    3247          99 :         bool useHD = args.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
    3248          99 :         if (walletInstance->IsHDEnabled() && !useHD) {
    3249          10 :             error = strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletInstance->GetName());
    3250          10 :             return nullptr;
    3251             :         }
    3252          89 :         if (!walletInstance->IsHDEnabled() && useHD) {
    3253           0 :             error = strprintf(_("Error loading %s: You can't enable HD on an already existing non-HD wallet"), walletInstance->GetName());
    3254           0 :             return nullptr;
    3255             :         }
    3256          89 :     }
    3257             : 
    3258             :     // Warn user every time a non-encrypted HD wallet is started
    3259        2234 :     if (walletInstance->IsHDEnabled() && !walletInstance->IsLocked()) {
    3260        1779 :         SetMiscWarning(_("Make sure to encrypt your wallet and delete all non-encrypted backups after you have verified that the wallet works!"));
    3261        1779 :     }
    3262             : 
    3263        2234 :     if (args.IsArgSet("-mintxfee")) {
    3264           4 :         std::optional<CAmount> min_tx_fee = ParseMoney(args.GetArg("-mintxfee", ""));
    3265           4 :         if (!min_tx_fee) {
    3266           0 :             error = AmountErrMsg("mintxfee", args.GetArg("-mintxfee", ""));
    3267           0 :             return nullptr;
    3268           4 :         } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) {
    3269           0 :             warnings.push_back(AmountHighWarn("-mintxfee") + Untranslated(" ") +
    3270           0 :                               _("This is the minimum transaction fee you pay on every transaction."));
    3271           0 :         }
    3272             : 
    3273           4 :         walletInstance->m_min_fee = CFeeRate{min_tx_fee.value()};
    3274           4 :     }
    3275             : 
    3276        2234 :     if (args.IsArgSet("-maxapsfee")) {
    3277           8 :         const std::string max_aps_fee{args.GetArg("-maxapsfee", "")};
    3278           8 :         if (max_aps_fee == "-1") {
    3279           0 :             walletInstance->m_max_aps_fee = -1;
    3280           8 :         } else if (std::optional<CAmount> max_fee = ParseMoney(max_aps_fee)) {
    3281           8 :             if (max_fee.value() > HIGH_APS_FEE) {
    3282           0 :                 warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") +
    3283           0 :                                   _("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection."));
    3284           0 :             }
    3285           8 :             walletInstance->m_max_aps_fee = max_fee.value();
    3286           8 :         } else {
    3287           0 :             error = AmountErrMsg("maxapsfee", max_aps_fee);
    3288           0 :             return nullptr;
    3289             :         }
    3290           8 :     }
    3291             : 
    3292        2234 :     if (args.IsArgSet("-fallbackfee")) {
    3293        2229 :         std::optional<CAmount> fallback_fee = ParseMoney(args.GetArg("-fallbackfee", ""));
    3294        2229 :         if (!fallback_fee) {
    3295           0 :             error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-fallbackfee", args.GetArg("-fallbackfee", ""));
    3296           0 :             return nullptr;
    3297        2229 :         } else if (fallback_fee.value() > HIGH_TX_FEE_PER_KB) {
    3298           0 :             warnings.push_back(AmountHighWarn("-fallbackfee") + Untranslated(" ") +
    3299           0 :                               _("This is the transaction fee you may pay when fee estimates are not available."));
    3300           0 :         }
    3301        2229 :         walletInstance->m_fallback_fee = CFeeRate{fallback_fee.value()};
    3302        2229 :     }
    3303             :     // Disable fallback fee in case value was set to 0, enable if non-null value
    3304        2234 :     walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0;
    3305             : 
    3306        2234 :     if (args.IsArgSet("-discardfee")) {
    3307          26 :         std::optional<CAmount> discard_fee = ParseMoney(args.GetArg("-discardfee", ""));
    3308          26 :         if (!discard_fee) {
    3309           0 :             error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-discardfee", args.GetArg("-discardfee", ""));
    3310           0 :             return nullptr;
    3311          26 :         } else if (discard_fee.value() > HIGH_TX_FEE_PER_KB) {
    3312          16 :             warnings.push_back(AmountHighWarn("-discardfee") + Untranslated(" ") +
    3313           8 :                               _("This is the transaction fee you may discard if change is smaller than dust at this level"));
    3314           8 :         }
    3315          26 :         walletInstance->m_discard_rate = CFeeRate{discard_fee.value()};
    3316          26 :     }
    3317             : 
    3318        2234 :     if (args.IsArgSet("-paytxfee")) {
    3319           4 :         std::optional<CAmount> pay_tx_fee = ParseMoney(args.GetArg("-paytxfee", ""));
    3320           4 :         if (!pay_tx_fee) {
    3321           0 :             error = AmountErrMsg("paytxfee", args.GetArg("-paytxfee", ""));
    3322           0 :             return nullptr;
    3323           4 :         } else if (pay_tx_fee.value() > HIGH_TX_FEE_PER_KB) {
    3324           0 :             warnings.push_back(AmountHighWarn("-paytxfee") + Untranslated(" ") +
    3325           0 :                               _("This is the transaction fee you will pay if you send a transaction."));
    3326           0 :         }
    3327           4 :         walletInstance->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000};
    3328           8 :         if (chain && walletInstance->m_pay_tx_fee < chain->relayMinFee()) {
    3329           0 :             error = strprintf(_("Invalid amount for %s=<amount>: '%s' (must be at least %s)"),
    3330           0 :                 "-paytxfee", args.GetArg("-paytxfee", ""), chain->relayMinFee().ToString());
    3331           0 :             return nullptr;
    3332             :         }
    3333           4 :     }
    3334             : 
    3335        2234 :     if (args.IsArgSet("-maxtxfee")) {
    3336           0 :         std::optional<CAmount> max_fee = ParseMoney(args.GetArg("-maxtxfee", ""));
    3337           0 :         if (!max_fee) {
    3338           0 :             error = AmountErrMsg("maxtxfee", args.GetArg("-maxtxfee", ""));
    3339           0 :             return nullptr;
    3340           0 :         } else if (max_fee.value() > HIGH_MAX_TX_FEE) {
    3341           0 :             warnings.push_back(strprintf(_("%s is set very high! Fees this large could be paid on a single transaction."), "-maxtxfee"));
    3342           0 :         }
    3343           0 :         if (chain && CFeeRate{max_fee.value(), 1000} < chain->relayMinFee()) {
    3344           0 :             error = strprintf(_("Invalid amount for %s=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
    3345           0 :                 "-maxtxfee", args.GetArg("-maxtxfee", ""), chain->relayMinFee().ToString());
    3346           0 :             return nullptr;
    3347             :         }
    3348             : 
    3349           0 :         walletInstance->m_default_max_tx_fee = max_fee.value();
    3350           0 :     }
    3351             : 
    3352        2234 :     if (args.IsArgSet("-consolidatefeerate")) {
    3353           0 :         if (std::optional<CAmount> consolidate_feerate = ParseMoney(args.GetArg("-consolidatefeerate", ""))) {
    3354           0 :             walletInstance->m_consolidate_feerate = CFeeRate(*consolidate_feerate);
    3355           0 :         } else {
    3356           0 :             error = AmountErrMsg("consolidatefeerate", args.GetArg("-consolidatefeerate", ""));
    3357           0 :             return nullptr;
    3358             :         }
    3359           0 :     }
    3360             : 
    3361        2234 :     if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB)
    3362          36 :         warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") +
    3363          18 :                     _("The wallet will avoid paying less than the minimum relay fee."));
    3364             : 
    3365        2234 :     walletInstance->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
    3366        2234 :     walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
    3367             : 
    3368        2234 :     walletInstance->m_dust_protection_threshold = args.GetIntArg("-dustprotectionthreshold", DEFAULT_DUST_PROTECTION_THRESHOLD);
    3369        2234 :     if (walletInstance->m_dust_protection_threshold < 0) {
    3370           4 :         error = strprintf(_("Invalid value for %s: must be >= 0"), "-dustprotectionthreshold");
    3371           4 :         return nullptr;
    3372             :     }
    3373        2230 :     if (walletInstance->m_dust_protection_threshold > MAX_DUST_PROTECTION_THRESHOLD) {
    3374           4 :         error = strprintf(_("Invalid value for %s: exceeds maximum (%d)"), "-dustprotectionthreshold", MAX_DUST_PROTECTION_THRESHOLD);
    3375           4 :         return nullptr;
    3376             :     }
    3377             : 
    3378        2226 :     walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start));
    3379             : 
    3380             :     // Try to top up keypool. No-op if the wallet is locked.
    3381        2226 :     walletInstance->TopUpKeyPool();
    3382             : 
    3383        2226 :     NotifyWalletLoading(context, walletInstance);
    3384             : 
    3385        2226 :     if (chain && !AttachChain(walletInstance, *chain, error, warnings)) {
    3386           8 :         walletInstance->m_chain_notifications_handler.reset(); // Reset this pointer so that the wallet will actually be unloaded
    3387           8 :         return nullptr;
    3388             :     }
    3389             : 
    3390        2218 :     if (coinjoin_loader) {
    3391        2183 :         coinjoin_loader->AddWallet(walletInstance);
    3392        2183 :     }
    3393             : 
    3394             :     {
    3395        2218 :         LOCK(walletInstance->cs_wallet);
    3396        2218 :         walletInstance->SetBroadcastTransactions(args.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
    3397        2218 :         walletInstance->LockExistingDustOutputs();
    3398        2218 :         walletInstance->WalletLogPrintf("setExternalKeyPool.size() = %u\n",   walletInstance->KeypoolCountExternalKeys());
    3399        2218 :         walletInstance->WalletLogPrintf("GetKeyPoolSize() = %u\n",   walletInstance->GetKeyPoolSize());
    3400        2218 :         walletInstance->WalletLogPrintf("mapWallet.size() = %u\n",            walletInstance->mapWallet.size());
    3401        2218 :         walletInstance->WalletLogPrintf("m_address_book.size() = %u\n",  walletInstance->m_address_book.size());
    3402        5862 :         for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) {
    3403        3644 :             walletInstance->WalletLogPrintf("nTimeFirstKey = %u\n", spk_man->GetTimeFirstKey());
    3404             :         }
    3405        2218 :     }
    3406             : 
    3407        2218 :     return walletInstance;
    3408        2264 : }
    3409             : 
    3410        2191 : bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings)
    3411             : {
    3412        2191 :     LOCK(walletInstance->cs_wallet);
    3413             :     // allow setting the chain if it hasn't been set already but prevent changing it
    3414        2191 :     assert(!walletInstance->m_chain || walletInstance->m_chain == &chain);
    3415        2191 :     walletInstance->m_chain = &chain;
    3416             : 
    3417             :     // Unless allowed, ensure wallet files are not reused across chains:
    3418        2191 :     if (!gArgs.GetBoolArg("-walletcrosschain", DEFAULT_WALLETCROSSCHAIN)) {
    3419        2187 :         WalletBatch batch(walletInstance->GetDatabase());
    3420        2187 :         CBlockLocator locator;
    3421        2187 :         if (batch.ReadBestBlock(locator) && locator.vHave.size() > 0 && chain.getHeight()) {
    3422             :             // Wallet is assumed to be from another chain, if genesis block in the active
    3423             :             // chain differs from the genesis block known to the wallet.
    3424        2153 :             if (chain.getBlockHash(0) != locator.vHave.back()) {
    3425           8 :                 error = Untranslated("Wallet files should not be reused across chains. Restart dashd with -walletcrosschain to override.");
    3426           8 :                 return false;
    3427             :             }
    3428        2145 :         }
    3429        2187 :     }
    3430             : 
    3431             :     // Register wallet with validationinterface. It's done before rescan to avoid
    3432             :     // missing block connections between end of rescan and validation subscribing.
    3433             :     // Because of wallet lock being hold, block connection notifications are going to
    3434             :     // be pending on the validation-side until lock release. It's likely to have
    3435             :     // block processing duplicata (if rescan block range overlaps with notification one)
    3436             :     // but we guarantee at least than wallet state is correct after notifications delivery.
    3437             :     // However, chainStateFlushed notifications are ignored until the rescan is finished
    3438             :     // so that in case of a shutdown event, the rescan will be repeated at the next start.
    3439             :     // This is temporary until rescan and notifications delivery are unified under same
    3440             :     // interface.
    3441        2183 :     walletInstance->m_attaching_chain = true; //ignores chainStateFlushed notifications
    3442        2183 :     walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);
    3443             : 
    3444        2183 :     int rescan_height = 0;
    3445        2183 :     if (!gArgs.GetBoolArg("-rescan", false))
    3446             :     {
    3447        2135 :         WalletBatch batch(walletInstance->GetDatabase());
    3448        2135 :         CBlockLocator locator;
    3449        2135 :         if (batch.ReadBestBlock(locator)) {
    3450        2131 :             if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) {
    3451        2101 :                 rescan_height = *fork_height;
    3452        2101 :             }
    3453        2131 :         }
    3454        2135 :     }
    3455             : 
    3456        2183 :     const std::optional<int> tip_height = chain.getHeight();
    3457        2183 :     if (tip_height) {
    3458        2153 :         walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height);
    3459        2153 :         walletInstance->m_last_block_processed_height = *tip_height;
    3460        2153 :     } else {
    3461          30 :         walletInstance->m_last_block_processed.SetNull();
    3462          30 :         walletInstance->m_last_block_processed_height = -1;
    3463             :     }
    3464             : 
    3465        2183 :     if (tip_height && *tip_height != rescan_height)
    3466             :     {
    3467             :         // No need to read and scan block if block was created before
    3468             :         // our wallet birthday (as adjusted for block time variability)
    3469             :         // unless a full rescan was requested
    3470         286 :         if (gArgs.GetIntArg("-rescan", 0) != 2) {
    3471         286 :             std::optional<int64_t> time_first_key;
    3472         821 :             for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) {
    3473         535 :                 int64_t time = spk_man->GetTimeFirstKey();
    3474         535 :                 if (!time_first_key || time < *time_first_key) time_first_key = time;
    3475             :             }
    3476         286 :             if (time_first_key) {
    3477         280 :                 FoundBlock found = FoundBlock().height(rescan_height);
    3478         280 :                 chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, found);
    3479         280 :                 if (!found.found) {
    3480             :                     // We were unable to find a block that had a time more recent than our earliest timestamp
    3481             :                     // or a height higher than the wallet was synced to, indicating that the wallet is newer than the
    3482             :                     // current chain tip. Skip rescanning in this case.
    3483           4 :                     rescan_height = *tip_height;
    3484           4 :                 }
    3485         280 :             }
    3486         286 :         }
    3487             : 
    3488             :         // Technically we could execute the code below in any case, but performing the
    3489             :         // `while` loop below can make startup very slow, so only check blocks on disk
    3490             :         // if necessary.
    3491         286 :         if (chain.havePruned() || chain.hasAssumedValidChain()) {
    3492           0 :             int block_height = *tip_height;
    3493           0 :             while (block_height > 0 && chain.haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
    3494           0 :                 --block_height;
    3495             :             }
    3496             : 
    3497           0 :             if (rescan_height != block_height) {
    3498             :                 // We can't rescan beyond blocks we don't have data for, stop and throw an error.
    3499             :                 // This might happen if a user uses an old wallet within a pruned node
    3500             :                 // or if they ran -disablewallet for a longer time, then decided to re-enable
    3501             :                 // Exit early and print an error.
    3502             :                 // It also may happen if an assumed-valid chain is in use and therefore not
    3503             :                 // all block data is available.
    3504             :                 // If a block is pruned after this check, we will load the wallet,
    3505             :                 // but fail the rescan with a generic error.
    3506             : 
    3507           0 :                 error = chain.havePruned() ?
    3508           0 :                      _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)") :
    3509           0 :                      strprintf(_(
    3510             :                         "Error loading wallet. Wallet requires blocks to be downloaded, "
    3511             :                         "and software does not currently support loading wallets while "
    3512             :                         "blocks are being downloaded out of order when using assumeutxo "
    3513             :                         "snapshots. Wallet should be able to load successfully after "
    3514             :                         "node sync reaches height %s"), block_height);
    3515           0 :                 return false;
    3516             :             }
    3517           0 :         }
    3518             : 
    3519         286 :         chain.initMessage(_("Rescanning…").translated);
    3520         286 :         walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
    3521             :         {
    3522         286 :             WalletRescanReserver reserver(*walletInstance);
    3523         286 :             if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(chain.getBlockHash(rescan_height), rescan_height, /*max_height=*/{}, reserver, /*fUpdate=*/true, /*save_progress=*/true).status)) {
    3524           0 :                 error = _("Failed to rescan the wallet during initialization");
    3525           0 :                 return false;
    3526             :             }
    3527         286 :         }
    3528         286 :         walletInstance->m_attaching_chain = false;
    3529         286 :         walletInstance->chainStateFlushed(chain.getTipLocator());
    3530         286 :         walletInstance->GetDatabase().IncrementUpdateCounter();
    3531         286 :     }
    3532        2183 :     walletInstance->m_attaching_chain = false;
    3533             : 
    3534        2183 :     return true;
    3535        2191 : }
    3536             : 
    3537           0 : bool CWallet::UpgradeWallet(int version, bilingual_str& error)
    3538             : {
    3539           0 :     int prev_version = GetVersion();
    3540           0 :     int nMaxVersion = version;
    3541           0 :     auto nMinVersion = DEFAULT_USE_HD_WALLET ? FEATURE_LATEST : FEATURE_COMPRPUBKEY;
    3542           0 :     if (nMaxVersion == 0) {
    3543           0 :         WalletLogPrintf("Performing wallet upgrade to %i\n", nMinVersion);
    3544           0 :         nMaxVersion = FEATURE_LATEST;
    3545           0 :         SetMinVersion(nMinVersion); // permanently upgrade the wallet immediately
    3546           0 :     } else {
    3547           0 :         WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
    3548             :     }
    3549             : 
    3550           0 :     if (nMaxVersion < GetVersion()) {
    3551           0 :         error = strprintf(_("Cannot downgrade wallet from version %i to version %i. Wallet version unchanged."), prev_version, version);
    3552           0 :         return false;
    3553             :     }
    3554             : 
    3555           0 :     SetMinVersion(GetClosestWalletFeature(version));
    3556             : 
    3557           0 :     return true;
    3558           0 : }
    3559             : 
    3560       37921 : const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest, bool allow_change) const
    3561             : {
    3562       37921 :     const auto& address_book_it = m_address_book.find(dest);
    3563       37921 :     if (address_book_it == m_address_book.end()) return nullptr;
    3564       27074 :     if ((!allow_change) && address_book_it->second.IsChange()) {
    3565          12 :         return nullptr;
    3566             :     }
    3567       27062 :     return &address_book_it->second;
    3568       37921 : }
    3569             : 
    3570        2175 : void CWallet::postInitProcess()
    3571             : {
    3572        2175 :     LOCK(cs_wallet);
    3573             : 
    3574             :     // Add wallet transactions that aren't already in a block to mempool
    3575             :     // Do this here as mempool requires genesis block to be loaded
    3576        2175 :     ReacceptWalletTransactions();
    3577             : 
    3578             :     // Update wallet transactions with current mempool transactions.
    3579        2175 :     chain().requestMempoolTransactions(*this);
    3580        2175 : }
    3581             : 
    3582        3011 : void CWallet::InitAutoBackup()
    3583             : {
    3584        3011 :     if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
    3585        1575 :         return;
    3586             : 
    3587        1436 :     nWalletBackups = gArgs.GetIntArg("-createwalletbackups", 10);
    3588        1436 :     nWalletBackups = std::max(0, std::min(10, nWalletBackups));
    3589        3011 : }
    3590             : 
    3591         147 : bool CWallet::BackupWallet(const std::string& strDest) const
    3592             : {
    3593         147 :     return GetDatabase().Backup(strDest);
    3594             : }
    3595             : 
    3596             : // This should be called carefully:
    3597             : // either supply the actual wallet_path to make a raw copy of wallet.dat or "" to backup current instance via BackupWallet()
    3598             : #ifdef USE_BDB
    3599        3443 : bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
    3600             : {
    3601        3443 :     std::string strWalletName = GetName();
    3602        3443 :     if (strWalletName.empty()) {
    3603        1232 :         strWalletName = "wallet.dat";
    3604        1232 :     }
    3605             :     // This condition is required to be sure that wallet.dat won't be re-opened by IsBDBFile
    3606             :     // Re-opening of database file brokes an exclusive inter-process lock for SQLite
    3607        3443 :     if (m_database && !m_database->SupportsAutoBackup()) {
    3608        1209 :         WalletLogPrintf("Automatic wallet backups are not supported!\n");
    3609        1209 :         return false;
    3610             :     }
    3611        2234 :     if (!wallet_path.empty() && !IsBDBFile(BDBDataFile(wallet_path))) {
    3612         755 :         WalletLogPrintf("Automatic wallet backups are currently only supported with Berkeley DB!\n");
    3613         755 :         return false;
    3614             :     }
    3615             : 
    3616        1479 :     if (IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
    3617          68 :         WalletLogPrintf("Wallet is blank, won't create new backup for it!\n");
    3618          68 :         return false;
    3619             :     }
    3620             : 
    3621        1411 :     if (nWalletBackups <= 0) {
    3622        1406 :         WalletLogPrintf("Automatic wallet backups are disabled!\n");
    3623        1406 :         return false;
    3624             :     }
    3625             : 
    3626           5 :     fs::path backupsDir = gArgs.GetBackupsDirPath();
    3627           5 :     backupsDir.make_preferred();
    3628             : 
    3629           5 :     if (!fs::exists(backupsDir))
    3630             :     {
    3631             :         // Always create backup folder to not confuse the operating system's file browser
    3632           3 :         WalletLogPrintf("Creating backup folder %s\n", fs::PathToString(backupsDir));
    3633           3 :         if(!fs::create_directories(backupsDir)) {
    3634             :             // something is wrong, we shouldn't continue until it's resolved
    3635           0 :             error_string = strprintf(_("Wasn't able to create wallet backup folder %s!"), fs::PathToString(backupsDir));
    3636           0 :             WalletLogPrintf("%s\n", error_string.translated);
    3637           0 :             nWalletBackups = -1;
    3638           0 :             return false;
    3639             :         }
    3640           5 :     } else if (!fs::is_directory(backupsDir)) {
    3641             :         // something is wrong, we shouldn't continue until it's resolved
    3642           0 :         error_string = strprintf(_("%s is not a valid backup folder!"), fs::PathToString(backupsDir));
    3643           0 :         WalletLogPrintf("%s\n", error_string.translated);
    3644           0 :         nWalletBackups = -1;
    3645           0 :         return false;
    3646             :     }
    3647             : 
    3648             :     // Create backup of the ...
    3649          10 :     std::string dateTimeStr = [&]() {
    3650           5 :         const std::chrono::sys_seconds secs{GetTime<std::chrono::seconds>()};
    3651           5 :         const auto days{std::chrono::floor<std::chrono::days>(secs)};
    3652           5 :         const std::chrono::year_month_day ymd{days};
    3653           5 :         const std::chrono::hh_mm_ss hms{secs - days};
    3654           5 :         return strprintf(".%04i-%02u-%02u-%02i-%02i", signed{ymd.year()}, unsigned{ymd.month()}, unsigned{ymd.day()}, hms.hours().count(), hms.minutes().count());
    3655             :     }();
    3656             : 
    3657           5 :     if (wallet_path.empty()) {
    3658             :         // ... opened wallet
    3659           3 :         LOCK(cs_wallet);
    3660           3 :         fs::path backupFile = backupsDir / fs::u8path(strWalletName + dateTimeStr);
    3661           3 :         backupFile.make_preferred();
    3662           3 :         if (!BackupWallet(fs::PathToString(backupFile))) {
    3663           0 :             warnings.push_back(strprintf(_("Failed to create backup %s!"), fs::PathToString(backupFile)));
    3664           0 :             WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original);
    3665           0 :             nWalletBackups = -1;
    3666           0 :             return false;
    3667             :         }
    3668             : 
    3669             :         // Update nKeysLeftSinceAutoBackup using current external keypool size
    3670           3 :         nKeysLeftSinceAutoBackup = KeypoolCountExternalKeys();
    3671           3 :         WalletLogPrintf("nKeysLeftSinceAutoBackup: %d\n", nKeysLeftSinceAutoBackup);
    3672           3 :         if (IsLocked(true)) {
    3673           0 :             warnings.push_back(_("Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool."));
    3674           0 :             WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original);
    3675           0 :             nWalletBackups = -2;
    3676           0 :             return false;
    3677             :         }
    3678           3 :     } else {
    3679             :         // ... strWalletName file
    3680           2 :         fs::path strSourceFile = BDBDataFile(wallet_path);
    3681           2 :         std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(strSourceFile.parent_path(), /*use_shared_memory=*/true);
    3682           2 :         fs::path sourceFile = env->Directory() / strSourceFile;
    3683           2 :         fs::path backupFile = backupsDir / fs::u8path(strWalletName + dateTimeStr);
    3684           2 :         sourceFile.make_preferred();
    3685           2 :         backupFile.make_preferred();
    3686           2 :         if (fs::exists(backupFile))
    3687             :         {
    3688           2 :             warnings.push_back(_("Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this."));
    3689           2 :             WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original);
    3690           2 :             return false;
    3691             :         }
    3692           0 :         if(fs::exists(sourceFile)) {
    3693             :             try {
    3694           0 :                 fs::copy_file(sourceFile, backupFile, fs::copy_options::none);
    3695           0 :                 WalletLogPrintf("Creating backup of %s -> %s\n", fs::PathToString(sourceFile), fs::PathToString(backupFile));
    3696           0 :             } catch(fs::filesystem_error &error) {
    3697           0 :                 warnings.push_back(strprintf(_("Failed to create backup, error: %s"), fsbridge::get_filesystem_error_message(error)));
    3698           0 :                 WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original);
    3699           0 :                 nWalletBackups = -1;
    3700           0 :                 return false;
    3701           0 :             }
    3702           0 :         }
    3703           2 :     }
    3704             : 
    3705             :     // Keep only the last 10 backups, including the new one of course
    3706           3 :     std::multimap<fs::file_time_type, fs::path> folder_set;
    3707             :     // Build map of backup files for current(!) wallet sorted by last write time
    3708           3 :     fs::path currentFile;
    3709           6 :     for (const auto& entry : fs::directory_iterator(backupsDir)) {
    3710             :         // Only check regular files
    3711           3 :         if (entry.is_regular_file()) {
    3712           3 :             currentFile = entry.path().filename();
    3713             :             // Only add the backups for the current wallet, e.g. wallet.dat.*
    3714           3 :             if (fs::PathToString(entry.path().stem()) == strWalletName) {
    3715           3 :                 folder_set.insert(decltype(folder_set)::value_type(fs::last_write_time(entry.path()), entry));
    3716           3 :             }
    3717           3 :         }
    3718             :     }
    3719             : 
    3720             :     // Loop backward through backup files and keep the N newest ones (1 <= N <= 10)
    3721           3 :     int counter{0};
    3722           6 :     for (const auto& [entry_time, entry] : folder_set | std::views::reverse) {
    3723           3 :         counter++;
    3724           3 :         if (counter > nWalletBackups) {
    3725             :             // More than nWalletBackups backups: delete oldest one(s)
    3726             :             try {
    3727           0 :                 fs::remove(entry);
    3728           0 :                 WalletLogPrintf("Old backup deleted: %s\n", fs::PathToString(entry));
    3729           0 :             } catch(fs::filesystem_error &error) {
    3730           0 :                 warnings.push_back(strprintf(_("Failed to delete backup, error: %s"), fsbridge::get_filesystem_error_message(error)));
    3731           0 :                 WalletLogPrintf("%s\n", Join(warnings, Untranslated("\n")).original);
    3732           0 :                 return false;
    3733           0 :             }
    3734           0 :         }
    3735             :     }
    3736             : 
    3737           3 :     return true;
    3738        3443 : }
    3739             : #elif defined(USE_SQLITE)
    3740             : bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
    3741             : {
    3742             :     WalletLogPrintf("Automatic wallet backups are currently only supported with Berkeley DB!\n");
    3743             :     return false;
    3744             : }
    3745             : #endif // USE_BDB
    3746             : 
    3747         244 : void CWallet::notifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr<const instantsend::InstantSendLock>& islock)
    3748             : {
    3749         244 :     LOCK(cs_wallet);
    3750             :     // Only notify UI if this transaction is in this wallet
    3751         244 :     uint256 txHash = tx->GetHash();
    3752         244 :     const auto mi = mapWallet.find(txHash);
    3753         244 :     if (mi != mapWallet.end()){
    3754         181 :         NotifyTransactionChanged(txHash, CT_UPDATED);
    3755         181 :         NotifyISLockReceived();
    3756             : #if HAVE_SYSTEM
    3757             :         // notify an external script
    3758         181 :         std::string strCmd = m_args.GetArg("-instantsendnotify", "");
    3759         181 :         if (!strCmd.empty()) {
    3760          20 :             ReplaceAll(strCmd, "%s", txHash.GetHex());
    3761             : #ifndef WIN32
    3762             :             // Substituting the wallet name isn't currently supported on windows
    3763             :             // because windows shell escaping has not been implemented yet:
    3764             :             // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875
    3765             :             // A few ways it could be implemented in the future are described in:
    3766             :             // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094
    3767          20 :             ReplaceAll(strCmd, "%w", ShellEscape(GetName()));
    3768             : #endif
    3769          20 :             std::thread t(runCommand, strCmd);
    3770          20 :             t.detach(); // thread runs free
    3771          20 :         }
    3772             : #endif
    3773         181 :     }
    3774         244 : }
    3775             : 
    3776        1768 : void CWallet::notifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr<const chainlock::ChainLockSig>& clsig)
    3777             : {
    3778        1768 :     NotifyChainLockReceived(pindexChainLock->nHeight);
    3779        1768 : }
    3780             : 
    3781          56 : bool CWallet::LoadGovernanceObject(const Governance::Object& obj)
    3782             : {
    3783          56 :     AssertLockHeld(cs_wallet);
    3784          56 :     return m_gobjects.emplace(obj.GetHash(), obj).second;
    3785             : }
    3786             : 
    3787          42 : bool CWallet::WriteGovernanceObject(const Governance::Object& obj)
    3788             : {
    3789          42 :     AssertLockHeld(cs_wallet);
    3790          42 :     WalletBatch batch(GetDatabase());
    3791          42 :     return batch.WriteGovernanceObject(obj) && LoadGovernanceObject(obj);
    3792          42 : }
    3793             : 
    3794          28 : std::vector<const Governance::Object*> CWallet::GetGovernanceObjects()
    3795             : {
    3796          28 :     AssertLockHeld(cs_wallet);
    3797          28 :     std::vector<const Governance::Object*> vecObjects;
    3798          28 :     vecObjects.reserve(m_gobjects.size());
    3799         166 :     for (auto& obj : m_gobjects) {
    3800         138 :         vecObjects.push_back(&obj.second);
    3801             :     }
    3802          28 :     return vecObjects;
    3803          28 : }
    3804             : 
    3805       84036 : CKeyPool::CKeyPool()
    3806       42018 : {
    3807       42018 :     nTime = GetTime();
    3808       42018 :     fInternal = false;
    3809       84036 : }
    3810             : 
    3811       87026 : CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool fInternalIn)
    3812       43513 : {
    3813       43513 :     nTime = GetTime();
    3814       43513 :     vchPubKey = vchPubKeyIn;
    3815       43513 :     fInternal = fInternalIn;
    3816       87026 : }
    3817             : 
    3818     4624104 : int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
    3819             : {
    3820     4624104 :     AssertLockHeld(cs_wallet);
    3821     4624104 :     if (auto* conf = wtx.state<TxStateConfirmed>()) {
    3822     4067221 :         return GetLastBlockHeight() - conf->confirmed_block_height + 1;
    3823      556883 :     } else if (auto* conf = wtx.state<TxStateConflicted>()) {
    3824        3718 :         return -1 * (GetLastBlockHeight() - conf->conflicting_block_height + 1);
    3825             :     } else {
    3826      553165 :         return 0;
    3827             :     }
    3828     4624104 : }
    3829             : 
    3830      289482 : bool CWallet::IsTxLockedByInstantSend(const CWalletTx& wtx) const
    3831             : {
    3832      289482 :     AssertLockHeld(cs_wallet);
    3833      289482 :     if (wtx.fIsChainlocked) {
    3834           0 :         wtx.fIsInstantSendLocked = false;
    3835      289482 :     } else if (!wtx.fIsInstantSendLocked) {
    3836      289287 :         wtx.fIsInstantSendLocked = chain().isInstantSendLockedTx(wtx.GetHash());
    3837      289287 :     }
    3838      289482 :     return wtx.fIsInstantSendLocked;
    3839             : }
    3840             : 
    3841        3675 : bool CWallet::IsTxChainLocked(const CWalletTx& wtx) const
    3842             : {
    3843        3675 :     AssertLockHeld(cs_wallet);
    3844        3675 :     if (!wtx.fIsChainlocked) {
    3845             :         bool active; int height;
    3846        3675 :         if (auto* conf = wtx.state<TxStateConfirmed>()) {
    3847        3675 :             if (chain().findBlock(conf->confirmed_block_hash, FoundBlock().inActiveChain(active).height(height)) && active) {
    3848        3675 :                 wtx.fIsChainlocked = chain().hasChainLock(height, conf->confirmed_block_hash);
    3849        3675 :             }
    3850        3675 :         }
    3851        3675 :     }
    3852        3675 :     return wtx.fIsChainlocked;
    3853             : }
    3854             : 
    3855     2591753 : int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const
    3856             : {
    3857     2591753 :     AssertLockHeld(cs_wallet);
    3858             : 
    3859     2591753 :     if (!wtx.IsCoinBase()) {
    3860      349966 :         return 0;
    3861             :     }
    3862     2241787 :     int chain_depth = GetTxDepthInMainChain(wtx);
    3863     2241787 :     assert(chain_depth >= 0); // coinbase tx should not be conflicted
    3864     2241787 :     return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
    3865     2591753 : }
    3866             : 
    3867     2430784 : bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const
    3868             : {
    3869     2430784 :     AssertLockHeld(cs_wallet);
    3870             : 
    3871             :     // note GetBlocksToMaturity is 0 for non-coinbase tx
    3872     2430784 :     return GetTxBlocksToMaturity(wtx) > 0;
    3873             : }
    3874             : 
    3875       64942 : bool CWallet::IsCrypted() const
    3876             : {
    3877       64942 :     return HasEncryptionKeys();
    3878             : }
    3879             : 
    3880             : // This function should be used in a different combinations to determine
    3881             : // if FillableSigningProvider is fully locked so that no operations requiring access
    3882             : // to private keys are possible:
    3883             : //      IsLocked(true)
    3884             : // or if FillableSigningProvider's private keys are available for mixing only:
    3885             : //      !IsLocked(true) && IsLocked()
    3886             : // or if they are available for everything:
    3887             : //      !IsLocked()
    3888       60711 : bool CWallet::IsLocked(bool fForMixing) const
    3889             : {
    3890       60711 :     if (!IsCrypted())
    3891       53716 :         return false;
    3892             : 
    3893        6995 :     if(!fForMixing && fOnlyMixingAllowed) return true;
    3894             : 
    3895        6995 :     LOCK(cs_wallet);
    3896        6995 :     return vMasterKey.empty();
    3897       60711 : }
    3898             : 
    3899         266 : bool CWallet::Lock(bool fAllowMixing)
    3900             : {
    3901         266 :     if (!IsCrypted())
    3902           0 :         return false;
    3903             : 
    3904         266 :     if(!fAllowMixing) {
    3905         266 :         LOCK(cs_wallet);
    3906         266 :         if (!vMasterKey.empty()) {
    3907         166 :             memory_cleanse(vMasterKey.data(), vMasterKey.size() * sizeof(decltype(vMasterKey)::value_type));
    3908         166 :             vMasterKey.clear();
    3909         166 :         }
    3910         266 :     }
    3911             : 
    3912         266 :     fOnlyMixingAllowed = fAllowMixing;
    3913         266 :     NotifyStatusChanged(this);
    3914         266 :     return true;
    3915         266 : }
    3916             : 
    3917         264 : bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly)
    3918             : {
    3919         264 :     if (!IsLocked()) // was already fully unlocked, not only for mixing
    3920          12 :         return true;
    3921             : 
    3922         252 :     CCrypter crypter;
    3923         252 :     CKeyingMaterial _vMasterKey;
    3924             : 
    3925             :     {
    3926         252 :         LOCK(cs_wallet);
    3927         274 :         for (const MasterKeyMap::value_type& pMasterKey : mapMasterKeys)
    3928             :         {
    3929         252 :             if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
    3930           0 :                 return false;
    3931         252 :             if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
    3932          22 :                 continue; // try another master key
    3933         230 :             if (Unlock(_vMasterKey, fForMixingOnly)) {
    3934             :                 // Now that we've unlocked, upgrade the key metadata
    3935         230 :                 UpgradeKeyMetadata();
    3936             :                 // Now that we've unlocked, upgrade the descriptor cache
    3937         230 :                 UpgradeDescriptorCache();
    3938         230 :                 if(nWalletBackups == -2) {
    3939           0 :                     TopUpKeyPool();
    3940           0 :                     WalletLogPrintf("Keypool replenished, re-initializing automatic backups.\n");
    3941           0 :                     nWalletBackups = m_args.GetIntArg("-createwalletbackups", 10);
    3942           0 :                 }
    3943         230 :                 return true;
    3944             :             }
    3945             :         }
    3946         252 :     }
    3947          22 :     return false;
    3948         264 : }
    3949             : 
    3950         238 : bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly)
    3951             : {
    3952             :     {
    3953         238 :         LOCK(cs_wallet);
    3954         708 :         for (const auto& spk_man_pair : m_spk_managers) {
    3955         470 :             if (!spk_man_pair.second->CheckDecryptionKey(vMasterKeyIn)) {
    3956           0 :                 return false;
    3957             :             }
    3958             :         }
    3959         238 :         vMasterKey = vMasterKeyIn;
    3960         238 :         fOnlyMixingAllowed = fForMixingOnly;
    3961         238 :     }
    3962         238 :     NotifyStatusChanged(this);
    3963         238 :     return true;
    3964         238 : }
    3965             : 
    3966       32170 : std::set<ScriptPubKeyMan*> CWallet::GetActiveScriptPubKeyMans() const
    3967             : {
    3968       32170 :     std::set<ScriptPubKeyMan*> spk_mans;
    3969       96510 :     for (bool internal : {false, true}) {
    3970       64340 :         auto spk_man = GetScriptPubKeyMan(internal);
    3971       64340 :         if (spk_man) {
    3972       47322 :             spk_mans.insert(spk_man);
    3973       47322 :         }
    3974             :     }
    3975       32170 :     return spk_mans;
    3976       32170 : }
    3977             : 
    3978       17186 : std::set<ScriptPubKeyMan*> CWallet::GetAllScriptPubKeyMans() const
    3979             : {
    3980       17186 :     std::set<ScriptPubKeyMan*> spk_mans;
    3981       50320 :     for (const auto& spk_man_pair : m_spk_managers) {
    3982       33134 :         spk_mans.insert(spk_man_pair.second.get());
    3983             :     }
    3984       17186 :     return spk_mans;
    3985       17186 : }
    3986             : 
    3987      132227 : ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(bool internal) const
    3988             : {
    3989      132227 :     const auto spk_manager = internal ? m_internal_spk_managers : m_external_spk_managers;
    3990      132227 :     if (spk_manager == nullptr) {
    3991       17146 :         return nullptr;
    3992             :     }
    3993      115081 :     return spk_manager;
    3994      132227 : }
    3995             : 
    3996      311482 : std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const
    3997             : {
    3998      311482 :     std::set<ScriptPubKeyMan*> spk_mans;
    3999      311482 :     SignatureData sigdata;
    4000      941535 :     for (const auto& spk_man_pair : m_spk_managers) {
    4001      630053 :         if (spk_man_pair.second->CanProvide(script, sigdata)) {
    4002      186561 :             spk_mans.insert(spk_man_pair.second.get());
    4003      186561 :         }
    4004             :     }
    4005      311482 :     return spk_mans;
    4006      311482 : }
    4007             : 
    4008        5029 : ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const
    4009             : {
    4010        5029 :     if (m_spk_managers.count(id) > 0) {
    4011        5029 :         return m_spk_managers.at(id).get();
    4012             :     }
    4013           0 :     return nullptr;
    4014        5029 : }
    4015             : 
    4016      780625 : std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script) const
    4017             : {
    4018      780625 :     SignatureData sigdata;
    4019      780625 :     return GetSolvingProvider(script, sigdata);
    4020      780625 : }
    4021             : 
    4022      780625 : std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script, SignatureData& sigdata) const
    4023             : {
    4024     1473575 :     for (const auto& spk_man_pair : m_spk_managers) {
    4025     1362865 :         if (spk_man_pair.second->CanProvide(script, sigdata)) {
    4026      669915 :             return spk_man_pair.second->GetSolvingProvider(script);
    4027             :         }
    4028             :     }
    4029      110710 :     return nullptr;
    4030      780625 : }
    4031             : 
    4032       96478 : LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const
    4033             : {
    4034       96478 :     if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    4035        5194 :         return nullptr;
    4036             :     }
    4037             :     // Legacy wallets only have one ScriptPubKeyMan which is a LegacyScriptPubKeyMan.
    4038             :     // Everything in m_internal_spk_managers and m_external_spk_managers point to the same legacyScriptPubKeyMan.
    4039       91284 :     if (m_internal_spk_managers == nullptr) return nullptr;
    4040       87514 :     return dynamic_cast<LegacyScriptPubKeyMan*>(m_internal_spk_managers);
    4041       96478 : }
    4042             : 
    4043       72008 : LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan()
    4044             : {
    4045       72008 :     SetupLegacyScriptPubKeyMan();
    4046       72008 :     return GetLegacyScriptPubKeyMan();
    4047             : }
    4048             : 
    4049       73947 : void CWallet::SetupLegacyScriptPubKeyMan()
    4050             : {
    4051       73947 :     if (m_internal_spk_managers || m_external_spk_managers || !m_spk_managers.empty() || IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    4052       72456 :         return;
    4053             :     }
    4054             : 
    4055        1491 :     auto spk_manager = std::make_unique<LegacyScriptPubKeyMan>(*this);
    4056        1491 :     m_internal_spk_managers = spk_manager.get();
    4057        1491 :     m_external_spk_managers = spk_manager.get();
    4058        1491 :     m_spk_managers[spk_manager->GetID()] = std::move(spk_manager);
    4059       73947 : }
    4060             : 
    4061        4271 : bool CWallet::WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const
    4062             : {
    4063        4271 :     LOCK(cs_wallet);
    4064        4271 :     return cb(vMasterKey);
    4065        4271 : }
    4066             : 
    4067     2287115 : bool CWallet::HasEncryptionKeys() const
    4068             : {
    4069     2287115 :     return !mapMasterKeys.empty();
    4070             : }
    4071             : 
    4072        2645 : void CWallet::ConnectScriptPubKeyManNotifiers()
    4073             : {
    4074        5959 :     for (const auto& spk_man : GetActiveScriptPubKeyMans()) {
    4075        3314 :         spk_man->NotifyWatchonlyChanged.connect(NotifyWatchonlyChanged);
    4076        3314 :         spk_man->NotifyCanGetAddressesChanged.connect(NotifyCanGetAddressesChanged);
    4077             :     }
    4078        2645 : }
    4079             : 
    4080          94 : void CWallet::UpdateProgress(const std::string& title, int nProgress)
    4081             : {
    4082          94 :     ShowProgress(title, nProgress);
    4083          94 : }
    4084             : 
    4085        1296 : void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
    4086             : {
    4087        1296 :     if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
    4088           0 :         auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, desc));
    4089           0 :         m_spk_managers[id] = std::move(spk_manager);
    4090           0 :     } else {
    4091        1296 :         auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
    4092        1296 :         m_spk_managers[id] = std::move(spk_manager);
    4093        1296 :     }
    4094        1296 : }
    4095             : 
    4096         395 : void CWallet::SetupDescriptorScriptPubKeyMans(const CExtKey& master_key, const SecureString& mnemonic, const SecureString mnemonic_passphrase)
    4097             : {
    4098         395 :     AssertLockHeld(cs_wallet);
    4099             : 
    4100        1580 :     for (auto type : {PathDerivationType::BIP44_External, PathDerivationType::BIP44_Internal, PathDerivationType::DIP0009_CoinJoin}) {
    4101             :         { // OUTPUT_TYPE is only one: LEGACY
    4102        1185 :             auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this));
    4103        1185 :             if (IsCrypted()) {
    4104          24 :                 if (IsLocked()) {
    4105           0 :                     throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
    4106             :                 }
    4107          24 :                 if (!spk_manager->CheckDecryptionKey(vMasterKey) && !spk_manager->Encrypt(vMasterKey, nullptr)) {
    4108           0 :                     throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors");
    4109             :                 }
    4110          24 :             }
    4111        1185 :             spk_manager->SetupDescriptorGeneration(master_key, mnemonic, mnemonic_passphrase, type);
    4112        1185 :             uint256 id = spk_manager->GetID();
    4113        1185 :             m_spk_managers[id] = std::move(spk_manager);
    4114        1185 :             if (type != PathDerivationType::DIP0009_CoinJoin) {
    4115         790 :                 AddActiveScriptPubKeyMan(id, type == PathDerivationType::BIP44_Internal);
    4116         790 :             }
    4117        1185 :         }
    4118             :     }
    4119         395 : }
    4120             : 
    4121         377 : void CWallet::SetupDescriptorScriptPubKeyMans(const SecureString& mnemonic_arg, const SecureString mnemonic_passphrase)
    4122             : {
    4123         377 :     AssertLockHeld(cs_wallet);
    4124             : 
    4125         377 :     if (!IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
    4126             :     // Make a seed
    4127             :     // TODO: remove duplicated code with CHDChain::SetMnemonic
    4128         375 :     const SecureString mnemonic = mnemonic_arg.empty() ? CMnemonic::Generate(m_args.GetIntArg("-mnemonicbits", CHDChain::DEFAULT_MNEMONIC_BITS)) : mnemonic_arg;
    4129         373 :     if (!CMnemonic::Check(mnemonic)) {
    4130           0 :         throw std::runtime_error(std::string(__func__) + ": invalid mnemonic: `" + std::string(mnemonic) + "`");
    4131             :     }
    4132         373 :     SecureVector seed_key;
    4133         373 :     CMnemonic::ToSeed(mnemonic, mnemonic_passphrase, seed_key);
    4134             : 
    4135             :     // Get the extended key
    4136         373 :     CExtKey master_key;
    4137         373 :     master_key.SetSeed(MakeByteSpan(seed_key));
    4138             : 
    4139         373 :         SetupDescriptorScriptPubKeyMans(master_key, mnemonic, mnemonic_passphrase);
    4140         373 :     } else {
    4141           4 :         ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
    4142             : 
    4143             :         // TODO: add account parameter
    4144           4 :         int account = 0;
    4145           4 :         UniValue signer_res = signer.GetDescriptors(account);
    4146             : 
    4147           4 :         if (!signer_res.isObject()) throw std::runtime_error(std::string(__func__) + ": Unexpected result");
    4148           8 :         for (bool internal : {false, true}) {
    4149           6 :             const UniValue& descriptor_vals = signer_res.find_value(internal ? "internal" : "receive");
    4150           6 :             if (!descriptor_vals.isArray()) throw std::runtime_error(std::string(__func__) + ": Unexpected result");
    4151          10 :             for (const UniValue& desc_val : descriptor_vals.get_array().getValues()) {
    4152           6 :                 const std::string& desc_str = desc_val.getValStr();
    4153           6 :                 FlatSigningProvider keys;
    4154           6 :                 std::string desc_error;
    4155           6 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, desc_error, false);
    4156           6 :                 if (desc == nullptr) {
    4157           2 :                     throw std::runtime_error(std::string(__func__) + ": Invalid descriptor \"" + desc_str + "\" (" + desc_error + ")");
    4158             :                 }
    4159           4 :                 if (!desc->GetOutputType()) {
    4160           0 :                     continue;
    4161             :                 }
    4162           4 :                 auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this));
    4163           4 :                 spk_manager->SetupDescriptor(std::move(desc));
    4164           4 :                 uint256 id = spk_manager->GetID();
    4165           4 :                 m_spk_managers[id] = std::move(spk_manager);
    4166             :                 // Only activate BIP44 descriptors, similar to how the non-external-signer branch
    4167             :                 // only activates certain types (External and Internal, but not CoinJoin)
    4168           4 :                 std::string bip44_purpose = strprintf("/%d'/%s'", BIP32_PURPOSE_STANDARD, Params().ExtCoinType());
    4169           4 :                 if (desc_str.find(bip44_purpose) != std::string::npos) {
    4170           4 :                     AddActiveScriptPubKeyMan(id, internal);
    4171           4 :                 }
    4172           6 :             }
    4173             :         }
    4174           4 :     }
    4175         377 : }
    4176             : 
    4177         895 : void CWallet::AddActiveScriptPubKeyMan(uint256 id, bool internal)
    4178             : {
    4179         895 :     WalletBatch batch(GetDatabase());
    4180         895 :     if (!batch.WriteActiveScriptPubKeyMan(id, internal)) {
    4181           0 :         throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed");
    4182             :     }
    4183         895 :     LoadActiveScriptPubKeyMan(id, internal);
    4184         895 : }
    4185             : 
    4186        1546 : void CWallet::LoadActiveScriptPubKeyMan(uint256 id, bool internal)
    4187             : {
    4188             :     // Activating ScriptPubKeyManager for a given output and change type is incompatible with legacy wallets.
    4189             :     // Legacy wallets have only one ScriptPubKeyManager and it's active for all output and change types.
    4190        1546 :     Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
    4191             : 
    4192        1546 :     WalletLogPrintf("Setting spkMan to active: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(OutputType::LEGACY), internal ? "true" : "false");
    4193        1546 :     auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
    4194        1546 :     auto& spk_mans_other = internal ? m_external_spk_managers : m_internal_spk_managers;
    4195        1546 :     auto spk_man = m_spk_managers.at(id).get();
    4196        1546 :     spk_mans = spk_man;
    4197        1546 :     if (spk_mans_other == spk_man) {
    4198           4 :         spk_mans_other = nullptr;
    4199           4 :     }
    4200             : 
    4201        1546 :     NotifyCanGetAddressesChanged();
    4202             : 
    4203        1546 : }
    4204             : 
    4205         311 : void CWallet::DeactivateScriptPubKeyMan(uint256 id, bool internal)
    4206             : {
    4207         311 :     auto spk_man = GetScriptPubKeyMan(internal);
    4208         311 :     if (spk_man != nullptr && spk_man->GetID() == id) {
    4209           2 :         WalletLogPrintf("Deactivate spkMan: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(OutputType::LEGACY), internal ? "true" : "false");
    4210           2 :         WalletBatch batch(GetDatabase());
    4211           2 :         if (!batch.EraseActiveScriptPubKeyMan(internal)) {
    4212           0 :             throw std::runtime_error(std::string(__func__) + ": erasing active ScriptPubKeyMan id failed");
    4213             :         }
    4214             : 
    4215           2 :         auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
    4216           2 :         spk_mans = nullptr;
    4217           2 :     }
    4218             : 
    4219         311 :     NotifyCanGetAddressesChanged();
    4220         311 : }
    4221             : 
    4222       25186 : bool CWallet::IsLegacy() const
    4223             : {
    4224       25186 :     if (m_internal_spk_managers == nullptr) return false;
    4225       16473 :     auto spk_man = dynamic_cast<LegacyScriptPubKeyMan*>(m_internal_spk_managers);
    4226       16473 :     return spk_man != nullptr;
    4227       25186 : }
    4228             : 
    4229         856 : DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const
    4230             : {
    4231        2779 :     for (auto& spk_man_pair : m_spk_managers) {
    4232             :         // Try to downcast to DescriptorScriptPubKeyMan then check if the descriptors match
    4233        1985 :         DescriptorScriptPubKeyMan* spk_manager = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man_pair.second.get());
    4234        1985 :         if (spk_manager != nullptr && spk_manager->HasWalletDescriptor(desc)) {
    4235          62 :             return spk_manager;
    4236             :         }
    4237             :     }
    4238             : 
    4239         794 :     return nullptr;
    4240         856 : }
    4241             : 
    4242       19042 : std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const
    4243             : {
    4244             :     // Legacy script pubkey man can't be either external or internal
    4245       19042 :     if (IsLegacy()) {
    4246           0 :         return std::nullopt;
    4247             :     }
    4248             : 
    4249             :     // only active ScriptPubKeyMan can be internal
    4250       19042 :     if (!GetActiveScriptPubKeyMans().count(spk_man)) {
    4251       11179 :         return std::nullopt;
    4252             :     }
    4253             : 
    4254        7863 :     const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
    4255        7863 :     if (!desc_spk_man) {
    4256           0 :         throw std::runtime_error(std::string(__func__) + ": unexpected ScriptPubKeyMan type.");
    4257             :     }
    4258             : 
    4259        7863 :     LOCK(desc_spk_man->cs_desc_man);
    4260        7863 :     const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType();
    4261        7863 :     assert(type.has_value());
    4262             : 
    4263        7863 :     return GetScriptPubKeyMan(/*internal=*/true) == desc_spk_man;
    4264       19042 : }
    4265             : 
    4266         436 : ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal)
    4267             : {
    4268         436 :     AssertLockHeld(cs_wallet);
    4269             : 
    4270         436 :     if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    4271           0 :         WalletLogPrintf("Cannot add WalletDescriptor to a non-descriptor wallet\n");
    4272           0 :         return nullptr;
    4273             :     }
    4274             : 
    4275         436 :     SecureString mnemonic;
    4276         436 :     SecureString mnemonic_passphrase;
    4277             : 
    4278         436 :     auto spk_man = GetDescriptorScriptPubKeyMan(desc);
    4279         436 :     if (spk_man) {
    4280          28 :         WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
    4281          28 :         spk_man->UpdateWalletDescriptor(desc);
    4282          28 :     } else {
    4283         408 :         auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
    4284         408 :         spk_man = new_spk_man.get();
    4285             : 
    4286             :         // Save the descriptor to memory
    4287         408 :         m_spk_managers[new_spk_man->GetID()] = std::move(new_spk_man);
    4288         408 :     }
    4289             : 
    4290             :     // Add the private keys to the descriptor
    4291         756 :     for (const auto& entry : signing_provider.keys) {
    4292         320 :         const CKey& key = entry.second;
    4293         320 :         spk_man->AddDescriptorKey(key, key.GetPubKey());
    4294             :     }
    4295             : 
    4296             :     // Top up key pool, the manager will generate new scriptPubKeys internally
    4297         436 :     if (!spk_man->TopUp()) {
    4298           0 :         WalletLogPrintf("Could not top up scriptPubKeys\n");
    4299           0 :         return nullptr;
    4300             :     }
    4301             : 
    4302             :     // Apply the label if necessary
    4303             :     // Note: we disable labels for ranged descriptors
    4304         436 :     if (!desc.descriptor->IsRange()) {
    4305         314 :         auto script_pub_keys = spk_man->GetScriptPubKeys();
    4306         314 :         if (script_pub_keys.empty()) {
    4307           0 :             WalletLogPrintf("Could not generate scriptPubKeys (cache is empty)\n");
    4308           0 :             return nullptr;
    4309             :         }
    4310             : 
    4311         314 :         if (!internal) {
    4312        1110 :             for (const auto& script : script_pub_keys) {
    4313         798 :                 CTxDestination dest;
    4314         798 :                 if (ExtractDestination(script, dest)) {
    4315         796 :                     SetAddressBook(dest, label, "receive");
    4316         796 :                 }
    4317             :             }
    4318         312 :         }
    4319         314 :     }
    4320             : 
    4321             :     // Save the descriptor to DB
    4322         436 :     spk_man->WriteDescriptor();
    4323             : 
    4324         436 :     return spk_man;
    4325         436 : }
    4326             : 
    4327          26 : bool CWallet::MigrateToSQLite(bilingual_str& error)
    4328             : {
    4329          26 :     AssertLockHeld(cs_wallet);
    4330             : 
    4331          26 :     WalletLogPrintf("Migrating wallet storage database from BerkeleyDB to SQLite.\n");
    4332             : 
    4333          26 :     if (m_database->Format() == "sqlite") {
    4334           0 :         error = _("Error: This wallet already uses SQLite");
    4335           0 :         return false;
    4336             :     }
    4337             : 
    4338             :     // Get all of the records for DB type migration
    4339          26 :     std::unique_ptr<DatabaseBatch> batch = m_database->MakeBatch();
    4340          26 :     std::vector<std::pair<SerializeData, SerializeData>> records;
    4341          26 :     if (!batch->StartCursor()) {
    4342           0 :         error = _("Error: Unable to begin reading all records in the database");
    4343           0 :         return false;
    4344             :     }
    4345          26 :     bool complete = false;
    4346         722 :     while (true) {
    4347         722 :         CDataStream ss_key(SER_DISK, CLIENT_VERSION);
    4348         722 :         CDataStream ss_value(SER_DISK, CLIENT_VERSION);
    4349         722 :         bool ret = batch->ReadAtCursor(ss_key, ss_value, complete);
    4350         722 :         if (!ret) {
    4351          26 :             break;
    4352             :         }
    4353         696 :         SerializeData key(ss_key.begin(), ss_key.end());
    4354         696 :         SerializeData value(ss_value.begin(), ss_value.end());
    4355         696 :         records.emplace_back(key, value);
    4356         722 :     }
    4357          26 :     batch->CloseCursor();
    4358          26 :     batch.reset();
    4359          26 :     if (!complete) {
    4360           0 :         error = _("Error: Unable to read all records in the database");
    4361           0 :         return false;
    4362             :     }
    4363             : 
    4364             :     // Close this database and delete the file
    4365          26 :     fs::path db_path = fs::PathFromString(m_database->Filename());
    4366          26 :     fs::path db_dir = db_path.parent_path();
    4367          26 :     m_database->Close();
    4368          26 :     fs::remove(db_path);
    4369             : 
    4370             :     // Make new DB
    4371          26 :     DatabaseOptions opts;
    4372          26 :     opts.require_create = true;
    4373          26 :     opts.require_format = DatabaseFormat::SQLITE;
    4374             :     DatabaseStatus db_status;
    4375          26 :     std::unique_ptr<WalletDatabase> new_db = MakeDatabase(db_dir, opts, db_status, error);
    4376          26 :     assert(new_db); // This is to prevent doing anything further with this wallet. The original file was deleted, but a backup exists.
    4377          26 :     m_database.reset();
    4378          26 :     m_database = std::move(new_db);
    4379             : 
    4380             :     // Write existing records into the new DB
    4381          26 :     batch = m_database->MakeBatch();
    4382          26 :     bool began = batch->TxnBegin();
    4383          26 :     assert(began); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.
    4384        1418 :     for (const auto& [key, value] : records) {
    4385         696 :         CDataStream ss_key(key, SER_DISK, CLIENT_VERSION);
    4386         696 :         CDataStream ss_value(value, SER_DISK, CLIENT_VERSION);
    4387         696 :         if (!batch->Write(ss_key, ss_value)) {
    4388           0 :             batch->TxnAbort();
    4389           0 :             m_database->Close();
    4390           0 :             fs::remove(m_database->Filename());
    4391           0 :             assert(false); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.
    4392             :         }
    4393         696 :     }
    4394          26 :     bool committed = batch->TxnCommit();
    4395          26 :     assert(committed); // This is a critical error, the new db could not be written to. The original db exists as a backup, but we should not continue execution.
    4396          26 :     return true;
    4397          26 : }
    4398             : 
    4399          26 : std::optional<MigrationData> CWallet::GetDescriptorsForLegacy(bilingual_str& error) const
    4400             : {
    4401          26 :     AssertLockHeld(cs_wallet);
    4402             : 
    4403          26 :     LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
    4404          26 :     assert(legacy_spkm);
    4405             : 
    4406          26 :     std::optional<MigrationData> res = legacy_spkm->MigrateToDescriptor();
    4407          26 :     if (res == std::nullopt) {
    4408           0 :         error = _("Error: Unable to produce descriptors for this legacy wallet. Make sure to provide the wallet's passphrase if it is encrypted.");
    4409           0 :         return std::nullopt;
    4410             :     }
    4411          26 :     return res;
    4412          26 : }
    4413             : 
    4414          26 : bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
    4415             : {
    4416          26 :     AssertLockHeld(cs_wallet);
    4417             : 
    4418          26 :     LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
    4419          26 :     if (!legacy_spkm) {
    4420           0 :         error = _("Error: This wallet is already a descriptor wallet");
    4421           0 :         return false;
    4422             :     }
    4423             : 
    4424          84 :     for (auto& desc_spkm : data.desc_spkms) {
    4425          58 :         if (m_spk_managers.count(desc_spkm->GetID()) > 0) {
    4426           0 :             error = _("Error: Duplicate descriptors created during migration. Your wallet may be corrupted.");
    4427           0 :             return false;
    4428             :         }
    4429          58 :         m_spk_managers[desc_spkm->GetID()] = std::move(desc_spkm);
    4430             :     }
    4431             : 
    4432             :     // Remove the LegacyScriptPubKeyMan from disk
    4433          26 :     if (!legacy_spkm->DeleteRecords()) {
    4434           0 :         return false;
    4435             :     }
    4436             : 
    4437             :     // Remove the LegacyScriptPubKeyMan from memory
    4438          26 :     m_spk_managers.erase(legacy_spkm->GetID());
    4439          26 :     m_external_spk_managers = nullptr;
    4440          26 :     m_internal_spk_managers = nullptr;
    4441             : 
    4442             :     // Setup new descriptors
    4443          26 :     SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
    4444          26 :     if (!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
    4445             :         // Use the existing master key if we have it
    4446          22 :         if (data.master_key.key.IsValid()) {
    4447          22 :             SetupDescriptorScriptPubKeyMans(data.master_key, data.mnemonic, data.mnemonic_passphrase);
    4448             : 
    4449             :             // Advance the active pkh() descriptors past the legacy chain
    4450             :             // counters so post-migration getnewaddress doesn't hand back
    4451             :             // addresses the legacy wallet already derived. The historical
    4452             :             // scripts themselves are still recognized by the inactive combo
    4453             :             // descriptors built in MigrateToDescriptor.
    4454          66 :             for (bool internal : {false, true}) {
    4455          44 :                 auto* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(GetScriptPubKeyMan(/*internal=*/internal));
    4456          44 :                 if (desc_spk_man == nullptr) continue;
    4457          44 :                 const uint32_t counter = internal ? data.internal_chain_counter : data.external_chain_counter;
    4458          44 :                 if (counter > 0 && !desc_spk_man->AdvanceNextIndexTo(counter)) {
    4459           0 :                     error = _("Error: Failed to advance active descriptor past legacy chain counter during migration.");
    4460           0 :                     return false;
    4461             :                 }
    4462             :             }
    4463          22 :         } else {
    4464             :             // Setup with a new seed if we don't.
    4465           0 :             SetupDescriptorScriptPubKeyMans("", "");
    4466             :         }
    4467          22 :     }
    4468             : 
    4469             :     // Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet.
    4470             :     // We need to go through these in the tx insertion order so that lookups to spends works.
    4471          26 :     std::vector<uint256> txids_to_delete;
    4472         124 :     for (const auto& [_pos, wtx] : wtxOrdered) {
    4473          56 :         if (!IsMine(*wtx->tx) && !IsFromMe(*wtx->tx)) {
    4474             :             // Check it is the watchonly wallet's
    4475             :             // solvable_wallet doesn't need to be checked because transactions for those scripts weren't being watched for
    4476           8 :             if (data.watchonly_wallet) {
    4477           8 :                 LOCK(data.watchonly_wallet->cs_wallet);
    4478          16 :                 if (data.watchonly_wallet->IsMine(*wtx->tx) || data.watchonly_wallet->IsFromMe(*wtx->tx)) {
    4479             :                     // Add to watchonly wallet
    4480          24 :                     if (!data.watchonly_wallet->AddToWallet(wtx->tx, wtx->m_state)) {
    4481           0 :                         error = _("Error: Could not add watchonly tx to watchonly wallet");
    4482           0 :                         return false;
    4483             :                     }
    4484             :                     // Mark as to remove from this wallet
    4485           8 :                     txids_to_delete.push_back(wtx->GetHash());
    4486           8 :                     continue;
    4487             :                 }
    4488           8 :             }
    4489             :             // Both not ours and not in the watchonly wallet
    4490           0 :             error = strprintf(_("Error: Transaction %s in wallet cannot be identified to belong to migrated wallets"), wtx->GetHash().GetHex());
    4491           0 :             return false;
    4492             :         }
    4493             :     }
    4494             :     // Do the removes
    4495          26 :     if (txids_to_delete.size() > 0) {
    4496           4 :         std::vector<uint256> deleted_txids;
    4497           4 :         if (ZapSelectTx(txids_to_delete, deleted_txids) != DBErrors::LOAD_OK) {
    4498           0 :             error = _("Error: Could not delete watchonly transactions");
    4499           0 :             return false;
    4500             :         }
    4501           4 :         if (deleted_txids != txids_to_delete) {
    4502           0 :             error = _("Error: Not all watchonly txs could be deleted");
    4503           0 :             return false;
    4504             :         }
    4505             :         // Tell the GUI of each tx
    4506          12 :         for (const uint256& txid : deleted_txids) {
    4507           8 :             NotifyTransactionChanged(txid, CT_UPDATED);
    4508             :         }
    4509           4 :     }
    4510             : 
    4511             :     // Check the address book data in the same way we did for transactions
    4512          26 :     std::vector<CTxDestination> dests_to_delete;
    4513          94 :     for (const auto& addr_pair : m_address_book) {
    4514             :         // Labels applied to receiving addresses should go based on IsMine
    4515          68 :         if (addr_pair.second.purpose == "receive") {
    4516          62 :             if (!IsMine(addr_pair.first)) {
    4517             :                 // Check the address book data is the watchonly wallet's
    4518           6 :                 if (data.watchonly_wallet) {
    4519           6 :                     LOCK(data.watchonly_wallet->cs_wallet);
    4520           6 :                     if (data.watchonly_wallet->IsMine(addr_pair.first)) {
    4521             :                         // Add to the watchonly. Preserve the labels, purpose, and change-ness
    4522           6 :                         std::string label = addr_pair.second.GetLabel();
    4523           6 :                         std::string purpose = addr_pair.second.purpose;
    4524           6 :                         if (!purpose.empty()) {
    4525           6 :                             data.watchonly_wallet->m_address_book[addr_pair.first].purpose = purpose;
    4526           6 :                         }
    4527           6 :                         if (!addr_pair.second.IsChange()) {
    4528           6 :                             data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label);
    4529           6 :                         }
    4530           6 :                         dests_to_delete.push_back(addr_pair.first);
    4531             :                         continue;
    4532           6 :                     }
    4533           6 :                 }
    4534           0 :                 if (data.solvable_wallet) {
    4535           0 :                     LOCK(data.solvable_wallet->cs_wallet);
    4536           0 :                     if (data.solvable_wallet->IsMine(addr_pair.first)) {
    4537             :                         // Add to the solvable. Preserve the labels, purpose, and change-ness
    4538           0 :                         std::string label = addr_pair.second.GetLabel();
    4539           0 :                         std::string purpose = addr_pair.second.purpose;
    4540           0 :                         if (!purpose.empty()) {
    4541           0 :                             data.solvable_wallet->m_address_book[addr_pair.first].purpose = purpose;
    4542           0 :                         }
    4543           0 :                         if (!addr_pair.second.IsChange()) {
    4544           0 :                             data.solvable_wallet->m_address_book[addr_pair.first].SetLabel(label);
    4545           0 :                         }
    4546           0 :                         dests_to_delete.push_back(addr_pair.first);
    4547             :                         continue;
    4548           0 :                     }
    4549           0 :                 }
    4550             :                 // Not ours, not in watchonly wallet, and not in solvable
    4551           0 :                 error = _("Error: Address book data in wallet cannot be identified to belong to migrated wallets");
    4552           0 :                 return false;
    4553             :             }
    4554          56 :         } else {
    4555             :             // Labels for everything else (send) should be cloned to all
    4556           6 :             if (data.watchonly_wallet) {
    4557           4 :                 LOCK(data.watchonly_wallet->cs_wallet);
    4558             :                 // Add to the watchonly. Preserve the labels, purpose, and change-ness
    4559           4 :                 std::string label = addr_pair.second.GetLabel();
    4560           4 :                 std::string purpose = addr_pair.second.purpose;
    4561           4 :                 if (!purpose.empty()) {
    4562           4 :                     data.watchonly_wallet->m_address_book[addr_pair.first].purpose = purpose;
    4563           4 :                 }
    4564           4 :                 if (!addr_pair.second.IsChange()) {
    4565           4 :                     data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label);
    4566           4 :                 }
    4567             :                 continue;
    4568           4 :             }
    4569           2 :             if (data.solvable_wallet) {
    4570           0 :                 LOCK(data.solvable_wallet->cs_wallet);
    4571             :                 // Add to the solvable. Preserve the labels, purpose, and change-ness
    4572           0 :                 std::string label = addr_pair.second.GetLabel();
    4573           0 :                 std::string purpose = addr_pair.second.purpose;
    4574           0 :                 if (!purpose.empty()) {
    4575           0 :                     data.solvable_wallet->m_address_book[addr_pair.first].purpose = purpose;
    4576           0 :                 }
    4577           0 :                 if (!addr_pair.second.IsChange()) {
    4578           0 :                     data.solvable_wallet->m_address_book[addr_pair.first].SetLabel(label);
    4579           0 :                 }
    4580             :                 continue;
    4581           0 :             }
    4582             :         }
    4583             :     }
    4584             : 
    4585             :     // Persist added address book entries (labels, purpose) for watchonly and solvable wallets
    4586          32 :     auto persist_address_book = [](const CWallet& wallet) {
    4587           6 :         LOCK(wallet.cs_wallet);
    4588           6 :         WalletBatch batch{wallet.GetDatabase()};
    4589          18 :         for (const auto& [destination, addr_book_data] : wallet.m_address_book) {
    4590          12 :             auto address{EncodeDestination(destination)};
    4591          12 :             auto purpose{addr_book_data.purpose};
    4592          12 :             auto label{addr_book_data.GetLabel()};
    4593             :             // don't bother writing default values (unknown purpose, empty label)
    4594          12 :             if (purpose != "unknown") batch.WritePurpose(address, purpose);
    4595          12 :             if (!label.empty()) batch.WriteName(address, label);
    4596          12 :         }
    4597           6 :     };
    4598          26 :     if (data.watchonly_wallet) persist_address_book(*data.watchonly_wallet);
    4599          26 :     if (data.solvable_wallet) persist_address_book(*data.solvable_wallet);
    4600             : 
    4601             :     // Remove the things to delete
    4602          26 :     if (dests_to_delete.size() > 0) {
    4603          10 :         for (const auto& dest : dests_to_delete) {
    4604           6 :             if (!DelAddressBook(dest)) {
    4605           0 :                 error = _("Error: Unable to remove watchonly address book data");
    4606           0 :                 return false;
    4607             :             }
    4608             :         }
    4609           4 :     }
    4610             : 
    4611             :     // Connect the SPKM signals
    4612          26 :     ConnectScriptPubKeyManNotifiers();
    4613          26 :     NotifyCanGetAddressesChanged();
    4614             : 
    4615          26 :     WalletLogPrintf("Wallet migration complete.\n");
    4616             : 
    4617          26 :     return true;
    4618          26 : }
    4619             : 
    4620          26 : bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
    4621             : {
    4622          26 :     AssertLockHeld(wallet.cs_wallet);
    4623             : 
    4624             :     // Get all of the descriptors from the legacy wallet
    4625          26 :     std::optional<MigrationData> data = wallet.GetDescriptorsForLegacy(error);
    4626          26 :     if (data == std::nullopt) return false;
    4627             : 
    4628             :     // Create the watchonly and solvable wallets if necessary
    4629          26 :     if (data->watch_descs.size() > 0 || data->solvable_descs.size() > 0) {
    4630           4 :         DatabaseOptions options;
    4631           4 :         options.require_existing = false;
    4632           4 :         options.require_create = true;
    4633             : 
    4634             :         // Make the wallets
    4635           4 :         options.create_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_DESCRIPTORS;
    4636           4 :         if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
    4637           0 :             options.create_flags |= WALLET_FLAG_AVOID_REUSE;
    4638           0 :         }
    4639           4 :         if (wallet.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
    4640           4 :             options.create_flags |= WALLET_FLAG_KEY_ORIGIN_METADATA;
    4641           4 :         }
    4642           4 :         if (data->watch_descs.size() > 0) {
    4643           4 :             wallet.WalletLogPrintf("Making a new watchonly wallet containing the watched scripts\n");
    4644             : 
    4645             :             DatabaseStatus status;
    4646           4 :             std::vector<bilingual_str> warnings;
    4647           4 :             std::string wallet_name = wallet.GetName() + "_watchonly";
    4648           4 :             data->watchonly_wallet = CreateWallet(context, wallet_name, std::nullopt, options, status, error, warnings);
    4649           4 :             if (status != DatabaseStatus::SUCCESS) {
    4650           0 :                 error = _("Error: Failed to create new watchonly wallet");
    4651           0 :                 return false;
    4652             :             }
    4653           4 :             res.watchonly_wallet = data->watchonly_wallet;
    4654           4 :             LOCK(data->watchonly_wallet->cs_wallet);
    4655             : 
    4656             :             // Parse the descriptors and add them to the new wallet
    4657          16 :             for (const auto& [desc_str, creation_time] : data->watch_descs) {
    4658             :                 // Parse the descriptor
    4659           6 :                 FlatSigningProvider keys;
    4660           6 :                 std::string parse_err;
    4661           6 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, parse_err, /* require_checksum */ true);
    4662           6 :                 assert(desc); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor
    4663           6 :                 assert(!desc->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor
    4664             : 
    4665             :                 // Add to the wallet
    4666           6 :                 WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
    4667           6 :                 data->watchonly_wallet->AddWalletDescriptor(w_desc, keys, "", false);
    4668           6 :             }
    4669             : 
    4670             :             // Add the wallet to settings
    4671           4 :             UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings);
    4672           4 :         }
    4673           4 :         if (data->solvable_descs.size() > 0) {
    4674           2 :             wallet.WalletLogPrintf("Making a new watchonly wallet containing the unwatched solvable scripts\n");
    4675             : 
    4676             :             DatabaseStatus status;
    4677           2 :             std::vector<bilingual_str> warnings;
    4678           2 :             std::string wallet_name = wallet.GetName() + "_solvables";
    4679           2 :             data->solvable_wallet = CreateWallet(context, wallet_name, std::nullopt, options, status, error, warnings);
    4680           2 :             if (status != DatabaseStatus::SUCCESS) {
    4681           0 :                 error = _("Error: Failed to create new watchonly wallet");
    4682           0 :                 return false;
    4683             :             }
    4684           2 :             res.solvables_wallet = data->solvable_wallet;
    4685           2 :             LOCK(data->solvable_wallet->cs_wallet);
    4686             : 
    4687             :             // Parse the descriptors and add them to the new wallet
    4688           6 :             for (const auto& [desc_str, creation_time] : data->solvable_descs) {
    4689             :                 // Parse the descriptor
    4690           2 :                 FlatSigningProvider keys;
    4691           2 :                 std::string parse_err;
    4692           2 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, parse_err, /* require_checksum */ true);
    4693           2 :                 assert(desc); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor
    4694           2 :                 assert(!desc->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor
    4695             : 
    4696             :                 // Add to the wallet
    4697           2 :                 WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
    4698           2 :                 data->solvable_wallet->AddWalletDescriptor(w_desc, keys, "", false);
    4699           2 :             }
    4700             : 
    4701             :             // Add the wallet to settings
    4702           2 :             UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings);
    4703           2 :         }
    4704           4 :     }
    4705             : 
    4706             :     // Add the descriptors to wallet, remove LegacyScriptPubKeyMan, and cleanup txs and address book data
    4707          26 :     if (!wallet.ApplyMigrationData(*data, error)) {
    4708           0 :         return false;
    4709             :     }
    4710          26 :     return true;
    4711          26 : }
    4712             : 
    4713          34 : util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context)
    4714             : {
    4715          34 :     MigrationResult res;
    4716          34 :     bilingual_str error;
    4717          34 :     std::vector<bilingual_str> warnings;
    4718             : 
    4719             :     // If the wallet is still loaded, unload it so that nothing else tries to use it while we're changing it
    4720          58 :     if (auto wallet = GetWallet(context, wallet_name)) {
    4721          24 :         if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
    4722           0 :             return util::Error{_("Unable to unload the wallet before migrating")};
    4723             :         }
    4724          24 :         UnloadWallet(std::move(wallet));
    4725          24 :     }
    4726             : 
    4727             :     // Load the wallet but only in the context of this function.
    4728             :     // No signals should be connected nor should anything else be aware of this wallet
    4729          34 :     WalletContext empty_context;
    4730          34 :     empty_context.args = context.args;
    4731          34 :     DatabaseOptions options;
    4732          34 :     options.require_existing = true;
    4733             :     DatabaseStatus status;
    4734          34 :     std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error);
    4735          34 :     if (!database) {
    4736           0 :         return util::Error{Untranslated("Wallet file verification failed.") + Untranslated(" ") + error};
    4737             :     }
    4738             : 
    4739             :     // Make the local wallet
    4740          34 :     std::shared_ptr<CWallet> local_wallet = CWallet::Create(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings);
    4741          34 :     if (!local_wallet) {
    4742           0 :         return util::Error{Untranslated("Wallet loading failed.") + Untranslated(" ") + error};
    4743             :     }
    4744             : 
    4745             :     // Before anything else, check if there is something to migrate.
    4746          34 :     if (!local_wallet->GetLegacyScriptPubKeyMan()) {
    4747           2 :         return util::Error{_("Error: This wallet is already a descriptor wallet")};
    4748             :     }
    4749             : 
    4750             :     // Make a backup of the DB in the wallet's directory with a unique filename
    4751             :     // based on the wallet name and current timestamp. The backup filename uses
    4752             :     // the basename of the wallet path (or "wallet" if the name is blank) so that
    4753             :     // path-style wallet names cannot escape the wallet directory.
    4754             :     // TODO: backport bitcoin/bitcoin#32273 to also move the backup into the
    4755             :     //       top-level walletdir and unify with upstream.
    4756          32 :     fs::path this_wallet_dir = fs::absolute(fs::PathFromString(local_wallet->GetDatabase().Filename())).parent_path();
    4757          32 :     std::string backup_prefix;
    4758          32 :     if (!wallet_name.empty()) {
    4759             :         // fs::weakly_canonical resolves relative specifiers and removes trailing slashes.
    4760          32 :         const auto legacy_wallet_path = fs::weakly_canonical(GetWalletDir() / fs::PathFromString(wallet_name));
    4761          32 :         backup_prefix = fs::PathToString(legacy_wallet_path.filename());
    4762          32 :     }
    4763             :     // Fall back when the name was blank or canonicalizes to an empty filename
    4764             :     // (e.g. "/", "."), so we never emit a file like "-1748432384.legacy.bak".
    4765          32 :     if (backup_prefix.empty()) backup_prefix = "wallet";
    4766          32 :     fs::path backup_filename = fs::PathFromString(strprintf("%s-%d.legacy.bak", backup_prefix, GetTime()));
    4767          32 :     fs::path backup_path = this_wallet_dir / backup_filename;
    4768          32 :     if (!local_wallet->BackupWallet(fs::PathToString(backup_path))) {
    4769           0 :         return util::Error{_("Error: Unable to make a backup of your wallet")};
    4770             :     }
    4771          32 :     res.backup_path = backup_path;
    4772             : 
    4773          32 :     bool success = false;
    4774             :     {
    4775          32 :         LOCK(local_wallet->cs_wallet);
    4776             : 
    4777             :         // Unlock the wallet if needed
    4778          32 :         if (local_wallet->IsLocked() && !local_wallet->Unlock(passphrase)) {
    4779           6 :             if (passphrase.find('\0') == std::string::npos) {
    4780           4 :                 return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect.")};
    4781             :             } else {
    4782           2 :                 return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase entered was incorrect. "
    4783             :                                                 "The passphrase contains a null character (ie - a zero byte). "
    4784             :                                                 "If this passphrase was set with a version of this software prior to 25.0, "
    4785             :                                                 "please try again with only the characters up to — but not including — "
    4786             :                                                 "the first null character.")};
    4787             :             }
    4788             :         }
    4789             : 
    4790             :         // First change to using SQLite
    4791          26 :         if (!local_wallet->MigrateToSQLite(error)) return util::Error{error};
    4792             : 
    4793             :         // Do the migration, and cleanup if it fails
    4794          26 :         success = DoMigration(*local_wallet, context, error, res);
    4795          32 :     }
    4796             : 
    4797          26 :     if (success) {
    4798             :         // Migration successful, unload the wallet locally, then reload it.
    4799          26 :         assert(local_wallet.use_count() == 1);
    4800          26 :         local_wallet.reset();
    4801          26 :         LoadWallet(context, wallet_name, /*load_on_start=*/std::nullopt, options, status, error, warnings);
    4802          26 :         res.wallet_name = wallet_name;
    4803          26 :     } else {
    4804             :         // Migration failed, cleanup
    4805             :         // Copy the backup to the actual wallet dir
    4806           0 :         fs::path temp_backup_location = fsbridge::AbsPathJoin(GetWalletDir(), backup_filename);
    4807           0 :         fs::copy_file(backup_path, temp_backup_location, fs::copy_options::none);
    4808             : 
    4809             :         // Remember this wallet's walletdir to remove after unloading
    4810           0 :         std::vector<fs::path> wallet_dirs;
    4811           0 :         wallet_dirs.push_back(fs::PathFromString(local_wallet->GetDatabase().Filename()).parent_path());
    4812             : 
    4813             :         // Unload the wallet locally
    4814           0 :         assert(local_wallet.use_count() == 1);
    4815           0 :         local_wallet.reset();
    4816             : 
    4817             :         // Make list of wallets to cleanup
    4818           0 :         std::vector<std::shared_ptr<CWallet>> created_wallets;
    4819           0 :         if (res.watchonly_wallet) created_wallets.push_back(std::move(res.watchonly_wallet));
    4820           0 :         if (res.solvables_wallet) created_wallets.push_back(std::move(res.solvables_wallet));
    4821             : 
    4822             :         // Get the directories to remove after unloading
    4823           0 :         for (std::shared_ptr<CWallet>& w : created_wallets) {
    4824           0 :             wallet_dirs.push_back(fs::PathFromString(w->GetDatabase().Filename()).parent_path());
    4825             :         }
    4826             : 
    4827             :         // Unload the wallets
    4828           0 :         for (std::shared_ptr<CWallet>& w : created_wallets) {
    4829           0 :             if (!RemoveWallet(context, w, /*load_on_start=*/false)) {
    4830           0 :                 error += _("\nUnable to cleanup failed migration");
    4831           0 :                 return util::Error{error};
    4832             :             }
    4833           0 :             UnloadWallet(std::move(w));
    4834             :         }
    4835             : 
    4836             :         // Delete the wallet directories
    4837           0 :         for (fs::path& dir : wallet_dirs) {
    4838           0 :             fs::remove_all(dir);
    4839             :         }
    4840             : 
    4841             :         // Restore the backup
    4842             :         DatabaseStatus status;
    4843           0 :         std::vector<bilingual_str> warnings;
    4844           0 :         if (!RestoreWallet(context, temp_backup_location, wallet_name, /*load_on_start=*/std::nullopt, status, error, warnings)) {
    4845           0 :             error += _("\nUnable to restore backup of wallet.");
    4846           0 :             return util::Error{error};
    4847             :         }
    4848             : 
    4849             :         // Move the backup to the wallet dir
    4850           0 :         fs::copy_file(temp_backup_location, backup_path, fs::copy_options::none);
    4851           0 :         fs::remove(temp_backup_location);
    4852             : 
    4853           0 :         return util::Error{error};
    4854           0 :     }
    4855          26 :     return res;
    4856          34 : }
    4857             : } // namespace wallet

Generated by: LCOV version 1.16