LCOV - code coverage report
Current view: top level - src/wallet - wallet.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 1423 3026 47.0 %
Date: 2026-06-25 07:23:51 Functions: 152 232 65.5 %

          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           0 : static isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
      61             : {
      62           0 :     AssertLockHeld(wallet.cs_wallet);
      63           0 :     const CWalletTx* prev = wallet.GetWalletTx(txin.prevout.hash);
      64           0 :     if (prev && txin.prevout.n < prev->tx->vout.size()) {
      65           0 :         return wallet.IsMine(prev->tx->vout[txin.prevout.n]);
      66             :     }
      67           0 :     return ISMINE_NO;
      68           0 : }
      69             : 
      70         146 : const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
      71         146 :     {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           0 : bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
      79             : {
      80           0 :     util::SettingsValue setting_value = chain.getRwSetting("wallet");
      81           0 :     if (!setting_value.isArray()) setting_value.setArray();
      82           0 :     for (const util::SettingsValue& value : setting_value.getValues()) {
      83           0 :         if (value.isStr() && value.get_str() == wallet_name) return true;
      84             :     }
      85           0 :     setting_value.push_back(wallet_name);
      86           0 :     return chain.updateRwSetting("wallet", setting_value);
      87           0 : }
      88             : 
      89           0 : bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name)
      90             : {
      91           0 :     util::SettingsValue setting_value = chain.getRwSetting("wallet");
      92           0 :     if (!setting_value.isArray()) return true;
      93           0 :     util::SettingsValue new_value(util::SettingsValue::VARR);
      94           0 :     for (const util::SettingsValue& value : setting_value.getValues()) {
      95           0 :         if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value);
      96             :     }
      97           0 :     if (new_value.size() == setting_value.size()) return true;
      98           0 :     return chain.updateRwSetting("wallet", new_value);
      99           0 : }
     100             : 
     101          11 : 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          11 :     if (load_on_startup == std::nullopt) return;
     107           0 :     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           0 :     } 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          11 : }
     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           7 : static void RefreshMempoolStatus(CWalletTx& tx, interfaces::Chain& chain)
     120             : {
     121           7 :     if (chain.isInMempool(tx.GetHash())) {
     122           3 :         tx.m_state = TxStateInMempool();
     123           7 :     } else if (tx.state<TxStateInMempool>()) {
     124           1 :         tx.m_state = TxStateInactive();
     125           1 :     }
     126           7 : }
     127             : 
     128          11 : bool AddWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet)
     129             : {
     130             :     {
     131          11 :         LOCK(context.wallets_mutex);
     132          11 :         assert(wallet);
     133          11 :         std::vector<std::shared_ptr<CWallet>>::const_iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet);
     134          11 :         if (i != context.wallets.end()) return false;
     135          11 :         context.wallets.push_back(wallet);
     136          11 :     }
     137          11 :     wallet->ConnectScriptPubKeyManNotifiers();
     138          11 :     wallet->AutoLockMasternodeCollaterals();
     139          11 :     if (wallet->coinjoin_available()) {
     140          11 :         wallet->coinjoin_loader().AddWallet(wallet);
     141          11 :     }
     142          11 :     wallet->NotifyCanGetAddressesChanged();
     143          11 :     return true;
     144          11 : }
     145             : 
     146          11 : bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
     147             : {
     148          11 :     assert(wallet);
     149             : 
     150          11 :     interfaces::Chain& chain = wallet->chain();
     151          11 :     std::string name = wallet->GetName();
     152             : 
     153             :     // Unregister with the validation interface which also drops shared pointers.
     154          11 :     wallet->m_chain_notifications_handler.reset();
     155             :     {
     156          11 :         LOCK(context.wallets_mutex);
     157          11 :         std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet);
     158          11 :         if (i == context.wallets.end()) return false;
     159          11 :         context.wallets.erase(i);
     160          11 :     }
     161             : 
     162          11 :     if (wallet->coinjoin_available()) {
     163          11 :         wallet->coinjoin_loader().RemoveWallet(name);
     164          11 :     }
     165             : 
     166             :     // Write the wallet setting
     167          11 :     UpdateWalletSetting(chain, name, load_on_start, warnings);
     168             : 
     169          11 :     return true;
     170          11 : }
     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         225 : std::vector<std::shared_ptr<CWallet>> GetWallets(WalletContext& context)
     179             : {
     180         225 :     LOCK(context.wallets_mutex);
     181         225 :     return context.wallets;
     182         225 : }
     183             : 
     184        6195 : std::shared_ptr<CWallet> GetDefaultWallet(WalletContext& context, size_t& count)
     185             : {
     186        6195 :     LOCK(context.wallets_mutex);
     187        6195 :     count = context.wallets.size();
     188        6195 :     return count == 1 ? context.wallets[0] : nullptr;
     189        6195 : }
     190             : 
     191           0 : std::shared_ptr<CWallet> GetWallet(WalletContext& context, const std::string& name)
     192             : {
     193           0 :     LOCK(context.wallets_mutex);
     194           0 :     for (const std::shared_ptr<CWallet>& wallet : context.wallets) {
     195           0 :         if (wallet->GetName() == name) return wallet;
     196             :     }
     197           0 :     return nullptr;
     198           0 : }
     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           5 : void NotifyWalletLoading(WalletContext& context, const std::shared_ptr<CWallet>& wallet)
     215             : {
     216           5 :     LOCK(context.wallets_mutex);
     217           5 :     for (auto& load_wallet : context.wallet_loading_fns) {
     218           0 :         load_wallet(interfaces::MakeWallet(context, wallet));
     219             :     }
     220           5 : }
     221             : 
     222           5 : void NotifyWalletLoaded(WalletContext& context, const std::shared_ptr<CWallet>& wallet)
     223             : {
     224           5 :     LOCK(context.wallets_mutex);
     225           6 :     for (auto& load_wallet : context.wallet_load_fns) {
     226           1 :         load_wallet(interfaces::MakeWallet(context, wallet));
     227             :     }
     228           5 : }
     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         146 : static std::set<std::string> g_loading_wallet_set GUARDED_BY(g_loading_wallet_mutex);
     234         146 : static std::set<std::string> g_unloading_wallet_set GUARDED_BY(g_wallet_release_mutex);
     235             : 
     236             : // Custom deleter for shared_ptr<CWallet>.
     237           5 : static void ReleaseWallet(CWallet* wallet)
     238             : {
     239           5 :     const std::string name = wallet->GetName();
     240           5 :     wallet->WalletLogPrintf("Releasing wallet\n");
     241           5 :     wallet->Flush();
     242           5 :     delete wallet;
     243             :     // Wallet is now released, notify UnloadWallet, if any.
     244             :     {
     245           5 :         LOCK(g_wallet_release_mutex);
     246           5 :         if (g_unloading_wallet_set.erase(name) == 0) {
     247             :             // UnloadWallet was not called for this wallet, all done.
     248           0 :             return;
     249             :         }
     250           5 :     }
     251           5 :     g_wallet_release_cv.notify_all();
     252           5 : }
     253             : 
     254           5 : void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
     255             : {
     256             :     // Mark wallet for unloading.
     257           5 :     const std::string name = wallet->GetName();
     258             :     {
     259           5 :         LOCK(g_wallet_release_mutex);
     260           5 :         auto it = g_unloading_wallet_set.insert(name);
     261           5 :         assert(it.second);
     262           5 :     }
     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           5 :     wallet->NotifyUnload();
     267             : 
     268             :     // Time to ditch our shared_ptr and wait for ReleaseWallet call.
     269           5 :     wallet.reset();
     270             :     {
     271           5 :         WAIT_LOCK(g_wallet_release_mutex, lock);
     272           5 :         while (g_unloading_wallet_set.count(name) == 1) {
     273           0 :             g_wallet_release_cv.wait(lock);
     274             :         }
     275           5 :     }
     276           5 : }
     277             : 
     278             : namespace {
     279           0 : 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           0 :         std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
     283           0 :         if (!database) {
     284           0 :             error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
     285           0 :             return nullptr;
     286             :         }
     287             : 
     288           0 :         context.chain->initMessage(_("Loading wallet…").translated);
     289           0 :         std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings);
     290           0 :         if (!wallet) {
     291           0 :             error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error;
     292           0 :             status = DatabaseStatus::FAILED_LOAD;
     293           0 :             return nullptr;
     294             :         }
     295             : 
     296           0 :         NotifyWalletLoaded(context, wallet);
     297           0 :         AddWallet(context, wallet);
     298           0 :         wallet->postInitProcess();
     299             : 
     300             :         // Write the wallet setting
     301           0 :         UpdateWalletSetting(*context.chain, name, load_on_start, warnings);
     302             : 
     303           0 :         return wallet;
     304           0 :     } catch (const std::runtime_error& e) {
     305           0 :         error = Untranslated(e.what());
     306           0 :         status = DatabaseStatus::FAILED_LOAD;
     307           0 :         return nullptr;
     308           0 :     }
     309           0 : }
     310             : 
     311             : class FastWalletRescanFilter
     312             : {
     313             : public:
     314           0 :     FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet)
     315           0 :     {
     316             :         // fast rescanning via block filters is only supported by descriptor wallets right now
     317           0 :         assert(!m_wallet.IsLegacy());
     318             : 
     319             :         // create initial filter with scripts from all ScriptPubKeyMans
     320           0 :         for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) {
     321           0 :             auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
     322           0 :             assert(desc_spkm != nullptr);
     323           0 :             AddScriptPubKeys(desc_spkm);
     324             :             // save each range descriptor's end for possible future filter updates
     325           0 :             if (desc_spkm->IsHDEnabled()) {
     326           0 :                 m_last_range_ends.emplace(desc_spkm->GetID(), desc_spkm->GetEndRange());
     327           0 :             }
     328             :         }
     329           0 :     }
     330             : 
     331           0 :     void UpdateIfNeeded()
     332             :     {
     333             :         // repopulate filter with new scripts if top-up has happened since last iteration
     334           0 :         for (const auto& [desc_spkm_id, last_range_end] : m_last_range_ends) {
     335           0 :             auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(m_wallet.GetScriptPubKeyMan(desc_spkm_id))};
     336           0 :             assert(desc_spkm != nullptr);
     337           0 :             int32_t current_range_end{desc_spkm->GetEndRange()};
     338           0 :             if (current_range_end > last_range_end) {
     339           0 :                 AddScriptPubKeys(desc_spkm, last_range_end);
     340           0 :                 m_last_range_ends.at(desc_spkm->GetID()) = current_range_end;
     341           0 :             }
     342             :         }
     343           0 :     }
     344             : 
     345           0 :     std::optional<bool> MatchesBlock(const uint256& block_hash) const
     346             :     {
     347           0 :         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           0 :     void AddScriptPubKeys(const DescriptorScriptPubKeyMan* desc_spkm, int32_t last_range_end = 0)
     362             :     {
     363           0 :         for (const auto& script_pub_key : desc_spkm->GetScriptPubKeys(last_range_end)) {
     364           0 :             m_filter_set.emplace(script_pub_key.begin(), script_pub_key.end());
     365             :         }
     366           0 :     }
     367             : };
     368             : } // namespace
     369             : 
     370           0 : 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           0 :     auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
     373           0 :     if (!result.second) {
     374           0 :         error = Untranslated("Wallet already loading.");
     375           0 :         status = DatabaseStatus::FAILED_LOAD;
     376           0 :         return nullptr;
     377             :     }
     378           0 :     auto wallet = LoadWalletInternal(context, name, load_on_start, options, status, error, warnings);
     379           0 :     WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first));
     380           0 :     return wallet;
     381           0 : }
     382             : 
     383           0 : 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           0 :     ArgsManager& args = *Assert(context.args);
     386             : 
     387           0 :     uint64_t wallet_creation_flags = options.create_flags;
     388           0 :     const SecureString& passphrase = options.create_passphrase;
     389             : 
     390           0 :     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           0 :     bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET);
     394             : 
     395             :     // Born encrypted wallets need to be created blank first.
     396           0 :     if (!passphrase.empty()) {
     397           0 :         wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET;
     398           0 :     }
     399             : 
     400             :     // Private keys must be disabled for an external signer wallet
     401           0 :     if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     402           0 :         error = Untranslated("Private keys must be disabled when using an external signer");
     403           0 :         status = DatabaseStatus::FAILED_CREATE;
     404           0 :         return nullptr;
     405             :     }
     406             : 
     407             :     // Do not allow a passphrase when private keys are disabled
     408           0 :     if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     409           0 :         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           0 :         status = DatabaseStatus::FAILED_CREATE;
     411           0 :         return nullptr;
     412             :     }
     413             : 
     414             :     // Descriptor support must be enabled for an external signer wallet
     415           0 :     if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS)) {
     416           0 :         error = Untranslated("Descriptor support must be enabled when using an external signer");
     417           0 :         status = DatabaseStatus::FAILED_CREATE;
     418           0 :         return nullptr;
     419             :     }
     420             : 
     421             :     // Wallet::Verify will check if we're trying to create a wallet with a duplicate name.
     422           0 :     std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
     423           0 :     if (!database) {
     424           0 :         error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error;
     425           0 :         status = DatabaseStatus::FAILED_VERIFY;
     426           0 :         return nullptr;
     427             :     }
     428             : 
     429             :     // Make the wallet
     430           0 :     context.chain->initMessage(_("Loading wallet…").translated);
     431           0 :     std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings);
     432           0 :     if (!wallet) {
     433           0 :         error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error;
     434           0 :         status = DatabaseStatus::FAILED_CREATE;
     435           0 :         return nullptr;
     436             :     }
     437           0 :     if (args.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) {
     438           0 :         wallet->WalletLogPrintf("Set HD by default\n");
     439           0 :         wallet->SetMinVersion(FEATURE_HD);
     440           0 :     }
     441             : 
     442             :     // Encrypt the wallet
     443           0 :     if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     444           0 :         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           0 :         if (!create_blank) {
     450             :             // Unlock the wallet
     451           0 :             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           0 :             if (wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
     459           0 :                 LOCK(wallet->cs_wallet);
     460           0 :                 wallet->SetupDescriptorScriptPubKeyMans("", "");
     461           0 :             } else {
     462             :                 // TODO: drop this condition after removing option to create non-HD wallets
     463             :                 // related backport bitcoin#11250
     464           0 :                 if (wallet->GetVersion() >= FEATURE_HD) {
     465           0 :                     auto spk_man = wallet->GetLegacyScriptPubKeyMan();
     466           0 :                     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           0 :                     wallet->WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
     473           0 :                             spk_man->GenerateNewHDChain(/*secureMnemonic=*/"", /*secureMnemonicPassphrase=*/"", encryption_key);
     474           0 :                             return true;
     475           0 :                         });
     476           0 :                 }
     477             :             }
     478             : 
     479             :             // backup the wallet we just encrypted
     480           0 :             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           0 :             wallet->Lock();
     487           0 :         }
     488           0 :     }
     489             : 
     490           0 :     NotifyWalletLoaded(context, wallet);
     491           0 :     AddWallet(context, wallet);
     492           0 :     wallet->postInitProcess();
     493             : 
     494             :     // Write the wallet settings
     495           0 :     UpdateWalletSetting(*context.chain, name, load_on_start, warnings);
     496             : 
     497           0 :     status = DatabaseStatus::SUCCESS;
     498           0 :     return wallet;
     499           0 : }
     500             : 
     501           0 : 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           0 :     DatabaseOptions options;
     504           0 :     ReadDatabaseArgs(*context.args, options);
     505           0 :     options.require_existing = true;
     506             : 
     507           0 :     const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name));
     508           0 :     auto wallet_file = wallet_path / "wallet.dat";
     509           0 :     std::shared_ptr<CWallet> wallet;
     510             : 
     511             :     try {
     512           0 :         if (!fs::exists(backup_file)) {
     513           0 :             error = Untranslated("Backup file does not exist");
     514           0 :             status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE;
     515           0 :             return nullptr;
     516             :         }
     517             : 
     518           0 :         if (fs::exists(wallet_path) || !TryCreateDirectories(wallet_path)) {
     519           0 :             error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(wallet_path)));
     520           0 :             status = DatabaseStatus::FAILED_ALREADY_EXISTS;
     521           0 :             return nullptr;
     522             :         }
     523             : 
     524           0 :         fs::copy_file(backup_file, wallet_file, fs::copy_options::none);
     525             : 
     526           0 :         wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings);
     527           0 :     } 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           0 :     if (!wallet) {
     533           0 :         fs::remove_all(wallet_path);
     534           0 :     }
     535             : 
     536           0 :     return wallet;
     537           0 : }
     538             : 
     539             : /** @defgroup mapWallet
     540             :  *
     541             :  * @{
     542             :  */
     543             : 
     544         684 : const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
     545             : {
     546         684 :     AssertLockHeld(cs_wallet);
     547         684 :     const auto it = mapWallet.find(hash);
     548         684 :     if (it == mapWallet.end())
     549         100 :         return nullptr;
     550         584 :     return &(it->second);
     551         684 : }
     552             : 
     553          45 : void CWallet::UpgradeKeyMetadata()
     554             : {
     555          45 :     if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
     556           0 :         return;
     557             :     }
     558             : 
     559          45 :     auto spk_man = GetLegacyScriptPubKeyMan();
     560          45 :     if (!spk_man) {
     561          43 :         return;
     562             :     }
     563             : 
     564           2 :     spk_man->UpgradeKeyMetadata();
     565           2 :     SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA);
     566          45 : }
     567             : 
     568          45 : void CWallet::UpgradeDescriptorCache()
     569             : {
     570          45 :     if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) {
     571          44 :         return;
     572             :     }
     573             : 
     574           5 :     for (ScriptPubKeyMan* spkm : GetAllScriptPubKeyMans()) {
     575           4 :         DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm);
     576           4 :         desc_spkm->UpgradeDescriptorCache();
     577             :     }
     578           1 :     SetWalletFlag(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED);
     579          45 : }
     580             : 
     581           0 : bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
     582             : {
     583           0 :     bool fWasLocked = IsLocked(true);
     584             : 
     585             :     {
     586           0 :         LOCK(cs_wallet);
     587           0 :         Lock();
     588             : 
     589           0 :         CCrypter crypter;
     590           0 :         CKeyingMaterial _vMasterKey;
     591           0 :         for (MasterKeyMap::value_type& pMasterKey : mapMasterKeys)
     592             :         {
     593           0 :             if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
     594           0 :                 return false;
     595           0 :             if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
     596           0 :                 return false;
     597           0 :             if (Unlock(_vMasterKey))
     598             :             {
     599           0 :                 constexpr MillisecondsDouble target{100};
     600           0 :                 auto start{SteadyClock::now()};
     601           0 :                 crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
     602           0 :                 pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start));
     603             : 
     604           0 :                 start = SteadyClock::now();
     605           0 :                 crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
     606           0 :                 pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * target / (SteadyClock::now() - start))) / 2;
     607             : 
     608           0 :                 if (pMasterKey.second.nDeriveIterations < 25000)
     609           0 :                     pMasterKey.second.nDeriveIterations = 25000;
     610             : 
     611           0 :                 WalletLogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
     612             : 
     613           0 :                 if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
     614           0 :                     return false;
     615           0 :                 if (!crypter.Encrypt(_vMasterKey, pMasterKey.second.vchCryptedKey))
     616           0 :                     return false;
     617           0 :                 WalletBatch(GetDatabase()).WriteMasterKey(pMasterKey.first, pMasterKey.second);
     618           0 :                 if (fWasLocked)
     619           0 :                     Lock();
     620             : 
     621           0 :                 return true;
     622             :             }
     623             :         }
     624           0 :     }
     625             : 
     626           0 :     return false;
     627           0 : }
     628             : 
     629           3 : 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           3 :     if (m_attaching_chain) {
     634           0 :         return;
     635             :     }
     636           3 :     WalletBatch batch(GetDatabase());
     637           3 :     batch.WriteBestBlock(loc);
     638           3 : }
     639             : 
     640           8 : void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in)
     641             : {
     642           8 :     LOCK(cs_wallet);
     643           8 :     if (nWalletVersion >= nVersion)
     644           3 :         return;
     645           5 :     WalletLogPrintf("Setting minversion to %d\n", nVersion);
     646           5 :     nWalletVersion = nVersion;
     647             : 
     648             :     {
     649           5 :         WalletBatch* batch = batch_in ? batch_in : new WalletBatch(GetDatabase());
     650           5 :         if (nWalletVersion > 40000)
     651           5 :             batch->WriteMinVersion(nWalletVersion);
     652           5 :         if (!batch_in)
     653           5 :             delete batch;
     654             :     }
     655           8 : }
     656             : 
     657           1 : std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
     658             : {
     659           1 :     std::set<uint256> result;
     660           1 :     AssertLockHeld(cs_wallet);
     661             : 
     662           1 :     const auto it = mapWallet.find(txid);
     663           1 :     if (it == mapWallet.end())
     664           0 :         return result;
     665           1 :     const CWalletTx& wtx = it->second;
     666             : 
     667           1 :     std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
     668             : 
     669           2 :     for (const CTxIn& txin : wtx.tx->vin)
     670             :     {
     671           1 :         if (mapTxSpends.count(txin.prevout) <= 1)
     672           0 :             continue;  // No conflict if zero or one spends
     673           1 :         range = mapTxSpends.equal_range(txin.prevout);
     674           3 :         for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it)
     675           2 :             result.insert(_it->second);
     676             :     }
     677           1 :     return result;
     678           1 : }
     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           5 : void CWallet::Flush()
     693             : {
     694           5 :     GetDatabase().Flush();
     695           5 : }
     696             : 
     697           0 : void CWallet::Close()
     698             : {
     699           0 :     GetDatabase().Close();
     700           0 : }
     701             : 
     702          40 : 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          40 :     int nMinOrderPos = std::numeric_limits<int>::max();
     709          40 :     const CWalletTx* copyFrom = nullptr;
     710          81 :     for (TxSpends::iterator it = range.first; it != range.second; ++it) {
     711          41 :         const CWalletTx* wtx = &mapWallet.at(it->second);
     712          41 :         if (wtx->nOrderPos < nMinOrderPos) {
     713          40 :             nMinOrderPos = wtx->nOrderPos;
     714          40 :             copyFrom = wtx;
     715          40 :         }
     716          41 :     }
     717             : 
     718          40 :     if (!copyFrom) {
     719           0 :         return;
     720             :     }
     721             : 
     722             :     // Now copy data from copyFrom to rest:
     723          81 :     for (TxSpends::iterator it = range.first; it != range.second; ++it)
     724             :     {
     725          41 :         const uint256& hash = it->second;
     726          41 :         CWalletTx* copyTo = &mapWallet.at(hash);
     727          41 :         if (copyFrom == copyTo) continue;
     728           2 :         assert(copyFrom && "Oldest wallet transaction in range assumed to have been found.");
     729           1 :         if (!copyFrom->IsEquivalentTo(*copyTo)) continue;
     730           0 :         copyTo->mapValue = copyFrom->mapValue;
     731           0 :         copyTo->vOrderForm = copyFrom->vOrderForm;
     732             :         // fTimeReceivedIsTxTime not copied on purpose
     733             :         // nTimeReceived not copied on purpose
     734           0 :         copyTo->nTimeSmart = copyFrom->nTimeSmart;
     735           0 :         copyTo->fFromMe = copyFrom->fFromMe;
     736             :         // nOrderPos not copied on purpose
     737             :         // cached members not copied on purpose
     738           0 :     }
     739          40 : }
     740             : 
     741             : /**
     742             :  * Outpoint is spent if any non-conflicted transaction
     743             :  * spends it:
     744             :  */
     745        2986 : bool CWallet::IsSpent(const COutPoint& outpoint) const
     746             : {
     747        2986 :     std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
     748        2986 :     range = mapTxSpends.equal_range(outpoint);
     749             : 
     750        2986 :     for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {
     751         505 :         const uint256& wtxid = it->second;
     752         505 :         const auto mit = mapWallet.find(wtxid);
     753         505 :         if (mit != mapWallet.end()) {
     754         505 :             int depth = GetTxDepthInMainChain(mit->second);
     755         505 :             if (depth > 0  || (depth == 0 && !mit->second.isAbandoned()))
     756         505 :                 return true; // Spent
     757           0 :         }
     758           0 :     }
     759        2481 :     return false;
     760        2986 : }
     761             : 
     762          40 : void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch)
     763             : {
     764          40 :     mapTxSpends.insert(std::make_pair(outpoint, wtxid));
     765          40 :     setWalletUTXO.erase(outpoint);
     766             : 
     767          40 :     if (batch) {
     768          38 :         UnlockCoin(outpoint, batch);
     769          38 :     } else {
     770           2 :         WalletBatch temp_batch(GetDatabase());
     771           2 :         UnlockCoin(outpoint, &temp_batch);
     772           2 :     }
     773             : 
     774          40 :     std::pair<TxSpends::iterator, TxSpends::iterator> range;
     775          40 :     range = mapTxSpends.equal_range(outpoint);
     776          40 :     SyncMetaData(range);
     777          40 : }
     778             : 
     779             : 
     780         746 : void CWallet::AddToSpends(const CWalletTx& wtx, WalletBatch* batch)
     781             : {
     782         746 :     if (wtx.IsCoinBase()) // Coinbases don't spend anything!
     783         713 :         return;
     784             : 
     785          73 :     for (const CTxIn& txin : wtx.tx->vin)
     786          40 :         AddToSpends(txin.prevout, wtx.GetHash(), batch);
     787         746 : }
     788             : 
     789           0 : bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
     790             : {
     791           0 :     if (IsCrypted())
     792           0 :         return false;
     793             : 
     794           0 :     CKeyingMaterial _vMasterKey;
     795             : 
     796           0 :     _vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
     797           0 :     GetStrongRandBytes(_vMasterKey);
     798             : 
     799           0 :     CMasterKey kMasterKey;
     800             : 
     801           0 :     kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
     802           0 :     GetStrongRandBytes(kMasterKey.vchSalt);
     803             : 
     804           0 :     CCrypter crypter;
     805           0 :     constexpr MillisecondsDouble target{100};
     806           0 :     auto start{SteadyClock::now()};
     807           0 :     crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
     808           0 :     kMasterKey.nDeriveIterations = static_cast<unsigned int>(25000 * target / (SteadyClock::now() - start));
     809             : 
     810           0 :     start = SteadyClock::now();
     811           0 :     crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
     812           0 :     kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * target / (SteadyClock::now() - start))) / 2;
     813             : 
     814           0 :     if (kMasterKey.nDeriveIterations < 25000)
     815           0 :         kMasterKey.nDeriveIterations = 25000;
     816             : 
     817           0 :     WalletLogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
     818             : 
     819           0 :     if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
     820           0 :         return false;
     821           0 :     if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey))
     822           0 :         return false;
     823             : 
     824             :     {
     825           0 :         LOCK(cs_wallet);
     826             : 
     827           0 :         if (IsCrypted()) {
     828             :             // verify again now that cs_wallet lock is held
     829           0 :             return false;
     830             :         }
     831             : 
     832           0 :         mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
     833           0 :         WalletBatch* encrypted_batch = new WalletBatch(GetDatabase());
     834           0 :         if (!encrypted_batch->TxnBegin()) {
     835           0 :             delete encrypted_batch;
     836           0 :             encrypted_batch = nullptr;
     837           0 :             return false;
     838             :         }
     839           0 :         encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
     840             : 
     841           0 :         for (const auto& spk_man_pair : m_spk_managers) {
     842           0 :             auto spk_man = spk_man_pair.second.get();
     843             : 
     844           0 :             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           0 :         SetMinVersion(FEATURE_WALLETCRYPT, encrypted_batch);
     857             : 
     858           0 :         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           0 :         delete encrypted_batch;
     867           0 :         encrypted_batch = nullptr;
     868             : 
     869           0 :         Lock();
     870           0 :         Unlock(strWalletPassphrase);
     871             : 
     872             :         // If we are using descriptors, make new descriptors with a new seed
     873           0 :         if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
     874             :             // Do nothing for descriptor wallets (keep old seed / mnemonic)
     875           0 :         } else if (auto spk_man = GetLegacyScriptPubKeyMan()) {
     876             :             // if we are not using HD, generate new keypool
     877           0 :             if (spk_man->IsHDEnabled()) {
     878             :                 // NOTE: using internal master key which should be populated on Unlock()
     879           0 :                 if (!spk_man->CheckDecryptionKey(vMasterKey)) {
     880           0 :                     return false;
     881             :                 }
     882           0 :                 if (!spk_man->TopUp()) {
     883           0 :                     return false;
     884             :                 }
     885           0 :             }
     886             :             else {
     887           0 :                 spk_man->NewKeyPool();
     888             :             }
     889           0 :         }
     890             : 
     891           0 :         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           0 :         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           0 :         GetDatabase().ReloadDbEnv();
     903             : 
     904           0 :     }
     905           0 :     NotifyStatusChanged(this);
     906             : 
     907           0 :     return true;
     908           0 : }
     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         744 : int64_t CWallet::IncOrderPosNext(WalletBatch* batch)
     968             : {
     969         744 :     AssertLockHeld(cs_wallet);
     970         744 :     int64_t nRet = nOrderPosNext++;
     971         744 :     if (batch) {
     972         744 :         batch->WriteOrderPosNext(nOrderPosNext);
     973         744 :     } else {
     974           0 :         WalletBatch(GetDatabase()).WriteOrderPosNext(nOrderPosNext);
     975             :     }
     976         744 :     return nRet;
     977           0 : }
     978             : 
     979           4 : void CWallet::MarkDirty()
     980             : {
     981             :     {
     982           4 :         LOCK(cs_wallet);
     983           7 :         for (std::pair<const uint256, CWalletTx>& item : mapWallet)
     984           3 :             item.second.MarkDirty();
     985           4 :     }
     986             : 
     987           4 :     fAnonymizableTallyCached = false;
     988           4 :     fAnonymizableTallyCachedNonDenom = false;
     989           4 : }
     990             : 
     991           0 : void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
     992             : {
     993           0 :     AssertLockHeld(cs_wallet);
     994           0 :     const CWalletTx* srctx = GetWalletTx(hash);
     995           0 :     if (!srctx) return;
     996             : 
     997           0 :     CTxDestination dst;
     998           0 :     if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) {
     999           0 :         if (IsMine(dst)) {
    1000           0 :             if (used != IsAddressPreviouslySpent(dst)) {
    1001           0 :                 if (used) {
    1002           0 :                     tx_destinations.insert(dst);
    1003           0 :                 }
    1004           0 :                 SetAddressPreviouslySpent(batch, dst, used);
    1005           0 :             }
    1006           0 :         }
    1007           0 :     }
    1008           0 : }
    1009             : 
    1010           0 : bool CWallet::IsSpentKey(const CScript& scriptPubKey) const
    1011             : {
    1012           0 :     AssertLockHeld(cs_wallet);
    1013           0 :     CTxDestination dest;
    1014           0 :     if (!ExtractDestination(scriptPubKey, dest)) {
    1015           0 :         return false;
    1016             :     }
    1017           0 :     if (IsAddressPreviouslySpent(dest)) {
    1018           0 :         return true;
    1019             :     }
    1020           0 :     if (IsLegacy()) {
    1021           0 :         LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
    1022           0 :         assert(spk_man != nullptr);
    1023           0 :         for (const auto& keyid : GetAffectedKeys(scriptPubKey, *spk_man)) {
    1024           0 :             if (IsAddressPreviouslySpent(PKHash(keyid))) {
    1025           0 :                 return true;
    1026             :             }
    1027             :         }
    1028           0 :     }
    1029           0 :     return false;
    1030           0 : }
    1031             : 
    1032         751 : CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose, bool rescanning_old_block)
    1033             : {
    1034         751 :     LOCK(cs_wallet);
    1035             : 
    1036         751 :     WalletBatch batch(GetDatabase(), fFlushOnClose);
    1037             : 
    1038         751 :     uint256 hash = tx->GetHash();
    1039             : 
    1040         747 :     if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
    1041             :         // Mark used destinations
    1042           0 :         std::set<CTxDestination> tx_destinations;
    1043             : 
    1044           0 :         for (const CTxIn& txin : tx->vin) {
    1045           0 :             const COutPoint& op = txin.prevout;
    1046           0 :             SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations);
    1047             :         }
    1048             : 
    1049           0 :         MarkDestinationsDirty(tx_destinations);
    1050           0 :     }
    1051             : 
    1052             :     // Inserts only if not already there, returns tx inserted or tx found
    1053         747 :     auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(tx, state));
    1054         747 :     CWalletTx& wtx = (*ret.first).second;
    1055         751 :     bool fInsertedNew = ret.second;
    1056         774 :     bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew);
    1057         751 :     std::set<COutPoint> candidates;
    1058         751 :     if (fInsertedNew) {
    1059         744 :         wtx.nTimeReceived = GetTime();
    1060         744 :         wtx.nOrderPos = IncOrderPosNext(&batch);
    1061         744 :         wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
    1062         744 :         wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
    1063         744 :         AddToSpends(wtx, &batch);
    1064         744 :         candidates = AddWalletUTXOs(wtx.tx, /*ret_dups=*/true);
    1065         744 :     }
    1066             : 
    1067         751 :     if (!fInsertedNew)
    1068             :     {
    1069           3 :         if (state.index() != wtx.m_state.index()) {
    1070           0 :             wtx.m_state = state;
    1071           0 :             fUpdated = true;
    1072           0 :         } else {
    1073           3 :             assert(TxStateSerializedIndex(wtx.m_state) == TxStateSerializedIndex(state));
    1074           3 :             assert(TxStateSerializedBlockHash(wtx.m_state) == TxStateSerializedBlockHash(state));
    1075             :         }
    1076           3 :         candidates = AddWalletUTXOs(wtx.tx, /*ret_dups=*/false);
    1077           3 :         if (!candidates.empty()) fUpdated = true;
    1078           3 :     }
    1079             : 
    1080         751 :     LockProTxCoins(candidates, &batch);
    1081             : 
    1082         747 :     if (fInsertedNew) {
    1083         744 :         CheckAndLockDustOutputs(hash, batch);
    1084         744 :     }
    1085             : 
    1086             :     //// debug print
    1087         747 :     WalletLogPrintf("AddToWallet %s  %s%s %s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""), TxStateString(state));
    1088             : 
    1089             :     // Write to disk
    1090         747 :     if (fInsertedNew || fUpdated)
    1091         747 :         if (!batch.WriteTx(wtx))
    1092           1 :             return nullptr;
    1093             : 
    1094             :     // Break debit/credit balance caches:
    1095         744 :     wtx.MarkDirty();
    1096             : 
    1097             :     // Notify UI of new or updated transaction
    1098         746 :     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         746 :     std::string strCmd = m_args.GetArg("-walletnotify", "");
    1103             : 
    1104         746 :     if (!strCmd.empty())
    1105             :     {
    1106           0 :         ReplaceAll(strCmd, "%s", hash.GetHex());
    1107           0 :         if (auto* conf = wtx.state<TxStateConfirmed>())
    1108             :         {
    1109           0 :             ReplaceAll(strCmd, "%b", conf->confirmed_block_hash.GetHex());
    1110           0 :             ReplaceAll(strCmd, "%h", ToString(conf->confirmed_block_height));
    1111           0 :         } else {
    1112           0 :             ReplaceAll(strCmd, "%b", "unconfirmed");
    1113           0 :             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           0 :         ReplaceAll(strCmd, "%w", ShellEscape(GetName()));
    1122             : #endif
    1123           0 :         std::thread t(runCommand, strCmd);
    1124           0 :         t.detach(); // thread runs free
    1125           0 :     }
    1126             : #endif
    1127             : 
    1128         746 :     fAnonymizableTallyCached = false;
    1129         746 :     fAnonymizableTallyCachedNonDenom = false;
    1130             : 
    1131         746 :     return &wtx;
    1132         771 : }
    1133             : 
    1134           2 : bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx)
    1135             : {
    1136           2 :     const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(nullptr, TxStateInactive{}));
    1137           2 :     CWalletTx& wtx = ins.first->second;
    1138           2 :     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           2 :     if (HaveChain()) {
    1144             :         bool active;
    1145           3 :         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           1 :             if (!chain().findBlock(hash, FoundBlock().inActiveChain(active).height(height)) || !active) {
    1153           0 :                 state = TxStateInactive{};
    1154           0 :             }
    1155           1 :         };
    1156           2 :         if (auto* conf = wtx.state<TxStateConfirmed>()) {
    1157           1 :             lookup_block(conf->confirmed_block_hash, conf->confirmed_block_height, wtx.m_state);
    1158           2 :         } else if (auto* conf = wtx.state<TxStateConflicted>()) {
    1159           0 :             lookup_block(conf->conflicting_block_hash, conf->conflicting_block_height, wtx.m_state);
    1160           0 :         }
    1161           2 :     }
    1162           2 :     if (/* insertion took place */ ins.second) {
    1163           2 :         wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
    1164           2 :     }
    1165           2 :     AddToSpends(wtx);
    1166           4 :     for (const CTxIn& txin : wtx.tx->vin) {
    1167           2 :         auto it = mapWallet.find(txin.prevout.hash);
    1168           2 :         if (it != mapWallet.end()) {
    1169           0 :             CWalletTx& prevtx = it->second;
    1170           0 :             if (auto* prev = prevtx.state<TxStateConflicted>()) {
    1171           0 :                 MarkConflicted(prev->conflicting_block_hash, prev->conflicting_block_height, wtx.GetHash());
    1172           0 :             }
    1173           0 :         }
    1174             :     }
    1175           2 :     return true;
    1176           2 : }
    1177             : 
    1178         751 : std::set<COutPoint> CWallet::AddWalletUTXOs(CTransactionRef tx, bool ret_dups)
    1179             : {
    1180         751 :     AssertLockHeld(cs_wallet);
    1181         751 :     std::set<COutPoint> ret;
    1182         751 :     uint256 hash{tx->GetHash()};
    1183        1638 :     for (size_t idx = 0; idx < tx->vout.size(); ++idx) {
    1184         887 :         COutPoint outpoint(hash, idx);
    1185         887 :         if (IsMine(tx->vout[idx]) && !IsSpent(outpoint)) {
    1186         884 :             if (auto [_, inserted] = setWalletUTXO.emplace(outpoint); inserted || ret_dups) {
    1187         882 :                 ret.emplace(outpoint);
    1188         882 :             }
    1189         884 :         }
    1190         887 :     }
    1191         751 :     return ret;
    1192         751 : }
    1193             : 
    1194         843 : bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxState& state, WalletBatch& batch, bool fUpdate, bool rescanning_old_block)
    1195             : {
    1196         843 :     const CTransaction& tx = *ptx;
    1197             :     {
    1198         843 :         AssertLockHeld(cs_wallet);
    1199             : 
    1200         843 :         if (auto* conf = std::get_if<TxStateConfirmed>(&state)) {
    1201        1676 :             for (const CTxIn& txin : tx.vin) {
    1202         838 :                 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);
    1203         840 :                 while (range.first != range.second) {
    1204           2 :                     if (range.first->second != tx.GetHash()) {
    1205           1 :                         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           1 :                         MarkConflicted(conf->confirmed_block_hash, conf->confirmed_block_height, range.first->second);
    1207           1 :                     }
    1208           2 :                     range.first++;
    1209             :                 }
    1210             :             }
    1211         838 :         }
    1212             : 
    1213         843 :         bool fExisted = mapWallet.count(tx.GetHash()) != 0;
    1214         843 :         if (fExisted && !fUpdate) return false;
    1215         742 :         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         723 :             std::optional<int64_t> block_time;
    1224         723 :             if (auto* conf = std::get_if<TxStateConfirmed>(&state)) {
    1225             :                 int64_t block_time_tmp;
    1226         718 :                 bool found_block = chain().findBlock(conf->confirmed_block_hash, FoundBlock().maxTime(block_time_tmp));
    1227         718 :                 assert(found_block);
    1228         718 :                 block_time = block_time_tmp;
    1229         718 :             }
    1230             :             // loop though all outputs
    1231        1447 :             for (const CTxOut& txout: tx.vout) {
    1232        1447 :                 for (const auto& spk_man : GetScriptPubKeyMans(txout.scriptPubKey)) {
    1233         723 :                     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           0 :                         if (!dest.internal.has_value()) {
    1236           0 :                             dest.internal = IsInternalScriptPubKeyMan(spk_man);
    1237           0 :                         }
    1238             : 
    1239             :                         // skip if can't determine whether it's a receiving address or not
    1240           0 :                         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           0 :                         if (!*dest.internal && !FindAddressBookEntry(dest.dest, /* allow_change= */ false)) {
    1246           0 :                             SetAddressBook(dest.dest, "", "receive");
    1247           0 :                         }
    1248             :                     }
    1249             :                 }
    1250             :             }
    1251             : 
    1252             :             // Block disconnection override an abandoned tx as unconfirmed
    1253             :             // which means user may have to call abandontransaction again
    1254        1446 :             TxState tx_state = std::visit([](auto&& s) -> TxState { return s; }, state);
    1255         723 :             CWalletTx* wtx = AddToWallet(MakeTransactionRef(tx), tx_state, /*update_wtx=*/nullptr, /*fFlushOnClose=*/false, rescanning_old_block);
    1256         723 :             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         722 :             return true;
    1262             :         }
    1263             :     }
    1264          19 :     return false;
    1265         842 : }
    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         723 : void CWallet::MarkInputsDirty(const CTransactionRef& tx)
    1282             : {
    1283        1446 :     for (const CTxIn& txin : tx->vin) {
    1284         723 :         auto it = mapWallet.find(txin.prevout.hash);
    1285         723 :         if (it != mapWallet.end()) {
    1286           3 :             it->second.MarkDirty();
    1287           3 :         }
    1288             :     }
    1289         723 : }
    1290             : 
    1291           0 : bool CWallet::AbandonTransaction(const uint256& hashTx)
    1292             : {
    1293           0 :     LOCK(cs_wallet);
    1294             : 
    1295             :     // Can't mark abandoned if confirmed or in mempool
    1296           0 :     auto it = mapWallet.find(hashTx);
    1297           0 :     assert(it != mapWallet.end());
    1298           0 :     const CWalletTx& origtx = it->second;
    1299           0 :     if (GetTxDepthInMainChain(origtx) != 0 || origtx.InMempool() || IsTxLockedByInstantSend(origtx)) {
    1300           0 :         return false;
    1301             :     }
    1302             : 
    1303           0 :     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           0 :         assert(!wtx.isConfirmed());
    1306           0 :         assert(!wtx.InMempool());
    1307             :         // If already conflicted or abandoned, no need to set abandoned
    1308           0 :         if (!wtx.isConflicted() && !wtx.isAbandoned()) {
    1309           0 :             wtx.m_state = TxStateInactive{/*abandoned=*/true};
    1310           0 :             return TxUpdate::NOTIFY_CHANGED;
    1311             :         }
    1312           0 :         return TxUpdate::UNCHANGED;
    1313           0 :     };
    1314             : 
    1315             :     // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too
    1316             : 
    1317           0 :     RecursiveUpdateTxState(hashTx, try_updating_state);
    1318             : 
    1319           0 :     fAnonymizableTallyCached = false;
    1320           0 :     fAnonymizableTallyCachedNonDenom = false;
    1321             : 
    1322           0 :     return true;
    1323           0 : }
    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           1 : void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx)
    1338             : {
    1339           1 :     LOCK(cs_wallet);
    1340             : 
    1341           1 :     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           1 :     if (conflictconfirms >= 0)
    1347           0 :         return;
    1348             : 
    1349           2 :     auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
    1350           1 :         if (conflictconfirms < GetTxDepthInMainChain(wtx)) {
    1351             :             // Block is 'more conflicted' than current confirm; update.
    1352             :             // Mark transaction as conflicted with this block.
    1353           1 :             wtx.m_state = TxStateConflicted{hashBlock, conflicting_height};
    1354           1 :             return TxUpdate::CHANGED;
    1355             :         }
    1356           0 :         return TxUpdate::UNCHANGED;
    1357           1 :     };
    1358             : 
    1359             :     // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too.
    1360           1 :     RecursiveUpdateTxState(hashTx, try_updating_state);
    1361             : 
    1362           1 : }
    1363             : 
    1364           1 : 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           1 :     WalletBatch batch(GetDatabase(), false);
    1367             : 
    1368           1 :     std::set<uint256> todo;
    1369           1 :     std::set<uint256> done;
    1370             : 
    1371           1 :     todo.insert(tx_hash);
    1372             : 
    1373           2 :     while (!todo.empty()) {
    1374           1 :         uint256 now = *todo.begin();
    1375           1 :         todo.erase(now);
    1376           1 :         done.insert(now);
    1377           1 :         auto it = mapWallet.find(now);
    1378           1 :         assert(it != mapWallet.end());
    1379           1 :         CWalletTx& wtx = it->second;
    1380             : 
    1381           1 :         TxUpdate update_state = try_updating_state(wtx);
    1382           1 :         if (update_state != TxUpdate::UNCHANGED) {
    1383           1 :             wtx.MarkDirty();
    1384           1 :             batch.WriteTx(wtx);
    1385             :             // Iterate over all its outputs, and update those tx states as well (if applicable)
    1386           3 :             for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
    1387           2 :                 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i));
    1388           2 :                 for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) {
    1389           0 :                     if (!done.count(iter->second)) {
    1390           0 :                         todo.insert(iter->second);
    1391           0 :                     }
    1392           0 :                 }
    1393           2 :             }
    1394             : 
    1395           1 :             if (update_state == TxUpdate::NOTIFY_CHANGED) {
    1396           0 :                 NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED);
    1397           0 :             }
    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           1 :             MarkInputsDirty(wtx.tx);
    1402           1 :         }
    1403             :     }
    1404             : 
    1405           1 :     fAnonymizableTallyCached = false;
    1406           1 :     fAnonymizableTallyCachedNonDenom = false;
    1407           1 : }
    1408             : 
    1409         842 : void CWallet::SyncTransaction(const CTransactionRef& ptx, const SyncTxState& state, WalletBatch& batch, bool update_tx, bool rescanning_old_block)
    1410             : {
    1411         842 :     if (!AddToWalletIfInvolvingMe(ptx, state, batch, update_tx, rescanning_old_block))
    1412         120 :         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         722 :     MarkInputsDirty(ptx);
    1418             : 
    1419         722 :     fAnonymizableTallyCached = false;
    1420         722 :     fAnonymizableTallyCachedNonDenom = false;
    1421         842 : }
    1422             : 
    1423           5 : void CWallet::transactionAddedToMempool(const CTransactionRef& tx, int64_t nAcceptTime) {
    1424           5 :     LOCK(cs_wallet);
    1425           5 :     WalletBatch batch(GetDatabase());
    1426           5 :     SyncTransaction(tx, TxStateInMempool{}, batch);
    1427             : 
    1428           4 :     auto it = mapWallet.find(tx->GetHash());
    1429           4 :     if (it != mapWallet.end()) {
    1430           4 :         RefreshMempoolStatus(it->second, chain());
    1431           4 :     }
    1432           5 : }
    1433             : 
    1434           9 : void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) {
    1435           9 :     if (reason != MemPoolRemovalReason::CONFLICT) {
    1436           9 :         LOCK(cs_wallet);
    1437           9 :         auto it = mapWallet.find(tx->GetHash());
    1438           9 :         if (it != mapWallet.end()) {
    1439           3 :             RefreshMempoolStatus(it->second, chain());
    1440           3 :         }
    1441           9 :     }
    1442             :     // Handle transactions that were removed from the mempool because they
    1443             :     // conflict with transactions in a newly connected block.
    1444           9 :     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           0 :         LOCK(cs_wallet);
    1470           0 :         WalletBatch batch(GetDatabase());
    1471           0 :         SyncTransaction(tx, TxStateInactive{}, batch);
    1472           0 :     }
    1473           9 : }
    1474             : 
    1475           6 : void CWallet::blockConnected(const CBlock& block, int height)
    1476             : {
    1477           6 :     const uint256& block_hash = block.GetHash();
    1478           6 :     LOCK(cs_wallet);
    1479             : 
    1480           6 :     m_last_block_processed_height = height;
    1481           6 :     m_last_block_processed = block_hash;
    1482           6 :     WalletBatch batch(GetDatabase());
    1483          15 :     for (size_t index = 0; index < block.vtx.size(); index++) {
    1484           9 :         SyncTransaction(block.vtx[index], TxStateConfirmed{block_hash, height, static_cast<int>(index)}, batch);
    1485           9 :         transactionRemovedFromMempool(block.vtx[index], MemPoolRemovalReason::BLOCK);
    1486           9 :     }
    1487             : 
    1488             :     // reset cache to make sure no longer immature coins are included
    1489           6 :     fAnonymizableTallyCached = false;
    1490           6 :     fAnonymizableTallyCachedNonDenom = false;
    1491           6 : }
    1492             : 
    1493           0 : void CWallet::blockDisconnected(const CBlock& block, int height)
    1494             : {
    1495           0 :     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           0 :     m_last_block_processed_height = height - 1;
    1502           0 :     m_last_block_processed = block.hashPrevBlock;
    1503             : 
    1504           0 :     int disconnect_height = height;
    1505             : 
    1506           0 :     WalletBatch batch(GetDatabase());
    1507           0 :     for (const CTransactionRef& ptx : block.vtx) {
    1508           0 :         SyncTransaction(ptx, TxStateInactive{}, batch);
    1509             : 
    1510           0 :         for (const CTxIn& tx_in : ptx->vin) {
    1511             :             // No other wallet transactions conflicted with this transaction
    1512           0 :             if (mapTxSpends.count(tx_in.prevout) < 1) continue;
    1513             : 
    1514           0 :             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           0 :             for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) {
    1518           0 :                 CWalletTx& wtx = mapWallet.find(_it->second)->second;
    1519             : 
    1520           0 :                 if (!wtx.isConflicted()) continue;
    1521             : 
    1522           0 :                 auto try_updating_state = [&](CWalletTx& tx) {
    1523           0 :                     if (!tx.isConflicted()) return TxUpdate::UNCHANGED;
    1524           0 :                     if (tx.state<TxStateConflicted>()->conflicting_block_height >= disconnect_height) {
    1525           0 :                         tx.m_state = TxStateInactive{};
    1526           0 :                         return TxUpdate::CHANGED;
    1527             :                     }
    1528           0 :                     return TxUpdate::UNCHANGED;
    1529           0 :                 };
    1530             : 
    1531           0 :                 RecursiveUpdateTxState(wtx.tx->GetHash(), try_updating_state);
    1532           0 :             }
    1533             :         }
    1534             :     }
    1535             : 
    1536             :     // reset cache to make sure no longer mature coins are excluded
    1537           0 :     fAnonymizableTallyCached = false;
    1538           0 :     fAnonymizableTallyCachedNonDenom = false;
    1539           0 : }
    1540             : 
    1541           6 : void CWallet::updatedBlockTip()
    1542             : {
    1543           6 :     m_best_block_time = GetTime();
    1544           6 : }
    1545             : 
    1546           2 : void CWallet::BlockUntilSyncedToCurrentChain() const {
    1547           2 :     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           4 :     uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
    1553           2 :     chain().waitForNotificationsIfTipChanged(last_block_hash);
    1554           2 : }
    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         111 : CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
    1559             : {
    1560             :     {
    1561         111 :         LOCK(cs_wallet);
    1562         111 :         const auto mi = mapWallet.find(txin.prevout.hash);
    1563         111 :         if (mi != mapWallet.end())
    1564             :         {
    1565          60 :             const CWalletTx& prev = (*mi).second;
    1566          60 :             if (txin.prevout.n < prev.tx->vout.size())
    1567          60 :                 if (IsMine(prev.tx->vout[txin.prevout.n]) & filter)
    1568          60 :                     return prev.tx->vout[txin.prevout.n].nValue;
    1569           0 :         }
    1570         111 :     }
    1571          51 :     return 0;
    1572         111 : }
    1573             : 
    1574        3293 : isminetype CWallet::IsMine(const CTxOut& txout) const
    1575             : {
    1576        3293 :     AssertLockHeld(cs_wallet);
    1577        3293 :     return IsMine(txout.scriptPubKey);
    1578             : }
    1579             : 
    1580       11767 : isminetype CWallet::IsMine(const CTxDestination& dest) const
    1581             : {
    1582       11767 :     AssertLockHeld(cs_wallet);
    1583       11767 :     return IsMine(GetScriptForDestination(dest));
    1584           0 : }
    1585             : 
    1586       15071 : isminetype CWallet::IsMine(const CScript& script) const
    1587             : {
    1588       15071 :     AssertLockHeld(cs_wallet);
    1589       15071 :     isminetype result = ISMINE_NO;
    1590       44011 :     for (const auto& spk_man_pair : m_spk_managers) {
    1591       28940 :         result = std::max(result, spk_man_pair.second->IsMine(script));
    1592             :     }
    1593       15071 :     return result;
    1594             : }
    1595             : 
    1596         740 : bool CWallet::IsMine(const CTransaction& tx) const
    1597             : {
    1598         740 :     AssertLockHeld(cs_wallet);
    1599         760 :     for (const CTxOut& txout : tx.vout)
    1600         741 :         if (IsMine(txout))
    1601         721 :             return true;
    1602          19 :     return false;
    1603         740 : }
    1604             : 
    1605           0 : isminetype CWallet::IsMine(const COutPoint& outpoint) const
    1606             : {
    1607           0 :     AssertLockHeld(cs_wallet);
    1608           0 :     auto wtx = GetWalletTx(outpoint.hash);
    1609           0 :     if (!wtx) {
    1610           0 :         return ISMINE_NO;
    1611             :     }
    1612           0 :     if (outpoint.n >= wtx->tx->vout.size()) {
    1613           0 :         return ISMINE_NO;
    1614             :     }
    1615           0 :     return IsMine(wtx->tx->vout[outpoint.n]);
    1616           0 : }
    1617             : 
    1618          19 : bool CWallet::IsFromMe(const CTransaction& tx) const
    1619             : {
    1620          19 :     return (GetDebit(tx, ISMINE_ALL) > 0);
    1621             : }
    1622             : 
    1623          86 : CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const
    1624             : {
    1625          86 :     CAmount nDebit = 0;
    1626         197 :     for (const CTxIn& txin : tx.vin)
    1627             :     {
    1628         111 :         nDebit += GetDebit(txin, filter);
    1629         111 :         if (!MoneyRange(nDebit))
    1630           0 :             throw std::runtime_error(std::string(__func__) + ": value out of range");
    1631             :     }
    1632          86 :     return nDebit;
    1633           0 : }
    1634             : 
    1635           8 : bool CWallet::IsHDEnabled() const
    1636             : {
    1637             :     // All Active ScriptPubKeyMans must be HD for this to be true
    1638           8 :     bool result = false;
    1639          18 :     for (const auto& spk_man : GetActiveScriptPubKeyMans()) {
    1640          10 :         if (!spk_man->IsHDEnabled()) return false;
    1641          10 :         result = true;
    1642             :     }
    1643           8 :     return result;
    1644           8 : }
    1645             : 
    1646        6189 : bool CWallet::CanGetAddresses(bool internal) const
    1647             : {
    1648        6189 :     LOCK(cs_wallet);
    1649        6189 :     if (m_spk_managers.empty()) return false;
    1650        6189 :     auto spk_man = GetScriptPubKeyMan(internal);
    1651        6189 :     if (spk_man && spk_man->CanGetAddresses(internal)) {
    1652        6189 :         return true;
    1653             :     }
    1654           0 :     return false;
    1655        6189 : }
    1656             : 
    1657          30 : void CWallet::SetWalletFlag(uint64_t flags)
    1658             : {
    1659          30 :     LOCK(cs_wallet);
    1660          30 :     m_wallet_flags |= flags;
    1661          30 :     if (!WalletBatch(GetDatabase()).WriteWalletFlags(m_wallet_flags))
    1662           0 :         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
    1663          30 : }
    1664             : 
    1665           0 : void CWallet::UnsetWalletFlag(uint64_t flag)
    1666             : {
    1667           0 :     LOCK(cs_wallet);
    1668           0 :     WalletBatch batch(GetDatabase());
    1669           0 :     UnsetWalletFlagWithDB(batch, flag);
    1670           0 : }
    1671             : 
    1672          77 : void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag)
    1673             : {
    1674          77 :     LOCK(cs_wallet);
    1675          77 :     m_wallet_flags &= ~flag;
    1676          77 :     if (!batch.WriteWalletFlags(m_wallet_flags))
    1677           0 :         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
    1678          77 : }
    1679             : 
    1680          77 : void CWallet::UnsetBlankWalletFlag(WalletBatch& batch)
    1681             : {
    1682          77 :     UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
    1683          77 : }
    1684             : 
    1685           0 : 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           0 :     if (std::unique_ptr<interfaces::CoinJoin::Client> coinjoin_client = coinjoin_available() ? coinjoin_loader().GetClient(GetName()) : nullptr) {
    1690           0 :         coinjoin_client->stopMixing();
    1691           0 :     }
    1692           0 :     nKeysLeftSinceAutoBackup = 0;
    1693           0 : }
    1694             : 
    1695        6391 : void CWallet::KeepDestinationCallback(bool erased)
    1696             : {
    1697        6391 :     if (erased) --nKeysLeftSinceAutoBackup;
    1698        6391 :     if (!nWalletBackups) nKeysLeftSinceAutoBackup = 0;
    1699        6391 : }
    1700             : 
    1701       49516 : bool CWallet::IsWalletFlagSet(uint64_t flag) const
    1702             : {
    1703       49516 :     return (m_wallet_flags & flag);
    1704             : }
    1705             : 
    1706           5 : bool CWallet::LoadWalletFlags(uint64_t flags)
    1707             : {
    1708           5 :     LOCK(cs_wallet);
    1709           5 :     if (((flags & KNOWN_WALLET_FLAGS) >> 32) ^ (flags >> 32)) {
    1710             :         // contains unknown non-tolerable wallet flags
    1711           0 :         return false;
    1712             :     }
    1713           5 :     m_wallet_flags = flags;
    1714             : 
    1715           5 :     return true;
    1716           5 : }
    1717             : 
    1718           3 : void CWallet::InitWalletFlags(uint64_t flags)
    1719             : {
    1720           3 :     LOCK(cs_wallet);
    1721             : 
    1722             :     // We should never be writing unknown non-tolerable wallet flags
    1723           3 :     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           3 :     assert(m_wallet_flags == 0);
    1726             : 
    1727           3 :     if (!WalletBatch(GetDatabase()).WriteWalletFlags(flags)) {
    1728           0 :         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed");
    1729             :     }
    1730             : 
    1731           3 :     if (!LoadWalletFlags(flags)) assert(false);
    1732           3 : }
    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        3520 : 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        3520 :     const CScript& scriptPubKey = txout.scriptPubKey;
    1740        3520 :     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        6722 :     const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(tx_in.prevout));
    1745        3520 :     if (!ProduceSignature(provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
    1746           0 :         return false;
    1747             :     }
    1748        3520 :     UpdateInput(tx_in, sigdata);
    1749        3520 :     return true;
    1750        3520 : }
    1751             : 
    1752          13 : bool FillInputToWeight(CTxIn& txin, int64_t target_weight)
    1753             : {
    1754          13 :     assert(txin.scriptSig.empty());
    1755             : 
    1756          13 :     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          13 :     if (target_weight < txin_weight) {
    1760           3 :         return false;
    1761             :     }
    1762          10 :     if (target_weight == txin_weight) {
    1763           1 :         return true;
    1764             :     }
    1765             : 
    1766             :     // Subtract current txin weight, which should include empty witness stack
    1767           9 :     int64_t add_weight = target_weight - txin_weight;
    1768           9 :     assert(add_weight > 0);
    1769             : 
    1770           9 :     add_weight -= (GetSizeOfCompactSize(add_weight) - 1);
    1771           9 :     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           9 :     assert(::GetSerializeSize(txin) <= static_cast<size_t>(target_weight));
    1780           9 :     assert(::GetSerializeSize(txin) >= static_cast<size_t>(target_weight) - 2);
    1781             : 
    1782           9 :     return true;
    1783          13 : }
    1784             : 
    1785             : // Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes)
    1786         262 : 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         262 :     int nIn = 0;
    1790        1380 :     for (const auto& txout : txouts)
    1791             :     {
    1792        1118 :         CTxIn& txin = txNew.vin[nIn];
    1793             :         // If weight was provided, fill the input to that weight
    1794        1118 :         if (coin_control && coin_control->HasInputWeight(txin.prevout)) {
    1795           0 :             if (!FillInputToWeight(txin, coin_control->GetInputWeight(txin.prevout))) {
    1796           0 :                 return false;
    1797             :             }
    1798           0 :             nIn++;
    1799           0 :             continue;
    1800             :         }
    1801        1118 :         const std::unique_ptr<SigningProvider> provider = GetSolvingProvider(txout.scriptPubKey);
    1802        1118 :         if (!provider || !DummySignInput(*provider, txin, txout, coin_control)) {
    1803           0 :             if (!coin_control || !DummySignInput(coin_control->m_external_provider, txin, txout, coin_control)) {
    1804           0 :                 return false;
    1805             :             }
    1806           0 :         }
    1807             : 
    1808        1118 :         nIn++;
    1809        1118 :     }
    1810         262 :     return true;
    1811         262 : }
    1812             : 
    1813           2 : bool CWallet::ImportScripts(const std::set<CScript> scripts, int64_t timestamp)
    1814             : {
    1815           2 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1816           2 :     if (!spk_man) {
    1817           0 :         return false;
    1818             :     }
    1819           2 :     LOCK(spk_man->cs_KeyStore);
    1820           2 :     return spk_man->ImportScripts(scripts, timestamp);
    1821           2 : }
    1822             : 
    1823           3 : bool CWallet::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp)
    1824             : {
    1825           3 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1826           3 :     if (!spk_man) {
    1827           0 :         return false;
    1828             :     }
    1829           3 :     LOCK(spk_man->cs_KeyStore);
    1830           3 :     return spk_man->ImportPrivKeys(privkey_map, timestamp);
    1831           3 : }
    1832             : 
    1833           2 : 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           2 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1836           2 :     if (!spk_man) {
    1837           0 :         return false;
    1838             :     }
    1839           2 :     LOCK(spk_man->cs_KeyStore);
    1840           2 :     return spk_man->ImportPubKeys(ordered_pubkeys, pubkey_map, key_origins, add_keypool, internal, timestamp);
    1841           2 : }
    1842             : 
    1843           2 : 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           2 :     auto spk_man = GetLegacyScriptPubKeyMan();
    1846           2 :     if (!spk_man) {
    1847           0 :         return false;
    1848             :     }
    1849           2 :     LOCK(spk_man->cs_KeyStore);
    1850           2 :     if (!spk_man->ImportScriptPubKeys(script_pub_keys, have_solving_data, timestamp)) {
    1851           0 :         return false;
    1852             :     }
    1853           2 :     if (apply_label) {
    1854           0 :         WalletBatch batch(GetDatabase());
    1855           0 :         for (const CScript& script : script_pub_keys) {
    1856           0 :             CTxDestination dest;
    1857           0 :             ExtractDestination(script, dest);
    1858           0 :             if (IsValidDestination(dest)) {
    1859           0 :                 SetAddressBookWithDB(batch, dest, label, "receive");
    1860           0 :             }
    1861             :         }
    1862           0 :     }
    1863           2 :     return true;
    1864           2 : }
    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           2 : 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           2 :     int start_height = 0;
    1880           2 :     uint256 start_block;
    1881           2 :     bool start = chain().findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, FoundBlock().hash(start_block).height(start_height));
    1882           4 :     WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - start_height + 1 : 0);
    1883             : 
    1884           2 :     if (start) {
    1885             :         // TODO: this should take into account failure by ScanResult::USER_ABORT
    1886           2 :         ScanResult result = ScanForWalletTransactions(start_block, start_height, /*max_height=*/{}, reserver, /*fUpdate=*/update, /*save_progress=*/false);
    1887           2 :         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           1 :     }
    1893           1 :     return startTime;
    1894           2 : }
    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          15 : 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          15 :     constexpr auto INTERVAL_TIME{60s};
    1921          15 :     auto current_time{reserver.now()};
    1922          15 :     auto start_time{reserver.now()};
    1923             : 
    1924          15 :     assert(reserver.isReserved());
    1925             : 
    1926          15 :     uint256 block_hash = start_block;
    1927          15 :     ScanResult result;
    1928             : 
    1929          15 :     std::unique_ptr<FastWalletRescanFilter> fast_rescan_filter;
    1930          15 :     if (!IsLegacy() && chain().hasBlockFilterIndex(BlockFilterType::BASIC_FILTER)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this);
    1931             : 
    1932          15 :     WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(),
    1933          15 :                     fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks");
    1934             : 
    1935          15 :     ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
    1936          30 :     uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
    1937          15 :     uint256 end_hash = tip_hash;
    1938          15 :     if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash));
    1939          15 :     double progress_begin = chain().guessVerificationProgress(block_hash);
    1940          15 :     double progress_end = chain().guessVerificationProgress(end_hash);
    1941          15 :     double progress_current = progress_begin;
    1942          15 :     int block_height = start_height;
    1943          15 :     WalletBatch batch(GetDatabase());
    1944         932 :     while (!fAbortRescan && !chain().shutdownRequested()) {
    1945         932 :         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         931 :             m_scanning_progress = 0;
    1949             :         }
    1950         932 :         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         932 :         bool next_interval = reserver.now() >= current_time + INTERVAL_TIME;
    1955         932 :         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         932 :         bool fetch_block{true};
    1961         932 :         if (fast_rescan_filter) {
    1962           0 :             fast_rescan_filter->UpdateIfNeeded();
    1963           0 :             auto matches_block{fast_rescan_filter->MatchesBlock(block_hash)};
    1964           0 :             if (matches_block.has_value()) {
    1965           0 :                 if (*matches_block) {
    1966           0 :                     LogPrint(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (filter matched)\n", block_height, block_hash.ToString());
    1967           0 :                 } else {
    1968           0 :                     result.last_scanned_block = block_hash;
    1969           0 :                     result.last_scanned_height = block_height;
    1970           0 :                     fetch_block = false;
    1971             :                 }
    1972           0 :             } 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           0 :         }
    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         932 :         bool block_still_active = false;
    1980         932 :         bool next_block = false;
    1981         932 :         uint256 next_block_hash;
    1982         932 :         chain().findBlock(block_hash, FoundBlock().inActiveChain(block_still_active).nextBlock(FoundBlock().inActiveChain(next_block).hash(next_block_hash)));
    1983             : 
    1984         932 :         if (fetch_block) {
    1985             :             // Read block data
    1986         932 :             CBlock block;
    1987         932 :             chain().findBlock(block_hash, FoundBlock().data(block));
    1988             : 
    1989         932 :             if (!block.IsNull()) {
    1990         827 :                 LOCK(cs_wallet);
    1991         827 :                 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        1656 :                 for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
    1999         829 :                     SyncTransaction(block.vtx[posInBlock], TxStateConfirmed{block_hash, block_height, static_cast<int>(posInBlock)}, batch, fUpdate, /*rescanning_old_block=*/true);
    2000         829 :                 }
    2001             :                 // scan succeeded, record block as most recent successfully scanned
    2002         827 :                 result.last_scanned_block = block_hash;
    2003         827 :                 result.last_scanned_height = block_height;
    2004             : 
    2005         827 :                 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         827 :             } 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         932 :         }
    2020         932 :         if (max_height && block_height >= *max_height) {
    2021           0 :             break;
    2022             :         }
    2023             :         {
    2024         932 :             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          15 :                 break;
    2028             :             }
    2029             : 
    2030             :             // increment block and verification progress
    2031         917 :             block_hash = next_block_hash;
    2032         917 :             ++block_height;
    2033         917 :             progress_current = chain().guessVerificationProgress(block_hash);
    2034             : 
    2035             :             // handle updated tip hash
    2036         917 :             const uint256 prev_tip_hash = tip_hash;
    2037        1834 :             tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
    2038         917 :             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          15 :     if (!max_height) {
    2045          15 :         WalletLogPrintf("Scanning current mempool transactions.\n");
    2046          30 :         WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this));
    2047          15 :     }
    2048          15 :     ShowProgress(strprintf("%s " + _("Rescanning…").translated, GetDisplayName()), 100); // hide progress dialog in GUI
    2049          15 :     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          15 :     } 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          15 :         WalletLogPrintf("Rescan completed in %15dms\n", Ticks<std::chrono::milliseconds>(reserver.now() - start_time));
    2057             :     }
    2058             :     return result;
    2059          15 : }
    2060             : 
    2061           1 : void CWallet::ReacceptWalletTransactions()
    2062             : {
    2063             :     // If transactions aren't being broadcasted, don't let them into local mempool either
    2064           1 :     if (!fBroadcastTransactions)
    2065           1 :         return;
    2066           0 :     std::map<int64_t, CWalletTx*> mapSorted;
    2067             : 
    2068             :     // Sort pending wallet transactions based on their initial wallet insertion order
    2069           0 :     for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
    2070           0 :         const uint256& wtxid = item.first;
    2071           0 :         CWalletTx& wtx = item.second;
    2072           0 :         assert(wtx.GetHash() == wtxid);
    2073             : 
    2074           0 :         int nDepth = GetTxDepthInMainChain(wtx);
    2075             : 
    2076           0 :         if (!wtx.IsCoinBase() && (nDepth == 0 && !IsTxLockedByInstantSend(wtx) && !wtx.isAbandoned())) {
    2077           0 :             mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
    2078           0 :         }
    2079             :     }
    2080             : 
    2081             :     // Try to add wallet transactions to memory pool
    2082           0 :     for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
    2083           0 :         CWalletTx& wtx = *(item.second);
    2084           0 :         bilingual_str unused_err_string;
    2085           0 :         SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, false);
    2086           0 :     }
    2087           1 : }
    2088             : 
    2089           0 : bool CWallet::CanTxBeResent(const CWalletTx& wtx) const
    2090             : {
    2091           0 :     AssertLockHeld(cs_wallet);
    2092             : 
    2093           0 :     return
    2094             :         // Can't relay if wallet is not broadcasting
    2095           0 :         GetBroadcastTransactions() &&
    2096             :         // Don't relay abandoned transactions
    2097           0 :         !wtx.isAbandoned() &&
    2098             :         // Don't try to submit coinbase transactions. These would fail anyway but would
    2099             :         // cause log spam.
    2100           0 :         !wtx.IsCoinBase() &&
    2101             :         // Don't try to submit conflicted or confirmed transactions.
    2102           0 :         GetTxDepthInMainChain(wtx) == 0 &&
    2103             :         // Don't try to submit transactions locked via InstantSend.
    2104           0 :         !IsTxLockedByInstantSend(wtx);
    2105             : }
    2106             : 
    2107           0 : bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, bilingual_str& err_string, bool relay) const
    2108             : {
    2109           0 :     AssertLockHeld(cs_wallet);
    2110             : 
    2111           0 :     if (!CanTxBeResent(wtx)) return false;
    2112             : 
    2113             :     // Submit transaction to mempool for relay
    2114           0 :     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           0 :     bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, relay, err_string);
    2125           0 :     if (ret) wtx.m_state = TxStateInMempool{};
    2126           0 :     return ret;
    2127           0 : }
    2128             : 
    2129           0 : std::set<uint256> CWallet::GetTxConflicts(const CWalletTx& wtx) const
    2130             : {
    2131           0 :     AssertLockHeld(cs_wallet);
    2132             : 
    2133           0 :     const uint256 myHash{wtx.GetHash()};
    2134           0 :     std::set<uint256> result{GetConflicts(myHash)};
    2135           0 :     result.erase(myHash);
    2136           0 :     return result;
    2137           0 : }
    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           0 : 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           0 :     if (!chain().isReadyToBroadcast()) return;
    2153             : 
    2154             :     // Do this infrequently and randomly to avoid giving away
    2155             :     // that these are our transactions.
    2156           0 :     if (GetTime() < m_next_resend || !fBroadcastTransactions) return;
    2157           0 :     bool fFirst = (m_next_resend == 0);
    2158             :     // resend 1-3 hours from now, ~2 hours on average.
    2159           0 :     m_next_resend = GetTime() + (1 * 60 * 60) + GetRand(2 * 60 * 60);
    2160           0 :     if (fFirst) return;
    2161             : 
    2162           0 :     int submitted_tx_count = 0;
    2163             : 
    2164             :     { // cs_wallet scope
    2165           0 :         LOCK(cs_wallet);
    2166             : 
    2167             :         // Relay transactions
    2168           0 :         for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
    2169           0 :             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           0 :             if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
    2174           0 :             bilingual_str unused_err_string;
    2175           0 :             if (SubmitTxMemoryPoolAndRelay(wtx, unused_err_string, true)) ++submitted_tx_count;
    2176           0 :         }
    2177           0 :     } // cs_wallet
    2178             : 
    2179           0 :     if (submitted_tx_count > 0) {
    2180           0 :         WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, submitted_tx_count);
    2181           0 :     }
    2182           0 : }
    2183             : 
    2184             : /** @} */ // end of mapWallet
    2185             : 
    2186           0 : void MaybeResendWalletTxs(WalletContext& context)
    2187             : {
    2188           0 :     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
    2189           0 :         pwallet->ResendWalletTransactions();
    2190             :     }
    2191           0 : }
    2192             : 
    2193             : 
    2194             : /** @defgroup Actions
    2195             :  *
    2196             :  * @{
    2197             :  */
    2198             : 
    2199         239 : std::unordered_set<const CWalletTx*, WalletTxHasher> CWallet::GetSpendableTXs() const
    2200             : {
    2201         239 :     AssertLockHeld(cs_wallet);
    2202             : 
    2203         239 :     std::unordered_set<const CWalletTx*, WalletTxHasher> ret;
    2204       24252 :     for (auto it = setWalletUTXO.begin(); it != setWalletUTXO.end(); ) {
    2205       24013 :         const auto& outpoint = *it;
    2206       24013 :         const auto jt = mapWallet.find(outpoint.hash);
    2207       24013 :         if (jt != mapWallet.end()) {
    2208       24013 :             ret.emplace(&jt->second);
    2209       24013 :         }
    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      100009 :         while (it != setWalletUTXO.end() && it->hash == outpoint.hash) {
    2214       26110 :             ++it;
    2215             :         }
    2216             :     }
    2217         239 :     return ret;
    2218         239 : }
    2219             : 
    2220         158 : bool CWallet::SignTransaction(CMutableTransaction& tx) const
    2221             : {
    2222         158 :     AssertLockHeld(cs_wallet);
    2223             : 
    2224             :     // Build coins map
    2225         158 :     std::map<COutPoint, Coin> coins;
    2226         743 :     for (auto& input : tx.vin) {
    2227         585 :         const auto mi = mapWallet.find(input.prevout.hash);
    2228         585 :         if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) {
    2229           0 :             return false;
    2230             :         }
    2231         585 :         const CWalletTx& wtx = mi->second;
    2232         585 :         int prev_height = wtx.state<TxStateConfirmed>() ? wtx.state<TxStateConfirmed>()->confirmed_block_height : 0;
    2233         585 :         coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], prev_height, wtx.IsCoinBase());
    2234             :     }
    2235         158 :     std::map<int, bilingual_str> input_errors;
    2236         158 :     return SignTransaction(tx, coins, SIGHASH_ALL, input_errors);
    2237         158 : }
    2238             : 
    2239         158 : 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         336 :     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         178 :         if (spk_man->SignTransaction(tx, coins, sighash, input_errors)) {
    2246         158 :             return true;
    2247             :         }
    2248             :     }
    2249             : 
    2250             :     // At this point, one input was not fully signed otherwise we would have exited already
    2251           0 :     return false;
    2252         158 : }
    2253             : 
    2254           2 : TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed, bool finalize) const
    2255             : {
    2256           2 :     if (n_signed) {
    2257           0 :         *n_signed = 0;
    2258           0 :     }
    2259           2 :     const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
    2260           2 :     LOCK(cs_wallet);
    2261             :     // Get all of the previous transactions
    2262           6 :     for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
    2263           4 :         const CTxIn& txin = psbtx.tx->vin[i];
    2264           4 :         PSBTInput& input = psbtx.inputs.at(i);
    2265             : 
    2266           4 :         if (PSBTInputSigned(input)) {
    2267           0 :             continue;
    2268             :         }
    2269             : 
    2270             :         // If we have no utxo, grab it from the wallet.
    2271           4 :         if (!input.non_witness_utxo) {
    2272           3 :             const uint256& txhash = txin.prevout.hash;
    2273           3 :             const auto it = mapWallet.find(txhash);
    2274           3 :             if (it != mapWallet.end()) {
    2275           1 :                 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           1 :                 input.non_witness_utxo = wtx.tx;
    2279           1 :             }
    2280           3 :         }
    2281           4 :     }
    2282             : 
    2283             :     // Fill in information from ScriptPubKeyMans
    2284           6 :     for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
    2285           4 :         int n_signed_this_spkm = 0;
    2286           4 :         TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm, finalize);
    2287           4 :         if (res != TransactionError::OK) {
    2288           1 :             return res;
    2289             :         }
    2290             : 
    2291           3 :         if (n_signed) {
    2292           0 :             (*n_signed) += n_signed_this_spkm;
    2293           0 :         }
    2294             :     }
    2295             : 
    2296           1 :     RemoveUnnecessaryTransactions(psbtx, sighash_type);
    2297             : 
    2298             :     // Complete if every input is now signed
    2299           1 :     complete = true;
    2300           3 :     for (const auto& input : psbtx.inputs) {
    2301           2 :         complete &= PSBTInputSigned(input);
    2302             :     }
    2303             : 
    2304           1 :     return TransactionError::OK;
    2305           2 : }
    2306             : 
    2307           0 : SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
    2308             : {
    2309           0 :     SignatureData sigdata;
    2310           0 :     CScript script_pub_key = GetScriptForDestination(pkhash);
    2311           0 :     for (const auto& spk_man_pair : m_spk_managers) {
    2312           0 :         if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {
    2313           0 :             LOCK(cs_wallet);  // DescriptorScriptPubKeyMan calls IsLocked which can lock cs_wallet in a deadlocking order
    2314           0 :             return spk_man_pair.second->SignMessage(message, pkhash, str_sig);
    2315           0 :         }
    2316             :     }
    2317           0 :     return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
    2318           0 : }
    2319             : 
    2320           0 : bool CWallet::SignSpecialTxPayload(const uint256& hash, const CKeyID& keyid, std::vector<unsigned char>& vchSig) const
    2321             : {
    2322           0 :     SignatureData sigdata;
    2323           0 :     CScript script_pub_key = GetScriptForDestination(PKHash(keyid));
    2324           0 :     for (const auto& spk_man_pair : m_spk_managers) {
    2325           0 :         if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) {
    2326           0 :             LOCK(cs_wallet);  // DescriptorScriptPubKeyMan calls IsLocked which can lock cs_wallet in a deadlocking order
    2327           0 :             return spk_man_pair.second->SignSpecialTxPayload(hash, keyid, vchSig);
    2328           0 :         }
    2329             :     }
    2330           0 :     return false;
    2331           0 : }
    2332             : 
    2333           0 : 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           0 :     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           0 :     std::string strMessage{vote.GetSignatureString()};
    2347           0 :     std::string signature;
    2348           0 :     SigningResult err = SignMessage(strMessage, PKHash{keyID}, signature);
    2349           0 :     if (err != SigningResult::OK) {
    2350           0 :         LogPrintf("SignGovernanceVote failed due to: %s\n", SigningResultString(err));
    2351           0 :         return false;
    2352             :     }
    2353           0 :     const auto opt_decoded = DecodeBase64(signature);
    2354           0 :     CHECK_NONFATAL(opt_decoded.has_value()); // DecodeBase64 should not fail
    2355             : 
    2356           0 :     vote.SetSignature(std::vector<unsigned char>(opt_decoded->data(), opt_decoded->data() + opt_decoded->size()));
    2357           0 :     return true;
    2358           0 : }
    2359             : 
    2360          17 : void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm)
    2361             : {
    2362          17 :     LOCK(cs_wallet);
    2363          17 :     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          34 :     CWalletTx* wtx = AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) {
    2368          17 :         CHECK_NONFATAL(wtx.mapValue.empty());
    2369          17 :         CHECK_NONFATAL(wtx.vOrderForm.empty());
    2370          17 :         wtx.mapValue = std::move(mapValue);
    2371          17 :         wtx.vOrderForm = std::move(orderForm);
    2372          17 :         wtx.fTimeReceivedIsTxTime = true;
    2373          17 :         wtx.fFromMe = true;
    2374          17 :         return true;
    2375             :     });
    2376             : 
    2377             :     // wtx can only be null if the db write failed.
    2378          17 :     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          17 :     std::set<uint256> updated_hahes;
    2384          46 :     for (const CTxIn& txin : tx->vin){
    2385             :         // notify only once
    2386          29 :         if(updated_hahes.find(txin.prevout.hash) != updated_hahes.end()) continue;
    2387             : 
    2388          23 :         CWalletTx &coin = mapWallet.at(txin.prevout.hash);
    2389          23 :         coin.MarkDirty();
    2390          23 :         NotifyTransactionChanged(txin.prevout.hash, CT_UPDATED);
    2391          23 :         updated_hahes.insert(txin.prevout.hash);
    2392             :     }
    2393             : 
    2394          17 :     if (!fBroadcastTransactions) {
    2395             :         // Don't submit tx to the mempool
    2396          17 :         return;
    2397             :     }
    2398             : 
    2399           0 :     bilingual_str err_string;
    2400           0 :     if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, true)) {
    2401           0 :         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           0 :     }
    2404          17 : }
    2405             : 
    2406          45 : DBErrors CWallet::LoadWallet()
    2407             : {
    2408          45 :     LOCK(cs_wallet);
    2409             : 
    2410          45 :     DBErrors nLoadWalletRet = WalletBatch(GetDatabase()).LoadWallet(this);
    2411          45 :     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          45 :     if (m_spk_managers.empty()) {
    2423          41 :         assert(m_external_spk_managers == nullptr);
    2424          41 :         assert(m_internal_spk_managers == nullptr);
    2425          41 :     }
    2426             : 
    2427          45 :     if (HaveChain()) {
    2428          38 :         const std::optional<int> tip_height = chain().getHeight();
    2429          38 :         if (tip_height) {
    2430          38 :             SetLastBlockProcessed(*tip_height, chain().getBlockHash(*tip_height));
    2431          40 :             for (auto& pair : mapWallet) {
    2432           4 :                 for(unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {
    2433           2 :                     COutPoint outpoint(pair.first, i);
    2434           2 :                     if (IsMine(pair.second.tx->vout[i]) && !IsSpent(outpoint)) {
    2435           2 :                         setWalletUTXO.insert(outpoint);
    2436           2 :                     }
    2437           2 :                 }
    2438             :             }
    2439          38 :         }
    2440          38 :     }
    2441             : 
    2442          45 :     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          45 :     if (GetCoinJoinSalt().IsNull() && !SetCoinJoinSalt(GetRandHash())) {
    2447           0 :         return DBErrors::LOAD_FAIL;
    2448             :     }
    2449             : 
    2450          45 :     return DBErrors::LOAD_OK;
    2451          45 : }
    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          11 : void CWallet::AutoLockMasternodeCollaterals()
    2456             : {
    2457          11 :     std::set<COutPoint> candidates;
    2458          11 :     LOCK(cs_wallet);
    2459          15 :     for (const auto& [txid, wtx] : mapWallet) {
    2460           4 :         auto tx_utxos{AddWalletUTXOs(wtx.tx, /*ret_dups=*/true)};
    2461           4 :         candidates.insert(tx_utxos.begin(), tx_utxos.end());
    2462           4 :     }
    2463          11 :     WalletBatch batch(GetDatabase());
    2464          11 :     LockProTxCoins(candidates, &batch);
    2465          11 : }
    2466             : 
    2467           1 : DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)
    2468             : {
    2469           1 :     AssertLockHeld(cs_wallet);
    2470             : 
    2471           1 :     WalletLogPrintf("ZapSelectTx started for %d transactions...\n", vHashIn.size());
    2472             : 
    2473           1 :     DBErrors nZapSelectTxRet = WalletBatch(GetDatabase()).ZapSelectTx(vHashIn, vHashOut);
    2474           2 :     for (const uint256& hash : vHashOut) {
    2475           1 :         const auto& it = mapWallet.find(hash);
    2476           1 :         wtxOrdered.erase(it->second.m_it_wtxOrdered);
    2477           2 :         for (const auto& txin : it->second.tx->vin)
    2478           1 :             mapTxSpends.erase(txin.prevout);
    2479           1 :         mapWallet.erase(it);
    2480           1 :         NotifyTransactionChanged(hash, CT_DELETED);
    2481             :     }
    2482             : 
    2483           1 :     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           1 :     if (nZapSelectTxRet != DBErrors::LOAD_OK)
    2494           0 :         return nZapSelectTxRet;
    2495             : 
    2496           1 :     MarkDirty();
    2497             : 
    2498           1 :     WalletLogPrintf("ZapSelectTx completed for %d transactions.\n", vHashOut.size());
    2499           1 :     return DBErrors::LOAD_OK;
    2500           1 : }
    2501             : 
    2502       11762 : bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
    2503             : {
    2504       11762 :     bool fUpdated = false;
    2505             :     bool is_mine;
    2506             :     {
    2507       11762 :         LOCK(cs_wallet);
    2508       11762 :         std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
    2509       11762 :         fUpdated = (mi != m_address_book.end() && !mi->second.IsChange());
    2510       11762 :         m_address_book[address].SetLabel(strName);
    2511       11762 :         if (!strPurpose.empty()) /* update purpose only if requested */
    2512       11762 :             m_address_book[address].purpose = strPurpose;
    2513       11762 :         is_mine = IsMine(address) != ISMINE_NO;
    2514       11762 :     }
    2515       23524 :     NotifyAddressBookChanged(address, strName, is_mine,
    2516       11762 :                              strPurpose, (fUpdated ? CT_UPDATED : CT_NEW));
    2517       11762 :     if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
    2518           0 :         return false;
    2519       11762 :     return batch.WriteName(EncodeDestination(address), strName);
    2520       11762 : }
    2521             : 
    2522       11762 : bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
    2523             : {
    2524       11762 :     WalletBatch batch(GetDatabase());
    2525       11762 :     return SetAddressBookWithDB(batch, address, strName, strPurpose);
    2526       11762 : }
    2527             : 
    2528           0 : bool CWallet::DelAddressBook(const CTxDestination& address)
    2529             : {
    2530             :     bool is_mine;
    2531           0 :     WalletBatch batch(GetDatabase());
    2532             :     {
    2533           0 :         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           0 :         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           0 :         batch.EraseAddressData(address);
    2543           0 :         m_address_book.erase(address);
    2544           0 :         is_mine = IsMine(address) != ISMINE_NO;
    2545           0 :     }
    2546             : 
    2547           0 :     NotifyAddressBookChanged(address, "", is_mine, "", CT_DELETED);
    2548             : 
    2549           0 :     batch.ErasePurpose(EncodeDestination(address));
    2550           0 :     return batch.EraseName(EncodeDestination(address));
    2551           0 : }
    2552             : 
    2553          53 : size_t CWallet::KeypoolCountExternalKeys() const
    2554             : {
    2555          53 :     AssertLockHeld(cs_wallet);
    2556             : 
    2557          53 :     auto legacy_spk_man = GetLegacyScriptPubKeyMan();
    2558          53 :     if (legacy_spk_man) {
    2559           2 :         return legacy_spk_man->KeypoolCountExternalKeys();
    2560             :     }
    2561             : 
    2562          51 :     unsigned int count = 0;
    2563          51 :     if (m_external_spk_managers) {
    2564          10 :         count += m_external_spk_managers->GetKeyPoolSize();
    2565          10 :     }
    2566             : 
    2567          51 :     return count;
    2568          53 : }
    2569             : 
    2570           5 : unsigned int CWallet::GetKeyPoolSize() const
    2571             : {
    2572           5 :     AssertLockHeld(cs_wallet);
    2573             : 
    2574           5 :     unsigned int count = 0;
    2575          15 :     for (auto spk_man : GetActiveScriptPubKeyMans()) {
    2576          10 :         count += spk_man->GetKeyPoolSize();
    2577             :     }
    2578           5 :     return count;
    2579           0 : }
    2580             : 
    2581           6 : bool CWallet::TopUpKeyPool(unsigned int kpSize)
    2582             : {
    2583           6 :     LOCK(cs_wallet);
    2584           6 :     bool res = true;
    2585          17 :     for (auto spk_man : GetActiveScriptPubKeyMans()) {
    2586          11 :         res &= spk_man->TopUp(kpSize);
    2587             :     }
    2588           6 :     return res;
    2589           6 : }
    2590             : 
    2591       11728 : util::Result<CTxDestination> CWallet::GetNewDestination(const std::string label)
    2592             : {
    2593       11728 :     LOCK(cs_wallet);
    2594       11728 :     auto spk_man = GetScriptPubKeyMan(false /* internal */);
    2595       11728 :     if (!spk_man) {
    2596           1 :         return util::Error{_("Error: No addresses available.")};
    2597             :     }
    2598             : 
    2599       11727 :     auto op_dest = spk_man->GetNewDestination();
    2600       11727 :     if (op_dest) {
    2601       11726 :         SetAddressBook(*op_dest, label, "receive");
    2602       11726 :     }
    2603             : 
    2604       11727 :     return op_dest;
    2605       23455 : }
    2606             : 
    2607           1 : util::Result<CTxDestination> CWallet::GetNewChangeDestination()
    2608             : {
    2609           1 :     LOCK(cs_wallet);
    2610             : 
    2611           1 :     ReserveDestination reservedest(this);
    2612           1 :     auto op_dest = reservedest.GetReservedDestination(true);
    2613           1 :     if (op_dest) reservedest.KeepDestination();
    2614             : 
    2615           1 :     return op_dest;
    2616           1 : }
    2617             : 
    2618           0 : std::optional<int64_t> CWallet::GetOldestKeyPoolTime() const
    2619             : {
    2620           0 :     LOCK(cs_wallet);
    2621           0 :     if (m_spk_managers.empty()) {
    2622           0 :         return std::nullopt;
    2623             :     }
    2624             : 
    2625           0 :     std::optional<int64_t> oldest_key{std::numeric_limits<int64_t>::max()};
    2626           0 :     for (const auto& spk_man_pair : m_spk_managers) {
    2627           0 :         oldest_key = std::min(oldest_key, spk_man_pair.second->GetOldestKeyPoolTime());
    2628             :     }
    2629           0 :     return oldest_key;
    2630           0 : }
    2631             : 
    2632           0 : void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations) {
    2633           0 :     for (auto& entry : mapWallet) {
    2634           0 :         CWalletTx& wtx = entry.second;
    2635           0 :         if (wtx.m_is_cache_empty) continue;
    2636           0 :         for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
    2637           0 :             CTxDestination dst;
    2638           0 :             if (ExtractDestination(wtx.tx->vout[i].scriptPubKey, dst) && destinations.count(dst)) {
    2639           0 :                 wtx.MarkDirty();
    2640           0 :                 break;
    2641             :             }
    2642           0 :         }
    2643             :     }
    2644           0 : }
    2645             : 
    2646           0 : void CWallet::ForEachAddrBookEntry(const ListAddrBookFunc& func) const
    2647             : {
    2648           0 :     for (const std::pair<const CTxDestination, CAddressBookData>& item : m_address_book) {
    2649           0 :         const auto& entry = item.second;
    2650           0 :         func(item.first, entry.GetLabel(), entry.purpose, entry.IsChange());
    2651             :     }
    2652           0 : }
    2653             : 
    2654           0 : std::vector<CTxDestination> CWallet::ListAddrBookAddresses(const std::optional<AddrBookFilter>& _filter) const
    2655             : {
    2656           0 :     AssertLockHeld(cs_wallet);
    2657           0 :     std::vector<CTxDestination> result;
    2658           0 :     AddrBookFilter filter = _filter ? *_filter : AddrBookFilter();
    2659           0 :     ForEachAddrBookEntry([&result, &filter](const CTxDestination& dest, const std::string& label, const std::string& purpose, bool is_change) {
    2660             :         // Filter by change
    2661           0 :         if (filter.ignore_change && is_change) return;
    2662             :         // Filter by label
    2663           0 :         if (filter.m_op_label && *filter.m_op_label != label) return;
    2664             :         // All good
    2665           0 :         result.emplace_back(dest);
    2666           0 :     });
    2667           0 :     return result;
    2668           0 : }
    2669             : 
    2670           0 : std::set<std::string> CWallet::ListAddrBookLabels(const std::string& purpose) const
    2671             : {
    2672           0 :     AssertLockHeld(cs_wallet);
    2673           0 :     std::set<std::string> label_set;
    2674           0 :     ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label,
    2675             :                              const std::string& _purpose, bool _is_change) {
    2676           0 :         if (_is_change) return;
    2677           0 :         if (purpose.empty() || _purpose == purpose) {
    2678           0 :             label_set.insert(_label);
    2679           0 :         }
    2680           0 :     });
    2681           0 :     return label_set;
    2682           0 : }
    2683             : 
    2684         284 : util::Result<CTxDestination> ReserveDestination::GetReservedDestination(bool fInternalIn)
    2685             : {
    2686         284 :     m_spk_man = pwallet->GetScriptPubKeyMan(fInternalIn);
    2687         284 :     if (!m_spk_man) {
    2688           0 :         return util::Error{_("Error: No addresses available.")};
    2689             :     }
    2690             : 
    2691         284 :     if (nIndex == -1) {
    2692         284 :         CKeyPool keypool;
    2693             :         int64_t index;
    2694         284 :         auto op_address = m_spk_man->GetReservedDestination(fInternalIn, index, keypool);
    2695         284 :         if (!op_address) return op_address;
    2696         284 :         address = *op_address;
    2697         284 :         nIndex = index;
    2698         284 :         fInternal = keypool.fInternal;
    2699         568 :     }
    2700         284 :     return address;
    2701         284 : }
    2702             : 
    2703         260 : void ReserveDestination::KeepDestination()
    2704             : {
    2705         260 :     if (nIndex != -1) {
    2706         215 :         m_spk_man->KeepDestination(nIndex);
    2707         215 :     }
    2708         260 :     nIndex = -1;
    2709         260 :     address = CNoDestination();
    2710         260 : }
    2711             : 
    2712         336 : void ReserveDestination::ReturnDestination()
    2713             : {
    2714         336 :     if (nIndex != -1) {
    2715          69 :         m_spk_man->ReturnDestination(nIndex, fInternal, address);
    2716          69 :     }
    2717         336 :     nIndex = -1;
    2718         336 :     address = CNoDestination();
    2719         336 : }
    2720             : 
    2721           0 : bool CWallet::DisplayAddress(const CTxDestination& dest)
    2722             : {
    2723           0 :     CScript scriptPubKey = GetScriptForDestination(dest);
    2724           0 :     for (const auto& spk_man : GetScriptPubKeyMans(scriptPubKey)) {
    2725           0 :         auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan *>(spk_man);
    2726           0 :         if (signer_spk_man == nullptr) {
    2727           0 :             continue;
    2728             :         }
    2729           0 :         ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
    2730           0 :         return signer_spk_man->DisplayAddress(scriptPubKey, signer);
    2731           0 :     }
    2732           0 :     return false;
    2733           0 : }
    2734             : 
    2735          43 : bool CWallet::LockCoin(const COutPoint& output, WalletBatch* batch)
    2736             : {
    2737          43 :     AssertLockHeld(cs_wallet);
    2738          43 :     setLockedCoins.insert(output);
    2739          43 :     RecalculateMixedCredit(output.hash);
    2740          43 :     if (batch) {
    2741           0 :         return batch->WriteLockedUTXO(output);
    2742             :     }
    2743          43 :     return true;
    2744          43 : }
    2745             : 
    2746          40 : bool CWallet::UnlockCoin(const COutPoint& output, WalletBatch* batch)
    2747             : {
    2748          40 :     AssertLockHeld(cs_wallet);
    2749          40 :     bool was_locked = setLockedCoins.erase(output);
    2750          40 :     RecalculateMixedCredit(output.hash);
    2751          40 :     if (batch && was_locked) {
    2752           0 :         return batch->EraseLockedUTXO(output);
    2753             :     }
    2754          40 :     return true;
    2755          40 : }
    2756             : 
    2757           2 : bool CWallet::UnlockAllCoins()
    2758             : {
    2759           2 :     AssertLockHeld(cs_wallet);
    2760           2 :     bool success = true;
    2761           2 :     WalletBatch batch(GetDatabase());
    2762          43 :     for (auto it = setLockedCoins.begin(); it != setLockedCoins.end(); ++it) {
    2763          41 :         success &= batch.EraseLockedUTXO(*it);
    2764          41 :     }
    2765           2 :     setLockedCoins.clear();
    2766           2 :     return success;
    2767           2 : }
    2768             : 
    2769        2722 : bool CWallet::IsLockedCoin(const COutPoint& output) const
    2770             : {
    2771        2722 :     AssertLockHeld(cs_wallet);
    2772        2722 :     return setLockedCoins.count(output) > 0;
    2773             : }
    2774             : 
    2775           0 : const std::set<COutPoint>& CWallet::ListLockedCoins() const
    2776             : {
    2777           0 :     AssertLockHeld(cs_wallet);
    2778           0 :     return setLockedCoins;
    2779             : }
    2780             : 
    2781           0 : std::vector<COutPoint> CWallet::ListProTxCoins() const { return ListProTxCoins(setWalletUTXO); }
    2782             : 
    2783         758 : std::vector<COutPoint> CWallet::ListProTxCoins(const std::set<COutPoint>& utxos) const
    2784             : {
    2785         758 :     AssertLockHeld(cs_wallet);
    2786             : 
    2787         758 :     if (!m_chain) return std::vector<COutPoint>();
    2788             : 
    2789         758 :     std::vector<std::pair<const CTransactionRef&, /*index=*/uint32_t>> candidates;
    2790        1640 :     for (const auto& output : utxos) {
    2791         882 :         if (auto it = mapWallet.find(output.hash); it != mapWallet.end()) {
    2792         882 :             const auto& [hash, wtx] = *it;
    2793         882 :             candidates.emplace_back(wtx.tx, output.n);
    2794         882 :         }
    2795             :     }
    2796         758 :     return m_chain->listMNCollaterials(candidates);
    2797         758 : }
    2798             : 
    2799         758 : void CWallet::LockProTxCoins(const std::set<COutPoint>& utxos, WalletBatch* batch)
    2800             : {
    2801         758 :     AssertLockHeld(cs_wallet);
    2802         758 :     for (const auto& utxo : ListProTxCoins(utxos)) {
    2803           0 :         LockCoin(utxo, batch);
    2804             :     }
    2805         758 : }
    2806             : 
    2807           0 : bool CWallet::IsDustProtectionTarget(const CWalletTx& wtx, unsigned int output_index) const
    2808             : {
    2809           0 :     AssertLockHeld(cs_wallet);
    2810             : 
    2811           0 :     if (m_dust_protection_threshold <= 0) return false;
    2812             : 
    2813           0 :     const CTransactionRef& tx = wtx.tx;
    2814           0 :     if (tx->IsCoinBase() || tx->nType != TRANSACTION_NORMAL) return false;
    2815             : 
    2816           0 :     if (output_index >= tx->vout.size()) return false;
    2817           0 :     const CTxOut& txout = tx->vout[output_index];
    2818             : 
    2819           0 :     if (txout.nValue <= 0 || txout.nValue > m_dust_protection_threshold) return false;
    2820           0 :     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           0 :     for (const auto& txin : tx->vin) {
    2824           0 :         if (InputIsMine(*this, txin) != ISMINE_NO) return false;
    2825             :     }
    2826             : 
    2827           0 :     return true;
    2828           0 : }
    2829             : 
    2830         744 : void CWallet::CheckAndLockDustOutputs(const uint256& txHash, WalletBatch& batch)
    2831             : {
    2832         744 :     AssertLockHeld(cs_wallet);
    2833             : 
    2834         744 :     if (m_dust_protection_threshold <= 0) return;
    2835             : 
    2836           0 :     auto it = mapWallet.find(txHash);
    2837           0 :     if (it == mapWallet.end()) return;
    2838             : 
    2839           0 :     const CWalletTx& wtx = it->second;
    2840           0 :     for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
    2841           0 :         if (IsDustProtectionTarget(wtx, i)) {
    2842           0 :             LockCoin(COutPoint(txHash, i), &batch);
    2843           0 :         }
    2844           0 :     }
    2845         744 : }
    2846             : 
    2847           5 : void CWallet::LockExistingDustOutputs()
    2848             : {
    2849           5 :     AssertLockHeld(cs_wallet);
    2850             : 
    2851           5 :     if (m_dust_protection_threshold <= 0) return;
    2852             : 
    2853           0 :     WalletBatch batch(GetDatabase());
    2854           0 :     for (const auto* pwtx : GetSpendableTXs()) {
    2855           0 :         const CWalletTx& wtx = *pwtx;
    2856             : 
    2857           0 :         if (IsTxImmatureCoinBase(wtx)) continue;
    2858             : 
    2859           0 :         const int depth = GetTxDepthInMainChain(wtx);
    2860           0 :         if (depth < 0) continue;
    2861           0 :         if (depth == 0 && !wtx.InMempool()) continue;
    2862             : 
    2863           0 :         for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
    2864           0 :             const COutPoint outpoint(wtx.GetHash(), i);
    2865           0 :             if (IsLockedCoin(outpoint) || IsSpent(outpoint)) continue;
    2866             : 
    2867           0 :             if (IsDustProtectionTarget(wtx, i)) {
    2868           0 :                 LockCoin(outpoint, &batch);
    2869           0 :             }
    2870           0 :         }
    2871             :     }
    2872           5 : }
    2873             : 
    2874             : /** @} */ // end of Actions
    2875             : 
    2876           1 : void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
    2877           1 :     AssertLockHeld(cs_wallet);
    2878           1 :     mapKeyBirth.clear();
    2879             : 
    2880             :     // map in which we'll infer heights of other keys
    2881           1 :     std::map<CKeyID, const TxStateConfirmed*> mapKeyFirstBlock;
    2882           1 :     TxStateConfirmed max_confirm{uint256{}, /*height=*/-1, /*index=*/-1};
    2883           1 :     max_confirm.confirmed_block_height = GetLastBlockHeight() > 144 ? GetLastBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin
    2884           1 :     CHECK_NONFATAL(chain().findAncestorByHeight(GetLastBlockHash(), max_confirm.confirmed_block_height, FoundBlock().hash(max_confirm.confirmed_block_hash)));
    2885             : 
    2886             :     {
    2887           1 :         LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
    2888           1 :         assert(spk_man != nullptr);
    2889           1 :         LOCK(spk_man->cs_KeyStore);
    2890             : 
    2891             :         // get birth times for keys with metadata
    2892           2 :         for (const auto& entry : spk_man->mapKeyMetadata) {
    2893           1 :             if (entry.second.nCreateTime) {
    2894           1 :                 mapKeyBirth[entry.first] = entry.second.nCreateTime;
    2895           1 :             }
    2896             :         }
    2897             : 
    2898             :         // Prepare to infer birth heights for keys without metadata
    2899           2 :         for (const CKeyID &keyid : spk_man->GetKeys()) {
    2900           1 :             if (mapKeyBirth.count(keyid) == 0)
    2901           0 :                 mapKeyFirstBlock[keyid] = &max_confirm;
    2902             :         }
    2903             : 
    2904             :         // if there are no such keys, we're done
    2905           1 :         if (mapKeyFirstBlock.empty())
    2906           1 :             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           1 :     }
    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           1 : }
    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         744 : unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const
    2960             : {
    2961         744 :     std::optional<uint256> block_hash;
    2962         744 :     if (auto* conf = wtx.state<TxStateConfirmed>()) {
    2963         721 :         block_hash = conf->confirmed_block_hash;
    2964         744 :     } else if (auto* conf = wtx.state<TxStateConflicted>()) {
    2965           0 :         block_hash = conf->conflicting_block_hash;
    2966           0 :     }
    2967             : 
    2968         744 :     unsigned int nTimeSmart = wtx.nTimeReceived;
    2969         744 :     if (block_hash) {
    2970             :         int64_t blocktime;
    2971             :         int64_t block_max_time;
    2972         721 :         if (chain().findBlock(*block_hash, FoundBlock().time(blocktime).maxTime(block_max_time))) {
    2973         721 :             if (rescanning_old_block) {
    2974         715 :                 nTimeSmart = block_max_time;
    2975         715 :             } else {
    2976           6 :                 int64_t latestNow = wtx.nTimeReceived;
    2977           6 :                 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           6 :                 int64_t latestTolerated = latestNow + 300;
    2981           6 :                 const TxItems& txOrdered = wtxOrdered;
    2982          14 :                 for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
    2983          12 :                     CWalletTx* const pwtx = it->second;
    2984          12 :                     if (pwtx == &wtx) {
    2985           6 :                         continue;
    2986             :                     }
    2987             :                     int64_t nSmartTime;
    2988           6 :                     nSmartTime = pwtx->nTimeSmart;
    2989           6 :                     if (!nSmartTime) {
    2990           0 :                         nSmartTime = pwtx->nTimeReceived;
    2991           0 :                     }
    2992           6 :                     if (nSmartTime <= latestTolerated) {
    2993           4 :                         latestEntry = nSmartTime;
    2994           4 :                         if (nSmartTime > latestNow) {
    2995           1 :                             latestNow = nSmartTime;
    2996           1 :                         }
    2997           4 :                         break;
    2998             :                     }
    2999           2 :                 }
    3000             : 
    3001           6 :                 nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
    3002             :             }
    3003         721 :         } else {
    3004           0 :             WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), block_hash->ToString());
    3005             :         }
    3006         721 :     }
    3007         744 :     return nTimeSmart;
    3008           0 : }
    3009             : 
    3010           0 : bool CWallet::SetAddressPreviouslySpent(WalletBatch& batch, const CTxDestination& dest, bool used)
    3011             : {
    3012           0 :     if (std::get_if<CNoDestination>(&dest))
    3013           0 :         return false;
    3014             : 
    3015           0 :     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           0 :     LoadAddressPreviouslySpent(dest);
    3021           0 :     return batch.WriteAddressPreviouslySpent(dest, true);
    3022           0 : }
    3023             : 
    3024           4 : void CWallet::LoadAddressPreviouslySpent(const CTxDestination& dest)
    3025             : {
    3026           4 :     m_address_book[dest].previously_spent = true;
    3027           4 : }
    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          10 : bool CWallet::IsAddressPreviouslySpent(const CTxDestination& dest) const
    3035             : {
    3036          10 :     if (auto* data{util::FindKey(m_address_book, dest)}) return data->previously_spent;
    3037           4 :     return false;
    3038          10 : }
    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          15 : 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          15 :     const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
    3074          15 :     fs::file_type path_type = fs::symlink_status(wallet_path).type();
    3075          15 :     if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory ||
    3076           0 :           (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) ||
    3077           0 :           (path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) {
    3078           0 :         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           0 :               name, fs::quoted(fs::PathToString(GetWalletDir()))));
    3083           0 :         status = DatabaseStatus::FAILED_BAD_PATH;
    3084           0 :         return nullptr;
    3085             :     }
    3086             : 
    3087          15 :     return MakeDatabase(wallet_path, options, status, error_string);
    3088          15 : }
    3089             : 
    3090           5 : 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           5 :     interfaces::Chain* chain = context.chain;
    3093           5 :     interfaces::CoinJoin::Loader* coinjoin_loader = context.coinjoin_loader;
    3094           5 :     ArgsManager& args = *Assert(context.args);
    3095           5 :     const std::string& walletFile = database->Filename();
    3096             : 
    3097           5 :     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           5 :     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           5 :     if (!walletInstance->AutoBackupWallet(fs::PathFromString(walletFile), error, warnings) && !error.original.empty()) {
    3103           0 :         return nullptr;
    3104             :     }
    3105           5 :     DBErrors nLoadWalletRet = walletInstance->LoadWallet();
    3106           5 :     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           8 :     const bool fFirstRun = walletInstance->m_spk_managers.empty() &&
    3139           3 :                      !walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) &&
    3140           3 :                      !walletInstance->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET);
    3141           5 :     if (fFirstRun)
    3142             :     {
    3143           3 :         walletInstance->SetMinVersion(FEATURE_LATEST);
    3144             : 
    3145           3 :         walletInstance->InitWalletFlags(wallet_creation_flags);
    3146             : 
    3147             :         // Only create LegacyScriptPubKeyMan when not descriptor wallet
    3148           3 :         if (!walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    3149           0 :             walletInstance->SetupLegacyScriptPubKeyMan();
    3150           0 :         }
    3151             : 
    3152           3 :         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           3 :             if (args.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {
    3155           3 :                 std::string strSeed = args.GetArg("-hdseed", "not hex");
    3156             : 
    3157             :                 // ensure this wallet.dat can only be opened by clients supporting HD
    3158           3 :                 walletInstance->WalletLogPrintf("Upgrading wallet to HD\n");
    3159           3 :                 walletInstance->SetMinVersion(FEATURE_HD);
    3160             : 
    3161           3 :                 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           3 :                     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           3 :                     SecureString mnemonic, mnemonic_passphrase;
    3184           3 :                     mnemonic.reserve(256);
    3185           3 :                     mnemonic_passphrase.reserve(256);
    3186             : 
    3187           3 :                     mnemonic = args.GetArg("-mnemonic", "");
    3188           3 :                     mnemonic_passphrase = args.GetArg("-mnemonicpassphrase", "");
    3189           3 :                     LOCK(walletInstance->cs_wallet);
    3190           3 :                     if (auto spk_man = walletInstance->GetLegacyScriptPubKeyMan()) {
    3191           0 :                         spk_man->GenerateNewHDChain(mnemonic, mnemonic_passphrase);
    3192           0 :                     }
    3193           3 :                 }
    3194             : 
    3195             :                 // clean up
    3196           3 :                 args.ForceRemoveArg("hdseed");
    3197           3 :                 args.ForceRemoveArg("mnemonic");
    3198           3 :                 args.ForceRemoveArg("mnemonicpassphrase");
    3199           3 :             } // Otherwise, do not create a new HD chain
    3200             : 
    3201           3 :             LOCK(walletInstance->cs_wallet);
    3202           3 :             if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    3203           3 :                 SecureString mnemonic, mnemonic_passphrase;
    3204           3 :                 mnemonic.reserve(256);
    3205           3 :                 mnemonic_passphrase.reserve(256);
    3206           3 :                 mnemonic = args.GetArg("-mnemonic", "");
    3207           3 :                 mnemonic_passphrase = args.GetArg("-mnemonicpassphrase", "");
    3208           3 :                 args.ForceRemoveArg("mnemonic");
    3209           3 :                 args.ForceRemoveArg("mnemonicpassphrase");
    3210           3 :                 walletInstance->SetupDescriptorScriptPubKeyMans(mnemonic, mnemonic_passphrase);
    3211             :                 // SetupDescriptorScriptPubKeyMans already calls SetupGeneration for us so we don't need to call SetupGeneration separately
    3212           3 :             } else { // Top up the keypool
    3213             :                 // Legacy wallets need SetupGeneration here.
    3214           0 :                 if (auto spk_man = walletInstance->GetLegacyScriptPubKeyMan()) {
    3215           0 :                     if (spk_man->CanGenerateKeys() && !spk_man->TopUp()) {
    3216           0 :                         error = _("Unable to generate initial keys");
    3217           0 :                         return nullptr;
    3218             :                     }
    3219           0 :                 }
    3220             :             }
    3221           3 :         }
    3222             : 
    3223           3 :         if (chain) {
    3224           2 :             walletInstance->chainStateFlushed(chain->getTipLocator());
    3225           2 :         }
    3226             : 
    3227             :         // Try to create wallet backup right after new wallet was created
    3228           3 :         bilingual_str strBackupError;
    3229           3 :         if(!walletInstance->AutoBackupWallet("", strBackupError, warnings)) {
    3230           0 :             if (!strBackupError.original.empty()) {
    3231           0 :                 error = strBackupError;
    3232           0 :                 return nullptr;
    3233             :             }
    3234           0 :         }
    3235           5 :     } 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           2 :     } else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
    3240           0 :         for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) {
    3241           0 :             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           0 :     }
    3246           2 :     else if (args.IsArgSet("-usehd")) {
    3247           0 :         bool useHD = args.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
    3248           0 :         if (walletInstance->IsHDEnabled() && !useHD) {
    3249           0 :             error = strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletInstance->GetName());
    3250           0 :             return nullptr;
    3251             :         }
    3252           0 :         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           0 :     }
    3257             : 
    3258             :     // Warn user every time a non-encrypted HD wallet is started
    3259           5 :     if (walletInstance->IsHDEnabled() && !walletInstance->IsLocked()) {
    3260           5 :         SetMiscWarning(_("Make sure to encrypt your wallet and delete all non-encrypted backups after you have verified that the wallet works!"));
    3261           5 :     }
    3262             : 
    3263           5 :     if (args.IsArgSet("-mintxfee")) {
    3264           0 :         std::optional<CAmount> min_tx_fee = ParseMoney(args.GetArg("-mintxfee", ""));
    3265           0 :         if (!min_tx_fee) {
    3266           0 :             error = AmountErrMsg("mintxfee", args.GetArg("-mintxfee", ""));
    3267           0 :             return nullptr;
    3268           0 :         } 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           0 :         walletInstance->m_min_fee = CFeeRate{min_tx_fee.value()};
    3274           0 :     }
    3275             : 
    3276           5 :     if (args.IsArgSet("-maxapsfee")) {
    3277           0 :         const std::string max_aps_fee{args.GetArg("-maxapsfee", "")};
    3278           0 :         if (max_aps_fee == "-1") {
    3279           0 :             walletInstance->m_max_aps_fee = -1;
    3280           0 :         } else if (std::optional<CAmount> max_fee = ParseMoney(max_aps_fee)) {
    3281           0 :             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           0 :             walletInstance->m_max_aps_fee = max_fee.value();
    3286           0 :         } else {
    3287           0 :             error = AmountErrMsg("maxapsfee", max_aps_fee);
    3288           0 :             return nullptr;
    3289             :         }
    3290           0 :     }
    3291             : 
    3292           5 :     if (args.IsArgSet("-fallbackfee")) {
    3293           0 :         std::optional<CAmount> fallback_fee = ParseMoney(args.GetArg("-fallbackfee", ""));
    3294           0 :         if (!fallback_fee) {
    3295           0 :             error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-fallbackfee", args.GetArg("-fallbackfee", ""));
    3296           0 :             return nullptr;
    3297           0 :         } 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           0 :         walletInstance->m_fallback_fee = CFeeRate{fallback_fee.value()};
    3302           0 :     }
    3303             :     // Disable fallback fee in case value was set to 0, enable if non-null value
    3304           5 :     walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0;
    3305             : 
    3306           5 :     if (args.IsArgSet("-discardfee")) {
    3307           0 :         std::optional<CAmount> discard_fee = ParseMoney(args.GetArg("-discardfee", ""));
    3308           0 :         if (!discard_fee) {
    3309           0 :             error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-discardfee", args.GetArg("-discardfee", ""));
    3310           0 :             return nullptr;
    3311           0 :         } else if (discard_fee.value() > HIGH_TX_FEE_PER_KB) {
    3312           0 :             warnings.push_back(AmountHighWarn("-discardfee") + Untranslated(" ") +
    3313           0 :                               _("This is the transaction fee you may discard if change is smaller than dust at this level"));
    3314           0 :         }
    3315           0 :         walletInstance->m_discard_rate = CFeeRate{discard_fee.value()};
    3316           0 :     }
    3317             : 
    3318           5 :     if (args.IsArgSet("-paytxfee")) {
    3319           0 :         std::optional<CAmount> pay_tx_fee = ParseMoney(args.GetArg("-paytxfee", ""));
    3320           0 :         if (!pay_tx_fee) {
    3321           0 :             error = AmountErrMsg("paytxfee", args.GetArg("-paytxfee", ""));
    3322           0 :             return nullptr;
    3323           0 :         } 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           0 :         walletInstance->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000};
    3328           0 :         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           0 :     }
    3334             : 
    3335           5 :     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           5 :     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           5 :     if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB)
    3362           0 :         warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") +
    3363           0 :                     _("The wallet will avoid paying less than the minimum relay fee."));
    3364             : 
    3365           5 :     walletInstance->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
    3366           5 :     walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
    3367             : 
    3368           5 :     walletInstance->m_dust_protection_threshold = args.GetIntArg("-dustprotectionthreshold", DEFAULT_DUST_PROTECTION_THRESHOLD);
    3369           5 :     if (walletInstance->m_dust_protection_threshold < 0) {
    3370           0 :         error = strprintf(_("Invalid value for %s: must be >= 0"), "-dustprotectionthreshold");
    3371           0 :         return nullptr;
    3372             :     }
    3373           5 :     if (walletInstance->m_dust_protection_threshold > MAX_DUST_PROTECTION_THRESHOLD) {
    3374           0 :         error = strprintf(_("Invalid value for %s: exceeds maximum (%d)"), "-dustprotectionthreshold", MAX_DUST_PROTECTION_THRESHOLD);
    3375           0 :         return nullptr;
    3376             :     }
    3377             : 
    3378           5 :     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           5 :     walletInstance->TopUpKeyPool();
    3382             : 
    3383           5 :     NotifyWalletLoading(context, walletInstance);
    3384             : 
    3385           5 :     if (chain && !AttachChain(walletInstance, *chain, error, warnings)) {
    3386           0 :         walletInstance->m_chain_notifications_handler.reset(); // Reset this pointer so that the wallet will actually be unloaded
    3387           0 :         return nullptr;
    3388             :     }
    3389             : 
    3390           5 :     if (coinjoin_loader) {
    3391           4 :         coinjoin_loader->AddWallet(walletInstance);
    3392           4 :     }
    3393             : 
    3394             :     {
    3395           5 :         LOCK(walletInstance->cs_wallet);
    3396           5 :         walletInstance->SetBroadcastTransactions(args.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
    3397           5 :         walletInstance->LockExistingDustOutputs();
    3398           5 :         walletInstance->WalletLogPrintf("setExternalKeyPool.size() = %u\n",   walletInstance->KeypoolCountExternalKeys());
    3399           5 :         walletInstance->WalletLogPrintf("GetKeyPoolSize() = %u\n",   walletInstance->GetKeyPoolSize());
    3400           5 :         walletInstance->WalletLogPrintf("mapWallet.size() = %u\n",            walletInstance->mapWallet.size());
    3401           5 :         walletInstance->WalletLogPrintf("m_address_book.size() = %u\n",  walletInstance->m_address_book.size());
    3402          22 :         for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) {
    3403          17 :             walletInstance->WalletLogPrintf("nTimeFirstKey = %u\n", spk_man->GetTimeFirstKey());
    3404             :         }
    3405           5 :     }
    3406             : 
    3407           5 :     return walletInstance;
    3408           5 : }
    3409             : 
    3410           4 : bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings)
    3411             : {
    3412           4 :     LOCK(walletInstance->cs_wallet);
    3413             :     // allow setting the chain if it hasn't been set already but prevent changing it
    3414           4 :     assert(!walletInstance->m_chain || walletInstance->m_chain == &chain);
    3415           4 :     walletInstance->m_chain = &chain;
    3416             : 
    3417             :     // Unless allowed, ensure wallet files are not reused across chains:
    3418           4 :     if (!gArgs.GetBoolArg("-walletcrosschain", DEFAULT_WALLETCROSSCHAIN)) {
    3419           4 :         WalletBatch batch(walletInstance->GetDatabase());
    3420           4 :         CBlockLocator locator;
    3421           4 :         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           4 :             if (chain.getBlockHash(0) != locator.vHave.back()) {
    3425           0 :                 error = Untranslated("Wallet files should not be reused across chains. Restart dashd with -walletcrosschain to override.");
    3426           0 :                 return false;
    3427             :             }
    3428           4 :         }
    3429           4 :     }
    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           4 :     walletInstance->m_attaching_chain = true; //ignores chainStateFlushed notifications
    3442           4 :     walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);
    3443             : 
    3444           4 :     int rescan_height = 0;
    3445           4 :     if (!gArgs.GetBoolArg("-rescan", false))
    3446             :     {
    3447           4 :         WalletBatch batch(walletInstance->GetDatabase());
    3448           4 :         CBlockLocator locator;
    3449           4 :         if (batch.ReadBestBlock(locator)) {
    3450           4 :             if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) {
    3451           4 :                 rescan_height = *fork_height;
    3452           4 :             }
    3453           4 :         }
    3454           4 :     }
    3455             : 
    3456           4 :     const std::optional<int> tip_height = chain.getHeight();
    3457           4 :     if (tip_height) {
    3458           4 :         walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height);
    3459           4 :         walletInstance->m_last_block_processed_height = *tip_height;
    3460           4 :     } else {
    3461           0 :         walletInstance->m_last_block_processed.SetNull();
    3462           0 :         walletInstance->m_last_block_processed_height = -1;
    3463             :     }
    3464             : 
    3465           4 :     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           1 :         if (gArgs.GetIntArg("-rescan", 0) != 2) {
    3471           1 :             std::optional<int64_t> time_first_key;
    3472           5 :             for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) {
    3473           4 :                 int64_t time = spk_man->GetTimeFirstKey();
    3474           4 :                 if (!time_first_key || time < *time_first_key) time_first_key = time;
    3475             :             }
    3476           1 :             if (time_first_key) {
    3477           1 :                 FoundBlock found = FoundBlock().height(rescan_height);
    3478           1 :                 chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, found);
    3479           1 :                 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           0 :                     rescan_height = *tip_height;
    3484           0 :                 }
    3485           1 :             }
    3486           1 :         }
    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           1 :         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           1 :         chain.initMessage(_("Rescanning…").translated);
    3520           1 :         walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
    3521             :         {
    3522           1 :             WalletRescanReserver reserver(*walletInstance);
    3523           1 :             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           1 :         }
    3528           1 :         walletInstance->m_attaching_chain = false;
    3529           1 :         walletInstance->chainStateFlushed(chain.getTipLocator());
    3530           1 :         walletInstance->GetDatabase().IncrementUpdateCounter();
    3531           1 :     }
    3532           4 :     walletInstance->m_attaching_chain = false;
    3533             : 
    3534           4 :     return true;
    3535           4 : }
    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          12 : const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest, bool allow_change) const
    3561             : {
    3562          12 :     const auto& address_book_it = m_address_book.find(dest);
    3563          12 :     if (address_book_it == m_address_book.end()) return nullptr;
    3564           7 :     if ((!allow_change) && address_book_it->second.IsChange()) {
    3565           0 :         return nullptr;
    3566             :     }
    3567           7 :     return &address_book_it->second;
    3568          12 : }
    3569             : 
    3570           0 : void CWallet::postInitProcess()
    3571             : {
    3572           0 :     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           0 :     ReacceptWalletTransactions();
    3577             : 
    3578             :     // Update wallet transactions with current mempool transactions.
    3579           0 :     chain().requestMempoolTransactions(*this);
    3580           0 : }
    3581             : 
    3582           0 : void CWallet::InitAutoBackup()
    3583             : {
    3584           0 :     if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))
    3585           0 :         return;
    3586             : 
    3587           0 :     nWalletBackups = gArgs.GetIntArg("-createwalletbackups", 10);
    3588           0 :     nWalletBackups = std::max(0, std::min(10, nWalletBackups));
    3589           0 : }
    3590             : 
    3591           3 : bool CWallet::BackupWallet(const std::string& strDest) const
    3592             : {
    3593           3 :     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           8 : bool CWallet::AutoBackupWallet(const fs::path& wallet_path, bilingual_str& error_string, std::vector<bilingual_str>& warnings)
    3600             : {
    3601           8 :     std::string strWalletName = GetName();
    3602           8 :     if (strWalletName.empty()) {
    3603           8 :         strWalletName = "wallet.dat";
    3604           8 :     }
    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           8 :     if (m_database && !m_database->SupportsAutoBackup()) {
    3608           0 :         WalletLogPrintf("Automatic wallet backups are not supported!\n");
    3609           0 :         return false;
    3610             :     }
    3611           8 :     if (!wallet_path.empty() && !IsBDBFile(BDBDataFile(wallet_path))) {
    3612           3 :         WalletLogPrintf("Automatic wallet backups are currently only supported with Berkeley DB!\n");
    3613           3 :         return false;
    3614             :     }
    3615             : 
    3616           5 :     if (IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
    3617           0 :         WalletLogPrintf("Wallet is blank, won't create new backup for it!\n");
    3618           0 :         return false;
    3619             :     }
    3620             : 
    3621           5 :     if (nWalletBackups <= 0) {
    3622           0 :         WalletLogPrintf("Automatic wallet backups are disabled!\n");
    3623           0 :         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           8 : }
    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           0 : void CWallet::notifyTransactionLock(const CTransactionRef &tx, const std::shared_ptr<const instantsend::InstantSendLock>& islock)
    3748             : {
    3749           0 :     LOCK(cs_wallet);
    3750             :     // Only notify UI if this transaction is in this wallet
    3751           0 :     uint256 txHash = tx->GetHash();
    3752           0 :     const auto mi = mapWallet.find(txHash);
    3753           0 :     if (mi != mapWallet.end()){
    3754           0 :         NotifyTransactionChanged(txHash, CT_UPDATED);
    3755           0 :         NotifyISLockReceived();
    3756             : #if HAVE_SYSTEM
    3757             :         // notify an external script
    3758           0 :         std::string strCmd = m_args.GetArg("-instantsendnotify", "");
    3759           0 :         if (!strCmd.empty()) {
    3760           0 :             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           0 :             ReplaceAll(strCmd, "%w", ShellEscape(GetName()));
    3768             : #endif
    3769           0 :             std::thread t(runCommand, strCmd);
    3770           0 :             t.detach(); // thread runs free
    3771           0 :         }
    3772             : #endif
    3773           0 :     }
    3774           0 : }
    3775             : 
    3776           0 : void CWallet::notifyChainLock(const CBlockIndex* pindexChainLock, const std::shared_ptr<const chainlock::ChainLockSig>& clsig)
    3777             : {
    3778           0 :     NotifyChainLockReceived(pindexChainLock->nHeight);
    3779           0 : }
    3780             : 
    3781           0 : bool CWallet::LoadGovernanceObject(const Governance::Object& obj)
    3782             : {
    3783           0 :     AssertLockHeld(cs_wallet);
    3784           0 :     return m_gobjects.emplace(obj.GetHash(), obj).second;
    3785             : }
    3786             : 
    3787           0 : bool CWallet::WriteGovernanceObject(const Governance::Object& obj)
    3788             : {
    3789           0 :     AssertLockHeld(cs_wallet);
    3790           0 :     WalletBatch batch(GetDatabase());
    3791           0 :     return batch.WriteGovernanceObject(obj) && LoadGovernanceObject(obj);
    3792           0 : }
    3793             : 
    3794           0 : std::vector<const Governance::Object*> CWallet::GetGovernanceObjects()
    3795             : {
    3796           0 :     AssertLockHeld(cs_wallet);
    3797           0 :     std::vector<const Governance::Object*> vecObjects;
    3798           0 :     vecObjects.reserve(m_gobjects.size());
    3799           0 :     for (auto& obj : m_gobjects) {
    3800           0 :         vecObjects.push_back(&obj.second);
    3801             :     }
    3802           0 :     return vecObjects;
    3803           0 : }
    3804             : 
    3805       12944 : CKeyPool::CKeyPool()
    3806        6472 : {
    3807        6472 :     nTime = GetTime();
    3808        6472 :     fInternal = false;
    3809       12944 : }
    3810             : 
    3811       18778 : CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool fInternalIn)
    3812        9389 : {
    3813        9389 :     nTime = GetTime();
    3814        9389 :     vchPubKey = vchPubKeyIn;
    3815        9389 :     fInternal = fInternalIn;
    3816       18778 : }
    3817             : 
    3818       26498 : int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
    3819             : {
    3820       26498 :     AssertLockHeld(cs_wallet);
    3821       26498 :     if (auto* conf = wtx.state<TxStateConfirmed>()) {
    3822       26492 :         return GetLastBlockHeight() - conf->confirmed_block_height + 1;
    3823           6 :     } else if (auto* conf = wtx.state<TxStateConflicted>()) {
    3824           3 :         return -1 * (GetLastBlockHeight() - conf->conflicting_block_height + 1);
    3825             :     } else {
    3826           3 :         return 0;
    3827             :     }
    3828       26498 : }
    3829             : 
    3830           0 : bool CWallet::IsTxLockedByInstantSend(const CWalletTx& wtx) const
    3831             : {
    3832           0 :     AssertLockHeld(cs_wallet);
    3833           0 :     if (wtx.fIsChainlocked) {
    3834           0 :         wtx.fIsInstantSendLocked = false;
    3835           0 :     } else if (!wtx.fIsInstantSendLocked) {
    3836           0 :         wtx.fIsInstantSendLocked = chain().isInstantSendLockedTx(wtx.GetHash());
    3837           0 :     }
    3838           0 :     return wtx.fIsInstantSendLocked;
    3839             : }
    3840             : 
    3841           0 : bool CWallet::IsTxChainLocked(const CWalletTx& wtx) const
    3842             : {
    3843           0 :     AssertLockHeld(cs_wallet);
    3844           0 :     if (!wtx.fIsChainlocked) {
    3845             :         bool active; int height;
    3846           0 :         if (auto* conf = wtx.state<TxStateConfirmed>()) {
    3847           0 :             if (chain().findBlock(conf->confirmed_block_hash, FoundBlock().inActiveChain(active).height(height)) && active) {
    3848           0 :                 wtx.fIsChainlocked = chain().hasChainLock(height, conf->confirmed_block_hash);
    3849           0 :             }
    3850           0 :         }
    3851           0 :     }
    3852           0 :     return wtx.fIsChainlocked;
    3853             : }
    3854             : 
    3855       24228 : int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const
    3856             : {
    3857       24228 :     AssertLockHeld(cs_wallet);
    3858             : 
    3859       24228 :     if (!wtx.IsCoinBase()) {
    3860         493 :         return 0;
    3861             :     }
    3862       23735 :     int chain_depth = GetTxDepthInMainChain(wtx);
    3863       23735 :     assert(chain_depth >= 0); // coinbase tx should not be conflicted
    3864       23735 :     return std::max(0, (COINBASE_MATURITY+1) - chain_depth);
    3865       24228 : }
    3866             : 
    3867       24027 : bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const
    3868             : {
    3869       24027 :     AssertLockHeld(cs_wallet);
    3870             : 
    3871             :     // note GetBlocksToMaturity is 0 for non-coinbase tx
    3872       24027 :     return GetTxBlocksToMaturity(wtx) > 0;
    3873             : }
    3874             : 
    3875        6590 : bool CWallet::IsCrypted() const
    3876             : {
    3877        6590 :     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        6524 : bool CWallet::IsLocked(bool fForMixing) const
    3889             : {
    3890        6524 :     if (!IsCrypted())
    3891        6524 :         return false;
    3892             : 
    3893           0 :     if(!fForMixing && fOnlyMixingAllowed) return true;
    3894             : 
    3895           0 :     LOCK(cs_wallet);
    3896           0 :     return vMasterKey.empty();
    3897        6524 : }
    3898             : 
    3899           0 : bool CWallet::Lock(bool fAllowMixing)
    3900             : {
    3901           0 :     if (!IsCrypted())
    3902           0 :         return false;
    3903             : 
    3904           0 :     if(!fAllowMixing) {
    3905           0 :         LOCK(cs_wallet);
    3906           0 :         if (!vMasterKey.empty()) {
    3907           0 :             memory_cleanse(vMasterKey.data(), vMasterKey.size() * sizeof(decltype(vMasterKey)::value_type));
    3908           0 :             vMasterKey.clear();
    3909           0 :         }
    3910           0 :     }
    3911             : 
    3912           0 :     fOnlyMixingAllowed = fAllowMixing;
    3913           0 :     NotifyStatusChanged(this);
    3914           0 :     return true;
    3915           0 : }
    3916             : 
    3917           0 : bool CWallet::Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly)
    3918             : {
    3919           0 :     if (!IsLocked()) // was already fully unlocked, not only for mixing
    3920           0 :         return true;
    3921             : 
    3922           0 :     CCrypter crypter;
    3923           0 :     CKeyingMaterial _vMasterKey;
    3924             : 
    3925             :     {
    3926           0 :         LOCK(cs_wallet);
    3927           0 :         for (const MasterKeyMap::value_type& pMasterKey : mapMasterKeys)
    3928             :         {
    3929           0 :             if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
    3930           0 :                 return false;
    3931           0 :             if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey))
    3932           0 :                 continue; // try another master key
    3933           0 :             if (Unlock(_vMasterKey, fForMixingOnly)) {
    3934             :                 // Now that we've unlocked, upgrade the key metadata
    3935           0 :                 UpgradeKeyMetadata();
    3936             :                 // Now that we've unlocked, upgrade the descriptor cache
    3937           0 :                 UpgradeDescriptorCache();
    3938           0 :                 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           0 :                 return true;
    3944             :             }
    3945             :         }
    3946           0 :     }
    3947           0 :     return false;
    3948           0 : }
    3949             : 
    3950           0 : bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool fForMixingOnly)
    3951             : {
    3952             :     {
    3953           0 :         LOCK(cs_wallet);
    3954           0 :         for (const auto& spk_man_pair : m_spk_managers) {
    3955           0 :             if (!spk_man_pair.second->CheckDecryptionKey(vMasterKeyIn)) {
    3956           0 :                 return false;
    3957             :             }
    3958             :         }
    3959           0 :         vMasterKey = vMasterKeyIn;
    3960           0 :         fOnlyMixingAllowed = fForMixingOnly;
    3961           0 :     }
    3962           0 :     NotifyStatusChanged(this);
    3963           0 :     return true;
    3964           0 : }
    3965             : 
    3966          30 : std::set<ScriptPubKeyMan*> CWallet::GetActiveScriptPubKeyMans() const
    3967             : {
    3968          30 :     std::set<ScriptPubKeyMan*> spk_mans;
    3969          90 :     for (bool internal : {false, true}) {
    3970          60 :         auto spk_man = GetScriptPubKeyMan(internal);
    3971          60 :         if (spk_man) {
    3972          52 :             spk_mans.insert(spk_man);
    3973          52 :         }
    3974             :     }
    3975          30 :     return spk_mans;
    3976          30 : }
    3977             : 
    3978         167 : std::set<ScriptPubKeyMan*> CWallet::GetAllScriptPubKeyMans() const
    3979             : {
    3980         167 :     std::set<ScriptPubKeyMan*> spk_mans;
    3981         404 :     for (const auto& spk_man_pair : m_spk_managers) {
    3982         237 :         spk_mans.insert(spk_man_pair.second.get());
    3983             :     }
    3984         167 :     return spk_mans;
    3985         167 : }
    3986             : 
    3987       18261 : ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(bool internal) const
    3988             : {
    3989       18261 :     const auto spk_manager = internal ? m_internal_spk_managers : m_external_spk_managers;
    3990       18261 :     if (spk_manager == nullptr) {
    3991           9 :         return nullptr;
    3992             :     }
    3993       18252 :     return spk_manager;
    3994       18261 : }
    3995             : 
    3996         726 : std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const
    3997             : {
    3998         726 :     std::set<ScriptPubKeyMan*> spk_mans;
    3999         726 :     SignatureData sigdata;
    4000        2692 :     for (const auto& spk_man_pair : m_spk_managers) {
    4001        1966 :         if (spk_man_pair.second->CanProvide(script, sigdata)) {
    4002         725 :             spk_mans.insert(spk_man_pair.second.get());
    4003         725 :         }
    4004             :     }
    4005         726 :     return spk_mans;
    4006         726 : }
    4007             : 
    4008          16 : ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const
    4009             : {
    4010          16 :     if (m_spk_managers.count(id) > 0) {
    4011          16 :         return m_spk_managers.at(id).get();
    4012             :     }
    4013           0 :     return nullptr;
    4014          16 : }
    4015             : 
    4016      113532 : std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script) const
    4017             : {
    4018      113532 :     SignatureData sigdata;
    4019      113532 :     return GetSolvingProvider(script, sigdata);
    4020      113532 : }
    4021             : 
    4022      113532 : std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script, SignatureData& sigdata) const
    4023             : {
    4024      443626 :     for (const auto& spk_man_pair : m_spk_managers) {
    4025      333618 :         if (spk_man_pair.second->CanProvide(script, sigdata)) {
    4026        3524 :             return spk_man_pair.second->GetSolvingProvider(script);
    4027             :         }
    4028             :     }
    4029      110008 :     return nullptr;
    4030      113532 : }
    4031             : 
    4032         239 : LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const
    4033             : {
    4034         239 :     if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    4035          20 :         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         219 :     if (m_internal_spk_managers == nullptr) return nullptr;
    4040          96 :     return dynamic_cast<LegacyScriptPubKeyMan*>(m_internal_spk_managers);
    4041         239 : }
    4042             : 
    4043           4 : LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan()
    4044             : {
    4045           4 :     SetupLegacyScriptPubKeyMan();
    4046           4 :     return GetLegacyScriptPubKeyMan();
    4047             : }
    4048             : 
    4049          21 : void CWallet::SetupLegacyScriptPubKeyMan()
    4050             : {
    4051          21 :     if (m_internal_spk_managers || m_external_spk_managers || !m_spk_managers.empty() || IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    4052           0 :         return;
    4053             :     }
    4054             : 
    4055          21 :     auto spk_manager = std::make_unique<LegacyScriptPubKeyMan>(*this);
    4056          21 :     m_internal_spk_managers = spk_manager.get();
    4057          21 :     m_external_spk_managers = spk_manager.get();
    4058          21 :     m_spk_managers[spk_manager->GetID()] = std::move(spk_manager);
    4059          21 : }
    4060             : 
    4061           0 : bool CWallet::WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const
    4062             : {
    4063           0 :     LOCK(cs_wallet);
    4064           0 :     return cb(vMasterKey);
    4065           0 : }
    4066             : 
    4067       77994 : bool CWallet::HasEncryptionKeys() const
    4068             : {
    4069       77994 :     return !mapMasterKeys.empty();
    4070             : }
    4071             : 
    4072          11 : void CWallet::ConnectScriptPubKeyManNotifiers()
    4073             : {
    4074          25 :     for (const auto& spk_man : GetActiveScriptPubKeyMans()) {
    4075          14 :         spk_man->NotifyWatchonlyChanged.connect(NotifyWatchonlyChanged);
    4076          14 :         spk_man->NotifyCanGetAddressesChanged.connect(NotifyCanGetAddressesChanged);
    4077             :     }
    4078          11 : }
    4079             : 
    4080           6 : void CWallet::UpdateProgress(const std::string& title, int nProgress)
    4081             : {
    4082           6 :     ShowProgress(title, nProgress);
    4083           6 : }
    4084             : 
    4085           8 : void CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc)
    4086             : {
    4087           8 :     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           8 :         auto spk_manager = std::unique_ptr<ScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
    4092           8 :         m_spk_managers[id] = std::move(spk_manager);
    4093           8 :     }
    4094           8 : }
    4095             : 
    4096          22 : void CWallet::SetupDescriptorScriptPubKeyMans(const CExtKey& master_key, const SecureString& mnemonic, const SecureString mnemonic_passphrase)
    4097             : {
    4098          22 :     AssertLockHeld(cs_wallet);
    4099             : 
    4100          88 :     for (auto type : {PathDerivationType::BIP44_External, PathDerivationType::BIP44_Internal, PathDerivationType::DIP0009_CoinJoin}) {
    4101             :         { // OUTPUT_TYPE is only one: LEGACY
    4102          66 :             auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this));
    4103          66 :             if (IsCrypted()) {
    4104           0 :                 if (IsLocked()) {
    4105           0 :                     throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors");
    4106             :                 }
    4107           0 :                 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           0 :             }
    4111          66 :             spk_manager->SetupDescriptorGeneration(master_key, mnemonic, mnemonic_passphrase, type);
    4112          66 :             uint256 id = spk_manager->GetID();
    4113          66 :             m_spk_managers[id] = std::move(spk_manager);
    4114          66 :             if (type != PathDerivationType::DIP0009_CoinJoin) {
    4115          44 :                 AddActiveScriptPubKeyMan(id, type == PathDerivationType::BIP44_Internal);
    4116          44 :             }
    4117          66 :         }
    4118             :     }
    4119          22 : }
    4120             : 
    4121          22 : void CWallet::SetupDescriptorScriptPubKeyMans(const SecureString& mnemonic_arg, const SecureString mnemonic_passphrase)
    4122             : {
    4123          22 :     AssertLockHeld(cs_wallet);
    4124             : 
    4125          22 :     if (!IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
    4126             :     // Make a seed
    4127             :     // TODO: remove duplicated code with CHDChain::SetMnemonic
    4128          22 :     const SecureString mnemonic = mnemonic_arg.empty() ? CMnemonic::Generate(m_args.GetIntArg("-mnemonicbits", CHDChain::DEFAULT_MNEMONIC_BITS)) : mnemonic_arg;
    4129          22 :     if (!CMnemonic::Check(mnemonic)) {
    4130           0 :         throw std::runtime_error(std::string(__func__) + ": invalid mnemonic: `" + std::string(mnemonic) + "`");
    4131             :     }
    4132          22 :     SecureVector seed_key;
    4133          22 :     CMnemonic::ToSeed(mnemonic, mnemonic_passphrase, seed_key);
    4134             : 
    4135             :     // Get the extended key
    4136          22 :     CExtKey master_key;
    4137          22 :     master_key.SetSeed(MakeByteSpan(seed_key));
    4138             : 
    4139          22 :         SetupDescriptorScriptPubKeyMans(master_key, mnemonic, mnemonic_passphrase);
    4140          22 :     } else {
    4141           0 :         ExternalSigner signer = ExternalSignerScriptPubKeyMan::GetExternalSigner();
    4142             : 
    4143             :         // TODO: add account parameter
    4144           0 :         int account = 0;
    4145           0 :         UniValue signer_res = signer.GetDescriptors(account);
    4146             : 
    4147           0 :         if (!signer_res.isObject()) throw std::runtime_error(std::string(__func__) + ": Unexpected result");
    4148           0 :         for (bool internal : {false, true}) {
    4149           0 :             const UniValue& descriptor_vals = signer_res.find_value(internal ? "internal" : "receive");
    4150           0 :             if (!descriptor_vals.isArray()) throw std::runtime_error(std::string(__func__) + ": Unexpected result");
    4151           0 :             for (const UniValue& desc_val : descriptor_vals.get_array().getValues()) {
    4152           0 :                 const std::string& desc_str = desc_val.getValStr();
    4153           0 :                 FlatSigningProvider keys;
    4154           0 :                 std::string desc_error;
    4155           0 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, desc_error, false);
    4156           0 :                 if (desc == nullptr) {
    4157           0 :                     throw std::runtime_error(std::string(__func__) + ": Invalid descriptor \"" + desc_str + "\" (" + desc_error + ")");
    4158             :                 }
    4159           0 :                 if (!desc->GetOutputType()) {
    4160           0 :                     continue;
    4161             :                 }
    4162           0 :                 auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this));
    4163           0 :                 spk_manager->SetupDescriptor(std::move(desc));
    4164           0 :                 uint256 id = spk_manager->GetID();
    4165           0 :                 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           0 :                 std::string bip44_purpose = strprintf("/%d'/%s'", BIP32_PURPOSE_STANDARD, Params().ExtCoinType());
    4169           0 :                 if (desc_str.find(bip44_purpose) != std::string::npos) {
    4170           0 :                     AddActiveScriptPubKeyMan(id, internal);
    4171           0 :                 }
    4172           0 :             }
    4173             :         }
    4174           0 :     }
    4175          22 : }
    4176             : 
    4177          44 : void CWallet::AddActiveScriptPubKeyMan(uint256 id, bool internal)
    4178             : {
    4179          44 :     WalletBatch batch(GetDatabase());
    4180          44 :     if (!batch.WriteActiveScriptPubKeyMan(id, internal)) {
    4181           0 :         throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed");
    4182             :     }
    4183          44 :     LoadActiveScriptPubKeyMan(id, internal);
    4184          44 : }
    4185             : 
    4186          48 : 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          48 :     Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
    4191             : 
    4192          48 :     WalletLogPrintf("Setting spkMan to active: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(OutputType::LEGACY), internal ? "true" : "false");
    4193          48 :     auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
    4194          48 :     auto& spk_mans_other = internal ? m_external_spk_managers : m_internal_spk_managers;
    4195          48 :     auto spk_man = m_spk_managers.at(id).get();
    4196          48 :     spk_mans = spk_man;
    4197          48 :     if (spk_mans_other == spk_man) {
    4198           0 :         spk_mans_other = nullptr;
    4199           0 :     }
    4200             : 
    4201          48 :     NotifyCanGetAddressesChanged();
    4202             : 
    4203          48 : }
    4204             : 
    4205           0 : void CWallet::DeactivateScriptPubKeyMan(uint256 id, bool internal)
    4206             : {
    4207           0 :     auto spk_man = GetScriptPubKeyMan(internal);
    4208           0 :     if (spk_man != nullptr && spk_man->GetID() == id) {
    4209           0 :         WalletLogPrintf("Deactivate spkMan: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(OutputType::LEGACY), internal ? "true" : "false");
    4210           0 :         WalletBatch batch(GetDatabase());
    4211           0 :         if (!batch.EraseActiveScriptPubKeyMan(internal)) {
    4212           0 :             throw std::runtime_error(std::string(__func__) + ": erasing active ScriptPubKeyMan id failed");
    4213             :         }
    4214             : 
    4215           0 :         auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers;
    4216           0 :         spk_mans = nullptr;
    4217           0 :     }
    4218             : 
    4219           0 :     NotifyCanGetAddressesChanged();
    4220           0 : }
    4221             : 
    4222          60 : bool CWallet::IsLegacy() const
    4223             : {
    4224          60 :     if (m_internal_spk_managers == nullptr) return false;
    4225          16 :     auto spk_man = dynamic_cast<LegacyScriptPubKeyMan*>(m_internal_spk_managers);
    4226          16 :     return spk_man != nullptr;
    4227          60 : }
    4228             : 
    4229          14 : DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const
    4230             : {
    4231          39 :     for (auto& spk_man_pair : m_spk_managers) {
    4232             :         // Try to downcast to DescriptorScriptPubKeyMan then check if the descriptors match
    4233          25 :         DescriptorScriptPubKeyMan* spk_manager = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man_pair.second.get());
    4234          25 :         if (spk_manager != nullptr && spk_manager->HasWalletDescriptor(desc)) {
    4235           0 :             return spk_manager;
    4236             :         }
    4237             :     }
    4238             : 
    4239          14 :     return nullptr;
    4240          14 : }
    4241             : 
    4242           0 : std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const
    4243             : {
    4244             :     // Legacy script pubkey man can't be either external or internal
    4245           0 :     if (IsLegacy()) {
    4246           0 :         return std::nullopt;
    4247             :     }
    4248             : 
    4249             :     // only active ScriptPubKeyMan can be internal
    4250           0 :     if (!GetActiveScriptPubKeyMans().count(spk_man)) {
    4251           0 :         return std::nullopt;
    4252             :     }
    4253             : 
    4254           0 :     const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man);
    4255           0 :     if (!desc_spk_man) {
    4256           0 :         throw std::runtime_error(std::string(__func__) + ": unexpected ScriptPubKeyMan type.");
    4257             :     }
    4258             : 
    4259           0 :     LOCK(desc_spk_man->cs_desc_man);
    4260           0 :     const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType();
    4261           0 :     assert(type.has_value());
    4262             : 
    4263           0 :     return GetScriptPubKeyMan(/*internal=*/true) == desc_spk_man;
    4264           0 : }
    4265             : 
    4266          14 : ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal)
    4267             : {
    4268          14 :     AssertLockHeld(cs_wallet);
    4269             : 
    4270          14 :     if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
    4271           0 :         WalletLogPrintf("Cannot add WalletDescriptor to a non-descriptor wallet\n");
    4272           0 :         return nullptr;
    4273             :     }
    4274             : 
    4275          14 :     SecureString mnemonic;
    4276          14 :     SecureString mnemonic_passphrase;
    4277             : 
    4278          14 :     auto spk_man = GetDescriptorScriptPubKeyMan(desc);
    4279          14 :     if (spk_man) {
    4280           0 :         WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
    4281           0 :         spk_man->UpdateWalletDescriptor(desc);
    4282           0 :     } else {
    4283          14 :         auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
    4284          14 :         spk_man = new_spk_man.get();
    4285             : 
    4286             :         // Save the descriptor to memory
    4287          14 :         m_spk_managers[new_spk_man->GetID()] = std::move(new_spk_man);
    4288          14 :     }
    4289             : 
    4290             :     // Add the private keys to the descriptor
    4291          28 :     for (const auto& entry : signing_provider.keys) {
    4292          14 :         const CKey& key = entry.second;
    4293          14 :         spk_man->AddDescriptorKey(key, key.GetPubKey());
    4294             :     }
    4295             : 
    4296             :     // Top up key pool, the manager will generate new scriptPubKeys internally
    4297          14 :     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          14 :     if (!desc.descriptor->IsRange()) {
    4305          13 :         auto script_pub_keys = spk_man->GetScriptPubKeys();
    4306          13 :         if (script_pub_keys.empty()) {
    4307           0 :             WalletLogPrintf("Could not generate scriptPubKeys (cache is empty)\n");
    4308           0 :             return nullptr;
    4309             :         }
    4310             : 
    4311          13 :         if (!internal) {
    4312          48 :             for (const auto& script : script_pub_keys) {
    4313          35 :                 CTxDestination dest;
    4314          35 :                 if (ExtractDestination(script, dest)) {
    4315          35 :                     SetAddressBook(dest, label, "receive");
    4316          35 :                 }
    4317             :             }
    4318          13 :         }
    4319          13 :     }
    4320             : 
    4321             :     // Save the descriptor to DB
    4322          14 :     spk_man->WriteDescriptor();
    4323             : 
    4324          14 :     return spk_man;
    4325          14 : }
    4326             : 
    4327           0 : bool CWallet::MigrateToSQLite(bilingual_str& error)
    4328             : {
    4329           0 :     AssertLockHeld(cs_wallet);
    4330             : 
    4331           0 :     WalletLogPrintf("Migrating wallet storage database from BerkeleyDB to SQLite.\n");
    4332             : 
    4333           0 :     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           0 :     std::unique_ptr<DatabaseBatch> batch = m_database->MakeBatch();
    4340           0 :     std::vector<std::pair<SerializeData, SerializeData>> records;
    4341           0 :     if (!batch->StartCursor()) {
    4342           0 :         error = _("Error: Unable to begin reading all records in the database");
    4343           0 :         return false;
    4344             :     }
    4345           0 :     bool complete = false;
    4346           0 :     while (true) {
    4347           0 :         CDataStream ss_key(SER_DISK, CLIENT_VERSION);
    4348           0 :         CDataStream ss_value(SER_DISK, CLIENT_VERSION);
    4349           0 :         bool ret = batch->ReadAtCursor(ss_key, ss_value, complete);
    4350           0 :         if (!ret) {
    4351           0 :             break;
    4352             :         }
    4353           0 :         SerializeData key(ss_key.begin(), ss_key.end());
    4354           0 :         SerializeData value(ss_value.begin(), ss_value.end());
    4355           0 :         records.emplace_back(key, value);
    4356           0 :     }
    4357           0 :     batch->CloseCursor();
    4358           0 :     batch.reset();
    4359           0 :     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           0 :     fs::path db_path = fs::PathFromString(m_database->Filename());
    4366           0 :     fs::path db_dir = db_path.parent_path();
    4367           0 :     m_database->Close();
    4368           0 :     fs::remove(db_path);
    4369             : 
    4370             :     // Make new DB
    4371           0 :     DatabaseOptions opts;
    4372           0 :     opts.require_create = true;
    4373           0 :     opts.require_format = DatabaseFormat::SQLITE;
    4374             :     DatabaseStatus db_status;
    4375           0 :     std::unique_ptr<WalletDatabase> new_db = MakeDatabase(db_dir, opts, db_status, error);
    4376           0 :     assert(new_db); // This is to prevent doing anything further with this wallet. The original file was deleted, but a backup exists.
    4377           0 :     m_database.reset();
    4378           0 :     m_database = std::move(new_db);
    4379             : 
    4380             :     // Write existing records into the new DB
    4381           0 :     batch = m_database->MakeBatch();
    4382           0 :     bool began = batch->TxnBegin();
    4383           0 :     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           0 :     for (const auto& [key, value] : records) {
    4385           0 :         CDataStream ss_key(key, SER_DISK, CLIENT_VERSION);
    4386           0 :         CDataStream ss_value(value, SER_DISK, CLIENT_VERSION);
    4387           0 :         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           0 :     }
    4394           0 :     bool committed = batch->TxnCommit();
    4395           0 :     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           0 :     return true;
    4397           0 : }
    4398             : 
    4399           0 : std::optional<MigrationData> CWallet::GetDescriptorsForLegacy(bilingual_str& error) const
    4400             : {
    4401           0 :     AssertLockHeld(cs_wallet);
    4402             : 
    4403           0 :     LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
    4404           0 :     assert(legacy_spkm);
    4405             : 
    4406           0 :     std::optional<MigrationData> res = legacy_spkm->MigrateToDescriptor();
    4407           0 :     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           0 :     return res;
    4412           0 : }
    4413             : 
    4414           0 : bool CWallet::ApplyMigrationData(MigrationData& data, bilingual_str& error)
    4415             : {
    4416           0 :     AssertLockHeld(cs_wallet);
    4417             : 
    4418           0 :     LegacyScriptPubKeyMan* legacy_spkm = GetLegacyScriptPubKeyMan();
    4419           0 :     if (!legacy_spkm) {
    4420           0 :         error = _("Error: This wallet is already a descriptor wallet");
    4421           0 :         return false;
    4422             :     }
    4423             : 
    4424           0 :     for (auto& desc_spkm : data.desc_spkms) {
    4425           0 :         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           0 :         m_spk_managers[desc_spkm->GetID()] = std::move(desc_spkm);
    4430             :     }
    4431             : 
    4432             :     // Remove the LegacyScriptPubKeyMan from disk
    4433           0 :     if (!legacy_spkm->DeleteRecords()) {
    4434           0 :         return false;
    4435             :     }
    4436             : 
    4437             :     // Remove the LegacyScriptPubKeyMan from memory
    4438           0 :     m_spk_managers.erase(legacy_spkm->GetID());
    4439           0 :     m_external_spk_managers = nullptr;
    4440           0 :     m_internal_spk_managers = nullptr;
    4441             : 
    4442             :     // Setup new descriptors
    4443           0 :     SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
    4444           0 :     if (!IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
    4445             :         // Use the existing master key if we have it
    4446           0 :         if (data.master_key.key.IsValid()) {
    4447           0 :             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           0 :             for (bool internal : {false, true}) {
    4455           0 :                 auto* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(GetScriptPubKeyMan(/*internal=*/internal));
    4456           0 :                 if (desc_spk_man == nullptr) continue;
    4457           0 :                 const uint32_t counter = internal ? data.internal_chain_counter : data.external_chain_counter;
    4458           0 :                 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           0 :         } else {
    4464             :             // Setup with a new seed if we don't.
    4465           0 :             SetupDescriptorScriptPubKeyMans("", "");
    4466             :         }
    4467           0 :     }
    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           0 :     std::vector<uint256> txids_to_delete;
    4472           0 :     for (const auto& [_pos, wtx] : wtxOrdered) {
    4473           0 :         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           0 :             if (data.watchonly_wallet) {
    4477           0 :                 LOCK(data.watchonly_wallet->cs_wallet);
    4478           0 :                 if (data.watchonly_wallet->IsMine(*wtx->tx) || data.watchonly_wallet->IsFromMe(*wtx->tx)) {
    4479             :                     // Add to watchonly wallet
    4480           0 :                     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           0 :                     txids_to_delete.push_back(wtx->GetHash());
    4486           0 :                     continue;
    4487             :                 }
    4488           0 :             }
    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           0 :     if (txids_to_delete.size() > 0) {
    4496           0 :         std::vector<uint256> deleted_txids;
    4497           0 :         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           0 :         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           0 :         for (const uint256& txid : deleted_txids) {
    4507           0 :             NotifyTransactionChanged(txid, CT_UPDATED);
    4508             :         }
    4509           0 :     }
    4510             : 
    4511             :     // Check the address book data in the same way we did for transactions
    4512           0 :     std::vector<CTxDestination> dests_to_delete;
    4513           0 :     for (const auto& addr_pair : m_address_book) {
    4514             :         // Labels applied to receiving addresses should go based on IsMine
    4515           0 :         if (addr_pair.second.purpose == "receive") {
    4516           0 :             if (!IsMine(addr_pair.first)) {
    4517             :                 // Check the address book data is the watchonly wallet's
    4518           0 :                 if (data.watchonly_wallet) {
    4519           0 :                     LOCK(data.watchonly_wallet->cs_wallet);
    4520           0 :                     if (data.watchonly_wallet->IsMine(addr_pair.first)) {
    4521             :                         // Add to the watchonly. Preserve the labels, purpose, and change-ness
    4522           0 :                         std::string label = addr_pair.second.GetLabel();
    4523           0 :                         std::string purpose = addr_pair.second.purpose;
    4524           0 :                         if (!purpose.empty()) {
    4525           0 :                             data.watchonly_wallet->m_address_book[addr_pair.first].purpose = purpose;
    4526           0 :                         }
    4527           0 :                         if (!addr_pair.second.IsChange()) {
    4528           0 :                             data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label);
    4529           0 :                         }
    4530           0 :                         dests_to_delete.push_back(addr_pair.first);
    4531             :                         continue;
    4532           0 :                     }
    4533           0 :                 }
    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           0 :         } else {
    4555             :             // Labels for everything else (send) should be cloned to all
    4556           0 :             if (data.watchonly_wallet) {
    4557           0 :                 LOCK(data.watchonly_wallet->cs_wallet);
    4558             :                 // Add to the watchonly. Preserve the labels, purpose, and change-ness
    4559           0 :                 std::string label = addr_pair.second.GetLabel();
    4560           0 :                 std::string purpose = addr_pair.second.purpose;
    4561           0 :                 if (!purpose.empty()) {
    4562           0 :                     data.watchonly_wallet->m_address_book[addr_pair.first].purpose = purpose;
    4563           0 :                 }
    4564           0 :                 if (!addr_pair.second.IsChange()) {
    4565           0 :                     data.watchonly_wallet->m_address_book[addr_pair.first].SetLabel(label);
    4566           0 :                 }
    4567             :                 continue;
    4568           0 :             }
    4569           0 :             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           0 :     auto persist_address_book = [](const CWallet& wallet) {
    4587           0 :         LOCK(wallet.cs_wallet);
    4588           0 :         WalletBatch batch{wallet.GetDatabase()};
    4589           0 :         for (const auto& [destination, addr_book_data] : wallet.m_address_book) {
    4590           0 :             auto address{EncodeDestination(destination)};
    4591           0 :             auto purpose{addr_book_data.purpose};
    4592           0 :             auto label{addr_book_data.GetLabel()};
    4593             :             // don't bother writing default values (unknown purpose, empty label)
    4594           0 :             if (purpose != "unknown") batch.WritePurpose(address, purpose);
    4595           0 :             if (!label.empty()) batch.WriteName(address, label);
    4596           0 :         }
    4597           0 :     };
    4598           0 :     if (data.watchonly_wallet) persist_address_book(*data.watchonly_wallet);
    4599           0 :     if (data.solvable_wallet) persist_address_book(*data.solvable_wallet);
    4600             : 
    4601             :     // Remove the things to delete
    4602           0 :     if (dests_to_delete.size() > 0) {
    4603           0 :         for (const auto& dest : dests_to_delete) {
    4604           0 :             if (!DelAddressBook(dest)) {
    4605           0 :                 error = _("Error: Unable to remove watchonly address book data");
    4606           0 :                 return false;
    4607             :             }
    4608             :         }
    4609           0 :     }
    4610             : 
    4611             :     // Connect the SPKM signals
    4612           0 :     ConnectScriptPubKeyManNotifiers();
    4613           0 :     NotifyCanGetAddressesChanged();
    4614             : 
    4615           0 :     WalletLogPrintf("Wallet migration complete.\n");
    4616             : 
    4617           0 :     return true;
    4618           0 : }
    4619             : 
    4620           0 : bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
    4621             : {
    4622           0 :     AssertLockHeld(wallet.cs_wallet);
    4623             : 
    4624             :     // Get all of the descriptors from the legacy wallet
    4625           0 :     std::optional<MigrationData> data = wallet.GetDescriptorsForLegacy(error);
    4626           0 :     if (data == std::nullopt) return false;
    4627             : 
    4628             :     // Create the watchonly and solvable wallets if necessary
    4629           0 :     if (data->watch_descs.size() > 0 || data->solvable_descs.size() > 0) {
    4630           0 :         DatabaseOptions options;
    4631           0 :         options.require_existing = false;
    4632           0 :         options.require_create = true;
    4633             : 
    4634             :         // Make the wallets
    4635           0 :         options.create_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_DESCRIPTORS;
    4636           0 :         if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
    4637           0 :             options.create_flags |= WALLET_FLAG_AVOID_REUSE;
    4638           0 :         }
    4639           0 :         if (wallet.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) {
    4640           0 :             options.create_flags |= WALLET_FLAG_KEY_ORIGIN_METADATA;
    4641           0 :         }
    4642           0 :         if (data->watch_descs.size() > 0) {
    4643           0 :             wallet.WalletLogPrintf("Making a new watchonly wallet containing the watched scripts\n");
    4644             : 
    4645             :             DatabaseStatus status;
    4646           0 :             std::vector<bilingual_str> warnings;
    4647           0 :             std::string wallet_name = wallet.GetName() + "_watchonly";
    4648           0 :             data->watchonly_wallet = CreateWallet(context, wallet_name, std::nullopt, options, status, error, warnings);
    4649           0 :             if (status != DatabaseStatus::SUCCESS) {
    4650           0 :                 error = _("Error: Failed to create new watchonly wallet");
    4651           0 :                 return false;
    4652             :             }
    4653           0 :             res.watchonly_wallet = data->watchonly_wallet;
    4654           0 :             LOCK(data->watchonly_wallet->cs_wallet);
    4655             : 
    4656             :             // Parse the descriptors and add them to the new wallet
    4657           0 :             for (const auto& [desc_str, creation_time] : data->watch_descs) {
    4658             :                 // Parse the descriptor
    4659           0 :                 FlatSigningProvider keys;
    4660           0 :                 std::string parse_err;
    4661           0 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, parse_err, /* require_checksum */ true);
    4662           0 :                 assert(desc); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor
    4663           0 :                 assert(!desc->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor
    4664             : 
    4665             :                 // Add to the wallet
    4666           0 :                 WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
    4667           0 :                 data->watchonly_wallet->AddWalletDescriptor(w_desc, keys, "", false);
    4668           0 :             }
    4669             : 
    4670             :             // Add the wallet to settings
    4671           0 :             UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings);
    4672           0 :         }
    4673           0 :         if (data->solvable_descs.size() > 0) {
    4674           0 :             wallet.WalletLogPrintf("Making a new watchonly wallet containing the unwatched solvable scripts\n");
    4675             : 
    4676             :             DatabaseStatus status;
    4677           0 :             std::vector<bilingual_str> warnings;
    4678           0 :             std::string wallet_name = wallet.GetName() + "_solvables";
    4679           0 :             data->solvable_wallet = CreateWallet(context, wallet_name, std::nullopt, options, status, error, warnings);
    4680           0 :             if (status != DatabaseStatus::SUCCESS) {
    4681           0 :                 error = _("Error: Failed to create new watchonly wallet");
    4682           0 :                 return false;
    4683             :             }
    4684           0 :             res.solvables_wallet = data->solvable_wallet;
    4685           0 :             LOCK(data->solvable_wallet->cs_wallet);
    4686             : 
    4687             :             // Parse the descriptors and add them to the new wallet
    4688           0 :             for (const auto& [desc_str, creation_time] : data->solvable_descs) {
    4689             :                 // Parse the descriptor
    4690           0 :                 FlatSigningProvider keys;
    4691           0 :                 std::string parse_err;
    4692           0 :                 std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, parse_err, /* require_checksum */ true);
    4693           0 :                 assert(desc); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor
    4694           0 :                 assert(!desc->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor
    4695             : 
    4696             :                 // Add to the wallet
    4697           0 :                 WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
    4698           0 :                 data->solvable_wallet->AddWalletDescriptor(w_desc, keys, "", false);
    4699           0 :             }
    4700             : 
    4701             :             // Add the wallet to settings
    4702           0 :             UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings);
    4703           0 :         }
    4704           0 :     }
    4705             : 
    4706             :     // Add the descriptors to wallet, remove LegacyScriptPubKeyMan, and cleanup txs and address book data
    4707           0 :     if (!wallet.ApplyMigrationData(*data, error)) {
    4708           0 :         return false;
    4709             :     }
    4710           0 :     return true;
    4711           0 : }
    4712             : 
    4713           0 : util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context)
    4714             : {
    4715           0 :     MigrationResult res;
    4716           0 :     bilingual_str error;
    4717           0 :     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           0 :     if (auto wallet = GetWallet(context, wallet_name)) {
    4721           0 :         if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
    4722           0 :             return util::Error{_("Unable to unload the wallet before migrating")};
    4723             :         }
    4724           0 :         UnloadWallet(std::move(wallet));
    4725           0 :     }
    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           0 :     WalletContext empty_context;
    4730           0 :     empty_context.args = context.args;
    4731           0 :     DatabaseOptions options;
    4732           0 :     options.require_existing = true;
    4733             :     DatabaseStatus status;
    4734           0 :     std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error);
    4735           0 :     if (!database) {
    4736           0 :         return util::Error{Untranslated("Wallet file verification failed.") + Untranslated(" ") + error};
    4737             :     }
    4738             : 
    4739             :     // Make the local wallet
    4740           0 :     std::shared_ptr<CWallet> local_wallet = CWallet::Create(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings);
    4741           0 :     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           0 :     if (!local_wallet->GetLegacyScriptPubKeyMan()) {
    4747           0 :         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           0 :     fs::path this_wallet_dir = fs::absolute(fs::PathFromString(local_wallet->GetDatabase().Filename())).parent_path();
    4757           0 :     std::string backup_prefix;
    4758           0 :     if (!wallet_name.empty()) {
    4759             :         // fs::weakly_canonical resolves relative specifiers and removes trailing slashes.
    4760           0 :         const auto legacy_wallet_path = fs::weakly_canonical(GetWalletDir() / fs::PathFromString(wallet_name));
    4761           0 :         backup_prefix = fs::PathToString(legacy_wallet_path.filename());
    4762           0 :     }
    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           0 :     if (backup_prefix.empty()) backup_prefix = "wallet";
    4766           0 :     fs::path backup_filename = fs::PathFromString(strprintf("%s-%d.legacy.bak", backup_prefix, GetTime()));
    4767           0 :     fs::path backup_path = this_wallet_dir / backup_filename;
    4768           0 :     if (!local_wallet->BackupWallet(fs::PathToString(backup_path))) {
    4769           0 :         return util::Error{_("Error: Unable to make a backup of your wallet")};
    4770             :     }
    4771           0 :     res.backup_path = backup_path;
    4772             : 
    4773           0 :     bool success = false;
    4774             :     {
    4775           0 :         LOCK(local_wallet->cs_wallet);
    4776             : 
    4777             :         // Unlock the wallet if needed
    4778           0 :         if (local_wallet->IsLocked() && !local_wallet->Unlock(passphrase)) {
    4779           0 :             if (passphrase.find('\0') == std::string::npos) {
    4780           0 :                 return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect.")};
    4781             :             } else {
    4782           0 :                 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           0 :         if (!local_wallet->MigrateToSQLite(error)) return util::Error{error};
    4792             : 
    4793             :         // Do the migration, and cleanup if it fails
    4794           0 :         success = DoMigration(*local_wallet, context, error, res);
    4795           0 :     }
    4796             : 
    4797           0 :     if (success) {
    4798             :         // Migration successful, unload the wallet locally, then reload it.
    4799           0 :         assert(local_wallet.use_count() == 1);
    4800           0 :         local_wallet.reset();
    4801           0 :         LoadWallet(context, wallet_name, /*load_on_start=*/std::nullopt, options, status, error, warnings);
    4802           0 :         res.wallet_name = wallet_name;
    4803           0 :     } 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           0 :     return res;
    4856           0 : }
    4857             : } // namespace wallet

Generated by: LCOV version 1.16