/Users/eugenesiegel/btc/bitcoin/src/wallet/wallet.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2009-2010 Satoshi Nakamoto | 
| 2 |  | // Copyright (c) 2009-present The Bitcoin Core developers | 
| 3 |  | // Distributed under the MIT software license, see the accompanying | 
| 4 |  | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | 
| 5 |  |  | 
| 6 |  | #include <wallet/wallet.h> | 
| 7 |  |  | 
| 8 |  | #include <bitcoin-build-config.h> // IWYU pragma: keep | 
| 9 |  | #include <addresstype.h> | 
| 10 |  | #include <blockfilter.h> | 
| 11 |  | #include <chain.h> | 
| 12 |  | #include <coins.h> | 
| 13 |  | #include <common/args.h> | 
| 14 |  | #include <common/messages.h> | 
| 15 |  | #include <common/settings.h> | 
| 16 |  | #include <common/signmessage.h> | 
| 17 |  | #include <common/system.h> | 
| 18 |  | #include <consensus/amount.h> | 
| 19 |  | #include <consensus/consensus.h> | 
| 20 |  | #include <consensus/validation.h> | 
| 21 |  | #include <external_signer.h> | 
| 22 |  | #include <interfaces/chain.h> | 
| 23 |  | #include <interfaces/handler.h> | 
| 24 |  | #include <interfaces/wallet.h> | 
| 25 |  | #include <kernel/chain.h> | 
| 26 |  | #include <kernel/mempool_removal_reason.h> | 
| 27 |  | #include <key.h> | 
| 28 |  | #include <key_io.h> | 
| 29 |  | #include <logging.h> | 
| 30 |  | #include <node/types.h> | 
| 31 |  | #include <outputtype.h> | 
| 32 |  | #include <policy/feerate.h> | 
| 33 |  | #include <policy/truc_policy.h> | 
| 34 |  | #include <primitives/block.h> | 
| 35 |  | #include <primitives/transaction.h> | 
| 36 |  | #include <psbt.h> | 
| 37 |  | #include <pubkey.h> | 
| 38 |  | #include <random.h> | 
| 39 |  | #include <script/descriptor.h> | 
| 40 |  | #include <script/interpreter.h> | 
| 41 |  | #include <script/script.h> | 
| 42 |  | #include <script/sign.h> | 
| 43 |  | #include <script/signingprovider.h> | 
| 44 |  | #include <script/solver.h> | 
| 45 |  | #include <serialize.h> | 
| 46 |  | #include <span.h> | 
| 47 |  | #include <streams.h> | 
| 48 |  | #include <support/allocators/secure.h> | 
| 49 |  | #include <support/allocators/zeroafterfree.h> | 
| 50 |  | #include <support/cleanse.h> | 
| 51 |  | #include <sync.h> | 
| 52 |  | #include <tinyformat.h> | 
| 53 |  | #include <uint256.h> | 
| 54 |  | #include <univalue.h> | 
| 55 |  | #include <util/check.h> | 
| 56 |  | #include <util/fs.h> | 
| 57 |  | #include <util/fs_helpers.h> | 
| 58 |  | #include <util/moneystr.h> | 
| 59 |  | #include <util/result.h> | 
| 60 |  | #include <util/string.h> | 
| 61 |  | #include <util/time.h> | 
| 62 |  | #include <util/translation.h> | 
| 63 |  | #include <wallet/coincontrol.h> | 
| 64 |  | #include <wallet/context.h> | 
| 65 |  | #include <wallet/crypter.h> | 
| 66 |  | #include <wallet/db.h> | 
| 67 |  | #include <wallet/external_signer_scriptpubkeyman.h> | 
| 68 |  | #include <wallet/scriptpubkeyman.h> | 
| 69 |  | #include <wallet/transaction.h> | 
| 70 |  | #include <wallet/types.h> | 
| 71 |  | #include <wallet/walletdb.h> | 
| 72 |  | #include <wallet/walletutil.h> | 
| 73 |  |  | 
| 74 |  | #include <algorithm> | 
| 75 |  | #include <cassert> | 
| 76 |  | #include <condition_variable> | 
| 77 |  | #include <exception> | 
| 78 |  | #include <optional> | 
| 79 |  | #include <stdexcept> | 
| 80 |  | #include <thread> | 
| 81 |  | #include <tuple> | 
| 82 |  | #include <variant> | 
| 83 |  |  | 
| 84 |  | struct KeyOriginInfo; | 
| 85 |  |  | 
| 86 |  | using common::AmountErrMsg; | 
| 87 |  | using common::AmountHighWarn; | 
| 88 |  | using common::PSBTError; | 
| 89 |  | using interfaces::FoundBlock; | 
| 90 |  | using util::ReplaceAll; | 
| 91 |  | using util::ToString; | 
| 92 |  |  | 
| 93 |  | namespace wallet { | 
| 94 |  |  | 
| 95 |  | bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name) | 
| 96 | 0 | { | 
| 97 | 0 |     const auto update_function = [&wallet_name](common::SettingsValue& setting_value) { | 
| 98 | 0 |         if (!setting_value.isArray()) setting_value.setArray(); | 
| 99 | 0 |         for (const auto& value : setting_value.getValues()) { | 
| 100 | 0 |             if (value.isStr() && value.get_str() == wallet_name) return interfaces::SettingsAction::SKIP_WRITE; | 
| 101 | 0 |         } | 
| 102 | 0 |         setting_value.push_back(wallet_name); | 
| 103 | 0 |         return interfaces::SettingsAction::WRITE; | 
| 104 | 0 |     }; | 
| 105 | 0 |     return chain.updateRwSetting("wallet", update_function); | 
| 106 | 0 | } | 
| 107 |  |  | 
| 108 |  | bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_name) | 
| 109 | 0 | { | 
| 110 | 0 |     const auto update_function = [&wallet_name](common::SettingsValue& setting_value) { | 
| 111 | 0 |         if (!setting_value.isArray()) return interfaces::SettingsAction::SKIP_WRITE; | 
| 112 | 0 |         common::SettingsValue new_value(common::SettingsValue::VARR); | 
| 113 | 0 |         for (const auto& value : setting_value.getValues()) { | 
| 114 | 0 |             if (!value.isStr() || value.get_str() != wallet_name) new_value.push_back(value); | 
| 115 | 0 |         } | 
| 116 | 0 |         if (new_value.size() == setting_value.size()) return interfaces::SettingsAction::SKIP_WRITE; | 
| 117 | 0 |         setting_value = std::move(new_value); | 
| 118 | 0 |         return interfaces::SettingsAction::WRITE; | 
| 119 | 0 |     }; | 
| 120 | 0 |     return chain.updateRwSetting("wallet", update_function); | 
| 121 | 0 | } | 
| 122 |  |  | 
| 123 |  | static void UpdateWalletSetting(interfaces::Chain& chain, | 
| 124 |  |                                 const std::string& wallet_name, | 
| 125 |  |                                 std::optional<bool> load_on_startup, | 
| 126 |  |                                 std::vector<bilingual_str>& warnings) | 
| 127 | 0 | { | 
| 128 | 0 |     if (!load_on_startup) return; | 
| 129 | 0 |     if (load_on_startup.value() && !AddWalletSetting(chain, wallet_name)) { | 
| 130 | 0 |         warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup.")); | 
| 131 | 0 |     } else if (!load_on_startup.value() && !RemoveWalletSetting(chain, wallet_name)) { | 
| 132 | 0 |         warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may still be loaded next node startup.")); | 
| 133 | 0 |     } | 
| 134 | 0 | } | 
| 135 |  |  | 
| 136 |  | /** | 
| 137 |  |  * Refresh mempool status so the wallet is in an internally consistent state and | 
| 138 |  |  * immediately knows the transaction's status: Whether it can be considered | 
| 139 |  |  * trusted and is eligible to be abandoned ... | 
| 140 |  |  */ | 
| 141 |  | static void RefreshMempoolStatus(CWalletTx& tx, interfaces::Chain& chain) | 
| 142 | 0 | { | 
| 143 | 0 |     if (chain.isInMempool(tx.GetHash())) { | 
| 144 | 0 |         tx.m_state = TxStateInMempool(); | 
| 145 | 0 |     } else if (tx.state<TxStateInMempool>()) { | 
| 146 | 0 |         tx.m_state = TxStateInactive(); | 
| 147 | 0 |     } | 
| 148 | 0 | } | 
| 149 |  |  | 
| 150 |  | bool AddWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet) | 
| 151 | 0 | { | 
| 152 | 0 |     LOCK(context.wallets_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 153 | 0 |     assert(wallet); | 
| 154 | 0 |     std::vector<std::shared_ptr<CWallet>>::const_iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet); | 
| 155 | 0 |     if (i != context.wallets.end()) return false; | 
| 156 | 0 |     context.wallets.push_back(wallet); | 
| 157 | 0 |     wallet->ConnectScriptPubKeyManNotifiers(); | 
| 158 | 0 |     wallet->NotifyCanGetAddressesChanged(); | 
| 159 | 0 |     return true; | 
| 160 | 0 | } | 
| 161 |  |  | 
| 162 |  | bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings) | 
| 163 | 0 | { | 
| 164 | 0 |     assert(wallet); | 
| 165 |  |  | 
| 166 | 0 |     interfaces::Chain& chain = wallet->chain(); | 
| 167 | 0 |     std::string name = wallet->GetName(); | 
| 168 | 0 |     WITH_LOCK(wallet->cs_wallet, wallet->WriteBestBlock()); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 169 |  |  | 
| 170 |  |     // Unregister with the validation interface which also drops shared pointers. | 
| 171 | 0 |     wallet->m_chain_notifications_handler.reset(); | 
| 172 | 0 |     { | 
| 173 | 0 |         LOCK(context.wallets_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 174 | 0 |         std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(context.wallets.begin(), context.wallets.end(), wallet); | 
| 175 | 0 |         if (i == context.wallets.end()) return false; | 
| 176 | 0 |         context.wallets.erase(i); | 
| 177 | 0 |     } | 
| 178 |  |     // Notify unload so that upper layers release the shared pointer. | 
| 179 | 0 |     wallet->NotifyUnload(); | 
| 180 |  |  | 
| 181 |  |     // Write the wallet setting | 
| 182 | 0 |     UpdateWalletSetting(chain, name, load_on_start, warnings); | 
| 183 |  | 
 | 
| 184 | 0 |     return true; | 
| 185 | 0 | } | 
| 186 |  |  | 
| 187 |  | bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start) | 
| 188 | 0 | { | 
| 189 | 0 |     std::vector<bilingual_str> warnings; | 
| 190 | 0 |     return RemoveWallet(context, wallet, load_on_start, warnings); | 
| 191 | 0 | } | 
| 192 |  |  | 
| 193 |  | std::vector<std::shared_ptr<CWallet>> GetWallets(WalletContext& context) | 
| 194 | 0 | { | 
| 195 | 0 |     LOCK(context.wallets_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 196 | 0 |     return context.wallets; | 
| 197 | 0 | } | 
| 198 |  |  | 
| 199 |  | std::shared_ptr<CWallet> GetDefaultWallet(WalletContext& context, size_t& count) | 
| 200 | 0 | { | 
| 201 | 0 |     LOCK(context.wallets_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 202 | 0 |     count = context.wallets.size(); | 
| 203 | 0 |     return count == 1 ? context.wallets[0] : nullptr; | 
| 204 | 0 | } | 
| 205 |  |  | 
| 206 |  | std::shared_ptr<CWallet> GetWallet(WalletContext& context, const std::string& name) | 
| 207 | 0 | { | 
| 208 | 0 |     LOCK(context.wallets_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 209 | 0 |     for (const std::shared_ptr<CWallet>& wallet : context.wallets) { | 
| 210 | 0 |         if (wallet->GetName() == name) return wallet; | 
| 211 | 0 |     } | 
| 212 | 0 |     return nullptr; | 
| 213 | 0 | } | 
| 214 |  |  | 
| 215 |  | std::unique_ptr<interfaces::Handler> HandleLoadWallet(WalletContext& context, LoadWalletFn load_wallet) | 
| 216 | 0 | { | 
| 217 | 0 |     LOCK(context.wallets_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 218 | 0 |     auto it = context.wallet_load_fns.emplace(context.wallet_load_fns.end(), std::move(load_wallet)); | 
| 219 | 0 |     return interfaces::MakeCleanupHandler([&context, it] { LOCK(context.wallets_mutex); context.wallet_load_fns.erase(it); });| Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 220 | 0 | } | 
| 221 |  |  | 
| 222 |  | void NotifyWalletLoaded(WalletContext& context, const std::shared_ptr<CWallet>& wallet) | 
| 223 | 0 | { | 
| 224 | 0 |     LOCK(context.wallets_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 225 | 0 |     for (auto& load_wallet : context.wallet_load_fns) { | 
| 226 | 0 |         load_wallet(interfaces::MakeWallet(context, wallet)); | 
| 227 | 0 |     } | 
| 228 | 0 | } | 
| 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 |  | static std::set<std::string> g_loading_wallet_set GUARDED_BY(g_loading_wallet_mutex); | 
| 234 |  | static std::set<std::string> g_unloading_wallet_set GUARDED_BY(g_wallet_release_mutex); | 
| 235 |  |  | 
| 236 |  | // Custom deleter for shared_ptr<CWallet>. | 
| 237 |  | static void FlushAndDeleteWallet(CWallet* wallet) | 
| 238 | 0 | { | 
| 239 | 0 |     const std::string name = wallet->GetName(); | 
| 240 | 0 |     wallet->WalletLogPrintf("Releasing wallet %s..\n", name); | 
| 241 | 0 |     delete wallet; | 
| 242 |  |     // Wallet is now released, notify WaitForDeleteWallet, if any. | 
| 243 | 0 |     { | 
| 244 | 0 |         LOCK(g_wallet_release_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 245 | 0 |         if (g_unloading_wallet_set.erase(name) == 0) { | 
| 246 |  |             // WaitForDeleteWallet was not called for this wallet, all done. | 
| 247 | 0 |             return; | 
| 248 | 0 |         } | 
| 249 | 0 |     } | 
| 250 | 0 |     g_wallet_release_cv.notify_all(); | 
| 251 | 0 | } | 
| 252 |  |  | 
| 253 |  | void WaitForDeleteWallet(std::shared_ptr<CWallet>&& wallet) | 
| 254 | 0 | { | 
| 255 |  |     // Mark wallet for unloading. | 
| 256 | 0 |     const std::string name = wallet->GetName(); | 
| 257 | 0 |     { | 
| 258 | 0 |         LOCK(g_wallet_release_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 259 | 0 |         g_unloading_wallet_set.insert(name); | 
| 260 |  |         // Do not expect to be the only one removing this wallet. | 
| 261 |  |         // Multiple threads could simultaneously be waiting for deletion. | 
| 262 | 0 |     } | 
| 263 |  |  | 
| 264 |  |     // Time to ditch our shared_ptr and wait for FlushAndDeleteWallet call. | 
| 265 | 0 |     wallet.reset(); | 
| 266 | 0 |     { | 
| 267 | 0 |         WAIT_LOCK(g_wallet_release_mutex, lock); | Line | Count | Source |  | 265 | 0 | #define WAIT_LOCK(cs, name) UniqueLock name(LOCK_ARGS(cs)) | Line | Count | Source |  | 263 | 0 | #define LOCK_ARGS(cs) MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__ | 
 | 
 | 
| 268 | 0 |         while (g_unloading_wallet_set.count(name) == 1) { | 
| 269 | 0 |             g_wallet_release_cv.wait(lock); | 
| 270 | 0 |         } | 
| 271 | 0 |     } | 
| 272 | 0 | } | 
| 273 |  |  | 
| 274 |  | namespace { | 
| 275 |  | 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) | 
| 276 | 0 | { | 
| 277 | 0 |     try { | 
| 278 | 0 |         std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error); | 
| 279 | 0 |         if (!database) { | 
| 280 | 0 |             error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error; | 
| 281 | 0 |             return nullptr; | 
| 282 | 0 |         } | 
| 283 |  |  | 
| 284 | 0 |         context.chain->initMessage(_("Loading wallet…")); | 
| 285 | 0 |         std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings); | 
| 286 | 0 |         if (!wallet) { | 
| 287 | 0 |             error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error; | 
| 288 | 0 |             status = DatabaseStatus::FAILED_LOAD; | 
| 289 | 0 |             return nullptr; | 
| 290 | 0 |         } | 
| 291 |  |  | 
| 292 | 0 |         NotifyWalletLoaded(context, wallet); | 
| 293 | 0 |         AddWallet(context, wallet); | 
| 294 | 0 |         wallet->postInitProcess(); | 
| 295 |  |  | 
| 296 |  |         // Write the wallet setting | 
| 297 | 0 |         UpdateWalletSetting(*context.chain, name, load_on_start, warnings); | 
| 298 |  | 
 | 
| 299 | 0 |         return wallet; | 
| 300 | 0 |     } catch (const std::runtime_error& e) { | 
| 301 | 0 |         error = Untranslated(e.what()); | 
| 302 | 0 |         status = DatabaseStatus::FAILED_LOAD; | 
| 303 | 0 |         return nullptr; | 
| 304 | 0 |     } | 
| 305 | 0 | } | 
| 306 |  |  | 
| 307 |  | class FastWalletRescanFilter | 
| 308 |  | { | 
| 309 |  | public: | 
| 310 | 0 |     FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet) | 
| 311 | 0 |     { | 
| 312 |  |         // create initial filter with scripts from all ScriptPubKeyMans | 
| 313 | 0 |         for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) { | 
| 314 | 0 |             auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)}; | 
| 315 | 0 |             assert(desc_spkm != nullptr); | 
| 316 | 0 |             AddScriptPubKeys(desc_spkm); | 
| 317 |  |             // save each range descriptor's end for possible future filter updates | 
| 318 | 0 |             if (desc_spkm->IsHDEnabled()) { | 
| 319 | 0 |                 m_last_range_ends.emplace(desc_spkm->GetID(), desc_spkm->GetEndRange()); | 
| 320 | 0 |             } | 
| 321 | 0 |         } | 
| 322 | 0 |     } | 
| 323 |  |  | 
| 324 |  |     void UpdateIfNeeded() | 
| 325 | 0 |     { | 
| 326 |  |         // repopulate filter with new scripts if top-up has happened since last iteration | 
| 327 | 0 |         for (const auto& [desc_spkm_id, last_range_end] : m_last_range_ends) { | 
| 328 | 0 |             auto desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(m_wallet.GetScriptPubKeyMan(desc_spkm_id))}; | 
| 329 | 0 |             assert(desc_spkm != nullptr); | 
| 330 | 0 |             int32_t current_range_end{desc_spkm->GetEndRange()}; | 
| 331 | 0 |             if (current_range_end > last_range_end) { | 
| 332 | 0 |                 AddScriptPubKeys(desc_spkm, last_range_end); | 
| 333 | 0 |                 m_last_range_ends.at(desc_spkm->GetID()) = current_range_end; | 
| 334 | 0 |             } | 
| 335 | 0 |         } | 
| 336 | 0 |     } | 
| 337 |  |  | 
| 338 |  |     std::optional<bool> MatchesBlock(const uint256& block_hash) const | 
| 339 | 0 |     { | 
| 340 | 0 |         return m_wallet.chain().blockFilterMatchesAny(BlockFilterType::BASIC, block_hash, m_filter_set); | 
| 341 | 0 |     } | 
| 342 |  |  | 
| 343 |  | private: | 
| 344 |  |     const CWallet& m_wallet; | 
| 345 |  |     /** Map for keeping track of each range descriptor's last seen end range. | 
| 346 |  |       * This information is used to detect whether new addresses were derived | 
| 347 |  |       * (that is, if the current end range is larger than the saved end range) | 
| 348 |  |       * after processing a block and hence a filter set update is needed to | 
| 349 |  |       * take possible keypool top-ups into account. | 
| 350 |  |       */ | 
| 351 |  |     std::map<uint256, int32_t> m_last_range_ends; | 
| 352 |  |     GCSFilter::ElementSet m_filter_set; | 
| 353 |  |  | 
| 354 |  |     void AddScriptPubKeys(const DescriptorScriptPubKeyMan* desc_spkm, int32_t last_range_end = 0) | 
| 355 | 0 |     { | 
| 356 | 0 |         for (const auto& script_pub_key : desc_spkm->GetScriptPubKeys(last_range_end)) { | 
| 357 | 0 |             m_filter_set.emplace(script_pub_key.begin(), script_pub_key.end()); | 
| 358 | 0 |         } | 
| 359 | 0 |     } | 
| 360 |  | }; | 
| 361 |  | } // namespace | 
| 362 |  |  | 
| 363 |  | 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) | 
| 364 | 0 | { | 
| 365 | 0 |     auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name)); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 366 | 0 |     if (!result.second) { | 
| 367 | 0 |         error = Untranslated("Wallet already loading."); | 
| 368 | 0 |         status = DatabaseStatus::FAILED_LOAD; | 
| 369 | 0 |         return nullptr; | 
| 370 | 0 |     } | 
| 371 | 0 |     auto wallet = LoadWalletInternal(context, name, load_on_start, options, status, error, warnings); | 
| 372 | 0 |     WITH_LOCK(g_loading_wallet_mutex, g_loading_wallet_set.erase(result.first)); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 373 | 0 |     return wallet; | 
| 374 | 0 | } | 
| 375 |  |  | 
| 376 |  | 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) | 
| 377 | 0 | { | 
| 378 | 0 |     uint64_t wallet_creation_flags = options.create_flags; | 
| 379 | 0 |     const SecureString& passphrase = options.create_passphrase; | 
| 380 |  |  | 
| 381 |  |     // Only descriptor wallets can be created | 
| 382 | 0 |     Assert(wallet_creation_flags & WALLET_FLAG_DESCRIPTORS); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 383 | 0 |     options.require_format = DatabaseFormat::SQLITE; | 
| 384 |  |  | 
| 385 |  |     // Indicate that the wallet is actually supposed to be blank and not just blank to make it encrypted | 
| 386 | 0 |     bool create_blank = (wallet_creation_flags & WALLET_FLAG_BLANK_WALLET); | 
| 387 |  |  | 
| 388 |  |     // Born encrypted wallets need to be created blank first. | 
| 389 | 0 |     if (!passphrase.empty()) { | 
| 390 | 0 |         wallet_creation_flags |= WALLET_FLAG_BLANK_WALLET; | 
| 391 | 0 |     } | 
| 392 |  |  | 
| 393 |  |     // Private keys must be disabled for an external signer wallet | 
| 394 | 0 |     if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | 
| 395 | 0 |         error = Untranslated("Private keys must be disabled when using an external signer"); | 
| 396 | 0 |         status = DatabaseStatus::FAILED_CREATE; | 
| 397 | 0 |         return nullptr; | 
| 398 | 0 |     } | 
| 399 |  |  | 
| 400 |  |     // Do not allow a passphrase when private keys are disabled | 
| 401 | 0 |     if (!passphrase.empty() && (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | 
| 402 | 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."); | 
| 403 | 0 |         status = DatabaseStatus::FAILED_CREATE; | 
| 404 | 0 |         return nullptr; | 
| 405 | 0 |     } | 
| 406 |  |  | 
| 407 |  |     // Wallet::Verify will check if we're trying to create a wallet with a duplicate name. | 
| 408 | 0 |     std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error); | 
| 409 | 0 |     if (!database) { | 
| 410 | 0 |         error = Untranslated("Wallet file verification failed.") + Untranslated(" ") + error; | 
| 411 | 0 |         status = DatabaseStatus::FAILED_VERIFY; | 
| 412 | 0 |         return nullptr; | 
| 413 | 0 |     } | 
| 414 |  |  | 
| 415 |  |     // Make the wallet | 
| 416 | 0 |     context.chain->initMessage(_("Loading wallet…")); | 
| 417 | 0 |     std::shared_ptr<CWallet> wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings); | 
| 418 | 0 |     if (!wallet) { | 
| 419 | 0 |         error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error; | 
| 420 | 0 |         status = DatabaseStatus::FAILED_CREATE; | 
| 421 | 0 |         return nullptr; | 
| 422 | 0 |     } | 
| 423 |  |  | 
| 424 |  |     // Encrypt the wallet | 
| 425 | 0 |     if (!passphrase.empty() && !(wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | 
| 426 | 0 |         if (!wallet->EncryptWallet(passphrase)) { | 
| 427 | 0 |             error = Untranslated("Error: Wallet created but failed to encrypt."); | 
| 428 | 0 |             status = DatabaseStatus::FAILED_ENCRYPT; | 
| 429 | 0 |             return nullptr; | 
| 430 | 0 |         } | 
| 431 | 0 |         if (!create_blank) { | 
| 432 |  |             // Unlock the wallet | 
| 433 | 0 |             if (!wallet->Unlock(passphrase)) { | 
| 434 | 0 |                 error = Untranslated("Error: Wallet was encrypted but could not be unlocked"); | 
| 435 | 0 |                 status = DatabaseStatus::FAILED_ENCRYPT; | 
| 436 | 0 |                 return nullptr; | 
| 437 | 0 |             } | 
| 438 |  |  | 
| 439 |  |             // Set a seed for the wallet | 
| 440 | 0 |             { | 
| 441 | 0 |                 LOCK(wallet->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 442 | 0 |                 wallet->SetupDescriptorScriptPubKeyMans(); | 
| 443 | 0 |             } | 
| 444 |  |  | 
| 445 |  |             // Relock the wallet | 
| 446 | 0 |             wallet->Lock(); | 
| 447 | 0 |         } | 
| 448 | 0 |     } | 
| 449 |  |  | 
| 450 | 0 |     NotifyWalletLoaded(context, wallet); | 
| 451 | 0 |     AddWallet(context, wallet); | 
| 452 | 0 |     wallet->postInitProcess(); | 
| 453 |  |  | 
| 454 |  |     // Write the wallet settings | 
| 455 | 0 |     UpdateWalletSetting(*context.chain, name, load_on_start, warnings); | 
| 456 |  | 
 | 
| 457 | 0 |     status = DatabaseStatus::SUCCESS; | 
| 458 | 0 |     return wallet; | 
| 459 | 0 | } | 
| 460 |  |  | 
| 461 |  | // Re-creates wallet from the backup file by renaming and moving it into the wallet's directory. | 
| 462 |  | // If 'load_after_restore=true', the wallet object will be fully initialized and appended to the context. | 
| 463 |  | 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, bool load_after_restore) | 
| 464 | 0 | { | 
| 465 | 0 |     DatabaseOptions options; | 
| 466 | 0 |     ReadDatabaseArgs(*context.args, options); | 
| 467 | 0 |     options.require_existing = true; | 
| 468 |  | 
 | 
| 469 | 0 |     const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::u8path(wallet_name)); | 
| 470 | 0 |     auto wallet_file = wallet_path / "wallet.dat"; | 
| 471 | 0 |     std::shared_ptr<CWallet> wallet; | 
| 472 |  | 
 | 
| 473 | 0 |     try { | 
| 474 | 0 |         if (!fs::exists(backup_file)) { | 
| 475 | 0 |             error = Untranslated("Backup file does not exist"); | 
| 476 | 0 |             status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE; | 
| 477 | 0 |             return nullptr; | 
| 478 | 0 |         } | 
| 479 |  |  | 
| 480 | 0 |         if (fs::exists(wallet_path) || !TryCreateDirectories(wallet_path)) { | 
| 481 | 0 |             error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(wallet_path)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 482 | 0 |             status = DatabaseStatus::FAILED_ALREADY_EXISTS; | 
| 483 | 0 |             return nullptr; | 
| 484 | 0 |         } | 
| 485 |  |  | 
| 486 | 0 |         fs::copy_file(backup_file, wallet_file, fs::copy_options::none); | 
| 487 |  | 
 | 
| 488 | 0 |         if (load_after_restore) { | 
| 489 | 0 |             wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings); | 
| 490 | 0 |         } | 
| 491 | 0 |     } catch (const std::exception& e) { | 
| 492 | 0 |         assert(!wallet); | 
| 493 | 0 |         if (!error.empty()) error += Untranslated("\n"); | 
| 494 | 0 |         error += Untranslated(strprintf("Unexpected exception: %s", e.what()));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 495 | 0 |     } | 
| 496 |  |  | 
| 497 |  |     // Remove created wallet path only when loading fails | 
| 498 | 0 |     if (load_after_restore && !wallet) { | 
| 499 | 0 |         fs::remove_all(wallet_path); | 
| 500 | 0 |     } | 
| 501 |  | 
 | 
| 502 | 0 |     return wallet; | 
| 503 | 0 | } | 
| 504 |  |  | 
| 505 |  | /** @defgroup mapWallet | 
| 506 |  |  * | 
| 507 |  |  * @{ | 
| 508 |  |  */ | 
| 509 |  |  | 
| 510 |  | const CWalletTx* CWallet::GetWalletTx(const Txid& hash) const | 
| 511 | 0 | { | 
| 512 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 513 | 0 |     const auto it = mapWallet.find(hash); | 
| 514 | 0 |     if (it == mapWallet.end()) | 
| 515 | 0 |         return nullptr; | 
| 516 | 0 |     return &(it->second); | 
| 517 | 0 | } | 
| 518 |  |  | 
| 519 |  | void CWallet::UpgradeDescriptorCache() | 
| 520 | 0 | { | 
| 521 | 0 |     if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) { | 
| 522 | 0 |         return; | 
| 523 | 0 |     } | 
| 524 |  |  | 
| 525 | 0 |     for (ScriptPubKeyMan* spkm : GetAllScriptPubKeyMans()) { | 
| 526 | 0 |         DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm); | 
| 527 | 0 |         desc_spkm->UpgradeDescriptorCache(); | 
| 528 | 0 |     } | 
| 529 | 0 |     SetWalletFlag(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED); | 
| 530 | 0 | } | 
| 531 |  |  | 
| 532 |  | /* Given a wallet passphrase string and an unencrypted master key, determine the proper key | 
| 533 |  |  * derivation parameters (should take at least 100ms) and encrypt the master key. */ | 
| 534 |  | static bool EncryptMasterKey(const SecureString& wallet_passphrase, const CKeyingMaterial& plain_master_key, CMasterKey& master_key) | 
| 535 | 0 | { | 
| 536 | 0 |     constexpr MillisecondsDouble target{100}; | 
| 537 | 0 |     auto start{SteadyClock::now()}; | 
| 538 | 0 |     CCrypter crypter; | 
| 539 |  | 
 | 
| 540 | 0 |     crypter.SetKeyFromPassphrase(wallet_passphrase, master_key.vchSalt, master_key.nDeriveIterations, master_key.nDerivationMethod); | 
| 541 | 0 |     master_key.nDeriveIterations = static_cast<unsigned int>(master_key.nDeriveIterations * target / (SteadyClock::now() - start)); | 
| 542 |  | 
 | 
| 543 | 0 |     start = SteadyClock::now(); | 
| 544 | 0 |     crypter.SetKeyFromPassphrase(wallet_passphrase, master_key.vchSalt, master_key.nDeriveIterations, master_key.nDerivationMethod); | 
| 545 | 0 |     master_key.nDeriveIterations = (master_key.nDeriveIterations + static_cast<unsigned int>(master_key.nDeriveIterations * target / (SteadyClock::now() - start))) / 2; | 
| 546 |  | 
 | 
| 547 | 0 |     if (master_key.nDeriveIterations < CMasterKey::DEFAULT_DERIVE_ITERATIONS) { | 
| 548 | 0 |         master_key.nDeriveIterations = CMasterKey::DEFAULT_DERIVE_ITERATIONS; | 
| 549 | 0 |     } | 
| 550 |  | 
 | 
| 551 | 0 |     if (!crypter.SetKeyFromPassphrase(wallet_passphrase, master_key.vchSalt, master_key.nDeriveIterations, master_key.nDerivationMethod)) { | 
| 552 | 0 |         return false; | 
| 553 | 0 |     } | 
| 554 | 0 |     if (!crypter.Encrypt(plain_master_key, master_key.vchCryptedKey)) { | 
| 555 | 0 |         return false; | 
| 556 | 0 |     } | 
| 557 |  |  | 
| 558 | 0 |     return true; | 
| 559 | 0 | } | 
| 560 |  |  | 
| 561 |  | static bool DecryptMasterKey(const SecureString& wallet_passphrase, const CMasterKey& master_key, CKeyingMaterial& plain_master_key) | 
| 562 | 0 | { | 
| 563 | 0 |     CCrypter crypter; | 
| 564 | 0 |     if (!crypter.SetKeyFromPassphrase(wallet_passphrase, master_key.vchSalt, master_key.nDeriveIterations, master_key.nDerivationMethod)) { | 
| 565 | 0 |         return false; | 
| 566 | 0 |     } | 
| 567 | 0 |     if (!crypter.Decrypt(master_key.vchCryptedKey, plain_master_key)) { | 
| 568 | 0 |         return false; | 
| 569 | 0 |     } | 
| 570 |  |  | 
| 571 | 0 |     return true; | 
| 572 | 0 | } | 
| 573 |  |  | 
| 574 |  | bool CWallet::Unlock(const SecureString& strWalletPassphrase) | 
| 575 | 0 | { | 
| 576 | 0 |     CKeyingMaterial plain_master_key; | 
| 577 |  | 
 | 
| 578 | 0 |     { | 
| 579 | 0 |         LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 580 | 0 |         for (const auto& [_, master_key] : mapMasterKeys) | 
| 581 | 0 |         { | 
| 582 | 0 |             if (!DecryptMasterKey(strWalletPassphrase, master_key, plain_master_key)) { | 
| 583 | 0 |                 continue; // try another master key | 
| 584 | 0 |             } | 
| 585 | 0 |             if (Unlock(plain_master_key)) { | 
| 586 |  |                 // Now that we've unlocked, upgrade the descriptor cache | 
| 587 | 0 |                 UpgradeDescriptorCache(); | 
| 588 | 0 |                 return true; | 
| 589 | 0 |             } | 
| 590 | 0 |         } | 
| 591 | 0 |     } | 
| 592 | 0 |     return false; | 
| 593 | 0 | } | 
| 594 |  |  | 
| 595 |  | bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) | 
| 596 | 0 | { | 
| 597 | 0 |     bool fWasLocked = IsLocked(); | 
| 598 |  | 
 | 
| 599 | 0 |     { | 
| 600 | 0 |         LOCK2(m_relock_mutex, cs_wallet); | Line | Count | Source |  | 261 | 0 |     UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \ |  | 262 | 0 |     UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__) | 
 | 
| 601 | 0 |         Lock(); | 
| 602 |  | 
 | 
| 603 | 0 |         CKeyingMaterial plain_master_key; | 
| 604 | 0 |         for (auto& [master_key_id, master_key] : mapMasterKeys) | 
| 605 | 0 |         { | 
| 606 | 0 |             if (!DecryptMasterKey(strOldWalletPassphrase, master_key, plain_master_key)) { | 
| 607 | 0 |                 return false; | 
| 608 | 0 |             } | 
| 609 | 0 |             if (Unlock(plain_master_key)) | 
| 610 | 0 |             { | 
| 611 | 0 |                 if (!EncryptMasterKey(strNewWalletPassphrase, plain_master_key, master_key)) { | 
| 612 | 0 |                     return false; | 
| 613 | 0 |                 } | 
| 614 | 0 |                 WalletLogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", master_key.nDeriveIterations); | 
| 615 |  | 
 | 
| 616 | 0 |                 WalletBatch(GetDatabase()).WriteMasterKey(master_key_id, master_key); | 
| 617 | 0 |                 if (fWasLocked) | 
| 618 | 0 |                     Lock(); | 
| 619 | 0 |                 return true; | 
| 620 | 0 |             } | 
| 621 | 0 |         } | 
| 622 | 0 |     } | 
| 623 |  |  | 
| 624 | 0 |     return false; | 
| 625 | 0 | } | 
| 626 |  |  | 
| 627 |  | void CWallet::SetLastBlockProcessedInMem(int block_height, uint256 block_hash) | 
| 628 | 0 | { | 
| 629 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 630 |  | 
 | 
| 631 | 0 |     m_last_block_processed = block_hash; | 
| 632 | 0 |     m_last_block_processed_height = block_height; | 
| 633 | 0 | } | 
| 634 |  |  | 
| 635 |  | void CWallet::SetLastBlockProcessed(int block_height, uint256 block_hash) | 
| 636 | 0 | { | 
| 637 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 638 |  | 
 | 
| 639 | 0 |     SetLastBlockProcessedInMem(block_height, block_hash); | 
| 640 | 0 |     WriteBestBlock(); | 
| 641 | 0 | } | 
| 642 |  |  | 
| 643 |  | std::set<Txid> CWallet::GetConflicts(const Txid& txid) const | 
| 644 | 0 | { | 
| 645 | 0 |     std::set<Txid> result; | 
| 646 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 647 |  | 
 | 
| 648 | 0 |     const auto it = mapWallet.find(txid); | 
| 649 | 0 |     if (it == mapWallet.end()) | 
| 650 | 0 |         return result; | 
| 651 | 0 |     const CWalletTx& wtx = it->second; | 
| 652 |  | 
 | 
| 653 | 0 |     std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; | 
| 654 |  | 
 | 
| 655 | 0 |     for (const CTxIn& txin : wtx.tx->vin) | 
| 656 | 0 |     { | 
| 657 | 0 |         if (mapTxSpends.count(txin.prevout) <= 1) | 
| 658 | 0 |             continue;  // No conflict if zero or one spends | 
| 659 | 0 |         range = mapTxSpends.equal_range(txin.prevout); | 
| 660 | 0 |         for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) | 
| 661 | 0 |             result.insert(_it->second); | 
| 662 | 0 |     } | 
| 663 | 0 |     return result; | 
| 664 | 0 | } | 
| 665 |  |  | 
| 666 |  | bool CWallet::HasWalletSpend(const CTransactionRef& tx) const | 
| 667 | 0 | { | 
| 668 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 669 | 0 |     const Txid& txid = tx->GetHash(); | 
| 670 | 0 |     for (unsigned int i = 0; i < tx->vout.size(); ++i) { | 
| 671 | 0 |         if (IsSpent(COutPoint(txid, i))) { | 
| 672 | 0 |             return true; | 
| 673 | 0 |         } | 
| 674 | 0 |     } | 
| 675 | 0 |     return false; | 
| 676 | 0 | } | 
| 677 |  |  | 
| 678 |  | void CWallet::Close() | 
| 679 | 0 | { | 
| 680 | 0 |     GetDatabase().Close(); | 
| 681 | 0 | } | 
| 682 |  |  | 
| 683 |  | void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range) | 
| 684 | 0 | { | 
| 685 |  |     // We want all the wallet transactions in range to have the same metadata as | 
| 686 |  |     // the oldest (smallest nOrderPos). | 
| 687 |  |     // So: find smallest nOrderPos: | 
| 688 |  | 
 | 
| 689 | 0 |     int nMinOrderPos = std::numeric_limits<int>::max(); | 
| 690 | 0 |     const CWalletTx* copyFrom = nullptr; | 
| 691 | 0 |     for (TxSpends::iterator it = range.first; it != range.second; ++it) { | 
| 692 | 0 |         const CWalletTx* wtx = &mapWallet.at(it->second); | 
| 693 | 0 |         if (wtx->nOrderPos < nMinOrderPos) { | 
| 694 | 0 |             nMinOrderPos = wtx->nOrderPos; | 
| 695 | 0 |             copyFrom = wtx; | 
| 696 | 0 |         } | 
| 697 | 0 |     } | 
| 698 |  | 
 | 
| 699 | 0 |     if (!copyFrom) { | 
| 700 | 0 |         return; | 
| 701 | 0 |     } | 
| 702 |  |  | 
| 703 |  |     // Now copy data from copyFrom to rest: | 
| 704 | 0 |     for (TxSpends::iterator it = range.first; it != range.second; ++it) | 
| 705 | 0 |     { | 
| 706 | 0 |         const Txid& hash = it->second; | 
| 707 | 0 |         CWalletTx* copyTo = &mapWallet.at(hash); | 
| 708 | 0 |         if (copyFrom == copyTo) continue; | 
| 709 | 0 |         assert(copyFrom && "Oldest wallet transaction in range assumed to have been found."); | 
| 710 | 0 |         if (!copyFrom->IsEquivalentTo(*copyTo)) continue; | 
| 711 | 0 |         copyTo->mapValue = copyFrom->mapValue; | 
| 712 | 0 |         copyTo->vOrderForm = copyFrom->vOrderForm; | 
| 713 |  |         // nTimeReceived not copied on purpose | 
| 714 | 0 |         copyTo->nTimeSmart = copyFrom->nTimeSmart; | 
| 715 |  |         // nOrderPos not copied on purpose | 
| 716 |  |         // cached members not copied on purpose | 
| 717 | 0 |     } | 
| 718 | 0 | } | 
| 719 |  |  | 
| 720 |  | /** | 
| 721 |  |  * Outpoint is spent if any non-conflicted transaction | 
| 722 |  |  * spends it: | 
| 723 |  |  */ | 
| 724 |  | bool CWallet::IsSpent(const COutPoint& outpoint) const | 
| 725 | 0 | { | 
| 726 | 0 |     std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; | 
| 727 | 0 |     range = mapTxSpends.equal_range(outpoint); | 
| 728 |  | 
 | 
| 729 | 0 |     for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { | 
| 730 | 0 |         const Txid& txid = it->second; | 
| 731 | 0 |         const auto mit = mapWallet.find(txid); | 
| 732 | 0 |         if (mit != mapWallet.end()) { | 
| 733 | 0 |             const auto& wtx = mit->second; | 
| 734 | 0 |             if (!wtx.isAbandoned() && !wtx.isBlockConflicted() && !wtx.isMempoolConflicted()) | 
| 735 | 0 |                 return true; // Spent | 
| 736 | 0 |         } | 
| 737 | 0 |     } | 
| 738 | 0 |     return false; | 
| 739 | 0 | } | 
| 740 |  |  | 
| 741 |  | void CWallet::AddToSpends(const COutPoint& outpoint, const Txid& txid) | 
| 742 | 0 | { | 
| 743 | 0 |     mapTxSpends.insert(std::make_pair(outpoint, txid)); | 
| 744 |  | 
 | 
| 745 | 0 |     UnlockCoin(outpoint); | 
| 746 |  | 
 | 
| 747 | 0 |     std::pair<TxSpends::iterator, TxSpends::iterator> range; | 
| 748 | 0 |     range = mapTxSpends.equal_range(outpoint); | 
| 749 | 0 |     SyncMetaData(range); | 
| 750 | 0 | } | 
| 751 |  |  | 
| 752 |  |  | 
| 753 |  | void CWallet::AddToSpends(const CWalletTx& wtx) | 
| 754 | 0 | { | 
| 755 | 0 |     if (wtx.IsCoinBase()) // Coinbases don't spend anything! | 
| 756 | 0 |         return; | 
| 757 |  |  | 
| 758 | 0 |     for (const CTxIn& txin : wtx.tx->vin) | 
| 759 | 0 |         AddToSpends(txin.prevout, wtx.GetHash()); | 
| 760 | 0 | } | 
| 761 |  |  | 
| 762 |  | bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) | 
| 763 | 0 | { | 
| 764 |  |     // Only descriptor wallets can be encrypted | 
| 765 | 0 |     Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 766 |  | 
 | 
| 767 | 0 |     if (IsCrypted()) | 
| 768 | 0 |         return false; | 
| 769 |  |  | 
| 770 | 0 |     CKeyingMaterial plain_master_key; | 
| 771 |  | 
 | 
| 772 | 0 |     plain_master_key.resize(WALLET_CRYPTO_KEY_SIZE); | 
| 773 | 0 |     GetStrongRandBytes(plain_master_key); | 
| 774 |  | 
 | 
| 775 | 0 |     CMasterKey master_key; | 
| 776 |  | 
 | 
| 777 | 0 |     master_key.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); | 
| 778 | 0 |     GetStrongRandBytes(master_key.vchSalt); | 
| 779 |  | 
 | 
| 780 | 0 |     if (!EncryptMasterKey(strWalletPassphrase, plain_master_key, master_key)) { | 
| 781 | 0 |         return false; | 
| 782 | 0 |     } | 
| 783 | 0 |     WalletLogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", master_key.nDeriveIterations); | 
| 784 |  | 
 | 
| 785 | 0 |     { | 
| 786 | 0 |         LOCK2(m_relock_mutex, cs_wallet); | Line | Count | Source |  | 261 | 0 |     UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \ |  | 262 | 0 |     UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__) | 
 | 
| 787 | 0 |         mapMasterKeys[++nMasterKeyMaxID] = master_key; | 
| 788 | 0 |         WalletBatch* encrypted_batch = new WalletBatch(GetDatabase()); | 
| 789 | 0 |         if (!encrypted_batch->TxnBegin()) { | 
| 790 | 0 |             delete encrypted_batch; | 
| 791 | 0 |             encrypted_batch = nullptr; | 
| 792 | 0 |             return false; | 
| 793 | 0 |         } | 
| 794 | 0 |         encrypted_batch->WriteMasterKey(nMasterKeyMaxID, master_key); | 
| 795 |  | 
 | 
| 796 | 0 |         for (const auto& spk_man_pair : m_spk_managers) { | 
| 797 | 0 |             auto spk_man = spk_man_pair.second.get(); | 
| 798 | 0 |             if (!spk_man->Encrypt(plain_master_key, encrypted_batch)) { | 
| 799 | 0 |                 encrypted_batch->TxnAbort(); | 
| 800 | 0 |                 delete encrypted_batch; | 
| 801 | 0 |                 encrypted_batch = nullptr; | 
| 802 |  |                 // We now probably have half of our keys encrypted in memory, and half not... | 
| 803 |  |                 // die and let the user reload the unencrypted wallet. | 
| 804 | 0 |                 assert(false); | 
| 805 | 0 |             } | 
| 806 | 0 |         } | 
| 807 |  |  | 
| 808 | 0 |         if (!encrypted_batch->TxnCommit()) { | 
| 809 | 0 |             delete encrypted_batch; | 
| 810 | 0 |             encrypted_batch = nullptr; | 
| 811 |  |             // We now have keys encrypted in memory, but not on disk... | 
| 812 |  |             // die to avoid confusion and let the user reload the unencrypted wallet. | 
| 813 | 0 |             assert(false); | 
| 814 | 0 |         } | 
| 815 |  |  | 
| 816 | 0 |         delete encrypted_batch; | 
| 817 | 0 |         encrypted_batch = nullptr; | 
| 818 |  | 
 | 
| 819 | 0 |         Lock(); | 
| 820 | 0 |         Unlock(strWalletPassphrase); | 
| 821 |  |  | 
| 822 |  |         // Make new descriptors with a new seed | 
| 823 | 0 |         if (!IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) { | 
| 824 | 0 |             SetupDescriptorScriptPubKeyMans(); | 
| 825 | 0 |         } | 
| 826 | 0 |         Lock(); | 
| 827 |  |  | 
| 828 |  |         // Need to completely rewrite the wallet file; if we don't, the database might keep | 
| 829 |  |         // bits of the unencrypted private key in slack space in the database file. | 
| 830 | 0 |         GetDatabase().Rewrite(); | 
| 831 | 0 |     } | 
| 832 | 0 |     NotifyStatusChanged(this); | 
| 833 |  | 
 | 
| 834 | 0 |     return true; | 
| 835 | 0 | } | 
| 836 |  |  | 
| 837 |  | DBErrors CWallet::ReorderTransactions() | 
| 838 | 0 | { | 
| 839 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 840 | 0 |     WalletBatch batch(GetDatabase()); | 
| 841 |  |  | 
| 842 |  |     // Old wallets didn't have any defined order for transactions | 
| 843 |  |     // Probably a bad idea to change the output of this | 
| 844 |  |  | 
| 845 |  |     // First: get all CWalletTx into a sorted-by-time multimap. | 
| 846 | 0 |     typedef std::multimap<int64_t, CWalletTx*> TxItems; | 
| 847 | 0 |     TxItems txByTime; | 
| 848 |  | 
 | 
| 849 | 0 |     for (auto& entry : mapWallet) | 
| 850 | 0 |     { | 
| 851 | 0 |         CWalletTx* wtx = &entry.second; | 
| 852 | 0 |         txByTime.insert(std::make_pair(wtx->nTimeReceived, wtx)); | 
| 853 | 0 |     } | 
| 854 |  | 
 | 
| 855 | 0 |     nOrderPosNext = 0; | 
| 856 | 0 |     std::vector<int64_t> nOrderPosOffsets; | 
| 857 | 0 |     for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) | 
| 858 | 0 |     { | 
| 859 | 0 |         CWalletTx *const pwtx = (*it).second; | 
| 860 | 0 |         int64_t& nOrderPos = pwtx->nOrderPos; | 
| 861 |  | 
 | 
| 862 | 0 |         if (nOrderPos == -1) | 
| 863 | 0 |         { | 
| 864 | 0 |             nOrderPos = nOrderPosNext++; | 
| 865 | 0 |             nOrderPosOffsets.push_back(nOrderPos); | 
| 866 |  | 
 | 
| 867 | 0 |             if (!batch.WriteTx(*pwtx)) | 
| 868 | 0 |                 return DBErrors::LOAD_FAIL; | 
| 869 | 0 |         } | 
| 870 | 0 |         else | 
| 871 | 0 |         { | 
| 872 | 0 |             int64_t nOrderPosOff = 0; | 
| 873 | 0 |             for (const int64_t& nOffsetStart : nOrderPosOffsets) | 
| 874 | 0 |             { | 
| 875 | 0 |                 if (nOrderPos >= nOffsetStart) | 
| 876 | 0 |                     ++nOrderPosOff; | 
| 877 | 0 |             } | 
| 878 | 0 |             nOrderPos += nOrderPosOff; | 
| 879 | 0 |             nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); | 
| 880 |  | 
 | 
| 881 | 0 |             if (!nOrderPosOff) | 
| 882 | 0 |                 continue; | 
| 883 |  |  | 
| 884 |  |             // Since we're changing the order, write it back | 
| 885 | 0 |             if (!batch.WriteTx(*pwtx)) | 
| 886 | 0 |                 return DBErrors::LOAD_FAIL; | 
| 887 | 0 |         } | 
| 888 | 0 |     } | 
| 889 | 0 |     batch.WriteOrderPosNext(nOrderPosNext); | 
| 890 |  | 
 | 
| 891 | 0 |     return DBErrors::LOAD_OK; | 
| 892 | 0 | } | 
| 893 |  |  | 
| 894 |  | int64_t CWallet::IncOrderPosNext(WalletBatch* batch) | 
| 895 | 0 | { | 
| 896 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 897 | 0 |     int64_t nRet = nOrderPosNext++; | 
| 898 | 0 |     if (batch) { | 
| 899 | 0 |         batch->WriteOrderPosNext(nOrderPosNext); | 
| 900 | 0 |     } else { | 
| 901 | 0 |         WalletBatch(GetDatabase()).WriteOrderPosNext(nOrderPosNext); | 
| 902 | 0 |     } | 
| 903 | 0 |     return nRet; | 
| 904 | 0 | } | 
| 905 |  |  | 
| 906 |  | void CWallet::MarkDirty() | 
| 907 | 0 | { | 
| 908 | 0 |     { | 
| 909 | 0 |         LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 910 | 0 |         for (auto& [_, wtx] : mapWallet) | 
| 911 | 0 |             wtx.MarkDirty(); | 
| 912 | 0 |     } | 
| 913 | 0 | } | 
| 914 |  |  | 
| 915 |  | bool CWallet::MarkReplaced(const Txid& originalHash, const Txid& newHash) | 
| 916 | 0 | { | 
| 917 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 918 |  | 
 | 
| 919 | 0 |     auto mi = mapWallet.find(originalHash); | 
| 920 |  |  | 
| 921 |  |     // There is a bug if MarkReplaced is not called on an existing wallet transaction. | 
| 922 | 0 |     assert(mi != mapWallet.end()); | 
| 923 |  |  | 
| 924 | 0 |     CWalletTx& wtx = (*mi).second; | 
| 925 |  |  | 
| 926 |  |     // Ensure for now that we're not overwriting data | 
| 927 | 0 |     assert(wtx.mapValue.count("replaced_by_txid") == 0); | 
| 928 |  |  | 
| 929 | 0 |     wtx.mapValue["replaced_by_txid"] = newHash.ToString(); | 
| 930 |  |  | 
| 931 |  |     // Refresh mempool status without waiting for transactionRemovedFromMempool or transactionAddedToMempool | 
| 932 | 0 |     RefreshMempoolStatus(wtx, chain()); | 
| 933 |  | 
 | 
| 934 | 0 |     WalletBatch batch(GetDatabase()); | 
| 935 |  | 
 | 
| 936 | 0 |     bool success = true; | 
| 937 | 0 |     if (!batch.WriteTx(wtx)) { | 
| 938 | 0 |         WalletLogPrintf("%s: Updating batch tx %s failed\n", __func__, wtx.GetHash().ToString()); | 
| 939 | 0 |         success = false; | 
| 940 | 0 |     } | 
| 941 |  | 
 | 
| 942 | 0 |     NotifyTransactionChanged(originalHash, CT_UPDATED); | 
| 943 |  | 
 | 
| 944 | 0 |     return success; | 
| 945 | 0 | } | 
| 946 |  |  | 
| 947 |  | void CWallet::SetSpentKeyState(WalletBatch& batch, const Txid& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) | 
| 948 | 0 | { | 
| 949 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 950 | 0 |     const CWalletTx* srctx = GetWalletTx(hash); | 
| 951 | 0 |     if (!srctx) return; | 
| 952 |  |  | 
| 953 | 0 |     CTxDestination dst; | 
| 954 | 0 |     if (ExtractDestination(srctx->tx->vout[n].scriptPubKey, dst)) { | 
| 955 | 0 |         if (IsMine(dst)) { | 
| 956 | 0 |             if (used != IsAddressPreviouslySpent(dst)) { | 
| 957 | 0 |                 if (used) { | 
| 958 | 0 |                     tx_destinations.insert(dst); | 
| 959 | 0 |                 } | 
| 960 | 0 |                 SetAddressPreviouslySpent(batch, dst, used); | 
| 961 | 0 |             } | 
| 962 | 0 |         } | 
| 963 | 0 |     } | 
| 964 | 0 | } | 
| 965 |  |  | 
| 966 |  | bool CWallet::IsSpentKey(const CScript& scriptPubKey) const | 
| 967 | 0 | { | 
| 968 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 969 | 0 |     CTxDestination dest; | 
| 970 | 0 |     if (!ExtractDestination(scriptPubKey, dest)) { | 
| 971 | 0 |         return false; | 
| 972 | 0 |     } | 
| 973 | 0 |     if (IsAddressPreviouslySpent(dest)) { | 
| 974 | 0 |         return true; | 
| 975 | 0 |     } | 
| 976 | 0 |     return false; | 
| 977 | 0 | } | 
| 978 |  |  | 
| 979 |  | CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const UpdateWalletTxFn& update_wtx, bool rescanning_old_block) | 
| 980 | 0 | { | 
| 981 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 982 |  | 
 | 
| 983 | 0 |     WalletBatch batch(GetDatabase()); | 
| 984 |  | 
 | 
| 985 | 0 |     Txid hash = tx->GetHash(); | 
| 986 |  | 
 | 
| 987 | 0 |     if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { | 
| 988 |  |         // Mark used destinations | 
| 989 | 0 |         std::set<CTxDestination> tx_destinations; | 
| 990 |  | 
 | 
| 991 | 0 |         for (const CTxIn& txin : tx->vin) { | 
| 992 | 0 |             const COutPoint& op = txin.prevout; | 
| 993 | 0 |             SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations); | 
| 994 | 0 |         } | 
| 995 |  | 
 | 
| 996 | 0 |         MarkDestinationsDirty(tx_destinations); | 
| 997 | 0 |     } | 
| 998 |  |  | 
| 999 |  |     // Inserts only if not already there, returns tx inserted or tx found | 
| 1000 | 0 |     auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(tx, state)); | 
| 1001 | 0 |     CWalletTx& wtx = (*ret.first).second; | 
| 1002 | 0 |     bool fInsertedNew = ret.second; | 
| 1003 | 0 |     bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew); | 
| 1004 | 0 |     if (fInsertedNew) { | 
| 1005 | 0 |         wtx.nTimeReceived = GetTime(); | 
| 1006 | 0 |         wtx.nOrderPos = IncOrderPosNext(&batch); | 
| 1007 | 0 |         wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); | 
| 1008 | 0 |         wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block); | 
| 1009 | 0 |         AddToSpends(wtx); | 
| 1010 |  |  | 
| 1011 |  |         // Update birth time when tx time is older than it. | 
| 1012 | 0 |         MaybeUpdateBirthTime(wtx.GetTxTime()); | 
| 1013 | 0 |     } | 
| 1014 |  | 
 | 
| 1015 | 0 |     if (!fInsertedNew) | 
| 1016 | 0 |     { | 
| 1017 | 0 |         if (state.index() != wtx.m_state.index()) { | 
| 1018 | 0 |             wtx.m_state = state; | 
| 1019 | 0 |             fUpdated = true; | 
| 1020 | 0 |         } else { | 
| 1021 | 0 |             assert(TxStateSerializedIndex(wtx.m_state) == TxStateSerializedIndex(state)); | 
| 1022 | 0 |             assert(TxStateSerializedBlockHash(wtx.m_state) == TxStateSerializedBlockHash(state)); | 
| 1023 | 0 |         } | 
| 1024 |  |         // If we have a witness-stripped version of this transaction, and we | 
| 1025 |  |         // see a new version with a witness, then we must be upgrading a pre-segwit | 
| 1026 |  |         // wallet.  Store the new version of the transaction with the witness, | 
| 1027 |  |         // as the stripped-version must be invalid. | 
| 1028 |  |         // TODO: Store all versions of the transaction, instead of just one. | 
| 1029 | 0 |         if (tx->HasWitness() && !wtx.tx->HasWitness()) { | 
| 1030 | 0 |             wtx.SetTx(tx); | 
| 1031 | 0 |             fUpdated = true; | 
| 1032 | 0 |         } | 
| 1033 | 0 |     } | 
| 1034 |  |  | 
| 1035 |  |     // Mark inactive coinbase transactions and their descendants as abandoned | 
| 1036 | 0 |     if (wtx.IsCoinBase() && wtx.isInactive()) { | 
| 1037 | 0 |         std::vector<CWalletTx*> txs{&wtx}; | 
| 1038 |  | 
 | 
| 1039 | 0 |         TxStateInactive inactive_state = TxStateInactive{/*abandoned=*/true}; | 
| 1040 |  | 
 | 
| 1041 | 0 |         while (!txs.empty()) { | 
| 1042 | 0 |             CWalletTx* desc_tx = txs.back(); | 
| 1043 | 0 |             txs.pop_back(); | 
| 1044 | 0 |             desc_tx->m_state = inactive_state; | 
| 1045 |  |             // Break caches since we have changed the state | 
| 1046 | 0 |             desc_tx->MarkDirty(); | 
| 1047 | 0 |             batch.WriteTx(*desc_tx); | 
| 1048 | 0 |             MarkInputsDirty(desc_tx->tx); | 
| 1049 | 0 |             for (unsigned int i = 0; i < desc_tx->tx->vout.size(); ++i) { | 
| 1050 | 0 |                 COutPoint outpoint(desc_tx->GetHash(), i); | 
| 1051 | 0 |                 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(outpoint); | 
| 1052 | 0 |                 for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { | 
| 1053 | 0 |                     const auto wit = mapWallet.find(it->second); | 
| 1054 | 0 |                     if (wit != mapWallet.end()) { | 
| 1055 | 0 |                         txs.push_back(&wit->second); | 
| 1056 | 0 |                     } | 
| 1057 | 0 |                 } | 
| 1058 | 0 |             } | 
| 1059 | 0 |         } | 
| 1060 | 0 |     } | 
| 1061 |  |  | 
| 1062 |  |     //// debug print | 
| 1063 | 0 |     WalletLogPrintf("AddToWallet %s  %s%s %s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""), TxStateString(state)); | 
| 1064 |  |  | 
| 1065 |  |     // Write to disk | 
| 1066 | 0 |     if (fInsertedNew || fUpdated) | 
| 1067 | 0 |         if (!batch.WriteTx(wtx)) | 
| 1068 | 0 |             return nullptr; | 
| 1069 |  |  | 
| 1070 |  |     // Break debit/credit balance caches: | 
| 1071 | 0 |     wtx.MarkDirty(); | 
| 1072 |  |  | 
| 1073 |  |     // Cache the outputs that belong to the wallet | 
| 1074 | 0 |     RefreshTXOsFromTx(wtx); | 
| 1075 |  |  | 
| 1076 |  |     // Notify UI of new or updated transaction | 
| 1077 | 0 |     NotifyTransactionChanged(hash, fInsertedNew ? CT_NEW : CT_UPDATED); | 
| 1078 |  | 
 | 
| 1079 | 0 | #if HAVE_SYSTEM | 
| 1080 |  |     // notify an external script when a wallet transaction comes in or is updated | 
| 1081 | 0 |     std::string strCmd = m_notify_tx_changed_script; | 
| 1082 |  | 
 | 
| 1083 | 0 |     if (!strCmd.empty()) | 
| 1084 | 0 |     { | 
| 1085 | 0 |         ReplaceAll(strCmd, "%s", hash.GetHex()); | 
| 1086 | 0 |         if (auto* conf = wtx.state<TxStateConfirmed>()) | 
| 1087 | 0 |         { | 
| 1088 | 0 |             ReplaceAll(strCmd, "%b", conf->confirmed_block_hash.GetHex()); | 
| 1089 | 0 |             ReplaceAll(strCmd, "%h", ToString(conf->confirmed_block_height)); | 
| 1090 | 0 |         } else { | 
| 1091 | 0 |             ReplaceAll(strCmd, "%b", "unconfirmed"); | 
| 1092 | 0 |             ReplaceAll(strCmd, "%h", "-1"); | 
| 1093 | 0 |         } | 
| 1094 | 0 | #ifndef WIN32 | 
| 1095 |  |         // Substituting the wallet name isn't currently supported on windows | 
| 1096 |  |         // because windows shell escaping has not been implemented yet: | 
| 1097 |  |         // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875 | 
| 1098 |  |         // A few ways it could be implemented in the future are described in: | 
| 1099 |  |         // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094 | 
| 1100 | 0 |         ReplaceAll(strCmd, "%w", ShellEscape(GetName())); | 
| 1101 | 0 | #endif | 
| 1102 | 0 |         std::thread t(runCommand, strCmd); | 
| 1103 | 0 |         t.detach(); // thread runs free | 
| 1104 | 0 |     } | 
| 1105 | 0 | #endif | 
| 1106 |  | 
 | 
| 1107 | 0 |     return &wtx; | 
| 1108 | 0 | } | 
| 1109 |  |  | 
| 1110 |  | bool CWallet::LoadToWallet(const Txid& hash, const UpdateWalletTxFn& fill_wtx) | 
| 1111 | 0 | { | 
| 1112 | 0 |     const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(nullptr, TxStateInactive{})); | 
| 1113 | 0 |     CWalletTx& wtx = ins.first->second; | 
| 1114 | 0 |     if (!fill_wtx(wtx, ins.second)) { | 
| 1115 | 0 |         return false; | 
| 1116 | 0 |     } | 
| 1117 |  |     // If wallet doesn't have a chain (e.g when using bitcoin-wallet tool), | 
| 1118 |  |     // don't bother to update txn. | 
| 1119 | 0 |     if (HaveChain()) { | 
| 1120 | 0 |       wtx.updateState(chain()); | 
| 1121 | 0 |     } | 
| 1122 | 0 |     if (/* insertion took place */ ins.second) { | 
| 1123 | 0 |         wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); | 
| 1124 | 0 |     } | 
| 1125 | 0 |     AddToSpends(wtx); | 
| 1126 | 0 |     for (const CTxIn& txin : wtx.tx->vin) { | 
| 1127 | 0 |         auto it = mapWallet.find(txin.prevout.hash); | 
| 1128 | 0 |         if (it != mapWallet.end()) { | 
| 1129 | 0 |             CWalletTx& prevtx = it->second; | 
| 1130 | 0 |             if (auto* prev = prevtx.state<TxStateBlockConflicted>()) { | 
| 1131 | 0 |                 MarkConflicted(prev->conflicting_block_hash, prev->conflicting_block_height, wtx.GetHash()); | 
| 1132 | 0 |             } | 
| 1133 | 0 |         } | 
| 1134 | 0 |     } | 
| 1135 |  |  | 
| 1136 |  |     // Update birth time when tx time is older than it. | 
| 1137 | 0 |     MaybeUpdateBirthTime(wtx.GetTxTime()); | 
| 1138 |  |  | 
| 1139 |  |     // Make sure the tx outputs are known by the wallet | 
| 1140 | 0 |     RefreshTXOsFromTx(wtx); | 
| 1141 | 0 |     return true; | 
| 1142 | 0 | } | 
| 1143 |  |  | 
| 1144 |  | bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const SyncTxState& state, bool fUpdate, bool rescanning_old_block) | 
| 1145 | 0 | { | 
| 1146 | 0 |     const CTransaction& tx = *ptx; | 
| 1147 | 0 |     { | 
| 1148 | 0 |         AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1149 |  | 
 | 
| 1150 | 0 |         if (auto* conf = std::get_if<TxStateConfirmed>(&state)) { | 
| 1151 | 0 |             for (const CTxIn& txin : tx.vin) { | 
| 1152 | 0 |                 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout); | 
| 1153 | 0 |                 while (range.first != range.second) { | 
| 1154 | 0 |                     if (range.first->second != tx.GetHash()) { | 
| 1155 | 0 |                         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); | 
| 1156 | 0 |                         MarkConflicted(conf->confirmed_block_hash, conf->confirmed_block_height, range.first->second); | 
| 1157 | 0 |                     } | 
| 1158 | 0 |                     range.first++; | 
| 1159 | 0 |                 } | 
| 1160 | 0 |             } | 
| 1161 | 0 |         } | 
| 1162 |  | 
 | 
| 1163 | 0 |         bool fExisted = mapWallet.count(tx.GetHash()) != 0; | 
| 1164 | 0 |         if (fExisted && !fUpdate) return false; | 
| 1165 | 0 |         if (fExisted || IsMine(tx) || IsFromMe(tx)) | 
| 1166 | 0 |         { | 
| 1167 |  |             /* Check if any keys in the wallet keypool that were supposed to be unused | 
| 1168 |  |              * have appeared in a new transaction. If so, remove those keys from the keypool. | 
| 1169 |  |              * This can happen when restoring an old wallet backup that does not contain | 
| 1170 |  |              * the mostly recently created transactions from newer versions of the wallet. | 
| 1171 |  |              */ | 
| 1172 |  |  | 
| 1173 |  |             // loop though all outputs | 
| 1174 | 0 |             for (const CTxOut& txout: tx.vout) { | 
| 1175 | 0 |                 for (const auto& spk_man : GetScriptPubKeyMans(txout.scriptPubKey)) { | 
| 1176 | 0 |                     for (auto &dest : spk_man->MarkUnusedAddresses(txout.scriptPubKey)) { | 
| 1177 |  |                         // If internal flag is not defined try to infer it from the ScriptPubKeyMan | 
| 1178 | 0 |                         if (!dest.internal.has_value()) { | 
| 1179 | 0 |                             dest.internal = IsInternalScriptPubKeyMan(spk_man); | 
| 1180 | 0 |                         } | 
| 1181 |  |  | 
| 1182 |  |                         // skip if can't determine whether it's a receiving address or not | 
| 1183 | 0 |                         if (!dest.internal.has_value()) continue; | 
| 1184 |  |  | 
| 1185 |  |                         // If this is a receiving address and it's not in the address book yet | 
| 1186 |  |                         // (e.g. it wasn't generated on this node or we're restoring from backup) | 
| 1187 |  |                         // add it to the address book for proper transaction accounting | 
| 1188 | 0 |                         if (!*dest.internal && !FindAddressBookEntry(dest.dest, /* allow_change= */ false)) { | 
| 1189 | 0 |                             SetAddressBook(dest.dest, "", AddressPurpose::RECEIVE); | 
| 1190 | 0 |                         } | 
| 1191 | 0 |                     } | 
| 1192 | 0 |                 } | 
| 1193 | 0 |             } | 
| 1194 |  |  | 
| 1195 |  |             // Block disconnection override an abandoned tx as unconfirmed | 
| 1196 |  |             // which means user may have to call abandontransaction again | 
| 1197 | 0 |             TxState tx_state = std::visit([](auto&& s) -> TxState { return s; }, state);Unexecuted instantiation: wallet.cpp:_ZZN6wallet7CWallet24AddToWalletIfInvolvingMeERKNSt3__110shared_ptrIK12CTransactionEERKNS1_7variantIJNS_16TxStateConfirmedENS_16TxStateInMempoolENS_15TxStateInactiveEEEEbbENK3$_0clIRKS9_EENS8_IJS9_SA_NS_22TxStateBlockConflictedESB_NS_19TxStateUnrecognizedEEEEOT_Unexecuted instantiation: wallet.cpp:_ZZN6wallet7CWallet24AddToWalletIfInvolvingMeERKNSt3__110shared_ptrIK12CTransactionEERKNS1_7variantIJNS_16TxStateConfirmedENS_16TxStateInMempoolENS_15TxStateInactiveEEEEbbENK3$_0clIRKSA_EENS8_IJS9_SA_NS_22TxStateBlockConflictedESB_NS_19TxStateUnrecognizedEEEEOT_Unexecuted instantiation: wallet.cpp:_ZZN6wallet7CWallet24AddToWalletIfInvolvingMeERKNSt3__110shared_ptrIK12CTransactionEERKNS1_7variantIJNS_16TxStateConfirmedENS_16TxStateInMempoolENS_15TxStateInactiveEEEEbbENK3$_0clIRKSB_EENS8_IJS9_SA_NS_22TxStateBlockConflictedESB_NS_19TxStateUnrecognizedEEEEOT_ | 
| 1198 | 0 |             CWalletTx* wtx = AddToWallet(MakeTransactionRef(tx), tx_state, /*update_wtx=*/nullptr, rescanning_old_block); | 
| 1199 | 0 |             if (!wtx) { | 
| 1200 |  |                 // Can only be nullptr if there was a db write error (missing db, read-only db or a db engine internal writing error). | 
| 1201 |  |                 // As we only store arriving transaction in this process, and we don't want an inconsistent state, let's throw an error. | 
| 1202 | 0 |                 throw std::runtime_error("DB error adding transaction to wallet, write failed"); | 
| 1203 | 0 |             } | 
| 1204 | 0 |             return true; | 
| 1205 | 0 |         } | 
| 1206 | 0 |     } | 
| 1207 | 0 |     return false; | 
| 1208 | 0 | } | 
| 1209 |  |  | 
| 1210 |  | bool CWallet::TransactionCanBeAbandoned(const Txid& hashTx) const | 
| 1211 | 0 | { | 
| 1212 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1213 | 0 |     const CWalletTx* wtx = GetWalletTx(hashTx); | 
| 1214 | 0 |     return wtx && !wtx->isAbandoned() && GetTxDepthInMainChain(*wtx) == 0 && !wtx->InMempool(); | 
| 1215 | 0 | } | 
| 1216 |  |  | 
| 1217 |  | void CWallet::UpdateTrucSiblingConflicts(const CWalletTx& parent_wtx, const Txid& child_txid, bool add_conflict) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) | 
| 1218 | 0 | { | 
| 1219 |  |     // Find all other txs in our wallet that spend utxos from this parent | 
| 1220 |  |     // so that we can mark them as mempool-conflicted by this new tx. | 
| 1221 | 0 |     for (long unsigned int i = 0; i < parent_wtx.tx->vout.size(); i++) { | 
| 1222 | 0 |         for (auto range = mapTxSpends.equal_range(COutPoint(parent_wtx.tx->GetHash(), i)); range.first != range.second; range.first++) { | 
| 1223 | 0 |             const Txid& sibling_txid = range.first->second; | 
| 1224 |  |             // Skip the child_tx itself | 
| 1225 | 0 |             if (sibling_txid == child_txid) continue; | 
| 1226 | 0 |             RecursiveUpdateTxState(/*batch=*/nullptr, sibling_txid, [&child_txid, add_conflict](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { | 
| 1227 | 0 |                 return add_conflict ? (wtx.mempool_conflicts.insert(child_txid).second ? TxUpdate::CHANGED : TxUpdate::UNCHANGED) | 
| 1228 | 0 |                                     : (wtx.mempool_conflicts.erase(child_txid) ? TxUpdate::CHANGED : TxUpdate::UNCHANGED); | 
| 1229 | 0 |             }); | 
| 1230 | 0 |         } | 
| 1231 | 0 |     } | 
| 1232 | 0 | } | 
| 1233 |  |  | 
| 1234 |  | void CWallet::MarkInputsDirty(const CTransactionRef& tx) | 
| 1235 | 0 | { | 
| 1236 | 0 |     for (const CTxIn& txin : tx->vin) { | 
| 1237 | 0 |         auto it = mapWallet.find(txin.prevout.hash); | 
| 1238 | 0 |         if (it != mapWallet.end()) { | 
| 1239 | 0 |             it->second.MarkDirty(); | 
| 1240 | 0 |         } | 
| 1241 | 0 |     } | 
| 1242 | 0 | } | 
| 1243 |  |  | 
| 1244 |  | bool CWallet::AbandonTransaction(const Txid& hashTx) | 
| 1245 | 0 | { | 
| 1246 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1247 | 0 |     auto it = mapWallet.find(hashTx); | 
| 1248 | 0 |     assert(it != mapWallet.end()); | 
| 1249 | 0 |     return AbandonTransaction(it->second); | 
| 1250 | 0 | } | 
| 1251 |  |  | 
| 1252 |  | bool CWallet::AbandonTransaction(CWalletTx& tx) | 
| 1253 | 0 | { | 
| 1254 |  |     // Can't mark abandoned if confirmed or in mempool | 
| 1255 | 0 |     if (GetTxDepthInMainChain(tx) != 0 || tx.InMempool()) { | 
| 1256 | 0 |         return false; | 
| 1257 | 0 |     } | 
| 1258 |  |  | 
| 1259 | 0 |     auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { | 
| 1260 |  |         // If the orig tx was not in block/mempool, none of its spends can be. | 
| 1261 | 0 |         assert(!wtx.isConfirmed()); | 
| 1262 | 0 |         assert(!wtx.InMempool()); | 
| 1263 |  |         // If already conflicted or abandoned, no need to set abandoned | 
| 1264 | 0 |         if (!wtx.isBlockConflicted() && !wtx.isAbandoned()) { | 
| 1265 | 0 |             wtx.m_state = TxStateInactive{/*abandoned=*/true}; | 
| 1266 | 0 |             return TxUpdate::NOTIFY_CHANGED; | 
| 1267 | 0 |         } | 
| 1268 | 0 |         return TxUpdate::UNCHANGED; | 
| 1269 | 0 |     }; | 
| 1270 |  |  | 
| 1271 |  |     // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too. | 
| 1272 |  |     // States are not permanent, so these transactions can become unabandoned if they are re-added to the | 
| 1273 |  |     // mempool, or confirmed in a block, or conflicted. | 
| 1274 |  |     // Note: If the reorged coinbase is re-added to the main chain, the descendants that have not had their | 
| 1275 |  |     // states change will remain abandoned and will require manual broadcast if the user wants them. | 
| 1276 |  | 
 | 
| 1277 | 0 |     RecursiveUpdateTxState(tx.GetHash(), try_updating_state); | 
| 1278 |  | 
 | 
| 1279 | 0 |     return true; | 
| 1280 | 0 | } | 
| 1281 |  |  | 
| 1282 |  | void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, const Txid& hashTx) | 
| 1283 | 0 | { | 
| 1284 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1285 |  |  | 
| 1286 |  |     // If number of conflict confirms cannot be determined, this means | 
| 1287 |  |     // that the block is still unknown or not yet part of the main chain, | 
| 1288 |  |     // for example when loading the wallet during a reindex. Do nothing in that | 
| 1289 |  |     // case. | 
| 1290 | 0 |     if (m_last_block_processed_height < 0 || conflicting_height < 0) { | 
| 1291 | 0 |         return; | 
| 1292 | 0 |     } | 
| 1293 | 0 |     int conflictconfirms = (m_last_block_processed_height - conflicting_height + 1) * -1; | 
| 1294 | 0 |     if (conflictconfirms >= 0) | 
| 1295 | 0 |         return; | 
| 1296 |  |  | 
| 1297 | 0 |     auto try_updating_state = [&](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { | 
| 1298 | 0 |         if (conflictconfirms < GetTxDepthInMainChain(wtx)) { | 
| 1299 |  |             // Block is 'more conflicted' than current confirm; update. | 
| 1300 |  |             // Mark transaction as conflicted with this block. | 
| 1301 | 0 |             wtx.m_state = TxStateBlockConflicted{hashBlock, conflicting_height}; | 
| 1302 | 0 |             return TxUpdate::CHANGED; | 
| 1303 | 0 |         } | 
| 1304 | 0 |         return TxUpdate::UNCHANGED; | 
| 1305 | 0 |     }; | 
| 1306 |  |  | 
| 1307 |  |     // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too. | 
| 1308 | 0 |     RecursiveUpdateTxState(hashTx, try_updating_state); | 
| 1309 |  | 
 | 
| 1310 | 0 | } | 
| 1311 |  |  | 
| 1312 | 0 | void CWallet::RecursiveUpdateTxState(const Txid& tx_hash, const TryUpdatingStateFn& try_updating_state) { | 
| 1313 | 0 |     WalletBatch batch(GetDatabase()); | 
| 1314 | 0 |     RecursiveUpdateTxState(&batch, tx_hash, try_updating_state); | 
| 1315 | 0 | } | 
| 1316 |  |  | 
| 1317 | 0 | void CWallet::RecursiveUpdateTxState(WalletBatch* batch, const Txid& tx_hash, const TryUpdatingStateFn& try_updating_state) { | 
| 1318 | 0 |     std::set<Txid> todo; | 
| 1319 | 0 |     std::set<Txid> done; | 
| 1320 |  | 
 | 
| 1321 | 0 |     todo.insert(tx_hash); | 
| 1322 |  | 
 | 
| 1323 | 0 |     while (!todo.empty()) { | 
| 1324 | 0 |         Txid now = *todo.begin(); | 
| 1325 | 0 |         todo.erase(now); | 
| 1326 | 0 |         done.insert(now); | 
| 1327 | 0 |         auto it = mapWallet.find(now); | 
| 1328 | 0 |         assert(it != mapWallet.end()); | 
| 1329 | 0 |         CWalletTx& wtx = it->second; | 
| 1330 |  | 
 | 
| 1331 | 0 |         TxUpdate update_state = try_updating_state(wtx); | 
| 1332 | 0 |         if (update_state != TxUpdate::UNCHANGED) { | 
| 1333 | 0 |             wtx.MarkDirty(); | 
| 1334 | 0 |             if (batch) batch->WriteTx(wtx); | 
| 1335 |  |             // Iterate over all its outputs, and update those tx states as well (if applicable) | 
| 1336 | 0 |             for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) { | 
| 1337 | 0 |                 std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(COutPoint(now, i)); | 
| 1338 | 0 |                 for (TxSpends::const_iterator iter = range.first; iter != range.second; ++iter) { | 
| 1339 | 0 |                     if (!done.count(iter->second)) { | 
| 1340 | 0 |                         todo.insert(iter->second); | 
| 1341 | 0 |                     } | 
| 1342 | 0 |                 } | 
| 1343 | 0 |             } | 
| 1344 |  | 
 | 
| 1345 | 0 |             if (update_state == TxUpdate::NOTIFY_CHANGED) { | 
| 1346 | 0 |                 NotifyTransactionChanged(wtx.GetHash(), CT_UPDATED); | 
| 1347 | 0 |             } | 
| 1348 |  |  | 
| 1349 |  |             // If a transaction changes its tx state, that usually changes the balance | 
| 1350 |  |             // available of the outputs it spends. So force those to be recomputed | 
| 1351 | 0 |             MarkInputsDirty(wtx.tx); | 
| 1352 | 0 |         } | 
| 1353 | 0 |     } | 
| 1354 | 0 | } | 
| 1355 |  |  | 
| 1356 |  | bool CWallet::SyncTransaction(const CTransactionRef& ptx, const SyncTxState& state, bool update_tx, bool rescanning_old_block) | 
| 1357 | 0 | { | 
| 1358 | 0 |     if (!AddToWalletIfInvolvingMe(ptx, state, update_tx, rescanning_old_block)) | 
| 1359 | 0 |         return false; // Not one of ours | 
| 1360 |  |  | 
| 1361 |  |     // If a transaction changes 'conflicted' state, that changes the balance | 
| 1362 |  |     // available of the outputs it spends. So force those to be | 
| 1363 |  |     // recomputed, also: | 
| 1364 | 0 |     MarkInputsDirty(ptx); | 
| 1365 | 0 |     return true; | 
| 1366 | 0 | } | 
| 1367 |  |  | 
| 1368 | 0 | void CWallet::transactionAddedToMempool(const CTransactionRef& tx) { | 
| 1369 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1370 | 0 |     SyncTransaction(tx, TxStateInMempool{}); | 
| 1371 |  | 
 | 
| 1372 | 0 |     auto it = mapWallet.find(tx->GetHash()); | 
| 1373 | 0 |     if (it != mapWallet.end()) { | 
| 1374 | 0 |         RefreshMempoolStatus(it->second, chain()); | 
| 1375 | 0 |     } | 
| 1376 |  | 
 | 
| 1377 | 0 |     const Txid& txid = tx->GetHash(); | 
| 1378 |  | 
 | 
| 1379 | 0 |     for (const CTxIn& tx_in : tx->vin) { | 
| 1380 |  |         // For each wallet transaction spending this prevout.. | 
| 1381 | 0 |         for (auto range = mapTxSpends.equal_range(tx_in.prevout); range.first != range.second; range.first++) { | 
| 1382 | 0 |             const Txid& spent_id = range.first->second; | 
| 1383 |  |             // Skip the recently added tx | 
| 1384 | 0 |             if (spent_id == txid) continue; | 
| 1385 | 0 |             RecursiveUpdateTxState(/*batch=*/nullptr, spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { | 
| 1386 | 0 |                 return wtx.mempool_conflicts.insert(txid).second ? TxUpdate::CHANGED : TxUpdate::UNCHANGED; | 
| 1387 | 0 |             }); | 
| 1388 | 0 |         } | 
| 1389 |  | 
 | 
| 1390 | 0 |     } | 
| 1391 |  | 
 | 
| 1392 | 0 |     if (tx->version == TRUC_VERSION) { | 
| 1393 |  |         // Unconfirmed TRUC transactions are only allowed a 1-parent-1-child topology. | 
| 1394 |  |         // For any unconfirmed v3 parents (there should be a maximum of 1 except in reorgs), | 
| 1395 |  |         // record this child so the wallet doesn't try to spend any other outputs | 
| 1396 | 0 |         for (const CTxIn& tx_in : tx->vin) { | 
| 1397 | 0 |             auto parent_it = mapWallet.find(tx_in.prevout.hash); | 
| 1398 | 0 |             if (parent_it != mapWallet.end()) { | 
| 1399 | 0 |                 CWalletTx& parent_wtx = parent_it->second; | 
| 1400 | 0 |                 if (parent_wtx.isUnconfirmed()) { | 
| 1401 | 0 |                     parent_wtx.truc_child_in_mempool = tx->GetHash(); | 
| 1402 |  |                     // Even though these siblings do not spend the same utxos, they can't | 
| 1403 |  |                     // be present in the mempool at the same time because of TRUC policy rules | 
| 1404 | 0 |                     UpdateTrucSiblingConflicts(parent_wtx, txid, /*add_conflict=*/true); | 
| 1405 | 0 |                 } | 
| 1406 | 0 |             } | 
| 1407 | 0 |         } | 
| 1408 | 0 |     } | 
| 1409 | 0 | } | 
| 1410 |  |  | 
| 1411 | 0 | void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason) { | 
| 1412 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1413 | 0 |     auto it = mapWallet.find(tx->GetHash()); | 
| 1414 | 0 |     if (it != mapWallet.end()) { | 
| 1415 | 0 |         RefreshMempoolStatus(it->second, chain()); | 
| 1416 | 0 |     } | 
| 1417 |  |     // Handle transactions that were removed from the mempool because they | 
| 1418 |  |     // conflict with transactions in a newly connected block. | 
| 1419 | 0 |     if (reason == MemPoolRemovalReason::CONFLICT) { | 
| 1420 |  |         // Trigger external -walletnotify notifications for these transactions. | 
| 1421 |  |         // Set Status::UNCONFIRMED instead of Status::CONFLICTED for a few reasons: | 
| 1422 |  |         // | 
| 1423 |  |         // 1. The transactionRemovedFromMempool callback does not currently | 
| 1424 |  |         //    provide the conflicting block's hash and height, and for backwards | 
| 1425 |  |         //    compatibility reasons it may not be not safe to store conflicted | 
| 1426 |  |         //    wallet transactions with a null block hash. See | 
| 1427 |  |         //    https://github.com/bitcoin/bitcoin/pull/18600#discussion_r420195993. | 
| 1428 |  |         // 2. For most of these transactions, the wallet's internal conflict | 
| 1429 |  |         //    detection in the blockConnected handler will subsequently call | 
| 1430 |  |         //    MarkConflicted and update them with CONFLICTED status anyway. This | 
| 1431 |  |         //    applies to any wallet transaction that has inputs spent in the | 
| 1432 |  |         //    block, or that has ancestors in the wallet with inputs spent by | 
| 1433 |  |         //    the block. | 
| 1434 |  |         // 3. Longstanding behavior since the sync implementation in | 
| 1435 |  |         //    https://github.com/bitcoin/bitcoin/pull/9371 and the prior sync | 
| 1436 |  |         //    implementation before that was to mark these transactions | 
| 1437 |  |         //    unconfirmed rather than conflicted. | 
| 1438 |  |         // | 
| 1439 |  |         // Nothing described above should be seen as an unchangeable requirement | 
| 1440 |  |         // when improving this code in the future. The wallet's heuristics for | 
| 1441 |  |         // distinguishing between conflicted and unconfirmed transactions are | 
| 1442 |  |         // imperfect, and could be improved in general, see | 
| 1443 |  |         // https://github.com/bitcoin-core/bitcoin-devwiki/wiki/Wallet-Transaction-Conflict-Tracking | 
| 1444 | 0 |         SyncTransaction(tx, TxStateInactive{}); | 
| 1445 | 0 |     } | 
| 1446 |  | 
 | 
| 1447 | 0 |     const Txid& txid = tx->GetHash(); | 
| 1448 |  | 
 | 
| 1449 | 0 |     for (const CTxIn& tx_in : tx->vin) { | 
| 1450 |  |         // Iterate over all wallet transactions spending txin.prev | 
| 1451 |  |         // and recursively mark them as no longer conflicting with | 
| 1452 |  |         // txid | 
| 1453 | 0 |         for (auto range = mapTxSpends.equal_range(tx_in.prevout); range.first != range.second; range.first++) { | 
| 1454 | 0 |             const Txid& spent_id = range.first->second; | 
| 1455 |  | 
 | 
| 1456 | 0 |             RecursiveUpdateTxState(/*batch=*/nullptr, spent_id, [&txid](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { | 
| 1457 | 0 |                 return wtx.mempool_conflicts.erase(txid) ? TxUpdate::CHANGED : TxUpdate::UNCHANGED; | 
| 1458 | 0 |             }); | 
| 1459 | 0 |         } | 
| 1460 | 0 |     } | 
| 1461 |  | 
 | 
| 1462 | 0 |     if (tx->version == TRUC_VERSION) { | 
| 1463 |  |         // If this tx has a parent, unset its truc_child_in_mempool to make it possible | 
| 1464 |  |         // to spend from the parent again. If this tx was replaced by another | 
| 1465 |  |         // child of the same parent, transactionAddedToMempool | 
| 1466 |  |         // will update truc_child_in_mempool | 
| 1467 | 0 |         for (const CTxIn& tx_in : tx->vin) { | 
| 1468 | 0 |             auto parent_it = mapWallet.find(tx_in.prevout.hash); | 
| 1469 | 0 |             if (parent_it != mapWallet.end()) { | 
| 1470 | 0 |                 CWalletTx& parent_wtx = parent_it->second; | 
| 1471 | 0 |                 if (parent_wtx.truc_child_in_mempool == tx->GetHash()) { | 
| 1472 | 0 |                     parent_wtx.truc_child_in_mempool = std::nullopt; | 
| 1473 | 0 |                     UpdateTrucSiblingConflicts(parent_wtx, txid, /*add_conflict=*/false); | 
| 1474 | 0 |                 } | 
| 1475 | 0 |             } | 
| 1476 | 0 |         } | 
| 1477 | 0 |     } | 
| 1478 | 0 | } | 
| 1479 |  |  | 
| 1480 |  | void CWallet::blockConnected(ChainstateRole role, const interfaces::BlockInfo& block) | 
| 1481 | 0 | { | 
| 1482 | 0 |     if (role == ChainstateRole::BACKGROUND) { | 
| 1483 | 0 |         return; | 
| 1484 | 0 |     } | 
| 1485 | 0 |     assert(block.data); | 
| 1486 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1487 |  |  | 
| 1488 |  |     // Update the best block in memory first. This will set the best block's height, which is | 
| 1489 |  |     // needed by MarkConflicted. | 
| 1490 | 0 |     SetLastBlockProcessedInMem(block.height, block.hash); | 
| 1491 |  |  | 
| 1492 |  |     // No need to scan block if it was created before the wallet birthday. | 
| 1493 |  |     // Uses chain max time and twice the grace period to adjust time for block time variability. | 
| 1494 | 0 |     if (block.chain_time_max < m_birth_time.load() - (TIMESTAMP_WINDOW * 2)) return; | 
| 1495 |  |  | 
| 1496 |  |     // Scan block | 
| 1497 | 0 |     bool wallet_updated = false; | 
| 1498 | 0 |     for (size_t index = 0; index < block.data->vtx.size(); index++) { | 
| 1499 | 0 |         wallet_updated |= SyncTransaction(block.data->vtx[index], TxStateConfirmed{block.hash, block.height, static_cast<int>(index)}); | 
| 1500 | 0 |         transactionRemovedFromMempool(block.data->vtx[index], MemPoolRemovalReason::BLOCK); | 
| 1501 | 0 |     } | 
| 1502 |  |  | 
| 1503 |  |     // Update on disk if this block resulted in us updating a tx, or periodically every 144 blocks (~1 day) | 
| 1504 | 0 |     if (wallet_updated || block.height % 144 == 0) { | 
| 1505 | 0 |         WriteBestBlock(); | 
| 1506 | 0 |     } | 
| 1507 | 0 | } | 
| 1508 |  |  | 
| 1509 |  | void CWallet::blockDisconnected(const interfaces::BlockInfo& block) | 
| 1510 | 0 | { | 
| 1511 | 0 |     assert(block.data); | 
| 1512 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1513 |  |  | 
| 1514 |  |     // At block disconnection, this will change an abandoned transaction to | 
| 1515 |  |     // be unconfirmed, whether or not the transaction is added back to the mempool. | 
| 1516 |  |     // User may have to call abandontransaction again. It may be addressed in the | 
| 1517 |  |     // future with a stickier abandoned state or even removing abandontransaction call. | 
| 1518 | 0 |     int disconnect_height = block.height; | 
| 1519 |  | 
 | 
| 1520 | 0 |     for (size_t index = 0; index < block.data->vtx.size(); index++) { | 
| 1521 | 0 |         const CTransactionRef& ptx = block.data->vtx[index]; | 
| 1522 |  |         // Coinbase transactions are not only inactive but also abandoned, | 
| 1523 |  |         // meaning they should never be relayed standalone via the p2p protocol. | 
| 1524 | 0 |         SyncTransaction(ptx, TxStateInactive{/*abandoned=*/index == 0}); | 
| 1525 |  | 
 | 
| 1526 | 0 |         for (const CTxIn& tx_in : ptx->vin) { | 
| 1527 |  |             // No other wallet transactions conflicted with this transaction | 
| 1528 | 0 |             if (mapTxSpends.count(tx_in.prevout) < 1) continue; | 
| 1529 |  |  | 
| 1530 | 0 |             std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(tx_in.prevout); | 
| 1531 |  |  | 
| 1532 |  |             // For all of the spends that conflict with this transaction | 
| 1533 | 0 |             for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) { | 
| 1534 | 0 |                 CWalletTx& wtx = mapWallet.find(_it->second)->second; | 
| 1535 |  | 
 | 
| 1536 | 0 |                 if (!wtx.isBlockConflicted()) continue; | 
| 1537 |  |  | 
| 1538 | 0 |                 auto try_updating_state = [&](CWalletTx& tx) { | 
| 1539 | 0 |                     if (!tx.isBlockConflicted()) return TxUpdate::UNCHANGED; | 
| 1540 | 0 |                     if (tx.state<TxStateBlockConflicted>()->conflicting_block_height >= disconnect_height) { | 
| 1541 | 0 |                         tx.m_state = TxStateInactive{}; | 
| 1542 | 0 |                         return TxUpdate::CHANGED; | 
| 1543 | 0 |                     } | 
| 1544 | 0 |                     return TxUpdate::UNCHANGED; | 
| 1545 | 0 |                 }; | 
| 1546 |  | 
 | 
| 1547 | 0 |                 RecursiveUpdateTxState(wtx.tx->GetHash(), try_updating_state); | 
| 1548 | 0 |             } | 
| 1549 | 0 |         } | 
| 1550 | 0 |     } | 
| 1551 |  |  | 
| 1552 |  |     // Update the best block | 
| 1553 | 0 |     SetLastBlockProcessed(block.height - 1, *Assert(block.prev_hash)); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 1554 | 0 | } | 
| 1555 |  |  | 
| 1556 |  | void CWallet::updatedBlockTip() | 
| 1557 | 0 | { | 
| 1558 | 0 |     m_best_block_time = GetTime(); | 
| 1559 | 0 | } | 
| 1560 |  |  | 
| 1561 | 0 | void CWallet::BlockUntilSyncedToCurrentChain() const { | 
| 1562 | 0 |     AssertLockNotHeld(cs_wallet); | Line | Count | Source |  | 142 | 0 | #define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1563 |  |     // Skip the queue-draining stuff if we know we're caught up with | 
| 1564 |  |     // chain().Tip(), otherwise put a callback in the validation interface queue and wait | 
| 1565 |  |     // for the queue to drain enough to execute it (indicating we are caught up | 
| 1566 |  |     // at least with the time we entered this function). | 
| 1567 | 0 |     uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1568 | 0 |     chain().waitForNotificationsIfTipChanged(last_block_hash); | 
| 1569 | 0 | } | 
| 1570 |  |  | 
| 1571 |  | // Note that this function doesn't distinguish between a 0-valued input, | 
| 1572 |  | // and a not-"is mine" input. | 
| 1573 |  | CAmount CWallet::GetDebit(const CTxIn &txin) const | 
| 1574 | 0 | { | 
| 1575 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1576 | 0 |     auto txo = GetTXO(txin.prevout); | 
| 1577 | 0 |     if (txo) { | 
| 1578 | 0 |         return txo->GetTxOut().nValue; | 
| 1579 | 0 |     } | 
| 1580 | 0 |     return 0; | 
| 1581 | 0 | } | 
| 1582 |  |  | 
| 1583 |  | bool CWallet::IsMine(const CTxOut& txout) const | 
| 1584 | 0 | { | 
| 1585 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1586 | 0 |     return IsMine(txout.scriptPubKey); | 
| 1587 | 0 | } | 
| 1588 |  |  | 
| 1589 |  | bool CWallet::IsMine(const CTxDestination& dest) const | 
| 1590 | 0 | { | 
| 1591 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1592 | 0 |     return IsMine(GetScriptForDestination(dest)); | 
| 1593 | 0 | } | 
| 1594 |  |  | 
| 1595 |  | bool CWallet::IsMine(const CScript& script) const | 
| 1596 | 0 | { | 
| 1597 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1598 |  |  | 
| 1599 |  |     // Search the cache so that IsMine is called only on the relevant SPKMs instead of on everything in m_spk_managers | 
| 1600 | 0 |     const auto& it = m_cached_spks.find(script); | 
| 1601 | 0 |     if (it != m_cached_spks.end()) { | 
| 1602 | 0 |         bool res = false; | 
| 1603 | 0 |         for (const auto& spkm : it->second) { | 
| 1604 | 0 |             res = res || spkm->IsMine(script); | 
| 1605 | 0 |         } | 
| 1606 | 0 |         Assume(res); | Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 1607 | 0 |         return res; | 
| 1608 | 0 |     } | 
| 1609 |  |  | 
| 1610 | 0 |     return false; | 
| 1611 | 0 | } | 
| 1612 |  |  | 
| 1613 |  | bool CWallet::IsMine(const CTransaction& tx) const | 
| 1614 | 0 | { | 
| 1615 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1616 | 0 |     for (const CTxOut& txout : tx.vout) | 
| 1617 | 0 |         if (IsMine(txout)) | 
| 1618 | 0 |             return true; | 
| 1619 | 0 |     return false; | 
| 1620 | 0 | } | 
| 1621 |  |  | 
| 1622 |  | bool CWallet::IsMine(const COutPoint& outpoint) const | 
| 1623 | 0 | { | 
| 1624 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1625 | 0 |     auto wtx = GetWalletTx(outpoint.hash); | 
| 1626 | 0 |     if (!wtx) { | 
| 1627 | 0 |         return false; | 
| 1628 | 0 |     } | 
| 1629 | 0 |     if (outpoint.n >= wtx->tx->vout.size()) { | 
| 1630 | 0 |         return false; | 
| 1631 | 0 |     } | 
| 1632 | 0 |     return IsMine(wtx->tx->vout[outpoint.n]); | 
| 1633 | 0 | } | 
| 1634 |  |  | 
| 1635 |  | bool CWallet::IsFromMe(const CTransaction& tx) const | 
| 1636 | 0 | { | 
| 1637 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1638 | 0 |     for (const CTxIn& txin : tx.vin) { | 
| 1639 | 0 |         if (GetTXO(txin.prevout)) return true; | 
| 1640 | 0 |     } | 
| 1641 | 0 |     return false; | 
| 1642 | 0 | } | 
| 1643 |  |  | 
| 1644 |  | CAmount CWallet::GetDebit(const CTransaction& tx) const | 
| 1645 | 0 | { | 
| 1646 | 0 |     CAmount nDebit = 0; | 
| 1647 | 0 |     for (const CTxIn& txin : tx.vin) | 
| 1648 | 0 |     { | 
| 1649 | 0 |         nDebit += GetDebit(txin); | 
| 1650 | 0 |         if (!MoneyRange(nDebit)) | 
| 1651 | 0 |             throw std::runtime_error(std::string(__func__) + ": value out of range"); | 
| 1652 | 0 |     } | 
| 1653 | 0 |     return nDebit; | 
| 1654 | 0 | } | 
| 1655 |  |  | 
| 1656 |  | bool CWallet::IsHDEnabled() const | 
| 1657 | 0 | { | 
| 1658 |  |     // All Active ScriptPubKeyMans must be HD for this to be true | 
| 1659 | 0 |     bool result = false; | 
| 1660 | 0 |     for (const auto& spk_man : GetActiveScriptPubKeyMans()) { | 
| 1661 | 0 |         if (!spk_man->IsHDEnabled()) return false; | 
| 1662 | 0 |         result = true; | 
| 1663 | 0 |     } | 
| 1664 | 0 |     return result; | 
| 1665 | 0 | } | 
| 1666 |  |  | 
| 1667 |  | bool CWallet::CanGetAddresses(bool internal) const | 
| 1668 | 0 | { | 
| 1669 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1670 | 0 |     if (m_spk_managers.empty()) return false; | 
| 1671 | 0 |     for (OutputType t : OUTPUT_TYPES) { | 
| 1672 | 0 |         auto spk_man = GetScriptPubKeyMan(t, internal); | 
| 1673 | 0 |         if (spk_man && spk_man->CanGetAddresses(internal)) { | 
| 1674 | 0 |             return true; | 
| 1675 | 0 |         } | 
| 1676 | 0 |     } | 
| 1677 | 0 |     return false; | 
| 1678 | 0 | } | 
| 1679 |  |  | 
| 1680 |  | void CWallet::SetWalletFlag(uint64_t flags) | 
| 1681 | 0 | { | 
| 1682 | 0 |     WalletBatch batch(GetDatabase()); | 
| 1683 | 0 |     return SetWalletFlagWithDB(batch, flags); | 
| 1684 | 0 | } | 
| 1685 |  |  | 
| 1686 |  | void CWallet::SetWalletFlagWithDB(WalletBatch& batch, uint64_t flags) | 
| 1687 | 0 | { | 
| 1688 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1689 | 0 |     m_wallet_flags |= flags; | 
| 1690 | 0 |     if (!batch.WriteWalletFlags(m_wallet_flags)) | 
| 1691 | 0 |         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed"); | 
| 1692 | 0 | } | 
| 1693 |  |  | 
| 1694 |  | void CWallet::UnsetWalletFlag(uint64_t flag) | 
| 1695 | 0 | { | 
| 1696 | 0 |     WalletBatch batch(GetDatabase()); | 
| 1697 | 0 |     UnsetWalletFlagWithDB(batch, flag); | 
| 1698 | 0 | } | 
| 1699 |  |  | 
| 1700 |  | void CWallet::UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag) | 
| 1701 | 0 | { | 
| 1702 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1703 | 0 |     m_wallet_flags &= ~flag; | 
| 1704 | 0 |     if (!batch.WriteWalletFlags(m_wallet_flags)) | 
| 1705 | 0 |         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed"); | 
| 1706 | 0 | } | 
| 1707 |  |  | 
| 1708 |  | void CWallet::UnsetBlankWalletFlag(WalletBatch& batch) | 
| 1709 | 0 | { | 
| 1710 | 0 |     UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET); | 
| 1711 | 0 | } | 
| 1712 |  |  | 
| 1713 |  | bool CWallet::IsWalletFlagSet(uint64_t flag) const | 
| 1714 | 0 | { | 
| 1715 | 0 |     return (m_wallet_flags & flag); | 
| 1716 | 0 | } | 
| 1717 |  |  | 
| 1718 |  | bool CWallet::LoadWalletFlags(uint64_t flags) | 
| 1719 | 0 | { | 
| 1720 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1721 | 0 |     if (((flags & KNOWN_WALLET_FLAGS) >> 32) ^ (flags >> 32)) { | 
| 1722 |  |         // contains unknown non-tolerable wallet flags | 
| 1723 | 0 |         return false; | 
| 1724 | 0 |     } | 
| 1725 | 0 |     m_wallet_flags = flags; | 
| 1726 |  | 
 | 
| 1727 | 0 |     return true; | 
| 1728 | 0 | } | 
| 1729 |  |  | 
| 1730 |  | void CWallet::InitWalletFlags(uint64_t flags) | 
| 1731 | 0 | { | 
| 1732 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1733 |  |  | 
| 1734 |  |     // We should never be writing unknown non-tolerable wallet flags | 
| 1735 | 0 |     assert(((flags & KNOWN_WALLET_FLAGS) >> 32) == (flags >> 32)); | 
| 1736 |  |     // This should only be used once, when creating a new wallet - so current flags are expected to be blank | 
| 1737 | 0 |     assert(m_wallet_flags == 0); | 
| 1738 |  |  | 
| 1739 | 0 |     if (!WalletBatch(GetDatabase()).WriteWalletFlags(flags)) { | 
| 1740 | 0 |         throw std::runtime_error(std::string(__func__) + ": writing wallet flags failed"); | 
| 1741 | 0 |     } | 
| 1742 |  |  | 
| 1743 | 0 |     if (!LoadWalletFlags(flags)) assert(false); | 
| 1744 | 0 | } | 
| 1745 |  |  | 
| 1746 |  | uint64_t CWallet::GetWalletFlags() const | 
| 1747 | 0 | { | 
| 1748 | 0 |     return m_wallet_flags; | 
| 1749 | 0 | } | 
| 1750 |  |  | 
| 1751 |  | void CWallet::MaybeUpdateBirthTime(int64_t time) | 
| 1752 | 0 | { | 
| 1753 | 0 |     int64_t birthtime = m_birth_time.load(); | 
| 1754 | 0 |     if (time < birthtime) { | 
| 1755 | 0 |         m_birth_time = time; | 
| 1756 | 0 |     } | 
| 1757 | 0 | } | 
| 1758 |  |  | 
| 1759 |  | /** | 
| 1760 |  |  * Scan active chain for relevant transactions after importing keys. This should | 
| 1761 |  |  * be called whenever new keys are added to the wallet, with the oldest key | 
| 1762 |  |  * creation time. | 
| 1763 |  |  * | 
| 1764 |  |  * @return Earliest timestamp that could be successfully scanned from. Timestamp | 
| 1765 |  |  * returned will be higher than startTime if relevant blocks could not be read. | 
| 1766 |  |  */ | 
| 1767 |  | int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update) | 
| 1768 | 0 | { | 
| 1769 |  |     // Find starting block. May be null if nCreateTime is greater than the | 
| 1770 |  |     // highest blockchain timestamp, in which case there is nothing that needs | 
| 1771 |  |     // to be scanned. | 
| 1772 | 0 |     int start_height = 0; | 
| 1773 | 0 |     uint256 start_block; | 
| 1774 | 0 |     bool start = chain().findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, FoundBlock().hash(start_block).height(start_height)); | 
| 1775 | 0 |     WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - start_height + 1 : 0);| Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1776 |  | 
 | 
| 1777 | 0 |     if (start) { | 
| 1778 |  |         // TODO: this should take into account failure by ScanResult::USER_ABORT | 
| 1779 | 0 |         ScanResult result = ScanForWalletTransactions(start_block, start_height, /*max_height=*/{}, reserver, /*fUpdate=*/update, /*save_progress=*/false); | 
| 1780 | 0 |         if (result.status == ScanResult::FAILURE) { | 
| 1781 | 0 |             int64_t time_max; | 
| 1782 | 0 |             CHECK_NONFATAL(chain().findBlock(result.last_failed_block, FoundBlock().maxTime(time_max))); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 1783 | 0 |             return time_max + TIMESTAMP_WINDOW + 1; | 
| 1784 | 0 |         } | 
| 1785 | 0 |     } | 
| 1786 | 0 |     return startTime; | 
| 1787 | 0 | } | 
| 1788 |  |  | 
| 1789 |  | /** | 
| 1790 |  |  * Scan the block chain (starting in start_block) for transactions | 
| 1791 |  |  * from or to us. If fUpdate is true, found transactions that already | 
| 1792 |  |  * exist in the wallet will be updated. If max_height is not set, the | 
| 1793 |  |  * mempool will be scanned as well. | 
| 1794 |  |  * | 
| 1795 |  |  * @param[in] start_block Scan starting block. If block is not on the active | 
| 1796 |  |  *                        chain, the scan will return SUCCESS immediately. | 
| 1797 |  |  * @param[in] start_height Height of start_block | 
| 1798 |  |  * @param[in] max_height  Optional max scanning height. If unset there is | 
| 1799 |  |  *                        no maximum and scanning can continue to the tip | 
| 1800 |  |  * | 
| 1801 |  |  * @return ScanResult returning scan information and indicating success or | 
| 1802 |  |  *         failure. Return status will be set to SUCCESS if scan was | 
| 1803 |  |  *         successful. FAILURE if a complete rescan was not possible (due to | 
| 1804 |  |  *         pruning or corruption). USER_ABORT if the rescan was aborted before | 
| 1805 |  |  *         it could complete. | 
| 1806 |  |  * | 
| 1807 |  |  * @pre Caller needs to make sure start_block (and the optional stop_block) are on | 
| 1808 |  |  * the main chain after to the addition of any new keys you want to detect | 
| 1809 |  |  * transactions for. | 
| 1810 |  |  */ | 
| 1811 |  | 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) | 
| 1812 | 0 | { | 
| 1813 | 0 |     constexpr auto INTERVAL_TIME{60s}; | 
| 1814 | 0 |     auto current_time{reserver.now()}; | 
| 1815 | 0 |     auto start_time{reserver.now()}; | 
| 1816 |  | 
 | 
| 1817 | 0 |     assert(reserver.isReserved()); | 
| 1818 |  |  | 
| 1819 | 0 |     uint256 block_hash = start_block; | 
| 1820 | 0 |     ScanResult result; | 
| 1821 |  | 
 | 
| 1822 | 0 |     std::unique_ptr<FastWalletRescanFilter> fast_rescan_filter; | 
| 1823 | 0 |     if (chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique<FastWalletRescanFilter>(*this); | 
| 1824 |  | 
 | 
| 1825 | 0 |     WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(), | 
| 1826 | 0 |                     fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks"); | 
| 1827 |  | 
 | 
| 1828 | 0 |     fAbortRescan = false; | 
| 1829 | 0 |     ShowProgress(strprintf("[%s] %s", DisplayName(), _("Rescanning…")), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 1830 | 0 |     uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash()); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1831 | 0 |     uint256 end_hash = tip_hash; | 
| 1832 | 0 |     if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash)); | 
| 1833 | 0 |     double progress_begin = chain().guessVerificationProgress(block_hash); | 
| 1834 | 0 |     double progress_end = chain().guessVerificationProgress(end_hash); | 
| 1835 | 0 |     double progress_current = progress_begin; | 
| 1836 | 0 |     int block_height = start_height; | 
| 1837 | 0 |     while (!fAbortRescan && !chain().shutdownRequested()) { | 
| 1838 | 0 |         if (progress_end - progress_begin > 0.0) { | 
| 1839 | 0 |             m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin); | 
| 1840 | 0 |         } else { // avoid divide-by-zero for single block scan range (i.e. start and stop hashes are equal) | 
| 1841 | 0 |             m_scanning_progress = 0; | 
| 1842 | 0 |         } | 
| 1843 | 0 |         if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) { | 
| 1844 | 0 |             ShowProgress(strprintf("[%s] %s", DisplayName(), _("Rescanning…")), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 1845 | 0 |         } | 
| 1846 |  | 
 | 
| 1847 | 0 |         bool next_interval = reserver.now() >= current_time + INTERVAL_TIME; | 
| 1848 | 0 |         if (next_interval) { | 
| 1849 | 0 |             current_time = reserver.now(); | 
| 1850 | 0 |             WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", block_height, progress_current); | 
| 1851 | 0 |         } | 
| 1852 |  | 
 | 
| 1853 | 0 |         bool fetch_block{true}; | 
| 1854 | 0 |         if (fast_rescan_filter) { | 
| 1855 | 0 |             fast_rescan_filter->UpdateIfNeeded(); | 
| 1856 | 0 |             auto matches_block{fast_rescan_filter->MatchesBlock(block_hash)}; | 
| 1857 | 0 |             if (matches_block.has_value()) { | 
| 1858 | 0 |                 if (*matches_block) { | 
| 1859 | 0 |                     LogDebug(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (filter matched)\n", block_height, block_hash.ToString()); | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 1860 | 0 |                 } else { | 
| 1861 | 0 |                     result.last_scanned_block = block_hash; | 
| 1862 | 0 |                     result.last_scanned_height = block_height; | 
| 1863 | 0 |                     fetch_block = false; | 
| 1864 | 0 |                 } | 
| 1865 | 0 |             } else { | 
| 1866 | 0 |                 LogDebug(BCLog::SCAN, "Fast rescan: inspect block %d [%s] (WARNING: block filter not found!)\n", block_height, block_hash.ToString()); | Line | Count | Source |  | 381 | 0 | #define LogDebug(category, ...) LogPrintLevel(category, BCLog::Level::Debug, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 1867 | 0 |             } | 
| 1868 | 0 |         } | 
| 1869 |  |  | 
| 1870 |  |         // Find next block separately from reading data above, because reading | 
| 1871 |  |         // is slow and there might be a reorg while it is read. | 
| 1872 | 0 |         bool block_still_active = false; | 
| 1873 | 0 |         bool next_block = false; | 
| 1874 | 0 |         uint256 next_block_hash; | 
| 1875 | 0 |         chain().findBlock(block_hash, FoundBlock().inActiveChain(block_still_active).nextBlock(FoundBlock().inActiveChain(next_block).hash(next_block_hash))); | 
| 1876 |  | 
 | 
| 1877 | 0 |         if (fetch_block) { | 
| 1878 |  |             // Read block data and locator if needed (the locator is usually null unless we need to save progress) | 
| 1879 | 0 |             CBlock block; | 
| 1880 | 0 |             CBlockLocator loc; | 
| 1881 |  |             // Find block | 
| 1882 | 0 |             FoundBlock found_block{FoundBlock().data(block)}; | 
| 1883 | 0 |             if (save_progress && next_interval) found_block.locator(loc); | 
| 1884 | 0 |             chain().findBlock(block_hash, found_block); | 
| 1885 |  | 
 | 
| 1886 | 0 |             if (!block.IsNull()) { | 
| 1887 | 0 |                 LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 1888 | 0 |                 if (!block_still_active) { | 
| 1889 |  |                     // Abort scan if current block is no longer active, to prevent | 
| 1890 |  |                     // marking transactions as coming from the wrong block. | 
| 1891 | 0 |                     result.last_failed_block = block_hash; | 
| 1892 | 0 |                     result.status = ScanResult::FAILURE; | 
| 1893 | 0 |                     break; | 
| 1894 | 0 |                 } | 
| 1895 | 0 |                 for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { | 
| 1896 | 0 |                     SyncTransaction(block.vtx[posInBlock], TxStateConfirmed{block_hash, block_height, static_cast<int>(posInBlock)}, fUpdate, /*rescanning_old_block=*/true); | 
| 1897 | 0 |                 } | 
| 1898 |  |                 // scan succeeded, record block as most recent successfully scanned | 
| 1899 | 0 |                 result.last_scanned_block = block_hash; | 
| 1900 | 0 |                 result.last_scanned_height = block_height; | 
| 1901 |  | 
 | 
| 1902 | 0 |                 if (!loc.IsNull()) { | 
| 1903 | 0 |                     WalletLogPrintf("Saving scan progress %d.\n", block_height); | 
| 1904 | 0 |                     WalletBatch batch(GetDatabase()); | 
| 1905 | 0 |                     batch.WriteBestBlock(loc); | 
| 1906 | 0 |                 } | 
| 1907 | 0 |             } else { | 
| 1908 |  |                 // could not scan block, keep scanning but record this block as the most recent failure | 
| 1909 | 0 |                 result.last_failed_block = block_hash; | 
| 1910 | 0 |                 result.status = ScanResult::FAILURE; | 
| 1911 | 0 |             } | 
| 1912 | 0 |         } | 
| 1913 | 0 |         if (max_height && block_height >= *max_height) { | 
| 1914 | 0 |             break; | 
| 1915 | 0 |         } | 
| 1916 |  |         // If rescanning was triggered with cs_wallet permanently locked (AttachChain), additional blocks that were connected during the rescan | 
| 1917 |  |         // aren't processed here but will be processed with the pending blockConnected notifications after the lock is released. | 
| 1918 |  |         // If rescanning without a permanent cs_wallet lock, additional blocks that were added during the rescan will be re-processed if | 
| 1919 |  |         // the notification was processed and the last block height was updated. | 
| 1920 | 0 |         if (block_height >= WITH_LOCK(cs_wallet, return GetLastBlockHeight())) {| Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1921 | 0 |             break; | 
| 1922 | 0 |         } | 
| 1923 |  |  | 
| 1924 | 0 |         { | 
| 1925 | 0 |             if (!next_block) { | 
| 1926 |  |                 // break successfully when rescan has reached the tip, or | 
| 1927 |  |                 // previous block is no longer on the chain due to a reorg | 
| 1928 | 0 |                 break; | 
| 1929 | 0 |             } | 
| 1930 |  |  | 
| 1931 |  |             // increment block and verification progress | 
| 1932 | 0 |             block_hash = next_block_hash; | 
| 1933 | 0 |             ++block_height; | 
| 1934 | 0 |             progress_current = chain().guessVerificationProgress(block_hash); | 
| 1935 |  |  | 
| 1936 |  |             // handle updated tip hash | 
| 1937 | 0 |             const uint256 prev_tip_hash = tip_hash; | 
| 1938 | 0 |             tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash()); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1939 | 0 |             if (!max_height && prev_tip_hash != tip_hash) { | 
| 1940 |  |                 // in case the tip has changed, update progress max | 
| 1941 | 0 |                 progress_end = chain().guessVerificationProgress(tip_hash); | 
| 1942 | 0 |             } | 
| 1943 | 0 |         } | 
| 1944 | 0 |     } | 
| 1945 | 0 |     if (!max_height) { | 
| 1946 | 0 |         WalletLogPrintf("Scanning current mempool transactions.\n"); | 
| 1947 | 0 |         WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this)); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 1948 | 0 |     } | 
| 1949 | 0 |     ShowProgress(strprintf("[%s] %s", DisplayName(), _("Rescanning…")), 100); // hide progress dialog in GUI| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 1950 | 0 |     if (block_height && fAbortRescan) { | 
| 1951 | 0 |         WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height, progress_current); | 
| 1952 | 0 |         result.status = ScanResult::USER_ABORT; | 
| 1953 | 0 |     } else if (block_height && chain().shutdownRequested()) { | 
| 1954 | 0 |         WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height, progress_current); | 
| 1955 | 0 |         result.status = ScanResult::USER_ABORT; | 
| 1956 | 0 |     } else { | 
| 1957 | 0 |         WalletLogPrintf("Rescan completed in %15dms\n", Ticks<std::chrono::milliseconds>(reserver.now() - start_time)); | 
| 1958 | 0 |     } | 
| 1959 | 0 |     return result; | 
| 1960 | 0 | } | 
| 1961 |  |  | 
| 1962 |  | bool CWallet::SubmitTxMemoryPoolAndRelay(CWalletTx& wtx, std::string& err_string, bool relay) const | 
| 1963 | 0 | { | 
| 1964 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1965 |  |  | 
| 1966 |  |     // Can't relay if wallet is not broadcasting | 
| 1967 | 0 |     if (!GetBroadcastTransactions()) return false; | 
| 1968 |  |     // Don't relay abandoned transactions | 
| 1969 | 0 |     if (wtx.isAbandoned()) return false; | 
| 1970 |  |     // Don't try to submit coinbase transactions. These would fail anyway but would | 
| 1971 |  |     // cause log spam. | 
| 1972 | 0 |     if (wtx.IsCoinBase()) return false; | 
| 1973 |  |     // Don't try to submit conflicted or confirmed transactions. | 
| 1974 | 0 |     if (GetTxDepthInMainChain(wtx) != 0) return false; | 
| 1975 |  |  | 
| 1976 |  |     // Submit transaction to mempool for relay | 
| 1977 | 0 |     WalletLogPrintf("Submitting wtx %s to mempool for relay\n", wtx.GetHash().ToString()); | 
| 1978 |  |     // We must set TxStateInMempool here. Even though it will also be set later by the | 
| 1979 |  |     // entered-mempool callback, if we did not there would be a race where a | 
| 1980 |  |     // user could call sendmoney in a loop and hit spurious out of funds errors | 
| 1981 |  |     // because we think that this newly generated transaction's change is | 
| 1982 |  |     // unavailable as we're not yet aware that it is in the mempool. | 
| 1983 |  |     // | 
| 1984 |  |     // If broadcast fails for any reason, trying to set wtx.m_state here would be incorrect. | 
| 1985 |  |     // If transaction was previously in the mempool, it should be updated when | 
| 1986 |  |     // TransactionRemovedFromMempool fires. | 
| 1987 | 0 |     bool ret = chain().broadcastTransaction(wtx.tx, m_default_max_tx_fee, relay, err_string); | 
| 1988 | 0 |     if (ret) wtx.m_state = TxStateInMempool{}; | 
| 1989 | 0 |     return ret; | 
| 1990 | 0 | } | 
| 1991 |  |  | 
| 1992 |  | std::set<Txid> CWallet::GetTxConflicts(const CWalletTx& wtx) const | 
| 1993 | 0 | { | 
| 1994 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 1995 |  | 
 | 
| 1996 | 0 |     const Txid myHash{wtx.GetHash()}; | 
| 1997 | 0 |     std::set<Txid> result{GetConflicts(myHash)}; | 
| 1998 | 0 |     result.erase(myHash); | 
| 1999 | 0 |     return result; | 
| 2000 | 0 | } | 
| 2001 |  |  | 
| 2002 |  | bool CWallet::ShouldResend() const | 
| 2003 | 0 | { | 
| 2004 |  |     // Don't attempt to resubmit if the wallet is configured to not broadcast | 
| 2005 | 0 |     if (!fBroadcastTransactions) return false; | 
| 2006 |  |  | 
| 2007 |  |     // During reindex, importing and IBD, old wallet transactions become | 
| 2008 |  |     // unconfirmed. Don't resend them as that would spam other nodes. | 
| 2009 |  |     // We only allow forcing mempool submission when not relaying to avoid this spam. | 
| 2010 | 0 |     if (!chain().isReadyToBroadcast()) return false; | 
| 2011 |  |  | 
| 2012 |  |     // Do this infrequently and randomly to avoid giving away | 
| 2013 |  |     // that these are our transactions. | 
| 2014 | 0 |     if (NodeClock::now() < m_next_resend) return false; | 
| 2015 |  |  | 
| 2016 | 0 |     return true; | 
| 2017 | 0 | } | 
| 2018 |  |  | 
| 2019 | 0 | NodeClock::time_point CWallet::GetDefaultNextResend() { return FastRandomContext{}.rand_uniform_delay(NodeClock::now() + 12h, 24h); } | 
| 2020 |  |  | 
| 2021 |  | // Resubmit transactions from the wallet to the mempool, optionally asking the | 
| 2022 |  | // mempool to relay them. On startup, we will do this for all unconfirmed | 
| 2023 |  | // transactions but will not ask the mempool to relay them. We do this on startup | 
| 2024 |  | // to ensure that our own mempool is aware of our transactions. There | 
| 2025 |  | // is a privacy side effect here as not broadcasting on startup also means that we won't | 
| 2026 |  | // inform the world of our wallet's state, particularly if the wallet (or node) is not | 
| 2027 |  | // yet synced. | 
| 2028 |  | // | 
| 2029 |  | // Otherwise this function is called periodically in order to relay our unconfirmed txs. | 
| 2030 |  | // We do this on a random timer to slightly obfuscate which transactions | 
| 2031 |  | // come from our wallet. | 
| 2032 |  | // | 
| 2033 |  | // TODO: Ideally, we'd only resend transactions that we think should have been | 
| 2034 |  | // mined in the most recent block. Any transaction that wasn't in the top | 
| 2035 |  | // blockweight of transactions in the mempool shouldn't have been mined, | 
| 2036 |  | // and so is probably just sitting in the mempool waiting to be confirmed. | 
| 2037 |  | // Rebroadcasting does nothing to speed up confirmation and only damages | 
| 2038 |  | // privacy. | 
| 2039 |  | // | 
| 2040 |  | // The `force` option results in all unconfirmed transactions being submitted to | 
| 2041 |  | // the mempool. This does not necessarily result in those transactions being relayed, | 
| 2042 |  | // that depends on the `relay` option. Periodic rebroadcast uses the pattern | 
| 2043 |  | // relay=true force=false, while loading into the mempool | 
| 2044 |  | // (on start, or after import) uses relay=false force=true. | 
| 2045 |  | void CWallet::ResubmitWalletTransactions(bool relay, bool force) | 
| 2046 | 0 | { | 
| 2047 |  |     // Don't attempt to resubmit if the wallet is configured to not broadcast, | 
| 2048 |  |     // even if forcing. | 
| 2049 | 0 |     if (!fBroadcastTransactions) return; | 
| 2050 |  |  | 
| 2051 | 0 |     int submitted_tx_count = 0; | 
| 2052 |  | 
 | 
| 2053 | 0 |     { // cs_wallet scope | 
| 2054 | 0 |         LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2055 |  |  | 
| 2056 |  |         // First filter for the transactions we want to rebroadcast. | 
| 2057 |  |         // We use a set with WalletTxOrderComparator so that rebroadcasting occurs in insertion order | 
| 2058 | 0 |         std::set<CWalletTx*, WalletTxOrderComparator> to_submit; | 
| 2059 | 0 |         for (auto& [txid, wtx] : mapWallet) { | 
| 2060 |  |             // Only rebroadcast unconfirmed txs | 
| 2061 | 0 |             if (!wtx.isUnconfirmed()) continue; | 
| 2062 |  |  | 
| 2063 |  |             // Attempt to rebroadcast all txes more than 5 minutes older than | 
| 2064 |  |             // the last block, or all txs if forcing. | 
| 2065 | 0 |             if (!force && wtx.nTimeReceived > m_best_block_time - 5 * 60) continue; | 
| 2066 | 0 |             to_submit.insert(&wtx); | 
| 2067 | 0 |         } | 
| 2068 |  |         // Now try submitting the transactions to the memory pool and (optionally) relay them. | 
| 2069 | 0 |         for (auto wtx : to_submit) { | 
| 2070 | 0 |             std::string unused_err_string; | 
| 2071 | 0 |             if (SubmitTxMemoryPoolAndRelay(*wtx, unused_err_string, relay)) ++submitted_tx_count; | 
| 2072 | 0 |         } | 
| 2073 | 0 |     } // cs_wallet | 
| 2074 |  | 
 | 
| 2075 | 0 |     if (submitted_tx_count > 0) { | 
| 2076 | 0 |         WalletLogPrintf("%s: resubmit %u unconfirmed transactions\n", __func__, submitted_tx_count); | 
| 2077 | 0 |     } | 
| 2078 | 0 | } | 
| 2079 |  |  | 
| 2080 |  | /** @} */ // end of mapWallet | 
| 2081 |  |  | 
| 2082 |  | void MaybeResendWalletTxs(WalletContext& context) | 
| 2083 | 0 | { | 
| 2084 | 0 |     for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) { | 
| 2085 | 0 |         if (!pwallet->ShouldResend()) continue; | 
| 2086 | 0 |         pwallet->ResubmitWalletTransactions(/*relay=*/true, /*force=*/false); | 
| 2087 | 0 |         pwallet->SetNextResend(); | 
| 2088 | 0 |     } | 
| 2089 | 0 | } | 
| 2090 |  |  | 
| 2091 |  |  | 
| 2092 |  | bool CWallet::SignTransaction(CMutableTransaction& tx) const | 
| 2093 | 0 | { | 
| 2094 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2095 |  |  | 
| 2096 |  |     // Build coins map | 
| 2097 | 0 |     std::map<COutPoint, Coin> coins; | 
| 2098 | 0 |     for (auto& input : tx.vin) { | 
| 2099 | 0 |         const auto mi = mapWallet.find(input.prevout.hash); | 
| 2100 | 0 |         if(mi == mapWallet.end() || input.prevout.n >= mi->second.tx->vout.size()) { | 
| 2101 | 0 |             return false; | 
| 2102 | 0 |         } | 
| 2103 | 0 |         const CWalletTx& wtx = mi->second; | 
| 2104 | 0 |         int prev_height = wtx.state<TxStateConfirmed>() ? wtx.state<TxStateConfirmed>()->confirmed_block_height : 0; | 
| 2105 | 0 |         coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], prev_height, wtx.IsCoinBase()); | 
| 2106 | 0 |     } | 
| 2107 | 0 |     std::map<int, bilingual_str> input_errors; | 
| 2108 | 0 |     return SignTransaction(tx, coins, SIGHASH_DEFAULT, input_errors); | 
| 2109 | 0 | } | 
| 2110 |  |  | 
| 2111 |  | bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const | 
| 2112 | 0 | { | 
| 2113 |  |     // Try to sign with all ScriptPubKeyMans | 
| 2114 | 0 |     for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) { | 
| 2115 |  |         // spk_man->SignTransaction will return true if the transaction is complete, | 
| 2116 |  |         // so we can exit early and return true if that happens | 
| 2117 | 0 |         if (spk_man->SignTransaction(tx, coins, sighash, input_errors)) { | 
| 2118 | 0 |             return true; | 
| 2119 | 0 |         } | 
| 2120 | 0 |     } | 
| 2121 |  |  | 
| 2122 |  |     // At this point, one input was not fully signed otherwise we would have exited already | 
| 2123 | 0 |     return false; | 
| 2124 | 0 | } | 
| 2125 |  |  | 
| 2126 |  | std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, std::optional<int> sighash_type, bool sign, bool bip32derivs, size_t * n_signed, bool finalize) const | 
| 2127 | 0 | { | 
| 2128 | 0 |     if (n_signed) { | 
| 2129 | 0 |         *n_signed = 0; | 
| 2130 | 0 |     } | 
| 2131 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2132 |  |     // Get all of the previous transactions | 
| 2133 | 0 |     for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { | 
| 2134 | 0 |         const CTxIn& txin = psbtx.tx->vin[i]; | 
| 2135 | 0 |         PSBTInput& input = psbtx.inputs.at(i); | 
| 2136 |  | 
 | 
| 2137 | 0 |         if (PSBTInputSigned(input)) { | 
| 2138 | 0 |             continue; | 
| 2139 | 0 |         } | 
| 2140 |  |  | 
| 2141 |  |         // If we have no utxo, grab it from the wallet. | 
| 2142 | 0 |         if (!input.non_witness_utxo) { | 
| 2143 | 0 |             const Txid& txhash = txin.prevout.hash; | 
| 2144 | 0 |             const auto it = mapWallet.find(txhash); | 
| 2145 | 0 |             if (it != mapWallet.end()) { | 
| 2146 | 0 |                 const CWalletTx& wtx = it->second; | 
| 2147 |  |                 // We only need the non_witness_utxo, which is a superset of the witness_utxo. | 
| 2148 |  |                 //   The signing code will switch to the smaller witness_utxo if this is ok. | 
| 2149 | 0 |                 input.non_witness_utxo = wtx.tx; | 
| 2150 | 0 |             } | 
| 2151 | 0 |         } | 
| 2152 | 0 |     } | 
| 2153 |  | 
 | 
| 2154 | 0 |     const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx); | 
| 2155 |  |  | 
| 2156 |  |     // Fill in information from ScriptPubKeyMans | 
| 2157 | 0 |     for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) { | 
| 2158 | 0 |         int n_signed_this_spkm = 0; | 
| 2159 | 0 |         const auto error{spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm, finalize)}; | 
| 2160 | 0 |         if (error) { | 
| 2161 | 0 |             return error; | 
| 2162 | 0 |         } | 
| 2163 |  |  | 
| 2164 | 0 |         if (n_signed) { | 
| 2165 | 0 |             (*n_signed) += n_signed_this_spkm; | 
| 2166 | 0 |         } | 
| 2167 | 0 |     } | 
| 2168 |  |  | 
| 2169 | 0 |     RemoveUnnecessaryTransactions(psbtx); | 
| 2170 |  |  | 
| 2171 |  |     // Complete if every input is now signed | 
| 2172 | 0 |     complete = true; | 
| 2173 | 0 |     for (size_t i = 0; i < psbtx.inputs.size(); ++i) { | 
| 2174 | 0 |         complete &= PSBTInputSignedAndVerified(psbtx, i, &txdata); | 
| 2175 | 0 |     } | 
| 2176 |  | 
 | 
| 2177 | 0 |     return {}; | 
| 2178 | 0 | } | 
| 2179 |  |  | 
| 2180 |  | SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const | 
| 2181 | 0 | { | 
| 2182 | 0 |     SignatureData sigdata; | 
| 2183 | 0 |     CScript script_pub_key = GetScriptForDestination(pkhash); | 
| 2184 | 0 |     for (const auto& spk_man_pair : m_spk_managers) { | 
| 2185 | 0 |         if (spk_man_pair.second->CanProvide(script_pub_key, sigdata)) { | 
| 2186 | 0 |             LOCK(cs_wallet);  // DescriptorScriptPubKeyMan calls IsLocked which can lock cs_wallet in a deadlocking order | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2187 | 0 |             return spk_man_pair.second->SignMessage(message, pkhash, str_sig); | 
| 2188 | 0 |         } | 
| 2189 | 0 |     } | 
| 2190 | 0 |     return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; | 
| 2191 | 0 | } | 
| 2192 |  |  | 
| 2193 |  | OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const | 
| 2194 | 0 | { | 
| 2195 |  |     // If -changetype is specified, always use that change type. | 
| 2196 | 0 |     if (change_type) { | 
| 2197 | 0 |         return *change_type; | 
| 2198 | 0 |     } | 
| 2199 |  |  | 
| 2200 |  |     // if m_default_address_type is legacy, use legacy address as change. | 
| 2201 | 0 |     if (m_default_address_type == OutputType::LEGACY) { | 
| 2202 | 0 |         return OutputType::LEGACY; | 
| 2203 | 0 |     } | 
| 2204 |  |  | 
| 2205 | 0 |     bool any_tr{false}; | 
| 2206 | 0 |     bool any_wpkh{false}; | 
| 2207 | 0 |     bool any_sh{false}; | 
| 2208 | 0 |     bool any_pkh{false}; | 
| 2209 |  | 
 | 
| 2210 | 0 |     for (const auto& recipient : vecSend) { | 
| 2211 | 0 |         if (std::get_if<WitnessV1Taproot>(&recipient.dest)) { | 
| 2212 | 0 |             any_tr = true; | 
| 2213 | 0 |         } else if (std::get_if<WitnessV0KeyHash>(&recipient.dest)) { | 
| 2214 | 0 |             any_wpkh = true; | 
| 2215 | 0 |         } else if (std::get_if<ScriptHash>(&recipient.dest)) { | 
| 2216 | 0 |             any_sh = true; | 
| 2217 | 0 |         } else if (std::get_if<PKHash>(&recipient.dest)) { | 
| 2218 | 0 |             any_pkh = true; | 
| 2219 | 0 |         } | 
| 2220 | 0 |     } | 
| 2221 |  | 
 | 
| 2222 | 0 |     const bool has_bech32m_spkman(GetScriptPubKeyMan(OutputType::BECH32M, /*internal=*/true)); | 
| 2223 | 0 |     if (has_bech32m_spkman && any_tr) { | 
| 2224 |  |         // Currently tr is the only type supported by the BECH32M spkman | 
| 2225 | 0 |         return OutputType::BECH32M; | 
| 2226 | 0 |     } | 
| 2227 | 0 |     const bool has_bech32_spkman(GetScriptPubKeyMan(OutputType::BECH32, /*internal=*/true)); | 
| 2228 | 0 |     if (has_bech32_spkman && any_wpkh) { | 
| 2229 |  |         // Currently wpkh is the only type supported by the BECH32 spkman | 
| 2230 | 0 |         return OutputType::BECH32; | 
| 2231 | 0 |     } | 
| 2232 | 0 |     const bool has_p2sh_segwit_spkman(GetScriptPubKeyMan(OutputType::P2SH_SEGWIT, /*internal=*/true)); | 
| 2233 | 0 |     if (has_p2sh_segwit_spkman && any_sh) { | 
| 2234 |  |         // Currently sh_wpkh is the only type supported by the P2SH_SEGWIT spkman | 
| 2235 |  |         // As of 2021 about 80% of all SH are wrapping WPKH, so use that | 
| 2236 | 0 |         return OutputType::P2SH_SEGWIT; | 
| 2237 | 0 |     } | 
| 2238 | 0 |     const bool has_legacy_spkman(GetScriptPubKeyMan(OutputType::LEGACY, /*internal=*/true)); | 
| 2239 | 0 |     if (has_legacy_spkman && any_pkh) { | 
| 2240 |  |         // Currently pkh is the only type supported by the LEGACY spkman | 
| 2241 | 0 |         return OutputType::LEGACY; | 
| 2242 | 0 |     } | 
| 2243 |  |  | 
| 2244 | 0 |     if (has_bech32m_spkman) { | 
| 2245 | 0 |         return OutputType::BECH32M; | 
| 2246 | 0 |     } | 
| 2247 | 0 |     if (has_bech32_spkman) { | 
| 2248 | 0 |         return OutputType::BECH32; | 
| 2249 | 0 |     } | 
| 2250 |  |     // else use m_default_address_type for change | 
| 2251 | 0 |     return m_default_address_type; | 
| 2252 | 0 | } | 
| 2253 |  |  | 
| 2254 |  | void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm) | 
| 2255 | 0 | { | 
| 2256 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2257 | 0 |     WalletLogPrintf("CommitTransaction:\n%s\n", util::RemoveSuffixView(tx->ToString(), "\n")); | 
| 2258 |  |  | 
| 2259 |  |     // Add tx to wallet, because if it has change it's also ours, | 
| 2260 |  |     // otherwise just for transaction history. | 
| 2261 | 0 |     CWalletTx* wtx = AddToWallet(tx, TxStateInactive{}, [&](CWalletTx& wtx, bool new_tx) { | 
| 2262 | 0 |         CHECK_NONFATAL(wtx.mapValue.empty()); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 2263 | 0 |         CHECK_NONFATAL(wtx.vOrderForm.empty()); | Line | Count | Source |  | 103 | 0 |     inline_check_non_fatal(condition, __FILE__, __LINE__, __func__, #condition) | 
 | 
| 2264 | 0 |         wtx.mapValue = std::move(mapValue); | 
| 2265 | 0 |         wtx.vOrderForm = std::move(orderForm); | 
| 2266 | 0 |         return true; | 
| 2267 | 0 |     }); | 
| 2268 |  |  | 
| 2269 |  |     // wtx can only be null if the db write failed. | 
| 2270 | 0 |     if (!wtx) { | 
| 2271 | 0 |         throw std::runtime_error(std::string(__func__) + ": Wallet db error, transaction commit failed"); | 
| 2272 | 0 |     } | 
| 2273 |  |  | 
| 2274 |  |     // Notify that old coins are spent | 
| 2275 | 0 |     for (const CTxIn& txin : tx->vin) { | 
| 2276 | 0 |         CWalletTx &coin = mapWallet.at(txin.prevout.hash); | 
| 2277 | 0 |         coin.MarkDirty(); | 
| 2278 | 0 |         NotifyTransactionChanged(coin.GetHash(), CT_UPDATED); | 
| 2279 | 0 |     } | 
| 2280 |  | 
 | 
| 2281 | 0 |     if (!fBroadcastTransactions) { | 
| 2282 |  |         // Don't submit tx to the mempool | 
| 2283 | 0 |         return; | 
| 2284 | 0 |     } | 
| 2285 |  |  | 
| 2286 | 0 |     std::string err_string; | 
| 2287 | 0 |     if (!SubmitTxMemoryPoolAndRelay(*wtx, err_string, true)) { | 
| 2288 | 0 |         WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string); | 
| 2289 |  |         // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. | 
| 2290 | 0 |     } | 
| 2291 | 0 | } | 
| 2292 |  |  | 
| 2293 |  | DBErrors CWallet::LoadWallet() | 
| 2294 | 0 | { | 
| 2295 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2296 |  | 
 | 
| 2297 | 0 |     Assert(m_spk_managers.empty()); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 2298 | 0 |     Assert(m_wallet_flags == 0); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 2299 | 0 |     DBErrors nLoadWalletRet = WalletBatch(GetDatabase()).LoadWallet(this); | 
| 2300 | 0 |     if (nLoadWalletRet == DBErrors::NEED_REWRITE) | 
| 2301 | 0 |     { | 
| 2302 | 0 |         if (GetDatabase().Rewrite()) | 
| 2303 | 0 |         { | 
| 2304 | 0 |             for (const auto& spk_man_pair : m_spk_managers) { | 
| 2305 | 0 |                 spk_man_pair.second->RewriteDB(); | 
| 2306 | 0 |             } | 
| 2307 | 0 |         } | 
| 2308 | 0 |     } | 
| 2309 |  | 
 | 
| 2310 | 0 |     if (m_spk_managers.empty()) { | 
| 2311 | 0 |         assert(m_external_spk_managers.empty()); | 
| 2312 | 0 |         assert(m_internal_spk_managers.empty()); | 
| 2313 | 0 |     } | 
| 2314 |  |  | 
| 2315 | 0 |     return nLoadWalletRet; | 
| 2316 | 0 | } | 
| 2317 |  |  | 
| 2318 |  | util::Result<void> CWallet::RemoveTxs(std::vector<Txid>& txs_to_remove) | 
| 2319 | 0 | { | 
| 2320 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2321 | 0 |     bilingual_str str_err;  // future: make RunWithinTxn return a util::Result | 
| 2322 | 0 |     bool was_txn_committed = RunWithinTxn(GetDatabase(), /*process_desc=*/"remove transactions", [&](WalletBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { | 
| 2323 | 0 |         util::Result<void> result{RemoveTxs(batch, txs_to_remove)}; | 
| 2324 | 0 |         if (!result) str_err = util::ErrorString(result); | 
| 2325 | 0 |         return result.has_value(); | 
| 2326 | 0 |     }); | 
| 2327 | 0 |     if (!str_err.empty()) return util::Error{str_err}; | 
| 2328 | 0 |     if (!was_txn_committed) return util::Error{_("Error starting/committing db txn for wallet transactions removal process")}; | 
| 2329 | 0 |     return {}; // all good | 
| 2330 | 0 | } | 
| 2331 |  |  | 
| 2332 |  | util::Result<void> CWallet::RemoveTxs(WalletBatch& batch, std::vector<Txid>& txs_to_remove) | 
| 2333 | 0 | { | 
| 2334 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2335 | 0 |     if (!batch.HasActiveTxn()) return util::Error{strprintf(_("The transactions removal process can only be executed within a db txn"))};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2336 |  |  | 
| 2337 |  |     // Check for transaction existence and remove entries from disk | 
| 2338 | 0 |     std::vector<decltype(mapWallet)::const_iterator> erased_txs; | 
| 2339 | 0 |     bilingual_str str_err; | 
| 2340 | 0 |     for (const Txid& hash : txs_to_remove) { | 
| 2341 | 0 |         auto it_wtx = mapWallet.find(hash); | 
| 2342 | 0 |         if (it_wtx == mapWallet.end()) { | 
| 2343 | 0 |             return util::Error{strprintf(_("Transaction %s does not belong to this wallet"), hash.GetHex())};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2344 | 0 |         } | 
| 2345 | 0 |         if (!batch.EraseTx(hash)) { | 
| 2346 | 0 |             return util::Error{strprintf(_("Failure removing transaction: %s"), hash.GetHex())};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2347 | 0 |         } | 
| 2348 | 0 |         erased_txs.emplace_back(it_wtx); | 
| 2349 | 0 |     } | 
| 2350 |  |  | 
| 2351 |  |     // Register callback to update the memory state only when the db txn is actually dumped to disk | 
| 2352 | 0 |     batch.RegisterTxnListener({.on_commit=[&, erased_txs]() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { | 
| 2353 |  |         // Update the in-memory state and notify upper layers about the removals | 
| 2354 | 0 |         for (const auto& it : erased_txs) { | 
| 2355 | 0 |             const Txid hash{it->first}; | 
| 2356 | 0 |             wtxOrdered.erase(it->second.m_it_wtxOrdered); | 
| 2357 | 0 |             for (const auto& txin : it->second.tx->vin) | 
| 2358 | 0 |                 mapTxSpends.erase(txin.prevout); | 
| 2359 | 0 |             for (unsigned int i = 0; i < it->second.tx->vout.size(); ++i) { | 
| 2360 | 0 |                 m_txos.erase(COutPoint(hash, i)); | 
| 2361 | 0 |             } | 
| 2362 | 0 |             mapWallet.erase(it); | 
| 2363 | 0 |             NotifyTransactionChanged(hash, CT_DELETED); | 
| 2364 | 0 |         } | 
| 2365 |  | 
 | 
| 2366 | 0 |         MarkDirty(); | 
| 2367 | 0 |     }, .on_abort={}}); | 
| 2368 |  | 
 | 
| 2369 | 0 |     return {}; | 
| 2370 | 0 | } | 
| 2371 |  |  | 
| 2372 |  | bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::optional<AddressPurpose>& new_purpose) | 
| 2373 | 0 | { | 
| 2374 | 0 |     bool fUpdated = false; | 
| 2375 | 0 |     bool is_mine; | 
| 2376 | 0 |     std::optional<AddressPurpose> purpose; | 
| 2377 | 0 |     { | 
| 2378 | 0 |         LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2379 | 0 |         std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address); | 
| 2380 | 0 |         fUpdated = mi != m_address_book.end() && !mi->second.IsChange(); | 
| 2381 |  | 
 | 
| 2382 | 0 |         CAddressBookData& record = mi != m_address_book.end() ? mi->second : m_address_book[address]; | 
| 2383 | 0 |         record.SetLabel(strName); | 
| 2384 | 0 |         is_mine = IsMine(address); | 
| 2385 | 0 |         if (new_purpose) { /* update purpose only if requested */ | 
| 2386 | 0 |             record.purpose = new_purpose; | 
| 2387 | 0 |         } | 
| 2388 | 0 |         purpose = record.purpose; | 
| 2389 | 0 |     } | 
| 2390 |  | 
 | 
| 2391 | 0 |     const std::string& encoded_dest = EncodeDestination(address); | 
| 2392 | 0 |     if (new_purpose && !batch.WritePurpose(encoded_dest, PurposeToString(*new_purpose))) { | 
| 2393 | 0 |         WalletLogPrintf("Error: fail to write address book 'purpose' entry\n"); | 
| 2394 | 0 |         return false; | 
| 2395 | 0 |     } | 
| 2396 | 0 |     if (!batch.WriteName(encoded_dest, strName)) { | 
| 2397 | 0 |         WalletLogPrintf("Error: fail to write address book 'name' entry\n"); | 
| 2398 | 0 |         return false; | 
| 2399 | 0 |     } | 
| 2400 |  |  | 
| 2401 |  |     // In very old wallets, address purpose may not be recorded so we derive it from IsMine | 
| 2402 | 0 |     NotifyAddressBookChanged(address, strName, is_mine, | 
| 2403 | 0 |                              purpose.value_or(is_mine ? AddressPurpose::RECEIVE : AddressPurpose::SEND), | 
| 2404 | 0 |                              (fUpdated ? CT_UPDATED : CT_NEW)); | 
| 2405 | 0 |     return true; | 
| 2406 | 0 | } | 
| 2407 |  |  | 
| 2408 |  | bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::optional<AddressPurpose>& purpose) | 
| 2409 | 0 | { | 
| 2410 | 0 |     WalletBatch batch(GetDatabase()); | 
| 2411 | 0 |     return SetAddressBookWithDB(batch, address, strName, purpose); | 
| 2412 | 0 | } | 
| 2413 |  |  | 
| 2414 |  | bool CWallet::DelAddressBook(const CTxDestination& address) | 
| 2415 | 0 | { | 
| 2416 | 0 |     return RunWithinTxn(GetDatabase(), /*process_desc=*/"address book entry removal", [&](WalletBatch& batch){ | 
| 2417 | 0 |         return DelAddressBookWithDB(batch, address); | 
| 2418 | 0 |     }); | 
| 2419 | 0 | } | 
| 2420 |  |  | 
| 2421 |  | bool CWallet::DelAddressBookWithDB(WalletBatch& batch, const CTxDestination& address) | 
| 2422 | 0 | { | 
| 2423 | 0 |     const std::string& dest = EncodeDestination(address); | 
| 2424 | 0 |     { | 
| 2425 | 0 |         LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2426 |  |         // 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. | 
| 2427 |  |         // NOTE: This isn't a problem for sending addresses because they don't have any data that needs to be kept. | 
| 2428 |  |         // When adding new address data, it should be considered here whether to retain or delete it. | 
| 2429 | 0 |         if (IsMine(address)) { | 
| 2430 | 0 |             WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, CLIENT_BUGREPORT);| Line | Count | Source |  | 95 | 0 | #define CLIENT_BUGREPORT "https://github.com/bitcoin/bitcoin/issues" | 
 | 
| 2431 | 0 |             return false; | 
| 2432 | 0 |         } | 
| 2433 |  |         // Delete data rows associated with this address | 
| 2434 | 0 |         if (!batch.EraseAddressData(address)) { | 
| 2435 | 0 |             WalletLogPrintf("Error: cannot erase address book entry data\n"); | 
| 2436 | 0 |             return false; | 
| 2437 | 0 |         } | 
| 2438 |  |  | 
| 2439 |  |         // Delete purpose entry | 
| 2440 | 0 |         if (!batch.ErasePurpose(dest)) { | 
| 2441 | 0 |             WalletLogPrintf("Error: cannot erase address book entry purpose\n"); | 
| 2442 | 0 |             return false; | 
| 2443 | 0 |         } | 
| 2444 |  |  | 
| 2445 |  |         // Delete name entry | 
| 2446 | 0 |         if (!batch.EraseName(dest)) { | 
| 2447 | 0 |             WalletLogPrintf("Error: cannot erase address book entry name\n"); | 
| 2448 | 0 |             return false; | 
| 2449 | 0 |         } | 
| 2450 |  |  | 
| 2451 |  |         // finally, remove it from the map | 
| 2452 | 0 |         m_address_book.erase(address); | 
| 2453 | 0 |     } | 
| 2454 |  |  | 
| 2455 |  |     // All good, signal changes | 
| 2456 | 0 |     NotifyAddressBookChanged(address, "", /*is_mine=*/false, AddressPurpose::SEND, CT_DELETED); | 
| 2457 | 0 |     return true; | 
| 2458 | 0 | } | 
| 2459 |  |  | 
| 2460 |  | size_t CWallet::KeypoolCountExternalKeys() const | 
| 2461 | 0 | { | 
| 2462 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2463 |  | 
 | 
| 2464 | 0 |     unsigned int count = 0; | 
| 2465 | 0 |     for (auto spk_man : m_external_spk_managers) { | 
| 2466 | 0 |         count += spk_man.second->GetKeyPoolSize(); | 
| 2467 | 0 |     } | 
| 2468 |  | 
 | 
| 2469 | 0 |     return count; | 
| 2470 | 0 | } | 
| 2471 |  |  | 
| 2472 |  | unsigned int CWallet::GetKeyPoolSize() const | 
| 2473 | 0 | { | 
| 2474 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2475 |  | 
 | 
| 2476 | 0 |     unsigned int count = 0; | 
| 2477 | 0 |     for (auto spk_man : GetActiveScriptPubKeyMans()) { | 
| 2478 | 0 |         count += spk_man->GetKeyPoolSize(); | 
| 2479 | 0 |     } | 
| 2480 | 0 |     return count; | 
| 2481 | 0 | } | 
| 2482 |  |  | 
| 2483 |  | bool CWallet::TopUpKeyPool(unsigned int kpSize) | 
| 2484 | 0 | { | 
| 2485 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2486 | 0 |     bool res = true; | 
| 2487 | 0 |     for (auto spk_man : GetActiveScriptPubKeyMans()) { | 
| 2488 | 0 |         res &= spk_man->TopUp(kpSize); | 
| 2489 | 0 |     } | 
| 2490 | 0 |     return res; | 
| 2491 | 0 | } | 
| 2492 |  |  | 
| 2493 |  | util::Result<CTxDestination> CWallet::GetNewDestination(const OutputType type, const std::string label) | 
| 2494 | 0 | { | 
| 2495 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2496 | 0 |     auto spk_man = GetScriptPubKeyMan(type, /*internal=*/false); | 
| 2497 | 0 |     if (!spk_man) { | 
| 2498 | 0 |         return util::Error{strprintf(_("Error: No %s addresses available."), FormatOutputType(type))};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2499 | 0 |     } | 
| 2500 |  |  | 
| 2501 | 0 |     auto op_dest = spk_man->GetNewDestination(type); | 
| 2502 | 0 |     if (op_dest) { | 
| 2503 | 0 |         SetAddressBook(*op_dest, label, AddressPurpose::RECEIVE); | 
| 2504 | 0 |     } | 
| 2505 |  | 
 | 
| 2506 | 0 |     return op_dest; | 
| 2507 | 0 | } | 
| 2508 |  |  | 
| 2509 |  | util::Result<CTxDestination> CWallet::GetNewChangeDestination(const OutputType type) | 
| 2510 | 0 | { | 
| 2511 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2512 |  | 
 | 
| 2513 | 0 |     ReserveDestination reservedest(this, type); | 
| 2514 | 0 |     auto op_dest = reservedest.GetReservedDestination(true); | 
| 2515 | 0 |     if (op_dest) reservedest.KeepDestination(); | 
| 2516 |  | 
 | 
| 2517 | 0 |     return op_dest; | 
| 2518 | 0 | } | 
| 2519 |  |  | 
| 2520 | 0 | void CWallet::MarkDestinationsDirty(const std::set<CTxDestination>& destinations) { | 
| 2521 | 0 |     for (auto& entry : mapWallet) { | 
| 2522 | 0 |         CWalletTx& wtx = entry.second; | 
| 2523 | 0 |         if (wtx.m_is_cache_empty) continue; | 
| 2524 | 0 |         for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { | 
| 2525 | 0 |             CTxDestination dst; | 
| 2526 | 0 |             if (ExtractDestination(wtx.tx->vout[i].scriptPubKey, dst) && destinations.count(dst)) { | 
| 2527 | 0 |                 wtx.MarkDirty(); | 
| 2528 | 0 |                 break; | 
| 2529 | 0 |             } | 
| 2530 | 0 |         } | 
| 2531 | 0 |     } | 
| 2532 | 0 | } | 
| 2533 |  |  | 
| 2534 |  | void CWallet::ForEachAddrBookEntry(const ListAddrBookFunc& func) const | 
| 2535 | 0 | { | 
| 2536 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2537 | 0 |     for (const std::pair<const CTxDestination, CAddressBookData>& item : m_address_book) { | 
| 2538 | 0 |         const auto& entry = item.second; | 
| 2539 | 0 |         func(item.first, entry.GetLabel(), entry.IsChange(), entry.purpose); | 
| 2540 | 0 |     } | 
| 2541 | 0 | } | 
| 2542 |  |  | 
| 2543 |  | std::vector<CTxDestination> CWallet::ListAddrBookAddresses(const std::optional<AddrBookFilter>& _filter) const | 
| 2544 | 0 | { | 
| 2545 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2546 | 0 |     std::vector<CTxDestination> result; | 
| 2547 | 0 |     AddrBookFilter filter = _filter ? *_filter : AddrBookFilter(); | 
| 2548 | 0 |     ForEachAddrBookEntry([&result, &filter](const CTxDestination& dest, const std::string& label, bool is_change, const std::optional<AddressPurpose>& purpose) { | 
| 2549 |  |         // Filter by change | 
| 2550 | 0 |         if (filter.ignore_change && is_change) return; | 
| 2551 |  |         // Filter by label | 
| 2552 | 0 |         if (filter.m_op_label && *filter.m_op_label != label) return; | 
| 2553 |  |         // All good | 
| 2554 | 0 |         result.emplace_back(dest); | 
| 2555 | 0 |     }); | 
| 2556 | 0 |     return result; | 
| 2557 | 0 | } | 
| 2558 |  |  | 
| 2559 |  | std::set<std::string> CWallet::ListAddrBookLabels(const std::optional<AddressPurpose> purpose) const | 
| 2560 | 0 | { | 
| 2561 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2562 | 0 |     std::set<std::string> label_set; | 
| 2563 | 0 |     ForEachAddrBookEntry([&](const CTxDestination& _dest, const std::string& _label, | 
| 2564 | 0 |                              bool _is_change, const std::optional<AddressPurpose>& _purpose) { | 
| 2565 | 0 |         if (_is_change) return; | 
| 2566 | 0 |         if (!purpose || purpose == _purpose) { | 
| 2567 | 0 |             label_set.insert(_label); | 
| 2568 | 0 |         } | 
| 2569 | 0 |     }); | 
| 2570 | 0 |     return label_set; | 
| 2571 | 0 | } | 
| 2572 |  |  | 
| 2573 |  | util::Result<CTxDestination> ReserveDestination::GetReservedDestination(bool internal) | 
| 2574 | 0 | { | 
| 2575 | 0 |     m_spk_man = pwallet->GetScriptPubKeyMan(type, internal); | 
| 2576 | 0 |     if (!m_spk_man) { | 
| 2577 | 0 |         return util::Error{strprintf(_("Error: No %s addresses available."), FormatOutputType(type))};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2578 | 0 |     } | 
| 2579 |  |  | 
| 2580 | 0 |     if (nIndex == -1) { | 
| 2581 | 0 |         int64_t index; | 
| 2582 | 0 |         auto op_address = m_spk_man->GetReservedDestination(type, internal, index); | 
| 2583 | 0 |         if (!op_address) return op_address; | 
| 2584 | 0 |         nIndex = index; | 
| 2585 | 0 |         address = *op_address; | 
| 2586 | 0 |     } | 
| 2587 | 0 |     return address; | 
| 2588 | 0 | } | 
| 2589 |  |  | 
| 2590 |  | void ReserveDestination::KeepDestination() | 
| 2591 | 0 | { | 
| 2592 | 0 |     if (nIndex != -1) { | 
| 2593 | 0 |         m_spk_man->KeepDestination(nIndex, type); | 
| 2594 | 0 |     } | 
| 2595 | 0 |     nIndex = -1; | 
| 2596 | 0 |     address = CNoDestination(); | 
| 2597 | 0 | } | 
| 2598 |  |  | 
| 2599 |  | void ReserveDestination::ReturnDestination() | 
| 2600 | 0 | { | 
| 2601 | 0 |     if (nIndex != -1) { | 
| 2602 | 0 |         m_spk_man->ReturnDestination(nIndex, fInternal, address); | 
| 2603 | 0 |     } | 
| 2604 | 0 |     nIndex = -1; | 
| 2605 | 0 |     address = CNoDestination(); | 
| 2606 | 0 | } | 
| 2607 |  |  | 
| 2608 |  | util::Result<void> CWallet::DisplayAddress(const CTxDestination& dest) | 
| 2609 | 0 | { | 
| 2610 | 0 |     CScript scriptPubKey = GetScriptForDestination(dest); | 
| 2611 | 0 |     for (const auto& spk_man : GetScriptPubKeyMans(scriptPubKey)) { | 
| 2612 | 0 |         auto signer_spk_man = dynamic_cast<ExternalSignerScriptPubKeyMan *>(spk_man); | 
| 2613 | 0 |         if (signer_spk_man == nullptr) { | 
| 2614 | 0 |             continue; | 
| 2615 | 0 |         } | 
| 2616 | 0 |         auto signer{ExternalSignerScriptPubKeyMan::GetExternalSigner()}; | 
| 2617 | 0 |         if (!signer) throw std::runtime_error(util::ErrorString(signer).original); | 
| 2618 | 0 |         return signer_spk_man->DisplayAddress(dest, *signer); | 
| 2619 | 0 |     } | 
| 2620 | 0 |     return util::Error{_("There is no ScriptPubKeyManager for this address")}; | 
| 2621 | 0 | } | 
| 2622 |  |  | 
| 2623 |  | void CWallet::LoadLockedCoin(const COutPoint& coin, bool persistent) | 
| 2624 | 0 | { | 
| 2625 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2626 | 0 |     m_locked_coins.emplace(coin, persistent); | 
| 2627 | 0 | } | 
| 2628 |  |  | 
| 2629 |  | bool CWallet::LockCoin(const COutPoint& output, bool persist) | 
| 2630 | 0 | { | 
| 2631 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2632 | 0 |     LoadLockedCoin(output, persist); | 
| 2633 | 0 |     if (persist) { | 
| 2634 | 0 |         WalletBatch batch(GetDatabase()); | 
| 2635 | 0 |         return batch.WriteLockedUTXO(output); | 
| 2636 | 0 |     } | 
| 2637 | 0 |     return true; | 
| 2638 | 0 | } | 
| 2639 |  |  | 
| 2640 |  | bool CWallet::UnlockCoin(const COutPoint& output) | 
| 2641 | 0 | { | 
| 2642 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2643 | 0 |     auto locked_coin_it = m_locked_coins.find(output); | 
| 2644 | 0 |     if (locked_coin_it != m_locked_coins.end()) { | 
| 2645 | 0 |         bool persisted = locked_coin_it->second; | 
| 2646 | 0 |         m_locked_coins.erase(locked_coin_it); | 
| 2647 | 0 |         if (persisted) { | 
| 2648 | 0 |             WalletBatch batch(GetDatabase()); | 
| 2649 | 0 |             return batch.EraseLockedUTXO(output); | 
| 2650 | 0 |         } | 
| 2651 | 0 |     } | 
| 2652 | 0 |     return true; | 
| 2653 | 0 | } | 
| 2654 |  |  | 
| 2655 |  | bool CWallet::UnlockAllCoins() | 
| 2656 | 0 | { | 
| 2657 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2658 | 0 |     bool success = true; | 
| 2659 | 0 |     WalletBatch batch(GetDatabase()); | 
| 2660 | 0 |     for (const auto& [coin, persistent] : m_locked_coins) { | 
| 2661 | 0 |         if (persistent) success = success && batch.EraseLockedUTXO(coin); | 
| 2662 | 0 |     } | 
| 2663 | 0 |     m_locked_coins.clear(); | 
| 2664 | 0 |     return success; | 
| 2665 | 0 | } | 
| 2666 |  |  | 
| 2667 |  | bool CWallet::IsLockedCoin(const COutPoint& output) const | 
| 2668 | 0 | { | 
| 2669 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2670 | 0 |     return m_locked_coins.count(output) > 0; | 
| 2671 | 0 | } | 
| 2672 |  |  | 
| 2673 |  | void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const | 
| 2674 | 0 | { | 
| 2675 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 2676 | 0 |     for (const auto& [coin, _] : m_locked_coins) { | 
| 2677 | 0 |         vOutpts.push_back(coin); | 
| 2678 | 0 |     } | 
| 2679 | 0 | } | 
| 2680 |  |  | 
| 2681 |  | /** | 
| 2682 |  |  * Compute smart timestamp for a transaction being added to the wallet. | 
| 2683 |  |  * | 
| 2684 |  |  * Logic: | 
| 2685 |  |  * - If sending a transaction, assign its timestamp to the current time. | 
| 2686 |  |  * - If receiving a transaction outside a block, assign its timestamp to the | 
| 2687 |  |  *   current time. | 
| 2688 |  |  * - If receiving a transaction during a rescanning process, assign all its | 
| 2689 |  |  *   (not already known) transactions' timestamps to the block time. | 
| 2690 |  |  * - If receiving a block with a future timestamp, assign all its (not already | 
| 2691 |  |  *   known) transactions' timestamps to the current time. | 
| 2692 |  |  * - If receiving a block with a past timestamp, before the most recent known | 
| 2693 |  |  *   transaction (that we care about), assign all its (not already known) | 
| 2694 |  |  *   transactions' timestamps to the same timestamp as that most-recent-known | 
| 2695 |  |  *   transaction. | 
| 2696 |  |  * - If receiving a block with a past timestamp, but after the most recent known | 
| 2697 |  |  *   transaction, assign all its (not already known) transactions' timestamps to | 
| 2698 |  |  *   the block time. | 
| 2699 |  |  * | 
| 2700 |  |  * For more information see CWalletTx::nTimeSmart, | 
| 2701 |  |  * https://bitcointalk.org/?topic=54527, or | 
| 2702 |  |  * https://github.com/bitcoin/bitcoin/pull/1393. | 
| 2703 |  |  */ | 
| 2704 |  | unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const | 
| 2705 | 0 | { | 
| 2706 | 0 |     std::optional<uint256> block_hash; | 
| 2707 | 0 |     if (auto* conf = wtx.state<TxStateConfirmed>()) { | 
| 2708 | 0 |         block_hash = conf->confirmed_block_hash; | 
| 2709 | 0 |     } else if (auto* conf = wtx.state<TxStateBlockConflicted>()) { | 
| 2710 | 0 |         block_hash = conf->conflicting_block_hash; | 
| 2711 | 0 |     } | 
| 2712 |  | 
 | 
| 2713 | 0 |     unsigned int nTimeSmart = wtx.nTimeReceived; | 
| 2714 | 0 |     if (block_hash) { | 
| 2715 | 0 |         int64_t blocktime; | 
| 2716 | 0 |         int64_t block_max_time; | 
| 2717 | 0 |         if (chain().findBlock(*block_hash, FoundBlock().time(blocktime).maxTime(block_max_time))) { | 
| 2718 | 0 |             if (rescanning_old_block) { | 
| 2719 | 0 |                 nTimeSmart = block_max_time; | 
| 2720 | 0 |             } else { | 
| 2721 | 0 |                 int64_t latestNow = wtx.nTimeReceived; | 
| 2722 | 0 |                 int64_t latestEntry = 0; | 
| 2723 |  |  | 
| 2724 |  |                 // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future | 
| 2725 | 0 |                 int64_t latestTolerated = latestNow + 300; | 
| 2726 | 0 |                 const TxItems& txOrdered = wtxOrdered; | 
| 2727 | 0 |                 for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { | 
| 2728 | 0 |                     CWalletTx* const pwtx = it->second; | 
| 2729 | 0 |                     if (pwtx == &wtx) { | 
| 2730 | 0 |                         continue; | 
| 2731 | 0 |                     } | 
| 2732 | 0 |                     int64_t nSmartTime; | 
| 2733 | 0 |                     nSmartTime = pwtx->nTimeSmart; | 
| 2734 | 0 |                     if (!nSmartTime) { | 
| 2735 | 0 |                         nSmartTime = pwtx->nTimeReceived; | 
| 2736 | 0 |                     } | 
| 2737 | 0 |                     if (nSmartTime <= latestTolerated) { | 
| 2738 | 0 |                         latestEntry = nSmartTime; | 
| 2739 | 0 |                         if (nSmartTime > latestNow) { | 
| 2740 | 0 |                             latestNow = nSmartTime; | 
| 2741 | 0 |                         } | 
| 2742 | 0 |                         break; | 
| 2743 | 0 |                     } | 
| 2744 | 0 |                 } | 
| 2745 |  | 
 | 
| 2746 | 0 |                 nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); | 
| 2747 | 0 |             } | 
| 2748 | 0 |         } else { | 
| 2749 | 0 |             WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), block_hash->ToString()); | 
| 2750 | 0 |         } | 
| 2751 | 0 |     } | 
| 2752 | 0 |     return nTimeSmart; | 
| 2753 | 0 | } | 
| 2754 |  |  | 
| 2755 |  | bool CWallet::SetAddressPreviouslySpent(WalletBatch& batch, const CTxDestination& dest, bool used) | 
| 2756 | 0 | { | 
| 2757 | 0 |     if (std::get_if<CNoDestination>(&dest)) | 
| 2758 | 0 |         return false; | 
| 2759 |  |  | 
| 2760 | 0 |     if (!used) { | 
| 2761 | 0 |         if (auto* data{common::FindKey(m_address_book, dest)}) data->previously_spent = false; | 
| 2762 | 0 |         return batch.WriteAddressPreviouslySpent(dest, false); | 
| 2763 | 0 |     } | 
| 2764 |  |  | 
| 2765 | 0 |     LoadAddressPreviouslySpent(dest); | 
| 2766 | 0 |     return batch.WriteAddressPreviouslySpent(dest, true); | 
| 2767 | 0 | } | 
| 2768 |  |  | 
| 2769 |  | void CWallet::LoadAddressPreviouslySpent(const CTxDestination& dest) | 
| 2770 | 0 | { | 
| 2771 | 0 |     m_address_book[dest].previously_spent = true; | 
| 2772 | 0 | } | 
| 2773 |  |  | 
| 2774 |  | void CWallet::LoadAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& request) | 
| 2775 | 0 | { | 
| 2776 | 0 |     m_address_book[dest].receive_requests[id] = request; | 
| 2777 | 0 | } | 
| 2778 |  |  | 
| 2779 |  | bool CWallet::IsAddressPreviouslySpent(const CTxDestination& dest) const | 
| 2780 | 0 | { | 
| 2781 | 0 |     if (auto* data{common::FindKey(m_address_book, dest)}) return data->previously_spent; | 
| 2782 | 0 |     return false; | 
| 2783 | 0 | } | 
| 2784 |  |  | 
| 2785 |  | std::vector<std::string> CWallet::GetAddressReceiveRequests() const | 
| 2786 | 0 | { | 
| 2787 | 0 |     std::vector<std::string> values; | 
| 2788 | 0 |     for (const auto& [dest, entry] : m_address_book) { | 
| 2789 | 0 |         for (const auto& [id, request] : entry.receive_requests) { | 
| 2790 | 0 |             values.emplace_back(request); | 
| 2791 | 0 |         } | 
| 2792 | 0 |     } | 
| 2793 | 0 |     return values; | 
| 2794 | 0 | } | 
| 2795 |  |  | 
| 2796 |  | bool CWallet::SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value) | 
| 2797 | 0 | { | 
| 2798 | 0 |     if (!batch.WriteAddressReceiveRequest(dest, id, value)) return false; | 
| 2799 | 0 |     m_address_book[dest].receive_requests[id] = value; | 
| 2800 | 0 |     return true; | 
| 2801 | 0 | } | 
| 2802 |  |  | 
| 2803 |  | bool CWallet::EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id) | 
| 2804 | 0 | { | 
| 2805 | 0 |     if (!batch.EraseAddressReceiveRequest(dest, id)) return false; | 
| 2806 | 0 |     m_address_book[dest].receive_requests.erase(id); | 
| 2807 | 0 |     return true; | 
| 2808 | 0 | } | 
| 2809 |  |  | 
| 2810 |  | static util::Result<fs::path> GetWalletPath(const std::string& name) | 
| 2811 | 0 | { | 
| 2812 |  |     // Do some checking on wallet path. It should be either a: | 
| 2813 |  |     // | 
| 2814 |  |     // 1. Path where a directory can be created. | 
| 2815 |  |     // 2. Path to an existing directory. | 
| 2816 |  |     // 3. Path to a symlink to a directory. | 
| 2817 |  |     // 4. For backwards compatibility, the name of a data file in -walletdir. | 
| 2818 | 0 |     const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name)); | 
| 2819 | 0 |     fs::file_type path_type = fs::symlink_status(wallet_path).type(); | 
| 2820 | 0 |     if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory || | 
| 2821 | 0 |           (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) || | 
| 2822 | 0 |           (path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) { | 
| 2823 | 0 |         return util::Error{Untranslated(strprintf(| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2824 | 0 |               "Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " | 
| 2825 | 0 |               "database/log.?????????? files can be stored, a location where such a directory could be created, " | 
| 2826 | 0 |               "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)", | 
| 2827 | 0 |               name, fs::quoted(fs::PathToString(GetWalletDir()))))}; | 
| 2828 | 0 |     } | 
| 2829 | 0 |     return wallet_path; | 
| 2830 | 0 | } | 
| 2831 |  |  | 
| 2832 |  | std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error_string) | 
| 2833 | 0 | { | 
| 2834 | 0 |     const auto& wallet_path = GetWalletPath(name); | 
| 2835 | 0 |     if (!wallet_path) { | 
| 2836 | 0 |         error_string = util::ErrorString(wallet_path); | 
| 2837 | 0 |         status = DatabaseStatus::FAILED_BAD_PATH; | 
| 2838 | 0 |         return nullptr; | 
| 2839 | 0 |     } | 
| 2840 | 0 |     return MakeDatabase(*wallet_path, options, status, error_string); | 
| 2841 | 0 | } | 
| 2842 |  |  | 
| 2843 |  | 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) | 
| 2844 | 0 | { | 
| 2845 | 0 |     interfaces::Chain* chain = context.chain; | 
| 2846 | 0 |     ArgsManager& args = *Assert(context.args); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 2847 | 0 |     const std::string& walletFile = database->Filename(); | 
| 2848 |  | 
 | 
| 2849 | 0 |     const auto start{SteadyClock::now()}; | 
| 2850 |  |     // TODO: Can't use std::make_shared because we need a custom deleter but | 
| 2851 |  |     // should be possible to use std::allocate_shared. | 
| 2852 | 0 |     std::shared_ptr<CWallet> walletInstance(new CWallet(chain, name, std::move(database)), FlushAndDeleteWallet); | 
| 2853 | 0 |     walletInstance->m_keypool_size = std::max(args.GetIntArg("-keypool", DEFAULT_KEYPOOL_SIZE), int64_t{1}); | 
| 2854 | 0 |     walletInstance->m_notify_tx_changed_script = args.GetArg("-walletnotify", ""); | 
| 2855 |  |  | 
| 2856 |  |     // Load wallet | 
| 2857 | 0 |     bool rescan_required = false; | 
| 2858 | 0 |     DBErrors nLoadWalletRet = walletInstance->LoadWallet(); | 
| 2859 | 0 |     if (nLoadWalletRet != DBErrors::LOAD_OK) { | 
| 2860 | 0 |         if (nLoadWalletRet == DBErrors::CORRUPT) { | 
| 2861 | 0 |             error = strprintf(_("Error loading %s: Wallet corrupted"), walletFile);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2862 | 0 |             return nullptr; | 
| 2863 | 0 |         } | 
| 2864 | 0 |         else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR) | 
| 2865 | 0 |         { | 
| 2866 | 0 |             warnings.push_back(strprintf(_("Error reading %s! All keys read correctly, but transaction data"| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2867 | 0 |                                            " or address metadata may be missing or incorrect."), | 
| 2868 | 0 |                 walletFile)); | 
| 2869 | 0 |         } | 
| 2870 | 0 |         else if (nLoadWalletRet == DBErrors::TOO_NEW) { | 
| 2871 | 0 |             error = strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, CLIENT_NAME);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 |             error = strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, CLIENT_NAME);| Line | Count | Source |  | 98 | 0 | #define CLIENT_NAME "Bitcoin Core" | 
 | 
| 2872 | 0 |             return nullptr; | 
| 2873 | 0 |         } | 
| 2874 | 0 |         else if (nLoadWalletRet == DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED) { | 
| 2875 | 0 |             error = strprintf(_("Error loading %s: External signer wallet being loaded without external signer support compiled"), walletFile);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2876 | 0 |             return nullptr; | 
| 2877 | 0 |         } | 
| 2878 | 0 |         else if (nLoadWalletRet == DBErrors::NEED_REWRITE) | 
| 2879 | 0 |         { | 
| 2880 | 0 |             error = strprintf(_("Wallet needed to be rewritten: restart %s to complete"), CLIENT_NAME);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 |             error = strprintf(_("Wallet needed to be rewritten: restart %s to complete"), CLIENT_NAME);| Line | Count | Source |  | 98 | 0 | #define CLIENT_NAME "Bitcoin Core" | 
 | 
| 2881 | 0 |             return nullptr; | 
| 2882 | 0 |         } else if (nLoadWalletRet == DBErrors::NEED_RESCAN) { | 
| 2883 | 0 |             warnings.push_back(strprintf(_("Error reading %s! Transaction data may be missing or incorrect."| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2884 | 0 |                                            " Rescanning wallet."), walletFile)); | 
| 2885 | 0 |             rescan_required = true; | 
| 2886 | 0 |         } else if (nLoadWalletRet == DBErrors::UNKNOWN_DESCRIPTOR) { | 
| 2887 | 0 |             error = strprintf(_("Unrecognized descriptor found. Loading wallet %s\n\n"| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2888 | 0 |                                 "The wallet might have been created on a newer version.\n" | 
| 2889 | 0 |                                 "Please try running the latest software version.\n"), walletFile); | 
| 2890 | 0 |             return nullptr; | 
| 2891 | 0 |         } else if (nLoadWalletRet == DBErrors::UNEXPECTED_LEGACY_ENTRY) { | 
| 2892 | 0 |             error = strprintf(_("Unexpected legacy entry in descriptor wallet found. Loading wallet %s\n\n"| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2893 | 0 |                                 "The wallet might have been tampered with or created with malicious intent.\n"), walletFile); | 
| 2894 | 0 |             return nullptr; | 
| 2895 | 0 |         } else if (nLoadWalletRet == DBErrors::LEGACY_WALLET) { | 
| 2896 | 0 |             error = strprintf(_("Error loading %s: Wallet is a legacy wallet. Please migrate to a descriptor wallet using the migration tool (migratewallet RPC)."), walletFile);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2897 | 0 |             return nullptr; | 
| 2898 | 0 |         } else { | 
| 2899 | 0 |             error = strprintf(_("Error loading %s"), walletFile);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2900 | 0 |             return nullptr; | 
| 2901 | 0 |         } | 
| 2902 | 0 |     } | 
| 2903 |  |  | 
| 2904 |  |     // This wallet is in its first run if there are no ScriptPubKeyMans and it isn't blank or no privkeys | 
| 2905 | 0 |     const bool fFirstRun = walletInstance->m_spk_managers.empty() && | 
| 2906 | 0 |                      !walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && | 
| 2907 | 0 |                      !walletInstance->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET); | 
| 2908 | 0 |     if (fFirstRun) | 
| 2909 | 0 |     { | 
| 2910 | 0 |         LOCK(walletInstance->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 2911 |  |  | 
| 2912 |  |         // Init with passed flags. | 
| 2913 |  |         // Always set the cache upgrade flag as this feature is supported from the beginning. | 
| 2914 | 0 |         walletInstance->InitWalletFlags(wallet_creation_flags | WALLET_FLAG_LAST_HARDENED_XPUB_CACHED); | 
| 2915 |  |  | 
| 2916 |  |         // Only descriptor wallets can be created | 
| 2917 | 0 |         assert(walletInstance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); | 
| 2918 |  |  | 
| 2919 | 0 |         if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) || !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) { | 
| 2920 | 0 |             walletInstance->SetupDescriptorScriptPubKeyMans(); | 
| 2921 | 0 |         } | 
| 2922 |  | 
 | 
| 2923 | 0 |         if (chain) { | 
| 2924 | 0 |             std::optional<int> tip_height = chain->getHeight(); | 
| 2925 | 0 |             if (tip_height) { | 
| 2926 | 0 |                 walletInstance->SetLastBlockProcessed(*tip_height, chain->getBlockHash(*tip_height)); | 
| 2927 | 0 |             } | 
| 2928 | 0 |         } | 
| 2929 | 0 |     } else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) { | 
| 2930 |  |         // Make it impossible to disable private keys after creation | 
| 2931 | 0 |         error = strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2932 | 0 |         return nullptr; | 
| 2933 | 0 |     } else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | 
| 2934 | 0 |         for (auto spk_man : walletInstance->GetActiveScriptPubKeyMans()) { | 
| 2935 | 0 |             if (spk_man->HavePrivateKeys()) { | 
| 2936 | 0 |                 warnings.push_back(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2937 | 0 |                 break; | 
| 2938 | 0 |             } | 
| 2939 | 0 |         } | 
| 2940 | 0 |     } | 
| 2941 |  |  | 
| 2942 | 0 |     if (!args.GetArg("-addresstype", "").empty()) { | 
| 2943 | 0 |         std::optional<OutputType> parsed = ParseOutputType(args.GetArg("-addresstype", "")); | 
| 2944 | 0 |         if (!parsed) { | 
| 2945 | 0 |             error = strprintf(_("Unknown address type '%s'"), args.GetArg("-addresstype", ""));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2946 | 0 |             return nullptr; | 
| 2947 | 0 |         } | 
| 2948 | 0 |         walletInstance->m_default_address_type = parsed.value(); | 
| 2949 | 0 |     } | 
| 2950 |  |  | 
| 2951 | 0 |     if (!args.GetArg("-changetype", "").empty()) { | 
| 2952 | 0 |         std::optional<OutputType> parsed = ParseOutputType(args.GetArg("-changetype", "")); | 
| 2953 | 0 |         if (!parsed) { | 
| 2954 | 0 |             error = strprintf(_("Unknown change type '%s'"), args.GetArg("-changetype", ""));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2955 | 0 |             return nullptr; | 
| 2956 | 0 |         } | 
| 2957 | 0 |         walletInstance->m_default_change_type = parsed.value(); | 
| 2958 | 0 |     } | 
| 2959 |  |  | 
| 2960 | 0 |     if (const auto arg{args.GetArg("-mintxfee")}) { | 
| 2961 | 0 |         std::optional<CAmount> min_tx_fee = ParseMoney(*arg); | 
| 2962 | 0 |         if (!min_tx_fee) { | 
| 2963 | 0 |             error = AmountErrMsg("mintxfee", *arg); | 
| 2964 | 0 |             return nullptr; | 
| 2965 | 0 |         } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) { | 
| 2966 | 0 |             warnings.push_back(AmountHighWarn("-mintxfee") + Untranslated(" ") + | 
| 2967 | 0 |                                _("This is the minimum transaction fee you pay on every transaction.")); | 
| 2968 | 0 |         } | 
| 2969 |  |  | 
| 2970 | 0 |         walletInstance->m_min_fee = CFeeRate{min_tx_fee.value()}; | 
| 2971 | 0 |     } | 
| 2972 |  |  | 
| 2973 | 0 |     if (const auto arg{args.GetArg("-maxapsfee")}) { | 
| 2974 | 0 |         const std::string& max_aps_fee{*arg}; | 
| 2975 | 0 |         if (max_aps_fee == "-1") { | 
| 2976 | 0 |             walletInstance->m_max_aps_fee = -1; | 
| 2977 | 0 |         } else if (std::optional<CAmount> max_fee = ParseMoney(max_aps_fee)) { | 
| 2978 | 0 |             if (max_fee.value() > HIGH_APS_FEE) { | 
| 2979 | 0 |                 warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") + | 
| 2980 | 0 |                                   _("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.")); | 
| 2981 | 0 |             } | 
| 2982 | 0 |             walletInstance->m_max_aps_fee = max_fee.value(); | 
| 2983 | 0 |         } else { | 
| 2984 | 0 |             error = AmountErrMsg("maxapsfee", max_aps_fee); | 
| 2985 | 0 |             return nullptr; | 
| 2986 | 0 |         } | 
| 2987 | 0 |     } | 
| 2988 |  |  | 
| 2989 | 0 |     if (const auto arg{args.GetArg("-fallbackfee")}) { | 
| 2990 | 0 |         std::optional<CAmount> fallback_fee = ParseMoney(*arg); | 
| 2991 | 0 |         if (!fallback_fee) { | 
| 2992 | 0 |             error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-fallbackfee", *arg);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 2993 | 0 |             return nullptr; | 
| 2994 | 0 |         } else if (fallback_fee.value() > HIGH_TX_FEE_PER_KB) { | 
| 2995 | 0 |             warnings.push_back(AmountHighWarn("-fallbackfee") + Untranslated(" ") + | 
| 2996 | 0 |                                _("This is the transaction fee you may pay when fee estimates are not available.")); | 
| 2997 | 0 |         } | 
| 2998 | 0 |         walletInstance->m_fallback_fee = CFeeRate{fallback_fee.value()}; | 
| 2999 | 0 |     } | 
| 3000 |  |  | 
| 3001 |  |     // Disable fallback fee in case value was set to 0, enable if non-null value | 
| 3002 | 0 |     walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0; | 
| 3003 |  | 
 | 
| 3004 | 0 |     if (const auto arg{args.GetArg("-discardfee")}) { | 
| 3005 | 0 |         std::optional<CAmount> discard_fee = ParseMoney(*arg); | 
| 3006 | 0 |         if (!discard_fee) { | 
| 3007 | 0 |             error = strprintf(_("Invalid amount for %s=<amount>: '%s'"), "-discardfee", *arg);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3008 | 0 |             return nullptr; | 
| 3009 | 0 |         } else if (discard_fee.value() > HIGH_TX_FEE_PER_KB) { | 
| 3010 | 0 |             warnings.push_back(AmountHighWarn("-discardfee") + Untranslated(" ") + | 
| 3011 | 0 |                                _("This is the transaction fee you may discard if change is smaller than dust at this level")); | 
| 3012 | 0 |         } | 
| 3013 | 0 |         walletInstance->m_discard_rate = CFeeRate{discard_fee.value()}; | 
| 3014 | 0 |     } | 
| 3015 |  |  | 
| 3016 | 0 |     if (const auto arg{args.GetArg("-paytxfee")}) { | 
| 3017 | 0 |         warnings.push_back(_("-paytxfee is deprecated and will be fully removed in v31.0.")); | 
| 3018 |  | 
 | 
| 3019 | 0 |         std::optional<CAmount> pay_tx_fee = ParseMoney(*arg); | 
| 3020 | 0 |         if (!pay_tx_fee) { | 
| 3021 | 0 |             error = AmountErrMsg("paytxfee", *arg); | 
| 3022 | 0 |             return nullptr; | 
| 3023 | 0 |         } else if (pay_tx_fee.value() > HIGH_TX_FEE_PER_KB) { | 
| 3024 | 0 |             warnings.push_back(AmountHighWarn("-paytxfee") + Untranslated(" ") + | 
| 3025 | 0 |                                _("This is the transaction fee you will pay if you send a transaction.")); | 
| 3026 | 0 |         } | 
| 3027 |  |  | 
| 3028 | 0 |         walletInstance->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000}; | 
| 3029 |  | 
 | 
| 3030 | 0 |         if (chain && walletInstance->m_pay_tx_fee < chain->relayMinFee()) { | 
| 3031 | 0 |             error = strprintf(_("Invalid amount for %s=<amount>: '%s' (must be at least %s)"),| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3032 | 0 |                 "-paytxfee", *arg, chain->relayMinFee().ToString()); | 
| 3033 | 0 |             return nullptr; | 
| 3034 | 0 |         } | 
| 3035 | 0 |     } | 
| 3036 |  |  | 
| 3037 | 0 |     if (const auto arg{args.GetArg("-maxtxfee")}) { | 
| 3038 | 0 |         std::optional<CAmount> max_fee = ParseMoney(*arg); | 
| 3039 | 0 |         if (!max_fee) { | 
| 3040 | 0 |             error = AmountErrMsg("maxtxfee", *arg); | 
| 3041 | 0 |             return nullptr; | 
| 3042 | 0 |         } else if (max_fee.value() > HIGH_MAX_TX_FEE) { | 
| 3043 | 0 |             warnings.push_back(strprintf(_("%s is set very high! Fees this large could be paid on a single transaction."), "-maxtxfee"));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3044 | 0 |         } | 
| 3045 |  |  | 
| 3046 | 0 |         if (chain && CFeeRate{max_fee.value(), 1000} < chain->relayMinFee()) { | 
| 3047 | 0 |             error = strprintf(_("Invalid amount for %s=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3048 | 0 |                 "-maxtxfee", *arg, chain->relayMinFee().ToString()); | 
| 3049 | 0 |             return nullptr; | 
| 3050 | 0 |         } | 
| 3051 |  |  | 
| 3052 | 0 |         walletInstance->m_default_max_tx_fee = max_fee.value(); | 
| 3053 | 0 |     } | 
| 3054 |  |  | 
| 3055 | 0 |     if (const auto arg{args.GetArg("-consolidatefeerate")}) { | 
| 3056 | 0 |         if (std::optional<CAmount> consolidate_feerate = ParseMoney(*arg)) { | 
| 3057 | 0 |             walletInstance->m_consolidate_feerate = CFeeRate(*consolidate_feerate); | 
| 3058 | 0 |         } else { | 
| 3059 | 0 |             error = AmountErrMsg("consolidatefeerate", *arg); | 
| 3060 | 0 |             return nullptr; | 
| 3061 | 0 |         } | 
| 3062 | 0 |     } | 
| 3063 |  |  | 
| 3064 | 0 |     if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { | 
| 3065 | 0 |         warnings.push_back(AmountHighWarn("-minrelaytxfee") + Untranslated(" ") + | 
| 3066 | 0 |                            _("The wallet will avoid paying less than the minimum relay fee.")); | 
| 3067 | 0 |     } | 
| 3068 |  | 
 | 
| 3069 | 0 |     walletInstance->m_confirm_target = args.GetIntArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); | 
| 3070 | 0 |     walletInstance->m_spend_zero_conf_change = args.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); | 
| 3071 | 0 |     walletInstance->m_signal_rbf = args.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); | 
| 3072 |  | 
 | 
| 3073 | 0 |     walletInstance->WalletLogPrintf("Wallet completed loading in %15dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - start)); | 
| 3074 |  |  | 
| 3075 |  |     // Try to top up keypool. No-op if the wallet is locked. | 
| 3076 | 0 |     walletInstance->TopUpKeyPool(); | 
| 3077 |  |  | 
| 3078 |  |     // Cache the first key time | 
| 3079 | 0 |     std::optional<int64_t> time_first_key; | 
| 3080 | 0 |     for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) { | 
| 3081 | 0 |         int64_t time = spk_man->GetTimeFirstKey(); | 
| 3082 | 0 |         if (!time_first_key || time < *time_first_key) time_first_key = time; | 
| 3083 | 0 |     } | 
| 3084 | 0 |     if (time_first_key) walletInstance->MaybeUpdateBirthTime(*time_first_key); | 
| 3085 |  | 
 | 
| 3086 | 0 |     if (chain && !AttachChain(walletInstance, *chain, rescan_required, error, warnings)) { | 
| 3087 | 0 |         walletInstance->m_chain_notifications_handler.reset(); // Reset this pointer so that the wallet will actually be unloaded | 
| 3088 | 0 |         return nullptr; | 
| 3089 | 0 |     } | 
| 3090 |  |  | 
| 3091 | 0 |     { | 
| 3092 | 0 |         LOCK(walletInstance->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3093 | 0 |         walletInstance->SetBroadcastTransactions(args.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); | 
| 3094 | 0 |         walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n",      walletInstance->GetKeyPoolSize()); | 
| 3095 | 0 |         walletInstance->WalletLogPrintf("mapWallet.size() = %u\n",       walletInstance->mapWallet.size()); | 
| 3096 | 0 |         walletInstance->WalletLogPrintf("m_address_book.size() = %u\n",  walletInstance->m_address_book.size()); | 
| 3097 | 0 |     } | 
| 3098 |  | 
 | 
| 3099 | 0 |     return walletInstance; | 
| 3100 | 0 | } | 
| 3101 |  |  | 
| 3102 |  | bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interfaces::Chain& chain, const bool rescan_required, bilingual_str& error, std::vector<bilingual_str>& warnings) | 
| 3103 | 0 | { | 
| 3104 | 0 |     LOCK(walletInstance->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3105 |  |     // allow setting the chain if it hasn't been set already but prevent changing it | 
| 3106 | 0 |     assert(!walletInstance->m_chain || walletInstance->m_chain == &chain); | 
| 3107 | 0 |     walletInstance->m_chain = &chain; | 
| 3108 |  |  | 
| 3109 |  |     // Unless allowed, ensure wallet files are not reused across chains: | 
| 3110 | 0 |     if (!gArgs.GetBoolArg("-walletcrosschain", DEFAULT_WALLETCROSSCHAIN)) { | 
| 3111 | 0 |         WalletBatch batch(walletInstance->GetDatabase()); | 
| 3112 | 0 |         CBlockLocator locator; | 
| 3113 | 0 |         if (batch.ReadBestBlock(locator) && locator.vHave.size() > 0 && chain.getHeight()) { | 
| 3114 |  |             // Wallet is assumed to be from another chain, if genesis block in the active | 
| 3115 |  |             // chain differs from the genesis block known to the wallet. | 
| 3116 | 0 |             if (chain.getBlockHash(0) != locator.vHave.back()) { | 
| 3117 | 0 |                 error = Untranslated("Wallet files should not be reused across chains. Restart bitcoind with -walletcrosschain to override."); | 
| 3118 | 0 |                 return false; | 
| 3119 | 0 |             } | 
| 3120 | 0 |         } | 
| 3121 | 0 |     } | 
| 3122 |  |  | 
| 3123 |  |     // Register wallet with validationinterface. It's done before rescan to avoid | 
| 3124 |  |     // missing block connections during the rescan. | 
| 3125 |  |     // Because of the wallet lock being held, block connection notifications are going to | 
| 3126 |  |     // be pending on the validation-side until lock release. Blocks that are connected while the | 
| 3127 |  |     // rescan is ongoing will not be processed in the rescan but with the block connected notifications, | 
| 3128 |  |     // so the wallet will only be completeley synced after the notifications delivery. | 
| 3129 | 0 |     walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance); | 
| 3130 |  |  | 
| 3131 |  |     // If rescan_required = true, rescan_height remains equal to 0 | 
| 3132 | 0 |     int rescan_height = 0; | 
| 3133 | 0 |     if (!rescan_required) | 
| 3134 | 0 |     { | 
| 3135 | 0 |         WalletBatch batch(walletInstance->GetDatabase()); | 
| 3136 | 0 |         CBlockLocator locator; | 
| 3137 | 0 |         if (batch.ReadBestBlock(locator)) { | 
| 3138 | 0 |             if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) { | 
| 3139 | 0 |                 rescan_height = *fork_height; | 
| 3140 | 0 |             } | 
| 3141 | 0 |         } | 
| 3142 | 0 |     } | 
| 3143 |  | 
 | 
| 3144 | 0 |     const std::optional<int> tip_height = chain.getHeight(); | 
| 3145 | 0 |     if (tip_height) { | 
| 3146 | 0 |         walletInstance->SetLastBlockProcessedInMem(*tip_height, chain.getBlockHash(*tip_height)); | 
| 3147 | 0 |     } else { | 
| 3148 | 0 |         walletInstance->SetLastBlockProcessedInMem(-1, uint256()); | 
| 3149 | 0 |     } | 
| 3150 |  | 
 | 
| 3151 | 0 |     if (tip_height && *tip_height != rescan_height) | 
| 3152 | 0 |     { | 
| 3153 |  |         // No need to read and scan block if block was created before | 
| 3154 |  |         // our wallet birthday (as adjusted for block time variability) | 
| 3155 | 0 |         std::optional<int64_t> time_first_key = walletInstance->m_birth_time.load(); | 
| 3156 | 0 |         if (time_first_key) { | 
| 3157 | 0 |             FoundBlock found = FoundBlock().height(rescan_height); | 
| 3158 | 0 |             chain.findFirstBlockWithTimeAndHeight(*time_first_key - TIMESTAMP_WINDOW, rescan_height, found); | 
| 3159 | 0 |             if (!found.found) { | 
| 3160 |  |                 // We were unable to find a block that had a time more recent than our earliest timestamp | 
| 3161 |  |                 // or a height higher than the wallet was synced to, indicating that the wallet is newer than the | 
| 3162 |  |                 // current chain tip. Skip rescanning in this case. | 
| 3163 | 0 |                 rescan_height = *tip_height; | 
| 3164 | 0 |             } | 
| 3165 | 0 |         } | 
| 3166 |  |  | 
| 3167 |  |         // Technically we could execute the code below in any case, but performing the | 
| 3168 |  |         // `while` loop below can make startup very slow, so only check blocks on disk | 
| 3169 |  |         // if necessary. | 
| 3170 | 0 |         if (chain.havePruned() || chain.hasAssumedValidChain()) { | 
| 3171 | 0 |             int block_height = *tip_height; | 
| 3172 | 0 |             while (block_height > 0 && chain.haveBlockOnDisk(block_height - 1) && rescan_height != block_height) { | 
| 3173 | 0 |                 --block_height; | 
| 3174 | 0 |             } | 
| 3175 |  | 
 | 
| 3176 | 0 |             if (rescan_height != block_height) { | 
| 3177 |  |                 // We can't rescan beyond blocks we don't have data for, stop and throw an error. | 
| 3178 |  |                 // This might happen if a user uses an old wallet within a pruned node | 
| 3179 |  |                 // or if they ran -disablewallet for a longer time, then decided to re-enable | 
| 3180 |  |                 // Exit early and print an error. | 
| 3181 |  |                 // It also may happen if an assumed-valid chain is in use and therefore not | 
| 3182 |  |                 // all block data is available. | 
| 3183 |  |                 // If a block is pruned after this check, we will load the wallet, | 
| 3184 |  |                 // but fail the rescan with a generic error. | 
| 3185 |  | 
 | 
| 3186 | 0 |                 error = chain.havePruned() ? | 
| 3187 | 0 |                      _("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of a pruned node)") : | 
| 3188 | 0 |                      strprintf(_( | Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3189 | 0 |                         "Error loading wallet. Wallet requires blocks to be downloaded, " | 
| 3190 | 0 |                         "and software does not currently support loading wallets while " | 
| 3191 | 0 |                         "blocks are being downloaded out of order when using assumeutxo " | 
| 3192 | 0 |                         "snapshots. Wallet should be able to load successfully after " | 
| 3193 | 0 |                         "node sync reaches height %s"), block_height); | 
| 3194 | 0 |                 return false; | 
| 3195 | 0 |             } | 
| 3196 | 0 |         } | 
| 3197 |  |  | 
| 3198 | 0 |         chain.initMessage(_("Rescanning…")); | 
| 3199 | 0 |         walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height); | 
| 3200 |  | 
 | 
| 3201 | 0 |         { | 
| 3202 | 0 |             WalletRescanReserver reserver(*walletInstance); | 
| 3203 | 0 |             if (!reserver.reserve()) { | 
| 3204 | 0 |                 error = _("Failed to acquire rescan reserver during wallet initialization"); | 
| 3205 | 0 |                 return false; | 
| 3206 | 0 |             } | 
| 3207 | 0 |             ScanResult scan_res = walletInstance->ScanForWalletTransactions(chain.getBlockHash(rescan_height), rescan_height, /*max_height=*/{}, reserver, /*fUpdate=*/true, /*save_progress=*/true); | 
| 3208 | 0 |             if (ScanResult::SUCCESS != scan_res.status) { | 
| 3209 | 0 |                 error = _("Failed to rescan the wallet during initialization"); | 
| 3210 | 0 |                 return false; | 
| 3211 | 0 |             } | 
| 3212 |  |             // Set and update the best block record | 
| 3213 |  |             // Set last block scanned as the last block processed as it may be different in case of a reorg. | 
| 3214 |  |             // Also save the best block locator because rescanning only updates it intermittently. | 
| 3215 | 0 |             walletInstance->SetLastBlockProcessed(*scan_res.last_scanned_height, scan_res.last_scanned_block); | 
| 3216 | 0 |         } | 
| 3217 | 0 |     } | 
| 3218 |  |  | 
| 3219 | 0 |     return true; | 
| 3220 | 0 | } | 
| 3221 |  |  | 
| 3222 |  | const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest, bool allow_change) const | 
| 3223 | 0 | { | 
| 3224 | 0 |     const auto& address_book_it = m_address_book.find(dest); | 
| 3225 | 0 |     if (address_book_it == m_address_book.end()) return nullptr; | 
| 3226 | 0 |     if ((!allow_change) && address_book_it->second.IsChange()) { | 
| 3227 | 0 |         return nullptr; | 
| 3228 | 0 |     } | 
| 3229 | 0 |     return &address_book_it->second; | 
| 3230 | 0 | } | 
| 3231 |  |  | 
| 3232 |  | void CWallet::postInitProcess() | 
| 3233 | 0 | { | 
| 3234 |  |     // Add wallet transactions that aren't already in a block to mempool | 
| 3235 |  |     // Do this here as mempool requires genesis block to be loaded | 
| 3236 | 0 |     ResubmitWalletTransactions(/*relay=*/false, /*force=*/true); | 
| 3237 |  |  | 
| 3238 |  |     // Update wallet transactions with current mempool transactions. | 
| 3239 | 0 |     WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this)); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 3240 | 0 | } | 
| 3241 |  |  | 
| 3242 |  | bool CWallet::BackupWallet(const std::string& strDest) const | 
| 3243 | 0 | { | 
| 3244 | 0 |     WITH_LOCK(cs_wallet, WriteBestBlock()); | Line | Count | Source |  | 290 | 0 | #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }()) | 
 | 
| 3245 | 0 |     return GetDatabase().Backup(strDest); | 
| 3246 | 0 | } | 
| 3247 |  |  | 
| 3248 |  | int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const | 
| 3249 | 0 | { | 
| 3250 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3251 | 0 |     if (auto* conf = wtx.state<TxStateConfirmed>()) { | 
| 3252 | 0 |         assert(conf->confirmed_block_height >= 0); | 
| 3253 | 0 |         return GetLastBlockHeight() - conf->confirmed_block_height + 1; | 
| 3254 | 0 |     } else if (auto* conf = wtx.state<TxStateBlockConflicted>()) { | 
| 3255 | 0 |         assert(conf->conflicting_block_height >= 0); | 
| 3256 | 0 |         return -1 * (GetLastBlockHeight() - conf->conflicting_block_height + 1); | 
| 3257 | 0 |     } else { | 
| 3258 | 0 |         return 0; | 
| 3259 | 0 |     } | 
| 3260 | 0 | } | 
| 3261 |  |  | 
| 3262 |  | int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const | 
| 3263 | 0 | { | 
| 3264 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3265 |  | 
 | 
| 3266 | 0 |     if (!wtx.IsCoinBase()) { | 
| 3267 | 0 |         return 0; | 
| 3268 | 0 |     } | 
| 3269 | 0 |     int chain_depth = GetTxDepthInMainChain(wtx); | 
| 3270 | 0 |     assert(chain_depth >= 0); // coinbase tx should not be conflicted | 
| 3271 | 0 |     return std::max(0, (COINBASE_MATURITY+1) - chain_depth); | 
| 3272 | 0 | } | 
| 3273 |  |  | 
| 3274 |  | bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const | 
| 3275 | 0 | { | 
| 3276 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3277 |  |  | 
| 3278 |  |     // note GetBlocksToMaturity is 0 for non-coinbase tx | 
| 3279 | 0 |     return GetTxBlocksToMaturity(wtx) > 0; | 
| 3280 | 0 | } | 
| 3281 |  |  | 
| 3282 |  | bool CWallet::IsCrypted() const | 
| 3283 | 0 | { | 
| 3284 | 0 |     return HasEncryptionKeys(); | 
| 3285 | 0 | } | 
| 3286 |  |  | 
| 3287 |  | bool CWallet::IsLocked() const | 
| 3288 | 0 | { | 
| 3289 | 0 |     if (!IsCrypted()) { | 
| 3290 | 0 |         return false; | 
| 3291 | 0 |     } | 
| 3292 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3293 | 0 |     return vMasterKey.empty(); | 
| 3294 | 0 | } | 
| 3295 |  |  | 
| 3296 |  | bool CWallet::Lock() | 
| 3297 | 0 | { | 
| 3298 | 0 |     if (!IsCrypted()) | 
| 3299 | 0 |         return false; | 
| 3300 |  |  | 
| 3301 | 0 |     { | 
| 3302 | 0 |         LOCK2(m_relock_mutex, cs_wallet); | Line | Count | Source |  | 261 | 0 |     UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \ |  | 262 | 0 |     UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__) | 
 | 
| 3303 | 0 |         if (!vMasterKey.empty()) { | 
| 3304 | 0 |             memory_cleanse(vMasterKey.data(), vMasterKey.size() * sizeof(decltype(vMasterKey)::value_type)); | 
| 3305 | 0 |             vMasterKey.clear(); | 
| 3306 | 0 |         } | 
| 3307 | 0 |     } | 
| 3308 |  | 
 | 
| 3309 | 0 |     NotifyStatusChanged(this); | 
| 3310 | 0 |     return true; | 
| 3311 | 0 | } | 
| 3312 |  |  | 
| 3313 |  | bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn) | 
| 3314 | 0 | { | 
| 3315 | 0 |     { | 
| 3316 | 0 |         LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3317 | 0 |         for (const auto& spk_man_pair : m_spk_managers) { | 
| 3318 | 0 |             if (!spk_man_pair.second->CheckDecryptionKey(vMasterKeyIn)) { | 
| 3319 | 0 |                 return false; | 
| 3320 | 0 |             } | 
| 3321 | 0 |         } | 
| 3322 | 0 |         vMasterKey = vMasterKeyIn; | 
| 3323 | 0 |     } | 
| 3324 | 0 |     NotifyStatusChanged(this); | 
| 3325 | 0 |     return true; | 
| 3326 | 0 | } | 
| 3327 |  |  | 
| 3328 |  | std::set<ScriptPubKeyMan*> CWallet::GetActiveScriptPubKeyMans() const | 
| 3329 | 0 | { | 
| 3330 | 0 |     std::set<ScriptPubKeyMan*> spk_mans; | 
| 3331 | 0 |     for (bool internal : {false, true}) { | 
| 3332 | 0 |         for (OutputType t : OUTPUT_TYPES) { | 
| 3333 | 0 |             auto spk_man = GetScriptPubKeyMan(t, internal); | 
| 3334 | 0 |             if (spk_man) { | 
| 3335 | 0 |                 spk_mans.insert(spk_man); | 
| 3336 | 0 |             } | 
| 3337 | 0 |         } | 
| 3338 | 0 |     } | 
| 3339 | 0 |     return spk_mans; | 
| 3340 | 0 | } | 
| 3341 |  |  | 
| 3342 |  | bool CWallet::IsActiveScriptPubKeyMan(const ScriptPubKeyMan& spkm) const | 
| 3343 | 0 | { | 
| 3344 | 0 |     for (const auto& [_, ext_spkm] : m_external_spk_managers) { | 
| 3345 | 0 |         if (ext_spkm == &spkm) return true; | 
| 3346 | 0 |     } | 
| 3347 | 0 |     for (const auto& [_, int_spkm] : m_internal_spk_managers) { | 
| 3348 | 0 |         if (int_spkm == &spkm) return true; | 
| 3349 | 0 |     } | 
| 3350 | 0 |     return false; | 
| 3351 | 0 | } | 
| 3352 |  |  | 
| 3353 |  | std::set<ScriptPubKeyMan*> CWallet::GetAllScriptPubKeyMans() const | 
| 3354 | 0 | { | 
| 3355 | 0 |     std::set<ScriptPubKeyMan*> spk_mans; | 
| 3356 | 0 |     for (const auto& spk_man_pair : m_spk_managers) { | 
| 3357 | 0 |         spk_mans.insert(spk_man_pair.second.get()); | 
| 3358 | 0 |     } | 
| 3359 | 0 |     return spk_mans; | 
| 3360 | 0 | } | 
| 3361 |  |  | 
| 3362 |  | ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const OutputType& type, bool internal) const | 
| 3363 | 0 | { | 
| 3364 | 0 |     const std::map<OutputType, ScriptPubKeyMan*>& spk_managers = internal ? m_internal_spk_managers : m_external_spk_managers; | 
| 3365 | 0 |     std::map<OutputType, ScriptPubKeyMan*>::const_iterator it = spk_managers.find(type); | 
| 3366 | 0 |     if (it == spk_managers.end()) { | 
| 3367 | 0 |         return nullptr; | 
| 3368 | 0 |     } | 
| 3369 | 0 |     return it->second; | 
| 3370 | 0 | } | 
| 3371 |  |  | 
| 3372 |  | std::set<ScriptPubKeyMan*> CWallet::GetScriptPubKeyMans(const CScript& script) const | 
| 3373 | 0 | { | 
| 3374 | 0 |     std::set<ScriptPubKeyMan*> spk_mans; | 
| 3375 |  |  | 
| 3376 |  |     // Search the cache for relevant SPKMs instead of iterating m_spk_managers | 
| 3377 | 0 |     const auto& it = m_cached_spks.find(script); | 
| 3378 | 0 |     if (it != m_cached_spks.end()) { | 
| 3379 | 0 |         spk_mans.insert(it->second.begin(), it->second.end()); | 
| 3380 | 0 |     } | 
| 3381 | 0 |     SignatureData sigdata; | 
| 3382 | 0 |     Assume(std::all_of(spk_mans.begin(), spk_mans.end(), [&script, &sigdata](ScriptPubKeyMan* spkm) { return spkm->CanProvide(script, sigdata); }));| Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3383 |  | 
 | 
| 3384 | 0 |     return spk_mans; | 
| 3385 | 0 | } | 
| 3386 |  |  | 
| 3387 |  | ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const uint256& id) const | 
| 3388 | 0 | { | 
| 3389 | 0 |     if (m_spk_managers.count(id) > 0) { | 
| 3390 | 0 |         return m_spk_managers.at(id).get(); | 
| 3391 | 0 |     } | 
| 3392 | 0 |     return nullptr; | 
| 3393 | 0 | } | 
| 3394 |  |  | 
| 3395 |  | std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script) const | 
| 3396 | 0 | { | 
| 3397 | 0 |     SignatureData sigdata; | 
| 3398 | 0 |     return GetSolvingProvider(script, sigdata); | 
| 3399 | 0 | } | 
| 3400 |  |  | 
| 3401 |  | std::unique_ptr<SigningProvider> CWallet::GetSolvingProvider(const CScript& script, SignatureData& sigdata) const | 
| 3402 | 0 | { | 
| 3403 |  |     // Search the cache for relevant SPKMs instead of iterating m_spk_managers | 
| 3404 | 0 |     const auto& it = m_cached_spks.find(script); | 
| 3405 | 0 |     if (it != m_cached_spks.end()) { | 
| 3406 |  |         // All spkms for a given script must already be able to make a SigningProvider for the script, so just return the first one. | 
| 3407 | 0 |         Assume(it->second.at(0)->CanProvide(script, sigdata)); | Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3408 | 0 |         return it->second.at(0)->GetSolvingProvider(script); | 
| 3409 | 0 |     } | 
| 3410 |  |  | 
| 3411 | 0 |     return nullptr; | 
| 3412 | 0 | } | 
| 3413 |  |  | 
| 3414 |  | std::vector<WalletDescriptor> CWallet::GetWalletDescriptors(const CScript& script) const | 
| 3415 | 0 | { | 
| 3416 | 0 |     std::vector<WalletDescriptor> descs; | 
| 3417 | 0 |     for (const auto spk_man: GetScriptPubKeyMans(script)) { | 
| 3418 | 0 |         if (const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man)) { | 
| 3419 | 0 |             LOCK(desc_spk_man->cs_desc_man); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3420 | 0 |             descs.push_back(desc_spk_man->GetWalletDescriptor()); | 
| 3421 | 0 |         } | 
| 3422 | 0 |     } | 
| 3423 | 0 |     return descs; | 
| 3424 | 0 | } | 
| 3425 |  |  | 
| 3426 |  | LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const | 
| 3427 | 0 | { | 
| 3428 | 0 |     if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { | 
| 3429 | 0 |         return nullptr; | 
| 3430 | 0 |     } | 
| 3431 | 0 |     auto it = m_internal_spk_managers.find(OutputType::LEGACY); | 
| 3432 | 0 |     if (it == m_internal_spk_managers.end()) return nullptr; | 
| 3433 | 0 |     return dynamic_cast<LegacyDataSPKM*>(it->second); | 
| 3434 | 0 | } | 
| 3435 |  |  | 
| 3436 |  | void CWallet::AddScriptPubKeyMan(const uint256& id, std::unique_ptr<ScriptPubKeyMan> spkm_man) | 
| 3437 | 0 | { | 
| 3438 |  |     // Add spkm_man to m_spk_managers before calling any method | 
| 3439 |  |     // that might access it. | 
| 3440 | 0 |     const auto& spkm = m_spk_managers[id] = std::move(spkm_man); | 
| 3441 |  |  | 
| 3442 |  |     // Update birth time if needed | 
| 3443 | 0 |     MaybeUpdateBirthTime(spkm->GetTimeFirstKey()); | 
| 3444 | 0 | } | 
| 3445 |  |  | 
| 3446 |  | LegacyDataSPKM* CWallet::GetOrCreateLegacyDataSPKM() | 
| 3447 | 0 | { | 
| 3448 | 0 |     SetupLegacyScriptPubKeyMan(); | 
| 3449 | 0 |     return GetLegacyDataSPKM(); | 
| 3450 | 0 | } | 
| 3451 |  |  | 
| 3452 |  | void CWallet::SetupLegacyScriptPubKeyMan() | 
| 3453 | 0 | { | 
| 3454 | 0 |     if (!m_internal_spk_managers.empty() || !m_external_spk_managers.empty() || !m_spk_managers.empty() || IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { | 
| 3455 | 0 |         return; | 
| 3456 | 0 |     } | 
| 3457 |  |  | 
| 3458 | 0 |     Assert(m_database->Format() == "bdb_ro" || m_database->Format() == "mock"); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3459 | 0 |     std::unique_ptr<ScriptPubKeyMan> spk_manager = std::make_unique<LegacyDataSPKM>(*this); | 
| 3460 |  | 
 | 
| 3461 | 0 |     for (const auto& type : LEGACY_OUTPUT_TYPES) { | 
| 3462 | 0 |         m_internal_spk_managers[type] = spk_manager.get(); | 
| 3463 | 0 |         m_external_spk_managers[type] = spk_manager.get(); | 
| 3464 | 0 |     } | 
| 3465 | 0 |     uint256 id = spk_manager->GetID(); | 
| 3466 | 0 |     AddScriptPubKeyMan(id, std::move(spk_manager)); | 
| 3467 | 0 | } | 
| 3468 |  |  | 
| 3469 |  | bool CWallet::WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const | 
| 3470 | 0 | { | 
| 3471 | 0 |     LOCK(cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3472 | 0 |     return cb(vMasterKey); | 
| 3473 | 0 | } | 
| 3474 |  |  | 
| 3475 |  | bool CWallet::HasEncryptionKeys() const | 
| 3476 | 0 | { | 
| 3477 | 0 |     return !mapMasterKeys.empty(); | 
| 3478 | 0 | } | 
| 3479 |  |  | 
| 3480 |  | bool CWallet::HaveCryptedKeys() const | 
| 3481 | 0 | { | 
| 3482 | 0 |     for (const auto& spkm : GetAllScriptPubKeyMans()) { | 
| 3483 | 0 |         if (spkm->HaveCryptedKeys()) return true; | 
| 3484 | 0 |     } | 
| 3485 | 0 |     return false; | 
| 3486 | 0 | } | 
| 3487 |  |  | 
| 3488 |  | void CWallet::ConnectScriptPubKeyManNotifiers() | 
| 3489 | 0 | { | 
| 3490 | 0 |     for (const auto& spk_man : GetActiveScriptPubKeyMans()) { | 
| 3491 | 0 |         spk_man->NotifyCanGetAddressesChanged.connect(NotifyCanGetAddressesChanged); | 
| 3492 | 0 |         spk_man->NotifyFirstKeyTimeChanged.connect(std::bind(&CWallet::MaybeUpdateBirthTime, this, std::placeholders::_2)); | 
| 3493 | 0 |     } | 
| 3494 | 0 | } | 
| 3495 |  |  | 
| 3496 |  | DescriptorScriptPubKeyMan& CWallet::LoadDescriptorScriptPubKeyMan(uint256 id, WalletDescriptor& desc) | 
| 3497 | 0 | { | 
| 3498 | 0 |     DescriptorScriptPubKeyMan* spk_manager; | 
| 3499 | 0 |     if (IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) { | 
| 3500 | 0 |         spk_manager = new ExternalSignerScriptPubKeyMan(*this, desc, m_keypool_size); | 
| 3501 | 0 |     } else { | 
| 3502 | 0 |         spk_manager = new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size); | 
| 3503 | 0 |     } | 
| 3504 | 0 |     AddScriptPubKeyMan(id, std::unique_ptr<ScriptPubKeyMan>(spk_manager)); | 
| 3505 | 0 |     return *spk_manager; | 
| 3506 | 0 | } | 
| 3507 |  |  | 
| 3508 |  | DescriptorScriptPubKeyMan& CWallet::SetupDescriptorScriptPubKeyMan(WalletBatch& batch, const CExtKey& master_key, const OutputType& output_type, bool internal) | 
| 3509 | 0 | { | 
| 3510 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3511 | 0 |     auto spk_manager = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, m_keypool_size)); | 
| 3512 | 0 |     if (IsCrypted()) { | 
| 3513 | 0 |         if (IsLocked()) { | 
| 3514 | 0 |             throw std::runtime_error(std::string(__func__) + ": Wallet is locked, cannot setup new descriptors"); | 
| 3515 | 0 |         } | 
| 3516 | 0 |         if (!spk_manager->CheckDecryptionKey(vMasterKey) && !spk_manager->Encrypt(vMasterKey, &batch)) { | 
| 3517 | 0 |             throw std::runtime_error(std::string(__func__) + ": Could not encrypt new descriptors"); | 
| 3518 | 0 |         } | 
| 3519 | 0 |     } | 
| 3520 | 0 |     spk_manager->SetupDescriptorGeneration(batch, master_key, output_type, internal); | 
| 3521 | 0 |     DescriptorScriptPubKeyMan* out = spk_manager.get(); | 
| 3522 | 0 |     uint256 id = spk_manager->GetID(); | 
| 3523 | 0 |     AddScriptPubKeyMan(id, std::move(spk_manager)); | 
| 3524 | 0 |     AddActiveScriptPubKeyManWithDb(batch, id, output_type, internal); | 
| 3525 | 0 |     return *out; | 
| 3526 | 0 | } | 
| 3527 |  |  | 
| 3528 |  | void CWallet::SetupDescriptorScriptPubKeyMans(WalletBatch& batch, const CExtKey& master_key) | 
| 3529 | 0 | { | 
| 3530 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3531 | 0 |     for (bool internal : {false, true}) { | 
| 3532 | 0 |         for (OutputType t : OUTPUT_TYPES) { | 
| 3533 | 0 |             SetupDescriptorScriptPubKeyMan(batch, master_key, t, internal); | 
| 3534 | 0 |         } | 
| 3535 | 0 |     } | 
| 3536 | 0 | } | 
| 3537 |  |  | 
| 3538 |  | void CWallet::SetupOwnDescriptorScriptPubKeyMans(WalletBatch& batch) | 
| 3539 | 0 | { | 
| 3540 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3541 | 0 |     assert(!IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)); | 
| 3542 |  |     // Make a seed | 
| 3543 | 0 |     CKey seed_key = GenerateRandomKey(); | 
| 3544 | 0 |     CPubKey seed = seed_key.GetPubKey(); | 
| 3545 | 0 |     assert(seed_key.VerifyPubKey(seed)); | 
| 3546 |  |  | 
| 3547 |  |     // Get the extended key | 
| 3548 | 0 |     CExtKey master_key; | 
| 3549 | 0 |     master_key.SetSeed(seed_key); | 
| 3550 |  | 
 | 
| 3551 | 0 |     SetupDescriptorScriptPubKeyMans(batch, master_key); | 
| 3552 | 0 | } | 
| 3553 |  |  | 
| 3554 |  | void CWallet::SetupDescriptorScriptPubKeyMans() | 
| 3555 | 0 | { | 
| 3556 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3557 |  | 
 | 
| 3558 | 0 |     if (!IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) { | 
| 3559 | 0 |         if (!RunWithinTxn(GetDatabase(), /*process_desc=*/"setup descriptors", [&](WalletBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet){ | 
| 3560 | 0 |             SetupOwnDescriptorScriptPubKeyMans(batch); | 
| 3561 | 0 |             return true; | 
| 3562 | 0 |         })) throw std::runtime_error("Error: cannot process db transaction for descriptors setup"); | 
| 3563 | 0 |     } else { | 
| 3564 | 0 |         auto signer = ExternalSignerScriptPubKeyMan::GetExternalSigner(); | 
| 3565 | 0 |         if (!signer) throw std::runtime_error(util::ErrorString(signer).original); | 
| 3566 |  |  | 
| 3567 |  |         // TODO: add account parameter | 
| 3568 | 0 |         int account = 0; | 
| 3569 | 0 |         UniValue signer_res = signer->GetDescriptors(account); | 
| 3570 |  | 
 | 
| 3571 | 0 |         if (!signer_res.isObject()) throw std::runtime_error(std::string(__func__) + ": Unexpected result"); | 
| 3572 |  |  | 
| 3573 | 0 |         WalletBatch batch(GetDatabase()); | 
| 3574 | 0 |         if (!batch.TxnBegin()) throw std::runtime_error("Error: cannot create db transaction for descriptors import"); | 
| 3575 |  |  | 
| 3576 | 0 |         for (bool internal : {false, true}) { | 
| 3577 | 0 |             const UniValue& descriptor_vals = signer_res.find_value(internal ? "internal" : "receive"); | 
| 3578 | 0 |             if (!descriptor_vals.isArray()) throw std::runtime_error(std::string(__func__) + ": Unexpected result"); | 
| 3579 | 0 |             for (const UniValue& desc_val : descriptor_vals.get_array().getValues()) { | 
| 3580 | 0 |                 const std::string& desc_str = desc_val.getValStr(); | 
| 3581 | 0 |                 FlatSigningProvider keys; | 
| 3582 | 0 |                 std::string desc_error; | 
| 3583 | 0 |                 auto descs = Parse(desc_str, keys, desc_error, false); | 
| 3584 | 0 |                 if (descs.empty()) { | 
| 3585 | 0 |                     throw std::runtime_error(std::string(__func__) + ": Invalid descriptor \"" + desc_str + "\" (" + desc_error + ")"); | 
| 3586 | 0 |                 } | 
| 3587 | 0 |                 auto& desc = descs.at(0); | 
| 3588 | 0 |                 if (!desc->GetOutputType()) { | 
| 3589 | 0 |                     continue; | 
| 3590 | 0 |                 } | 
| 3591 | 0 |                 OutputType t =  *desc->GetOutputType(); | 
| 3592 | 0 |                 auto spk_manager = std::unique_ptr<ExternalSignerScriptPubKeyMan>(new ExternalSignerScriptPubKeyMan(*this, m_keypool_size)); | 
| 3593 | 0 |                 spk_manager->SetupDescriptor(batch, std::move(desc)); | 
| 3594 | 0 |                 uint256 id = spk_manager->GetID(); | 
| 3595 | 0 |                 AddScriptPubKeyMan(id, std::move(spk_manager)); | 
| 3596 | 0 |                 AddActiveScriptPubKeyManWithDb(batch, id, t, internal); | 
| 3597 | 0 |             } | 
| 3598 | 0 |         } | 
| 3599 |  |  | 
| 3600 |  |         // Ensure imported descriptors are committed to disk | 
| 3601 | 0 |         if (!batch.TxnCommit()) throw std::runtime_error("Error: cannot commit db transaction for descriptors import"); | 
| 3602 | 0 |     } | 
| 3603 | 0 | } | 
| 3604 |  |  | 
| 3605 |  | void CWallet::AddActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal) | 
| 3606 | 0 | { | 
| 3607 | 0 |     WalletBatch batch(GetDatabase()); | 
| 3608 | 0 |     return AddActiveScriptPubKeyManWithDb(batch, id, type, internal); | 
| 3609 | 0 | } | 
| 3610 |  |  | 
| 3611 |  | void CWallet::AddActiveScriptPubKeyManWithDb(WalletBatch& batch, uint256 id, OutputType type, bool internal) | 
| 3612 | 0 | { | 
| 3613 | 0 |     if (!batch.WriteActiveScriptPubKeyMan(static_cast<uint8_t>(type), id, internal)) { | 
| 3614 | 0 |         throw std::runtime_error(std::string(__func__) + ": writing active ScriptPubKeyMan id failed"); | 
| 3615 | 0 |     } | 
| 3616 | 0 |     LoadActiveScriptPubKeyMan(id, type, internal); | 
| 3617 | 0 | } | 
| 3618 |  |  | 
| 3619 |  | void CWallet::LoadActiveScriptPubKeyMan(uint256 id, OutputType type, bool internal) | 
| 3620 | 0 | { | 
| 3621 |  |     // Activating ScriptPubKeyManager for a given output and change type is incompatible with legacy wallets. | 
| 3622 |  |     // Legacy wallets have only one ScriptPubKeyManager and it's active for all output and change types. | 
| 3623 | 0 |     Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3624 |  | 
 | 
| 3625 | 0 |     WalletLogPrintf("Setting spkMan to active: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(type), internal ? "true" : "false"); | 
| 3626 | 0 |     auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers; | 
| 3627 | 0 |     auto& spk_mans_other = internal ? m_external_spk_managers : m_internal_spk_managers; | 
| 3628 | 0 |     auto spk_man = m_spk_managers.at(id).get(); | 
| 3629 | 0 |     spk_mans[type] = spk_man; | 
| 3630 |  | 
 | 
| 3631 | 0 |     const auto it = spk_mans_other.find(type); | 
| 3632 | 0 |     if (it != spk_mans_other.end() && it->second == spk_man) { | 
| 3633 | 0 |         spk_mans_other.erase(type); | 
| 3634 | 0 |     } | 
| 3635 |  | 
 | 
| 3636 | 0 |     NotifyCanGetAddressesChanged(); | 
| 3637 | 0 | } | 
| 3638 |  |  | 
| 3639 |  | void CWallet::DeactivateScriptPubKeyMan(uint256 id, OutputType type, bool internal) | 
| 3640 | 0 | { | 
| 3641 | 0 |     auto spk_man = GetScriptPubKeyMan(type, internal); | 
| 3642 | 0 |     if (spk_man != nullptr && spk_man->GetID() == id) { | 
| 3643 | 0 |         WalletLogPrintf("Deactivate spkMan: id = %s, type = %s, internal = %s\n", id.ToString(), FormatOutputType(type), internal ? "true" : "false"); | 
| 3644 | 0 |         WalletBatch batch(GetDatabase()); | 
| 3645 | 0 |         if (!batch.EraseActiveScriptPubKeyMan(static_cast<uint8_t>(type), internal)) { | 
| 3646 | 0 |             throw std::runtime_error(std::string(__func__) + ": erasing active ScriptPubKeyMan id failed"); | 
| 3647 | 0 |         } | 
| 3648 |  |  | 
| 3649 | 0 |         auto& spk_mans = internal ? m_internal_spk_managers : m_external_spk_managers; | 
| 3650 | 0 |         spk_mans.erase(type); | 
| 3651 | 0 |     } | 
| 3652 |  |  | 
| 3653 | 0 |     NotifyCanGetAddressesChanged(); | 
| 3654 | 0 | } | 
| 3655 |  |  | 
| 3656 |  | DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const | 
| 3657 | 0 | { | 
| 3658 | 0 |     auto spk_man_pair = m_spk_managers.find(desc.id); | 
| 3659 |  | 
 | 
| 3660 | 0 |     if (spk_man_pair != m_spk_managers.end()) { | 
| 3661 |  |         // Try to downcast to DescriptorScriptPubKeyMan then check if the descriptors match | 
| 3662 | 0 |         DescriptorScriptPubKeyMan* spk_manager = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man_pair->second.get()); | 
| 3663 | 0 |         if (spk_manager != nullptr && spk_manager->HasWalletDescriptor(desc)) { | 
| 3664 | 0 |             return spk_manager; | 
| 3665 | 0 |         } | 
| 3666 | 0 |     } | 
| 3667 |  |  | 
| 3668 | 0 |     return nullptr; | 
| 3669 | 0 | } | 
| 3670 |  |  | 
| 3671 |  | std::optional<bool> CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const | 
| 3672 | 0 | { | 
| 3673 |  |     // only active ScriptPubKeyMan can be internal | 
| 3674 | 0 |     if (!GetActiveScriptPubKeyMans().count(spk_man)) { | 
| 3675 | 0 |         return std::nullopt; | 
| 3676 | 0 |     } | 
| 3677 |  |  | 
| 3678 | 0 |     const auto desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(spk_man); | 
| 3679 | 0 |     if (!desc_spk_man) { | 
| 3680 | 0 |         throw std::runtime_error(std::string(__func__) + ": unexpected ScriptPubKeyMan type."); | 
| 3681 | 0 |     } | 
| 3682 |  |  | 
| 3683 | 0 |     LOCK(desc_spk_man->cs_desc_man); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3684 | 0 |     const auto& type = desc_spk_man->GetWalletDescriptor().descriptor->GetOutputType(); | 
| 3685 | 0 |     assert(type.has_value()); | 
| 3686 |  |  | 
| 3687 | 0 |     return GetScriptPubKeyMan(*type, /* internal= */ true) == desc_spk_man; | 
| 3688 | 0 | } | 
| 3689 |  |  | 
| 3690 |  | util::Result<std::reference_wrapper<DescriptorScriptPubKeyMan>> CWallet::AddWalletDescriptor(WalletDescriptor& desc, const FlatSigningProvider& signing_provider, const std::string& label, bool internal) | 
| 3691 | 0 | { | 
| 3692 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3693 |  | 
 | 
| 3694 | 0 |     Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3695 |  | 
 | 
| 3696 | 0 |     auto spk_man = GetDescriptorScriptPubKeyMan(desc); | 
| 3697 | 0 |     if (spk_man) { | 
| 3698 | 0 |         WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString()); | 
| 3699 | 0 |         if (auto spkm_res = spk_man->UpdateWalletDescriptor(desc); !spkm_res) { | 
| 3700 | 0 |             return util::Error{util::ErrorString(spkm_res)}; | 
| 3701 | 0 |         } | 
| 3702 | 0 |     } else { | 
| 3703 | 0 |         auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc, m_keypool_size)); | 
| 3704 | 0 |         spk_man = new_spk_man.get(); | 
| 3705 |  |  | 
| 3706 |  |         // Save the descriptor to memory | 
| 3707 | 0 |         uint256 id = new_spk_man->GetID(); | 
| 3708 | 0 |         AddScriptPubKeyMan(id, std::move(new_spk_man)); | 
| 3709 | 0 |     } | 
| 3710 |  |  | 
| 3711 |  |     // Add the private keys to the descriptor | 
| 3712 | 0 |     for (const auto& entry : signing_provider.keys) { | 
| 3713 | 0 |         const CKey& key = entry.second; | 
| 3714 | 0 |         spk_man->AddDescriptorKey(key, key.GetPubKey()); | 
| 3715 | 0 |     } | 
| 3716 |  |  | 
| 3717 |  |     // Top up key pool, the manager will generate new scriptPubKeys internally | 
| 3718 | 0 |     if (!spk_man->TopUp()) { | 
| 3719 | 0 |         return util::Error{_("Could not top up scriptPubKeys")}; | 
| 3720 | 0 |     } | 
| 3721 |  |  | 
| 3722 |  |     // Apply the label if necessary | 
| 3723 |  |     // Note: we disable labels for ranged descriptors | 
| 3724 | 0 |     if (!desc.descriptor->IsRange()) { | 
| 3725 | 0 |         auto script_pub_keys = spk_man->GetScriptPubKeys(); | 
| 3726 | 0 |         if (script_pub_keys.empty()) { | 
| 3727 | 0 |             return util::Error{_("Could not generate scriptPubKeys (cache is empty)")}; | 
| 3728 | 0 |         } | 
| 3729 |  |  | 
| 3730 | 0 |         if (!internal) { | 
| 3731 | 0 |             for (const auto& script : script_pub_keys) { | 
| 3732 | 0 |                 CTxDestination dest; | 
| 3733 | 0 |                 if (ExtractDestination(script, dest)) { | 
| 3734 | 0 |                     SetAddressBook(dest, label, AddressPurpose::RECEIVE); | 
| 3735 | 0 |                 } | 
| 3736 | 0 |             } | 
| 3737 | 0 |         } | 
| 3738 | 0 |     } | 
| 3739 |  |  | 
| 3740 |  |     // Save the descriptor to DB | 
| 3741 | 0 |     spk_man->WriteDescriptor(); | 
| 3742 |  |  | 
| 3743 |  |     // Break balance caches so that outputs that are now IsMine in already known txs will be included in the balance | 
| 3744 | 0 |     MarkDirty(); | 
| 3745 |  | 
 | 
| 3746 | 0 |     return std::reference_wrapper(*spk_man); | 
| 3747 | 0 | } | 
| 3748 |  |  | 
| 3749 |  | bool CWallet::MigrateToSQLite(bilingual_str& error) | 
| 3750 | 0 | { | 
| 3751 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3752 |  | 
 | 
| 3753 | 0 |     WalletLogPrintf("Migrating wallet storage database from BerkeleyDB to SQLite.\n"); | 
| 3754 |  | 
 | 
| 3755 | 0 |     if (m_database->Format() == "sqlite") { | 
| 3756 | 0 |         error = _("Error: This wallet already uses SQLite"); | 
| 3757 | 0 |         return false; | 
| 3758 | 0 |     } | 
| 3759 |  |  | 
| 3760 |  |     // Get all of the records for DB type migration | 
| 3761 | 0 |     std::unique_ptr<DatabaseBatch> batch = m_database->MakeBatch(); | 
| 3762 | 0 |     std::unique_ptr<DatabaseCursor> cursor = batch->GetNewCursor(); | 
| 3763 | 0 |     std::vector<std::pair<SerializeData, SerializeData>> records; | 
| 3764 | 0 |     if (!cursor) { | 
| 3765 | 0 |         error = _("Error: Unable to begin reading all records in the database"); | 
| 3766 | 0 |         return false; | 
| 3767 | 0 |     } | 
| 3768 | 0 |     DatabaseCursor::Status status = DatabaseCursor::Status::FAIL; | 
| 3769 | 0 |     while (true) { | 
| 3770 | 0 |         DataStream ss_key{}; | 
| 3771 | 0 |         DataStream ss_value{}; | 
| 3772 | 0 |         status = cursor->Next(ss_key, ss_value); | 
| 3773 | 0 |         if (status != DatabaseCursor::Status::MORE) { | 
| 3774 | 0 |             break; | 
| 3775 | 0 |         } | 
| 3776 | 0 |         SerializeData key(ss_key.begin(), ss_key.end()); | 
| 3777 | 0 |         SerializeData value(ss_value.begin(), ss_value.end()); | 
| 3778 | 0 |         records.emplace_back(key, value); | 
| 3779 | 0 |     } | 
| 3780 | 0 |     cursor.reset(); | 
| 3781 | 0 |     batch.reset(); | 
| 3782 | 0 |     if (status != DatabaseCursor::Status::DONE) { | 
| 3783 | 0 |         error = _("Error: Unable to read all records in the database"); | 
| 3784 | 0 |         return false; | 
| 3785 | 0 |     } | 
| 3786 |  |  | 
| 3787 |  |     // Close this database and delete the file | 
| 3788 | 0 |     fs::path db_path = fs::PathFromString(m_database->Filename()); | 
| 3789 | 0 |     m_database->Close(); | 
| 3790 | 0 |     fs::remove(db_path); | 
| 3791 |  |  | 
| 3792 |  |     // Generate the path for the location of the migrated wallet | 
| 3793 |  |     // Wallets that are plain files rather than wallet directories will be migrated to be wallet directories. | 
| 3794 | 0 |     const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(m_name)); | 
| 3795 |  |  | 
| 3796 |  |     // Make new DB | 
| 3797 | 0 |     DatabaseOptions opts; | 
| 3798 | 0 |     opts.require_create = true; | 
| 3799 | 0 |     opts.require_format = DatabaseFormat::SQLITE; | 
| 3800 | 0 |     DatabaseStatus db_status; | 
| 3801 | 0 |     std::unique_ptr<WalletDatabase> new_db = MakeDatabase(wallet_path, opts, db_status, error); | 
| 3802 | 0 |     assert(new_db); // This is to prevent doing anything further with this wallet. The original file was deleted, but a backup exists. | 
| 3803 | 0 |     m_database.reset(); | 
| 3804 | 0 |     m_database = std::move(new_db); | 
| 3805 |  |  | 
| 3806 |  |     // Write existing records into the new DB | 
| 3807 | 0 |     batch = m_database->MakeBatch(); | 
| 3808 | 0 |     bool began = batch->TxnBegin(); | 
| 3809 | 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. | 
| 3810 | 0 |     for (const auto& [key, value] : records) { | 
| 3811 | 0 |         if (!batch->Write(std::span{key}, std::span{value})) { | 
| 3812 | 0 |             batch->TxnAbort(); | 
| 3813 | 0 |             m_database->Close(); | 
| 3814 | 0 |             fs::remove(m_database->Filename()); | 
| 3815 | 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. | 
| 3816 | 0 |         } | 
| 3817 | 0 |     } | 
| 3818 | 0 |     bool committed = batch->TxnCommit(); | 
| 3819 | 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. | 
| 3820 | 0 |     return true; | 
| 3821 | 0 | } | 
| 3822 |  |  | 
| 3823 |  | std::optional<MigrationData> CWallet::GetDescriptorsForLegacy(bilingual_str& error) const | 
| 3824 | 0 | { | 
| 3825 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3826 |  | 
 | 
| 3827 | 0 |     LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM(); | 
| 3828 | 0 |     if (!Assume(legacy_spkm)) {| Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3829 |  |         // This shouldn't happen | 
| 3830 | 0 |         error = Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"));| Line | Count | Source |  | 89 | 0 | #define STR_INTERNAL_BUG(msg) StrFormatInternalBug((msg), __FILE__, __LINE__, __func__) | 
 | 
| 3831 | 0 |         return std::nullopt; | 
| 3832 | 0 |     } | 
| 3833 |  |  | 
| 3834 | 0 |     std::optional<MigrationData> res = legacy_spkm->MigrateToDescriptor(); | 
| 3835 | 0 |     if (res == std::nullopt) { | 
| 3836 | 0 |         error = _("Error: Unable to produce descriptors for this legacy wallet. Make sure to provide the wallet's passphrase if it is encrypted."); | 
| 3837 | 0 |         return std::nullopt; | 
| 3838 | 0 |     } | 
| 3839 | 0 |     return res; | 
| 3840 | 0 | } | 
| 3841 |  |  | 
| 3842 |  | util::Result<void> CWallet::ApplyMigrationData(WalletBatch& local_wallet_batch, MigrationData& data) | 
| 3843 | 0 | { | 
| 3844 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 3845 |  | 
 | 
| 3846 | 0 |     LegacyDataSPKM* legacy_spkm = GetLegacyDataSPKM(); | 
| 3847 | 0 |     if (!Assume(legacy_spkm)) {| Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3848 |  |         // This shouldn't happen | 
| 3849 | 0 |         return util::Error{Untranslated(STR_INTERNAL_BUG("Error: Legacy wallet data missing"))};| Line | Count | Source |  | 89 | 0 | #define STR_INTERNAL_BUG(msg) StrFormatInternalBug((msg), __FILE__, __LINE__, __func__) | 
 | 
| 3850 | 0 |     } | 
| 3851 |  |  | 
| 3852 |  |     // Note: when the legacy wallet has no spendable scripts, it must be empty at the end of the process. | 
| 3853 | 0 |     bool has_spendable_material = !data.desc_spkms.empty() || data.master_key.key.IsValid(); | 
| 3854 |  |  | 
| 3855 |  |     // Get all invalid or non-watched scripts that will not be migrated | 
| 3856 | 0 |     std::set<CTxDestination> not_migrated_dests; | 
| 3857 | 0 |     for (const auto& script : legacy_spkm->GetNotMineScriptPubKeys()) { | 
| 3858 | 0 |         CTxDestination dest; | 
| 3859 | 0 |         if (ExtractDestination(script, dest)) not_migrated_dests.emplace(dest); | 
| 3860 | 0 |     } | 
| 3861 |  |  | 
| 3862 |  |     // When the legacy wallet has no spendable scripts, the main wallet will be empty, leaving its script cache empty as well. | 
| 3863 |  |     // The watch-only and/or solvable wallet(s) will contain the scripts in their respective caches. | 
| 3864 | 0 |     if (!data.desc_spkms.empty()) Assume(!m_cached_spks.empty()); | Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3865 | 0 |     if (!data.watch_descs.empty()) Assume(!data.watchonly_wallet->m_cached_spks.empty()); | Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3866 | 0 |     if (!data.solvable_descs.empty()) Assume(!data.solvable_wallet->m_cached_spks.empty()); | Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 3867 |  | 
 | 
| 3868 | 0 |     for (auto& desc_spkm : data.desc_spkms) { | 
| 3869 | 0 |         if (m_spk_managers.count(desc_spkm->GetID()) > 0) { | 
| 3870 | 0 |             return util::Error{_("Error: Duplicate descriptors created during migration. Your wallet may be corrupted.")}; | 
| 3871 | 0 |         } | 
| 3872 | 0 |         uint256 id = desc_spkm->GetID(); | 
| 3873 | 0 |         AddScriptPubKeyMan(id, std::move(desc_spkm)); | 
| 3874 | 0 |     } | 
| 3875 |  |  | 
| 3876 |  |     // Remove the LegacyScriptPubKeyMan from disk | 
| 3877 | 0 |     if (!legacy_spkm->DeleteRecordsWithDB(local_wallet_batch)) { | 
| 3878 | 0 |         return util::Error{_("Error: cannot remove legacy wallet records")}; | 
| 3879 | 0 |     } | 
| 3880 |  |  | 
| 3881 |  |     // Remove the LegacyScriptPubKeyMan from memory | 
| 3882 | 0 |     m_spk_managers.erase(legacy_spkm->GetID()); | 
| 3883 | 0 |     m_external_spk_managers.clear(); | 
| 3884 | 0 |     m_internal_spk_managers.clear(); | 
| 3885 |  |  | 
| 3886 |  |     // Setup new descriptors (only if we are migrating any key material) | 
| 3887 | 0 |     SetWalletFlagWithDB(local_wallet_batch, WALLET_FLAG_DESCRIPTORS | WALLET_FLAG_LAST_HARDENED_XPUB_CACHED); | 
| 3888 | 0 |     if (has_spendable_material && !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { | 
| 3889 |  |         // Use the existing master key if we have it | 
| 3890 | 0 |         if (data.master_key.key.IsValid()) { | 
| 3891 | 0 |             SetupDescriptorScriptPubKeyMans(local_wallet_batch, data.master_key); | 
| 3892 | 0 |         } else { | 
| 3893 |  |             // Setup with a new seed if we don't. | 
| 3894 | 0 |             SetupOwnDescriptorScriptPubKeyMans(local_wallet_batch); | 
| 3895 | 0 |         } | 
| 3896 | 0 |     } | 
| 3897 |  |  | 
| 3898 |  |     // Get best block locator so that we can copy it to the watchonly and solvables | 
| 3899 | 0 |     CBlockLocator best_block_locator; | 
| 3900 | 0 |     if (!local_wallet_batch.ReadBestBlock(best_block_locator)) { | 
| 3901 | 0 |         return util::Error{_("Error: Unable to read wallet's best block locator record")}; | 
| 3902 | 0 |     } | 
| 3903 |  |  | 
| 3904 |  |     // Update m_txos to match the descriptors remaining in this wallet | 
| 3905 | 0 |     m_txos.clear(); | 
| 3906 | 0 |     RefreshAllTXOs(); | 
| 3907 |  |  | 
| 3908 |  |     // Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet. | 
| 3909 |  |     // We need to go through these in the tx insertion order so that lookups to spends works. | 
| 3910 | 0 |     std::vector<Txid> txids_to_delete; | 
| 3911 | 0 |     std::unique_ptr<WalletBatch> watchonly_batch; | 
| 3912 | 0 |     if (data.watchonly_wallet) { | 
| 3913 | 0 |         watchonly_batch = std::make_unique<WalletBatch>(data.watchonly_wallet->GetDatabase()); | 
| 3914 | 0 |         if (!watchonly_batch->TxnBegin()) return util::Error{strprintf(_("Error: database transaction cannot be executed for wallet %s"), data.watchonly_wallet->GetName())};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3915 |  |         // Copy the next tx order pos to the watchonly wallet | 
| 3916 | 0 |         LOCK(data.watchonly_wallet->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3917 | 0 |         data.watchonly_wallet->nOrderPosNext = nOrderPosNext; | 
| 3918 | 0 |         watchonly_batch->WriteOrderPosNext(data.watchonly_wallet->nOrderPosNext); | 
| 3919 |  |         // Write the best block locator to avoid rescanning on reload | 
| 3920 | 0 |         if (!watchonly_batch->WriteBestBlock(best_block_locator)) { | 
| 3921 | 0 |             return util::Error{_("Error: Unable to write watchonly wallet best block locator record")}; | 
| 3922 | 0 |         } | 
| 3923 | 0 |     } | 
| 3924 | 0 |     std::unique_ptr<WalletBatch> solvables_batch; | 
| 3925 | 0 |     if (data.solvable_wallet) { | 
| 3926 | 0 |         solvables_batch = std::make_unique<WalletBatch>(data.solvable_wallet->GetDatabase()); | 
| 3927 | 0 |         if (!solvables_batch->TxnBegin()) return util::Error{strprintf(_("Error: database transaction cannot be executed for wallet %s"), data.solvable_wallet->GetName())};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3928 |  |         // Write the best block locator to avoid rescanning on reload | 
| 3929 | 0 |         if (!solvables_batch->WriteBestBlock(best_block_locator)) { | 
| 3930 | 0 |             return util::Error{_("Error: Unable to write solvable wallet best block locator record")}; | 
| 3931 | 0 |         } | 
| 3932 | 0 |     } | 
| 3933 | 0 |     for (const auto& [_pos, wtx] : wtxOrdered) { | 
| 3934 |  |         // Check it is the watchonly wallet's | 
| 3935 |  |         // solvable_wallet doesn't need to be checked because transactions for those scripts weren't being watched for | 
| 3936 | 0 |         bool is_mine = IsMine(*wtx->tx) || IsFromMe(*wtx->tx); | 
| 3937 | 0 |         if (data.watchonly_wallet) { | 
| 3938 | 0 |             LOCK(data.watchonly_wallet->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3939 | 0 |             if (data.watchonly_wallet->IsMine(*wtx->tx) || data.watchonly_wallet->IsFromMe(*wtx->tx)) { | 
| 3940 |  |                 // Add to watchonly wallet | 
| 3941 | 0 |                 const Txid& hash = wtx->GetHash(); | 
| 3942 | 0 |                 const CWalletTx& to_copy_wtx = *wtx; | 
| 3943 | 0 |                 if (!data.watchonly_wallet->LoadToWallet(hash, [&](CWalletTx& ins_wtx, bool new_tx) EXCLUSIVE_LOCKS_REQUIRED(data.watchonly_wallet->cs_wallet) { | 
| 3944 | 0 |                     if (!new_tx) return false; | 
| 3945 | 0 |                     ins_wtx.SetTx(to_copy_wtx.tx); | 
| 3946 | 0 |                     ins_wtx.CopyFrom(to_copy_wtx); | 
| 3947 | 0 |                     return true; | 
| 3948 | 0 |                 })) { | 
| 3949 | 0 |                     return util::Error{strprintf(_("Error: Could not add watchonly tx %s to watchonly wallet"), wtx->GetHash().GetHex())};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3950 | 0 |                 } | 
| 3951 | 0 |                 watchonly_batch->WriteTx(data.watchonly_wallet->mapWallet.at(hash)); | 
| 3952 |  |                 // Mark as to remove from the migrated wallet only if it does not also belong to it | 
| 3953 | 0 |                 if (!is_mine) { | 
| 3954 | 0 |                     txids_to_delete.push_back(hash); | 
| 3955 | 0 |                 } | 
| 3956 | 0 |                 continue; | 
| 3957 | 0 |             } | 
| 3958 | 0 |         } | 
| 3959 | 0 |         if (!is_mine) { | 
| 3960 |  |             // Both not ours and not in the watchonly wallet | 
| 3961 | 0 |             return util::Error{strprintf(_("Error: Transaction %s in wallet cannot be identified to belong to migrated wallets"), wtx->GetHash().GetHex())};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 3962 | 0 |         } | 
| 3963 | 0 |     } | 
| 3964 |  |  | 
| 3965 |  |     // Do the removes | 
| 3966 | 0 |     if (txids_to_delete.size() > 0) { | 
| 3967 | 0 |         if (auto res = RemoveTxs(local_wallet_batch, txids_to_delete); !res) { | 
| 3968 | 0 |             return util::Error{_("Error: Could not delete watchonly transactions. ") + util::ErrorString(res)}; | 
| 3969 | 0 |         } | 
| 3970 | 0 |     } | 
| 3971 |  |  | 
| 3972 |  |     // Pair external wallets with their corresponding db handler | 
| 3973 | 0 |     std::vector<std::pair<std::shared_ptr<CWallet>, std::unique_ptr<WalletBatch>>> wallets_vec; | 
| 3974 | 0 |     if (data.watchonly_wallet) wallets_vec.emplace_back(data.watchonly_wallet, std::move(watchonly_batch)); | 
| 3975 | 0 |     if (data.solvable_wallet) wallets_vec.emplace_back(data.solvable_wallet, std::move(solvables_batch)); | 
| 3976 |  |  | 
| 3977 |  |     // Write address book entry to disk | 
| 3978 | 0 |     auto func_store_addr = [](WalletBatch& batch, const CTxDestination& dest, const CAddressBookData& entry) { | 
| 3979 | 0 |         auto address{EncodeDestination(dest)}; | 
| 3980 | 0 |         if (entry.purpose) batch.WritePurpose(address, PurposeToString(*entry.purpose)); | 
| 3981 | 0 |         if (entry.label) batch.WriteName(address, *entry.label); | 
| 3982 | 0 |         for (const auto& [id, request] : entry.receive_requests) { | 
| 3983 | 0 |             batch.WriteAddressReceiveRequest(dest, id, request); | 
| 3984 | 0 |         } | 
| 3985 | 0 |         if (entry.previously_spent) batch.WriteAddressPreviouslySpent(dest, true); | 
| 3986 | 0 |     }; | 
| 3987 |  |  | 
| 3988 |  |     // Check the address book data in the same way we did for transactions | 
| 3989 | 0 |     std::vector<CTxDestination> dests_to_delete; | 
| 3990 | 0 |     for (const auto& [dest, record] : m_address_book) { | 
| 3991 |  |         // Ensure "receive" entries that are no longer part of the original wallet are transferred to another wallet | 
| 3992 |  |         // Entries for everything else ("send") will be cloned to all wallets. | 
| 3993 | 0 |         bool require_transfer = record.purpose == AddressPurpose::RECEIVE && !IsMine(dest); | 
| 3994 | 0 |         bool copied = false; | 
| 3995 | 0 |         for (auto& [wallet, batch] : wallets_vec) { | 
| 3996 | 0 |             LOCK(wallet->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 3997 | 0 |             if (require_transfer && !wallet->IsMine(dest)) continue; | 
| 3998 |  |  | 
| 3999 |  |             // Copy the entire address book entry | 
| 4000 | 0 |             wallet->m_address_book[dest] = record; | 
| 4001 | 0 |             func_store_addr(*batch, dest, record); | 
| 4002 |  | 
 | 
| 4003 | 0 |             copied = true; | 
| 4004 |  |             // Only delete 'receive' records that are no longer part of the original wallet | 
| 4005 | 0 |             if (require_transfer) { | 
| 4006 | 0 |                 dests_to_delete.push_back(dest); | 
| 4007 | 0 |                 break; | 
| 4008 | 0 |             } | 
| 4009 | 0 |         } | 
| 4010 |  |  | 
| 4011 |  |         // Fail immediately if we ever found an entry that was ours and cannot be transferred | 
| 4012 |  |         // to any of the created wallets (watch-only, solvable). | 
| 4013 |  |         // Means that no inferred descriptor maps to the stored entry. Which mustn't happen. | 
| 4014 | 0 |         if (require_transfer && !copied) { | 
| 4015 |  |  | 
| 4016 |  |             // Skip invalid/non-watched scripts that will not be migrated | 
| 4017 | 0 |             if (not_migrated_dests.count(dest) > 0) { | 
| 4018 | 0 |                 dests_to_delete.push_back(dest); | 
| 4019 | 0 |                 continue; | 
| 4020 | 0 |             } | 
| 4021 |  |  | 
| 4022 | 0 |             return util::Error{_("Error: Address book data in wallet cannot be identified to belong to migrated wallets")}; | 
| 4023 | 0 |         } | 
| 4024 | 0 |     } | 
| 4025 |  |  | 
| 4026 |  |     // Persist external wallets address book entries | 
| 4027 | 0 |     for (auto& [wallet, batch] : wallets_vec) { | 
| 4028 | 0 |         if (!batch->TxnCommit()) { | 
| 4029 | 0 |             return util::Error{strprintf(_("Error: Unable to write data to disk for wallet %s"), wallet->GetName())};| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 4030 | 0 |         } | 
| 4031 | 0 |     } | 
| 4032 |  |  | 
| 4033 |  |     // Remove the things to delete in this wallet | 
| 4034 | 0 |     if (dests_to_delete.size() > 0) { | 
| 4035 | 0 |         for (const auto& dest : dests_to_delete) { | 
| 4036 | 0 |             if (!DelAddressBookWithDB(local_wallet_batch, dest)) { | 
| 4037 | 0 |                 return util::Error{_("Error: Unable to remove watchonly address book data")}; | 
| 4038 | 0 |             } | 
| 4039 | 0 |         } | 
| 4040 | 0 |     } | 
| 4041 |  |  | 
| 4042 |  |     // If there was no key material in the main wallet, there should be no records on it anymore. | 
| 4043 |  |     // This wallet will be discarded at the end of the process. Only wallets that contain the | 
| 4044 |  |     // migrated records will be presented to the user. | 
| 4045 | 0 |     if (!has_spendable_material) { | 
| 4046 | 0 |         if (!m_address_book.empty()) return util::Error{_("Error: Not all address book records were migrated")}; | 
| 4047 | 0 |         if (!mapWallet.empty()) return util::Error{_("Error: Not all transaction records were migrated")}; | 
| 4048 | 0 |     } | 
| 4049 |  |  | 
| 4050 | 0 |     return {}; // all good | 
| 4051 | 0 | } | 
| 4052 |  |  | 
| 4053 |  | bool CWallet::CanGrindR() const | 
| 4054 | 0 | { | 
| 4055 | 0 |     return !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); | 
| 4056 | 0 | } | 
| 4057 |  |  | 
| 4058 |  | bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) | 
| 4059 | 0 | { | 
| 4060 | 0 |     AssertLockHeld(wallet.cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 4061 |  |  | 
| 4062 |  |     // Get all of the descriptors from the legacy wallet | 
| 4063 | 0 |     std::optional<MigrationData> data = wallet.GetDescriptorsForLegacy(error); | 
| 4064 | 0 |     if (data == std::nullopt) return false; | 
| 4065 |  |  | 
| 4066 |  |     // Create the watchonly and solvable wallets if necessary | 
| 4067 | 0 |     if (data->watch_descs.size() > 0 || data->solvable_descs.size() > 0) { | 
| 4068 | 0 |         DatabaseOptions options; | 
| 4069 | 0 |         options.require_existing = false; | 
| 4070 | 0 |         options.require_create = true; | 
| 4071 | 0 |         options.require_format = DatabaseFormat::SQLITE; | 
| 4072 |  | 
 | 
| 4073 | 0 |         WalletContext empty_context; | 
| 4074 | 0 |         empty_context.args = context.args; | 
| 4075 |  |  | 
| 4076 |  |         // Make the wallets | 
| 4077 | 0 |         options.create_flags = WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET | WALLET_FLAG_DESCRIPTORS; | 
| 4078 | 0 |         if (wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { | 
| 4079 | 0 |             options.create_flags |= WALLET_FLAG_AVOID_REUSE; | 
| 4080 | 0 |         } | 
| 4081 | 0 |         if (wallet.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { | 
| 4082 | 0 |             options.create_flags |= WALLET_FLAG_KEY_ORIGIN_METADATA; | 
| 4083 | 0 |         } | 
| 4084 | 0 |         if (data->watch_descs.size() > 0) { | 
| 4085 | 0 |             wallet.WalletLogPrintf("Making a new watchonly wallet containing the watched scripts\n"); | 
| 4086 |  | 
 | 
| 4087 | 0 |             DatabaseStatus status; | 
| 4088 | 0 |             std::vector<bilingual_str> warnings; | 
| 4089 | 0 |             std::string wallet_name = wallet.GetName() + "_watchonly"; | 
| 4090 | 0 |             std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error); | 
| 4091 | 0 |             if (!database) { | 
| 4092 | 0 |                 error = strprintf(_("Wallet file creation failed: %s"), error);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 4093 | 0 |                 return false; | 
| 4094 | 0 |             } | 
| 4095 |  |  | 
| 4096 | 0 |             data->watchonly_wallet = CWallet::Create(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings); | 
| 4097 | 0 |             if (!data->watchonly_wallet) { | 
| 4098 | 0 |                 error = _("Error: Failed to create new watchonly wallet"); | 
| 4099 | 0 |                 return false; | 
| 4100 | 0 |             } | 
| 4101 | 0 |             res.watchonly_wallet = data->watchonly_wallet; | 
| 4102 | 0 |             LOCK(data->watchonly_wallet->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 4103 |  |  | 
| 4104 |  |             // Parse the descriptors and add them to the new wallet | 
| 4105 | 0 |             for (const auto& [desc_str, creation_time] : data->watch_descs) { | 
| 4106 |  |                 // Parse the descriptor | 
| 4107 | 0 |                 FlatSigningProvider keys; | 
| 4108 | 0 |                 std::string parse_err; | 
| 4109 | 0 |                 std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, parse_err, /* require_checksum */ true); | 
| 4110 | 0 |                 assert(descs.size() == 1); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor or a multipath descriptors | 
| 4111 | 0 |                 assert(!descs.at(0)->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor | 
| 4112 |  |  | 
| 4113 |  |                 // Add to the wallet | 
| 4114 | 0 |                 WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); | 
| 4115 | 0 |                 if (auto spkm_res = data->watchonly_wallet->AddWalletDescriptor(w_desc, keys, "", false); !spkm_res) { | 
| 4116 | 0 |                     throw std::runtime_error(util::ErrorString(spkm_res).original); | 
| 4117 | 0 |                 } | 
| 4118 | 0 |             } | 
| 4119 |  |  | 
| 4120 |  |             // Add the wallet to settings | 
| 4121 | 0 |             UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings); | 
| 4122 | 0 |         } | 
| 4123 | 0 |         if (data->solvable_descs.size() > 0) { | 
| 4124 | 0 |             wallet.WalletLogPrintf("Making a new watchonly wallet containing the unwatched solvable scripts\n"); | 
| 4125 |  | 
 | 
| 4126 | 0 |             DatabaseStatus status; | 
| 4127 | 0 |             std::vector<bilingual_str> warnings; | 
| 4128 | 0 |             std::string wallet_name = wallet.GetName() + "_solvables"; | 
| 4129 | 0 |             std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error); | 
| 4130 | 0 |             if (!database) { | 
| 4131 | 0 |                 error = strprintf(_("Wallet file creation failed: %s"), error);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 4132 | 0 |                 return false; | 
| 4133 | 0 |             } | 
| 4134 |  |  | 
| 4135 | 0 |             data->solvable_wallet = CWallet::Create(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings); | 
| 4136 | 0 |             if (!data->solvable_wallet) { | 
| 4137 | 0 |                 error = _("Error: Failed to create new watchonly wallet"); | 
| 4138 | 0 |                 return false; | 
| 4139 | 0 |             } | 
| 4140 | 0 |             res.solvables_wallet = data->solvable_wallet; | 
| 4141 | 0 |             LOCK(data->solvable_wallet->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 4142 |  |  | 
| 4143 |  |             // Parse the descriptors and add them to the new wallet | 
| 4144 | 0 |             for (const auto& [desc_str, creation_time] : data->solvable_descs) { | 
| 4145 |  |                 // Parse the descriptor | 
| 4146 | 0 |                 FlatSigningProvider keys; | 
| 4147 | 0 |                 std::string parse_err; | 
| 4148 | 0 |                 std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, parse_err, /* require_checksum */ true); | 
| 4149 | 0 |                 assert(descs.size() == 1); // It shouldn't be possible to have the LegacyScriptPubKeyMan make an invalid descriptor or a multipath descriptors | 
| 4150 | 0 |                 assert(!descs.at(0)->IsRange()); // It shouldn't be possible to have LegacyScriptPubKeyMan make a ranged watchonly descriptor | 
| 4151 |  |  | 
| 4152 |  |                 // Add to the wallet | 
| 4153 | 0 |                 WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); | 
| 4154 | 0 |                 if (auto spkm_res = data->solvable_wallet->AddWalletDescriptor(w_desc, keys, "", false); !spkm_res) { | 
| 4155 | 0 |                     throw std::runtime_error(util::ErrorString(spkm_res).original); | 
| 4156 | 0 |                 } | 
| 4157 | 0 |             } | 
| 4158 |  |  | 
| 4159 |  |             // Add the wallet to settings | 
| 4160 | 0 |             UpdateWalletSetting(*context.chain, wallet_name, /*load_on_startup=*/true, warnings); | 
| 4161 | 0 |         } | 
| 4162 | 0 |     } | 
| 4163 |  |  | 
| 4164 |  |     // Add the descriptors to wallet, remove LegacyScriptPubKeyMan, and cleanup txs and address book data | 
| 4165 | 0 |     return RunWithinTxn(wallet.GetDatabase(), /*process_desc=*/"apply migration process", [&](WalletBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet){ | 
| 4166 | 0 |         if (auto res_migration = wallet.ApplyMigrationData(batch, *data); !res_migration) { | 
| 4167 | 0 |             error = util::ErrorString(res_migration); | 
| 4168 | 0 |             return false; | 
| 4169 | 0 |         } | 
| 4170 | 0 |         wallet.WalletLogPrintf("Wallet migration complete.\n"); | 
| 4171 | 0 |         return true; | 
| 4172 | 0 |     }); | 
| 4173 | 0 | } | 
| 4174 |  |  | 
| 4175 |  | util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context) | 
| 4176 | 0 | { | 
| 4177 | 0 |     std::vector<bilingual_str> warnings; | 
| 4178 | 0 |     bilingual_str error; | 
| 4179 |  |  | 
| 4180 |  |     // The only kind of wallets that could be loaded are descriptor ones, which don't need to be migrated. | 
| 4181 | 0 |     if (auto wallet = GetWallet(context, wallet_name)) { | 
| 4182 | 0 |         assert(wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); | 
| 4183 | 0 |         return util::Error{_("Error: This wallet is already a descriptor wallet")}; | 
| 4184 | 0 |     } else { | 
| 4185 |  |         // Check if the wallet is BDB | 
| 4186 | 0 |         const auto& wallet_path = GetWalletPath(wallet_name); | 
| 4187 | 0 |         if (!wallet_path) { | 
| 4188 | 0 |             return util::Error{util::ErrorString(wallet_path)}; | 
| 4189 | 0 |         } | 
| 4190 | 0 |         if (!fs::exists(*wallet_path)) { | 
| 4191 | 0 |             return util::Error{_("Error: Wallet does not exist")}; | 
| 4192 | 0 |         } | 
| 4193 | 0 |         if (!IsBDBFile(BDBDataFile(*wallet_path))) { | 
| 4194 | 0 |             return util::Error{_("Error: This wallet is already a descriptor wallet")}; | 
| 4195 | 0 |         } | 
| 4196 | 0 |     } | 
| 4197 |  |  | 
| 4198 |  |     // Load the wallet but only in the context of this function. | 
| 4199 |  |     // No signals should be connected nor should anything else be aware of this wallet | 
| 4200 | 0 |     WalletContext empty_context; | 
| 4201 | 0 |     empty_context.args = context.args; | 
| 4202 | 0 |     DatabaseOptions options; | 
| 4203 | 0 |     options.require_existing = true; | 
| 4204 | 0 |     options.require_format = DatabaseFormat::BERKELEY_RO; | 
| 4205 | 0 |     DatabaseStatus status; | 
| 4206 | 0 |     std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(wallet_name, options, status, error); | 
| 4207 | 0 |     if (!database) { | 
| 4208 | 0 |         return util::Error{Untranslated("Wallet file verification failed.") + Untranslated(" ") + error}; | 
| 4209 | 0 |     } | 
| 4210 |  |  | 
| 4211 |  |     // Make the local wallet | 
| 4212 | 0 |     std::shared_ptr<CWallet> local_wallet = CWallet::Create(empty_context, wallet_name, std::move(database), options.create_flags, error, warnings); | 
| 4213 | 0 |     if (!local_wallet) { | 
| 4214 | 0 |         return util::Error{Untranslated("Wallet loading failed.") + Untranslated(" ") + error}; | 
| 4215 | 0 |     } | 
| 4216 |  |  | 
| 4217 | 0 |     return MigrateLegacyToDescriptor(std::move(local_wallet), passphrase, context); | 
| 4218 | 0 | } | 
| 4219 |  |  | 
| 4220 |  | util::Result<MigrationResult> MigrateLegacyToDescriptor(std::shared_ptr<CWallet> local_wallet, const SecureString& passphrase, WalletContext& context) | 
| 4221 | 0 | { | 
| 4222 | 0 |     MigrationResult res; | 
| 4223 | 0 |     bilingual_str error; | 
| 4224 | 0 |     std::vector<bilingual_str> warnings; | 
| 4225 |  | 
 | 
| 4226 | 0 |     DatabaseOptions options; | 
| 4227 | 0 |     options.require_existing = true; | 
| 4228 | 0 |     DatabaseStatus status; | 
| 4229 |  | 
 | 
| 4230 | 0 |     const std::string wallet_name = local_wallet->GetName(); | 
| 4231 |  |  | 
| 4232 |  |     // Before anything else, check if there is something to migrate. | 
| 4233 | 0 |     if (local_wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { | 
| 4234 | 0 |         return util::Error{_("Error: This wallet is already a descriptor wallet")}; | 
| 4235 | 0 |     } | 
| 4236 |  |  | 
| 4237 |  |     // Make a backup of the DB in the wallet's directory with a unique filename | 
| 4238 |  |     // using the wallet name and current timestamp. The backup filename is based | 
| 4239 |  |     // on the name of the parent directory containing the wallet data in most | 
| 4240 |  |     // cases, but in the case where the wallet name is a path to a data file, | 
| 4241 |  |     // the name of the data file is used, and in the case where the wallet name | 
| 4242 |  |     // is blank, "default_wallet" is used. | 
| 4243 | 0 |     const std::string backup_prefix = wallet_name.empty() ? "default_wallet" : [&] { | 
| 4244 |  |         // fs::weakly_canonical resolves relative specifiers and remove trailing slashes. | 
| 4245 | 0 |         const auto legacy_wallet_path = fs::weakly_canonical(GetWalletDir() / fs::PathFromString(wallet_name)); | 
| 4246 | 0 |         return fs::PathToString(legacy_wallet_path.filename()); | 
| 4247 | 0 |     }(); | 
| 4248 |  | 
 | 
| 4249 | 0 |     fs::path backup_filename = fs::PathFromString(strprintf("%s_%d.legacy.bak", backup_prefix, GetTime()));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 4250 | 0 |     fs::path backup_path = fsbridge::AbsPathJoin(GetWalletDir(), backup_filename); | 
| 4251 | 0 |     if (!local_wallet->BackupWallet(fs::PathToString(backup_path))) { | 
| 4252 | 0 |         return util::Error{_("Error: Unable to make a backup of your wallet")}; | 
| 4253 | 0 |     } | 
| 4254 | 0 |     res.backup_path = backup_path; | 
| 4255 |  | 
 | 
| 4256 | 0 |     bool success = false; | 
| 4257 |  |  | 
| 4258 |  |     // Unlock the wallet if needed | 
| 4259 | 0 |     if (local_wallet->IsLocked() && !local_wallet->Unlock(passphrase)) { | 
| 4260 | 0 |         if (passphrase.find('\0') == std::string::npos) { | 
| 4261 | 0 |             return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect.")}; | 
| 4262 | 0 |         } else { | 
| 4263 | 0 |             return util::Error{Untranslated("Error: Wallet decryption failed, the wallet passphrase entered was incorrect. " | 
| 4264 | 0 |                                             "The passphrase contains a null character (ie - a zero byte). " | 
| 4265 | 0 |                                             "If this passphrase was set with a version of this software prior to 25.0, " | 
| 4266 | 0 |                                             "please try again with only the characters up to — but not including — " | 
| 4267 | 0 |                                             "the first null character.")}; | 
| 4268 | 0 |         } | 
| 4269 | 0 |     } | 
| 4270 |  |  | 
| 4271 |  |     // Indicates whether the current wallet is empty after migration. | 
| 4272 |  |     // Notes: | 
| 4273 |  |     // When non-empty: the local wallet becomes the main spendable wallet. | 
| 4274 |  |     // When empty: The local wallet is excluded from the result, as the | 
| 4275 |  |     //             user does not expect an empty spendable wallet after | 
| 4276 |  |     //             migrating only watch-only scripts. | 
| 4277 | 0 |     bool empty_local_wallet = false; | 
| 4278 |  | 
 | 
| 4279 | 0 |     { | 
| 4280 | 0 |         LOCK(local_wallet->cs_wallet); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 4281 |  |         // First change to using SQLite | 
| 4282 | 0 |         if (!local_wallet->MigrateToSQLite(error)) return util::Error{error}; | 
| 4283 |  |  | 
| 4284 |  |         // Do the migration of keys and scripts for non-empty wallets, and cleanup if it fails | 
| 4285 | 0 |         if (HasLegacyRecords(*local_wallet)) { | 
| 4286 | 0 |             success = DoMigration(*local_wallet, context, error, res); | 
| 4287 |  |             // No scripts mean empty wallet after migration | 
| 4288 | 0 |             empty_local_wallet = local_wallet->GetAllScriptPubKeyMans().empty(); | 
| 4289 | 0 |         } else { | 
| 4290 |  |             // Make sure that descriptors flag is actually set | 
| 4291 | 0 |             local_wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS); | 
| 4292 | 0 |             success = true; | 
| 4293 | 0 |         } | 
| 4294 | 0 |     } | 
| 4295 |  |  | 
| 4296 |  |     // In case of loading failure, we need to remember the wallet dirs to remove. | 
| 4297 |  |     // A `set` is used as it may be populated with the same wallet directory paths multiple times, | 
| 4298 |  |     // both before and after loading. This ensures the set is complete even if one of the wallets | 
| 4299 |  |     // fails to load. | 
| 4300 | 0 |     std::set<fs::path> wallet_dirs; | 
| 4301 | 0 |     if (success) { | 
| 4302 | 0 |         Assume(!res.wallet); // We will set it here. | Line | Count | Source |  | 118 | 0 | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 4303 |  |         // Check if the local wallet is empty after migration | 
| 4304 | 0 |         if (empty_local_wallet) { | 
| 4305 |  |             // This wallet has no records. We can safely remove it. | 
| 4306 | 0 |             std::vector<fs::path> paths_to_remove = local_wallet->GetDatabase().Files(); | 
| 4307 | 0 |             local_wallet.reset(); | 
| 4308 | 0 |             for (const auto& path_to_remove : paths_to_remove) fs::remove_all(path_to_remove); | 
| 4309 | 0 |         } | 
| 4310 |  |  | 
| 4311 |  |         // Migration successful, load all the migrated wallets. | 
| 4312 | 0 |         for (std::shared_ptr<CWallet>* wallet_ptr : {&local_wallet, &res.watchonly_wallet, &res.solvables_wallet}) { | 
| 4313 | 0 |             if (success && *wallet_ptr) { | 
| 4314 | 0 |                 std::shared_ptr<CWallet>& wallet = *wallet_ptr; | 
| 4315 |  |                 // Save db path and load wallet | 
| 4316 | 0 |                 wallet_dirs.insert(fs::PathFromString(wallet->GetDatabase().Filename()).parent_path()); | 
| 4317 | 0 |                 assert(wallet.use_count() == 1); | 
| 4318 | 0 |                 std::string wallet_name = wallet->GetName(); | 
| 4319 | 0 |                 wallet.reset(); | 
| 4320 | 0 |                 wallet = LoadWallet(context, wallet_name, /*load_on_start=*/std::nullopt, options, status, error, warnings); | 
| 4321 | 0 |                 success = (wallet != nullptr); | 
| 4322 |  |  | 
| 4323 |  |                 // When no wallet is set, set the main wallet. | 
| 4324 | 0 |                 if (success && !res.wallet) { | 
| 4325 | 0 |                     res.wallet_name = wallet->GetName(); | 
| 4326 | 0 |                     res.wallet = std::move(wallet); | 
| 4327 | 0 |                 } | 
| 4328 | 0 |             } | 
| 4329 | 0 |         } | 
| 4330 | 0 |     } | 
| 4331 | 0 |     if (!success) { | 
| 4332 |  |         // Make list of wallets to cleanup | 
| 4333 | 0 |         std::vector<std::shared_ptr<CWallet>> created_wallets; | 
| 4334 | 0 |         if (local_wallet) created_wallets.push_back(std::move(local_wallet)); | 
| 4335 | 0 |         if (res.watchonly_wallet) created_wallets.push_back(std::move(res.watchonly_wallet)); | 
| 4336 | 0 |         if (res.solvables_wallet) created_wallets.push_back(std::move(res.solvables_wallet)); | 
| 4337 |  |  | 
| 4338 |  |         // Get the directories to remove after unloading | 
| 4339 | 0 |         for (std::shared_ptr<CWallet>& w : created_wallets) { | 
| 4340 | 0 |             wallet_dirs.emplace(fs::PathFromString(w->GetDatabase().Filename()).parent_path()); | 
| 4341 | 0 |         } | 
| 4342 |  |  | 
| 4343 |  |         // Unload the wallets | 
| 4344 | 0 |         for (std::shared_ptr<CWallet>& w : created_wallets) { | 
| 4345 | 0 |             if (w->HaveChain()) { | 
| 4346 |  |                 // Unloading for wallets that were loaded for normal use | 
| 4347 | 0 |                 if (!RemoveWallet(context, w, /*load_on_start=*/false)) { | 
| 4348 | 0 |                     error += _("\nUnable to cleanup failed migration"); | 
| 4349 | 0 |                     return util::Error{error}; | 
| 4350 | 0 |                 } | 
| 4351 | 0 |                 WaitForDeleteWallet(std::move(w)); | 
| 4352 | 0 |             } else { | 
| 4353 |  |                 // Unloading for wallets in local context | 
| 4354 | 0 |                 assert(w.use_count() == 1); | 
| 4355 | 0 |                 w.reset(); | 
| 4356 | 0 |             } | 
| 4357 | 0 |         } | 
| 4358 |  |  | 
| 4359 |  |         // Delete the wallet directories | 
| 4360 | 0 |         for (const fs::path& dir : wallet_dirs) { | 
| 4361 | 0 |             fs::remove_all(dir); | 
| 4362 | 0 |         } | 
| 4363 |  |  | 
| 4364 |  |         // Restore the backup | 
| 4365 |  |         // Convert the backup file to the wallet db file by renaming it and moving it into the wallet's directory. | 
| 4366 | 0 |         bilingual_str restore_error; | 
| 4367 | 0 |         const auto& ptr_wallet = RestoreWallet(context, backup_path, wallet_name, /*load_on_start=*/std::nullopt, status, restore_error, warnings, /*load_after_restore=*/false); | 
| 4368 | 0 |         if (!restore_error.empty()) { | 
| 4369 | 0 |             error += restore_error + _("\nUnable to restore backup of wallet."); | 
| 4370 | 0 |             return util::Error{error}; | 
| 4371 | 0 |         } | 
| 4372 |  |         // Verify that the legacy wallet is not loaded after restoring from the backup. | 
| 4373 | 0 |         assert(!ptr_wallet); | 
| 4374 |  |  | 
| 4375 | 0 |         return util::Error{error}; | 
| 4376 | 0 |     } | 
| 4377 | 0 |     return res; | 
| 4378 | 0 | } | 
| 4379 |  |  | 
| 4380 |  | void CWallet::CacheNewScriptPubKeys(const std::set<CScript>& spks, ScriptPubKeyMan* spkm) | 
| 4381 | 0 | { | 
| 4382 | 0 |     for (const auto& script : spks) { | 
| 4383 | 0 |         m_cached_spks[script].push_back(spkm); | 
| 4384 | 0 |     } | 
| 4385 | 0 | } | 
| 4386 |  |  | 
| 4387 |  | void CWallet::TopUpCallback(const std::set<CScript>& spks, ScriptPubKeyMan* spkm) | 
| 4388 | 0 | { | 
| 4389 |  |     // Update scriptPubKey cache | 
| 4390 | 0 |     CacheNewScriptPubKeys(spks, spkm); | 
| 4391 | 0 | } | 
| 4392 |  |  | 
| 4393 |  | std::set<CExtPubKey> CWallet::GetActiveHDPubKeys() const | 
| 4394 | 0 | { | 
| 4395 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 4396 |  | 
 | 
| 4397 | 0 |     Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 4398 |  | 
 | 
| 4399 | 0 |     std::set<CExtPubKey> active_xpubs; | 
| 4400 | 0 |     for (const auto& spkm : GetActiveScriptPubKeyMans()) { | 
| 4401 | 0 |         const DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm); | 
| 4402 | 0 |         assert(desc_spkm); | 
| 4403 | 0 |         LOCK(desc_spkm->cs_desc_man); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 4404 | 0 |         WalletDescriptor w_desc = desc_spkm->GetWalletDescriptor(); | 
| 4405 |  | 
 | 
| 4406 | 0 |         std::set<CPubKey> desc_pubkeys; | 
| 4407 | 0 |         std::set<CExtPubKey> desc_xpubs; | 
| 4408 | 0 |         w_desc.descriptor->GetPubKeys(desc_pubkeys, desc_xpubs); | 
| 4409 | 0 |         active_xpubs.merge(std::move(desc_xpubs)); | 
| 4410 | 0 |     } | 
| 4411 | 0 |     return active_xpubs; | 
| 4412 | 0 | } | 
| 4413 |  |  | 
| 4414 |  | std::optional<CKey> CWallet::GetKey(const CKeyID& keyid) const | 
| 4415 | 0 | { | 
| 4416 | 0 |     Assert(IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 4417 |  | 
 | 
| 4418 | 0 |     for (const auto& spkm : GetAllScriptPubKeyMans()) { | 
| 4419 | 0 |         const DescriptorScriptPubKeyMan* desc_spkm = dynamic_cast<DescriptorScriptPubKeyMan*>(spkm); | 
| 4420 | 0 |         assert(desc_spkm); | 
| 4421 | 0 |         LOCK(desc_spkm->cs_desc_man); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 4422 | 0 |         if (std::optional<CKey> key = desc_spkm->GetKey(keyid)) { | 
| 4423 | 0 |             return key; | 
| 4424 | 0 |         } | 
| 4425 | 0 |     } | 
| 4426 | 0 |     return std::nullopt; | 
| 4427 | 0 | } | 
| 4428 |  |  | 
| 4429 |  | void CWallet::WriteBestBlock() const | 
| 4430 | 0 | { | 
| 4431 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 4432 |  | 
 | 
| 4433 | 0 |     if (!m_last_block_processed.IsNull()) { | 
| 4434 | 0 |         CBlockLocator loc; | 
| 4435 | 0 |         chain().findBlock(m_last_block_processed, FoundBlock().locator(loc)); | 
| 4436 |  | 
 | 
| 4437 | 0 |         if (!loc.IsNull()) { | 
| 4438 | 0 |             WalletBatch batch(GetDatabase()); | 
| 4439 | 0 |             batch.WriteBestBlock(loc); | 
| 4440 | 0 |         } | 
| 4441 | 0 |     } | 
| 4442 | 0 | } | 
| 4443 |  |  | 
| 4444 |  | void CWallet::RefreshTXOsFromTx(const CWalletTx& wtx) | 
| 4445 | 0 | { | 
| 4446 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 4447 | 0 |     for (uint32_t i = 0; i < wtx.tx->vout.size(); ++i) { | 
| 4448 | 0 |         const CTxOut& txout = wtx.tx->vout.at(i); | 
| 4449 | 0 |         if (!IsMine(txout)) continue; | 
| 4450 | 0 |         COutPoint outpoint(wtx.GetHash(), i); | 
| 4451 | 0 |         if (m_txos.contains(outpoint)) { | 
| 4452 | 0 |         } else { | 
| 4453 | 0 |             m_txos.emplace(outpoint, WalletTXO{wtx, txout}); | 
| 4454 | 0 |         } | 
| 4455 | 0 |     } | 
| 4456 | 0 | } | 
| 4457 |  |  | 
| 4458 |  | void CWallet::RefreshAllTXOs() | 
| 4459 | 0 | { | 
| 4460 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 4461 | 0 |     for (const auto& [_, wtx] : mapWallet) { | 
| 4462 | 0 |         RefreshTXOsFromTx(wtx); | 
| 4463 | 0 |     } | 
| 4464 | 0 | } | 
| 4465 |  |  | 
| 4466 |  | std::optional<WalletTXO> CWallet::GetTXO(const COutPoint& outpoint) const | 
| 4467 | 0 | { | 
| 4468 | 0 |     AssertLockHeld(cs_wallet); | Line | Count | Source |  | 137 | 0 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 4469 | 0 |     const auto& it = m_txos.find(outpoint); | 
| 4470 | 0 |     if (it == m_txos.end()) { | 
| 4471 | 0 |         return std::nullopt; | 
| 4472 | 0 |     } | 
| 4473 | 0 |     return it->second; | 
| 4474 | 0 | } | 
| 4475 |  | } // namespace wallet |