LCOV - code coverage report
Current view: top level - src/wallet/rpc - wallet.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 732 812 90.1 %
Date: 2026-06-25 07:23:43 Functions: 36 37 97.3 %

          Line data    Source code
       1             : // Copyright (c) 2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2020 The Bitcoin Core developers
       3             : // Copyright (c) 2014-2025 The Dash Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #include <chainparams.h>
       8             : #include <common/url.h>
       9             : #include <core_io.h>
      10             : #include <httpserver.h>
      11             : #include <policy/policy.h>
      12             : #include <rpc/blockchain.h>
      13             : #include <rpc/rawtransaction_util.h>
      14             : #include <rpc/server.h>
      15             : #include <rpc/util.h>
      16             : #include <util/bip32.h>
      17             : #include <util/fees.h>
      18             : #include <util/translation.h>
      19             : #include <util/vector.h>
      20             : #include <wallet/context.h>
      21             : #include <wallet/receive.h>
      22             : #include <wallet/rpc/wallet.h>
      23             : #include <wallet/rpc/util.h>
      24             : #include <wallet/scriptpubkeyman.h>
      25             : #include <wallet/spend.h>
      26             : #include <wallet/wallet.h>
      27             : #include <key_io.h>
      28             : 
      29             : #include <coinjoin/client.h>
      30             : #include <coinjoin/options.h>
      31             : 
      32             : #include <optional>
      33             : 
      34             : #include <univalue.h>
      35             : 
      36             : namespace wallet {
      37             : /** Checks if a CKey is in the given CWallet compressed or otherwise*/
      38          17 : bool HaveKey(const SigningProvider& wallet, const CKey& key)
      39             : {
      40          17 :     CKey key2;
      41          17 :     key2.Set(key.begin(), key.end(), !key.IsCompressed());
      42          17 :     return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
      43          17 : }
      44             : 
      45        2906 : static RPCHelpMan setcoinjoinrounds()
      46             : {
      47        5812 :     return RPCHelpMan{"setcoinjoinrounds",
      48        2906 :         "\nSet the number of rounds for CoinJoin.\n",
      49        5812 :         {
      50        5812 :             {"rounds", RPCArg::Type::NUM, RPCArg::Optional::NO,
      51        2906 :                 "The default number of rounds is " + ToString(DEFAULT_COINJOIN_ROUNDS) +
      52        2906 :                 " Cannot be more than " + ToString(MAX_COINJOIN_ROUNDS) + " nor less than " + ToString(MIN_COINJOIN_ROUNDS)},
      53             :         },
      54        2906 :         RPCResult{RPCResult::Type::NONE, "", ""},
      55        2906 :         RPCExamples{
      56        2906 :             HelpExampleCli("setcoinjoinrounds", "4")
      57        2906 :     + HelpExampleRpc("setcoinjoinrounds", "16")
      58             :         },
      59        2916 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      60             : {
      61          10 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
      62          10 :     if (!wallet) return UniValue::VNULL;
      63             : 
      64          10 :     int nRounds = request.params[0].getInt<int>();
      65             : 
      66          10 :     if (nRounds > MAX_COINJOIN_ROUNDS || nRounds < MIN_COINJOIN_ROUNDS)
      67           4 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid number of rounds");
      68             : 
      69           6 :     CCoinJoinClientOptions::SetRounds(nRounds);
      70             : 
      71           6 :     return UniValue::VNULL;
      72          14 : },
      73             :     };
      74           0 : }
      75             : 
      76        2908 : static RPCHelpMan setcoinjoinamount()
      77             : {
      78        5816 :     return RPCHelpMan{"setcoinjoinamount",
      79        2908 :         "\nSet the goal amount in " + CURRENCY_UNIT + " for CoinJoin.\n",
      80        5816 :         {
      81        5816 :             {"amount", RPCArg::Type::NUM, RPCArg::Optional::NO,
      82        2908 :                 "The default amount is " + ToString(DEFAULT_COINJOIN_AMOUNT) +
      83        2908 :                 " Cannot be more than " + ToString(MAX_COINJOIN_AMOUNT) + " nor less than " + ToString(MIN_COINJOIN_AMOUNT)},
      84             :         },
      85        2908 :         RPCResult{RPCResult::Type::NONE, "", ""},
      86        2908 :         RPCExamples{
      87        2908 :             HelpExampleCli("setcoinjoinamount", "500")
      88        2908 :     + HelpExampleRpc("setcoinjoinamount", "208")
      89             :         },
      90        2920 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      91             : {
      92          12 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
      93          12 :     if (!wallet) return UniValue::VNULL;
      94             : 
      95          12 :     int nAmount = request.params[0].getInt<int>();
      96             : 
      97          12 :     if (nAmount > MAX_COINJOIN_AMOUNT || nAmount < MIN_COINJOIN_AMOUNT)
      98           4 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount of " + CURRENCY_UNIT + " as mixing goal amount");
      99             : 
     100           8 :     CCoinJoinClientOptions::SetAmount(nAmount);
     101             : 
     102           8 :     return UniValue::VNULL;
     103          16 : },
     104             :     };
     105           0 : }
     106             : 
     107        5026 : static RPCHelpMan getwalletinfo()
     108             : {
     109       10052 :     return RPCHelpMan{"getwalletinfo",
     110        5026 :                 "Returns an object containing various wallet state info.\n",
     111        5026 :                 {},
     112        5026 :                 RPCResult{
     113        5026 :                     RPCResult::Type::OBJ, "", "",
     114      120624 :                     {
     115        5026 :                         {RPCResult::Type::STR, "walletname", "the wallet name"},
     116        5026 :                         {RPCResult::Type::NUM, "walletversion", "the wallet version"},
     117        5026 :                         {RPCResult::Type::STR, "format", "the database format (bdb or sqlite)"},
     118        5026 :                         {RPCResult::Type::NUM, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
     119        5026 :                         {RPCResult::Type::NUM, "coinjoin_balance", "DEPRECATED. Identical to getbalances().mine.coinjoin"},
     120        5026 :                         {RPCResult::Type::NUM, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
     121        5026 :                         {RPCResult::Type::NUM, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
     122        5026 :                         {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
     123        5026 :                         {RPCResult::Type::NUM_TIME, "timefirstkey", "the " + UNIX_EPOCH_TIME + " of the oldest known key in the wallet"},
     124        5026 :                         {RPCResult::Type::NUM_TIME, "keypoololdest", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only"},
     125        5026 :                         {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
     126        5026 :                         {RPCResult::Type::NUM, "keypoolsize_hd_internal", /*optional=*/ true, "how many new keys are pre-generated for internal use (used for change outputs and mobile coinjoin, only appears if the wallet is using this feature, otherwise external keys are used)"},
     127        5026 :                         {RPCResult::Type::NUM, "keys_left", "how many new keys are left since last automatic backup"},
     128        5026 :                         {RPCResult::Type::NUM_TIME, "unlocked_until", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"},
     129        5026 :                         {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB"},
     130        5026 :                         {RPCResult::Type::STR_HEX, "hdchainid", "the ID of the HD chain"},
     131        5026 :                         {RPCResult::Type::NUM, "hdaccountcount", "how many accounts of the HD chain are in this wallet"},
     132       10052 :                         {RPCResult::Type::ARR, "hdaccounts", "",
     133       10052 :                             {
     134       10052 :                             {RPCResult::Type::OBJ, "", "",
     135       20104 :                                 {
     136        5026 :                                     {RPCResult::Type::NUM, "hdaccountindex", "the index of the account"},
     137        5026 :                                     {RPCResult::Type::NUM, "hdexternalkeyindex", "current external childkey index"},
     138        5026 :                                     {RPCResult::Type::NUM, "hdinternalkeyindex", "current internal childkey index"},
     139             :                             }},
     140             :                         }},
     141        5026 :                         {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
     142        5026 :                         {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
     143       10052 :                         {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
     144       15078 :                         {
     145        5026 :                             {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
     146        5026 :                             {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
     147             :                         }},
     148        5026 :                         {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for scriptPubKey management"},
     149        5026 :                         {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
     150        5026 :                         RESULT_LAST_PROCESSED_BLOCK,
     151             :                     },
     152             :                 },
     153        5026 :                 RPCExamples{
     154        5026 :                     HelpExampleCli("getwalletinfo", "")
     155        5026 :             + HelpExampleRpc("getwalletinfo", "")
     156             :                 },
     157        7098 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     158             : {
     159        2072 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
     160        2072 :     if (!pwallet) return UniValue::VNULL;
     161             : 
     162             :     // Make sure the results are valid at least up to the most recent block
     163             :     // the user could have gotten from another RPC command prior to now
     164        2072 :     pwallet->BlockUntilSyncedToCurrentChain();
     165             : 
     166        2072 :     LOCK(pwallet->cs_wallet);
     167             : 
     168        2072 :     LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
     169        2072 :     CHDChain hdChainCurrent;
     170        3356 :     bool fHDEnabled = spk_man && spk_man->GetHDChain(hdChainCurrent);
     171        2072 :     UniValue obj(UniValue::VOBJ);
     172             : 
     173        2072 :     const auto bal = GetBalance(*pwallet);
     174        2072 :     obj.pushKV("walletname", pwallet->GetName());
     175        2072 :     obj.pushKV("walletversion", pwallet->GetVersion());
     176        2072 :     obj.pushKV("format", pwallet->GetDatabase().Format());
     177        2072 :     obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
     178        2072 :     obj.pushKV("coinjoin_balance",       ValueFromAmount(bal.m_anonymized));
     179        2072 :     obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
     180        2072 :     obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
     181        2072 :     obj.pushKV("txcount",       (int)pwallet->mapWallet.size());
     182             :     // TODO: implement timefirstkey for Descriptor KeyMan or explain why it's not provided
     183        2072 :     if (spk_man) {
     184        1284 :         obj.pushKV("timefirstkey", spk_man->GetTimeFirstKey());
     185        1284 :     }
     186        2072 :     const auto kp_oldest = pwallet->GetOldestKeyPoolTime();
     187        2072 :     if (kp_oldest.has_value()) {
     188        1284 :         obj.pushKV("keypoololdest", kp_oldest.value());
     189        1284 :     }
     190        2072 :     size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
     191        2072 :     obj.pushKV("keypoolsize", kpExternalSize);
     192        2072 :     obj.pushKV("keypoolsize_hd_internal", pwallet->GetKeyPoolSize() - kpExternalSize);
     193        2072 :     obj.pushKV("keys_left", pwallet->nKeysLeftSinceAutoBackup);
     194        2072 :     if (pwallet->IsCrypted()) {
     195         102 :         obj.pushKV("unlocked_until", pwallet->nRelockTime);
     196         102 :     }
     197        2072 :     obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
     198        2072 :     if (fHDEnabled) {
     199        1063 :         obj.pushKV("hdchainid", hdChainCurrent.GetID().GetHex());
     200        1063 :         obj.pushKV("hdaccountcount", hdChainCurrent.CountAccounts());
     201        1063 :         UniValue accounts(UniValue::VARR);
     202        2123 :         for (size_t i = 0; i < hdChainCurrent.CountAccounts(); ++i)
     203             :         {
     204        1060 :             CHDAccount acc;
     205        1060 :             UniValue account(UniValue::VOBJ);
     206        1060 :             account.pushKV("hdaccountindex", i);
     207        1060 :             if(hdChainCurrent.GetAccount(i, acc)) {
     208        1060 :                 account.pushKV("hdexternalkeyindex", acc.nExternalChainCounter);
     209        1060 :                 account.pushKV("hdinternalkeyindex", acc.nInternalChainCounter);
     210        1060 :             } else {
     211           0 :                 account.pushKV("error", strprintf("account %d is missing", i));
     212             :             }
     213        1060 :             accounts.push_back(account);
     214        1060 :         }
     215        1063 :         obj.pushKV("hdaccounts", accounts);
     216        1063 :     }
     217        2072 :     obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
     218        2072 :     obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
     219        2072 :     if (pwallet->IsScanning()) {
     220           0 :         UniValue scanning(UniValue::VOBJ);
     221           0 :         scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->ScanningDuration()));
     222           0 :         scanning.pushKV("progress", pwallet->ScanningProgress());
     223           0 :         obj.pushKV("scanning", scanning);
     224           0 :     } else {
     225        2072 :         obj.pushKV("scanning", false);
     226             :     }
     227        2072 :     obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
     228        2072 :     obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
     229             : 
     230        2072 :     AppendLastProcessedBlock(obj, *pwallet);
     231        2072 :     return obj;
     232        2072 : },
     233             :     };
     234           0 : }
     235             : 
     236        2922 : static RPCHelpMan listwalletdir()
     237             : {
     238        5844 :     return RPCHelpMan{"listwalletdir",
     239        2922 :                 "Returns a list of wallets in the wallet directory.\n",
     240        2922 :                 {},
     241        2922 :                 RPCResult{
     242        2922 :                     RPCResult::Type::OBJ, "", "",
     243        5844 :                     {
     244        5844 :                         {RPCResult::Type::ARR, "wallets", "",
     245        5844 :                         {
     246        5844 :                             {RPCResult::Type::OBJ, "", "",
     247        5844 :                             {
     248        2922 :                                 {RPCResult::Type::STR, "name", "The wallet name"},
     249             :                             }},
     250             :                         }},
     251             :                     }
     252             :                 },
     253        2922 :                 RPCExamples{
     254        2922 :                     HelpExampleCli("listwalletdir", "")
     255        2922 :             + HelpExampleRpc("listwalletdir", "")
     256             :                 },
     257        2948 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     258             : {
     259          26 :     UniValue wallets(UniValue::VARR);
     260         202 :     for (const auto& path : ListDatabases(GetWalletDir())) {
     261         176 :         UniValue wallet(UniValue::VOBJ);
     262         176 :         wallet.pushKV("name", path.utf8string());
     263         176 :         wallets.push_back(wallet);
     264         176 :     }
     265             : 
     266          26 :     UniValue result(UniValue::VOBJ);
     267          26 :     result.pushKV("wallets", wallets);
     268          26 :     return result;
     269          26 : },
     270             :     };
     271           0 : }
     272             : 
     273        3072 : static RPCHelpMan listwallets()
     274             : {
     275        6144 :     return RPCHelpMan{"listwallets",
     276        3072 :                 "Returns a list of currently loaded wallets.\n"
     277             :                 "For full information on the wallet, use \"getwalletinfo\"\n",
     278        3072 :                 {},
     279        3072 :                 RPCResult{
     280        3072 :                     RPCResult::Type::ARR, "", "",
     281        6144 :                     {
     282        3072 :                         {RPCResult::Type::STR, "walletname", "the wallet name"},
     283             :                     }
     284             :                 },
     285        3072 :                 RPCExamples{
     286        3072 :                     HelpExampleCli("listwallets", "")
     287        3072 :             + HelpExampleRpc("listwallets", "")
     288             :                 },
     289        3248 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     290             : {
     291         176 :     UniValue obj(UniValue::VARR);
     292             : 
     293         176 :     WalletContext& context = EnsureWalletContext(request.context);
     294         896 :     for (const std::shared_ptr<CWallet>& wallet : GetWallets(context)) {
     295         720 :         LOCK(wallet->cs_wallet);
     296         720 :         obj.push_back(wallet->GetName());
     297         720 :     }
     298             : 
     299         176 :     return obj;
     300         176 : },
     301             :     };
     302           0 : }
     303             : 
     304        2968 : static RPCHelpMan upgradetohd()
     305             : {
     306        5936 :     return RPCHelpMan{"upgradetohd",
     307        2968 :         "\nUpgrades non-HD wallets to HD.\n"
     308             :         "\nIf your wallet is encrypted, the wallet passphrase must be supplied. Supplying an incorrect"
     309             :         "\npassphrase may result in your wallet getting locked.\n"
     310             :         "\nWarning: You will need to make a new backup of your wallet after setting the HD wallet mnemonic.\n",
     311       14840 :         {
     312        2968 :             {"mnemonic", RPCArg::Type::STR, RPCArg::Default{""}, "Mnemonic as defined in BIP39 to use for the new HD wallet. Use an empty string \"\" to generate a new random mnemonic."},
     313        2968 :             {"mnemonicpassphrase", RPCArg::Type::STR, RPCArg::Default{""}, "Optional mnemonic passphrase as defined in BIP39"},
     314        2968 :             {"walletpassphrase", RPCArg::Type::STR, RPCArg::Default{""}, "If your wallet is encrypted you must have your wallet passphrase here. If your wallet is not encrypted, specifying wallet passphrase will trigger wallet encryption."},
     315        2968 :             {"rescan", RPCArg::Type::BOOL, RPCArg::DefaultHint{"false if mnemonic is empty"}, "Whether to rescan the blockchain for missing transactions or not"},
     316             :         },
     317        2968 :         RPCResult{RPCResult::Type::STR, "", "A string with further instructions"},
     318        2968 :         RPCExamples{
     319        2968 :             HelpExampleCli("upgradetohd", "")
     320        2968 :     + HelpExampleCli("upgradetohd", "\"mnemonicword1 ... mnemonicwordN\"")
     321        2968 :     + HelpExampleCli("upgradetohd", "\"mnemonicword1 ... mnemonicwordN\" \"mnemonicpassphrase\"")
     322        2968 :     + HelpExampleCli("upgradetohd", "\"mnemonicword1 ... mnemonicwordN\" \"\" \"walletpassphrase\"")
     323        2968 :     + HelpExampleCli("upgradetohd", "\"mnemonicword1 ... mnemonicwordN\" \"mnemonicpassphrase\" \"walletpassphrase\"")
     324             :         },
     325        3040 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     326             : {
     327          72 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     328          72 :     if (!pwallet) return UniValue::VNULL;
     329             : 
     330          72 :     bool generate_mnemonic = request.params[0].isNull() || request.params[0].get_str().empty();
     331          72 :     bool mnemonic_passphrase_has_null{false};
     332             :     {
     333          72 :         LOCK(pwallet->cs_wallet);
     334             : 
     335          72 :         SecureString wallet_passphrase;
     336          72 :         wallet_passphrase.reserve(100);
     337             : 
     338          72 :         if (request.params[2].isNull()) {
     339          48 :             if (pwallet->IsCrypted()) {
     340           4 :                 throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Wallet encrypted but passphrase not supplied to RPC.");
     341             :             }
     342          44 :         } else {
     343          24 :             wallet_passphrase = std::string_view{request.params[2].get_str()};
     344             :         }
     345             : 
     346          68 :         SecureString mnemonic;
     347          68 :         mnemonic.reserve(256);
     348          68 :         if (!generate_mnemonic) {
     349          44 :             mnemonic = std::string_view{request.params[0].get_str()};
     350          44 :         }
     351             : 
     352          68 :         SecureString mnemonic_passphrase;
     353          68 :         mnemonic_passphrase.reserve(256);
     354          68 :         if (!request.params[1].isNull()) {
     355          44 :             mnemonic_passphrase = std::string_view{request.params[1].get_str()};
     356          44 :             mnemonic_passphrase_has_null = (mnemonic_passphrase.find('\0') != std::string::npos);
     357          44 :         }
     358             : 
     359             :         // Do not do anything to HD wallets
     360          68 :         if (pwallet->IsHDEnabled()) {
     361           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Cannot upgrade a wallet to HD if it is already upgraded to HD");
     362             :         }
     363             : 
     364          68 :         if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     365           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Private keys are disabled for this wallet");
     366             :         }
     367             : 
     368          68 :         pwallet->WalletLogPrintf("Upgrading wallet to HD\n");
     369          68 :         pwallet->SetMinVersion(FEATURE_HD);
     370             : 
     371          68 :         if (pwallet->IsCrypted()) {
     372          12 :             if (wallet_passphrase.empty()) {
     373           0 :                 throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: Wallet encrypted but supplied empty wallet passphrase");
     374             :             }
     375             : 
     376             :             // We are intentionally re-locking the wallet so we can validate passphrase
     377             :             // by verifying if it can unlock the wallet
     378          12 :             pwallet->Lock();
     379             : 
     380             :             // Unlock the wallet
     381          12 :             if (!pwallet->Unlock(wallet_passphrase)) {
     382             :                 // Check if the passphrase has a null character (see bitcoin#27067 for details)
     383           4 :                 if (wallet_passphrase.find('\0') == std::string::npos) {
     384           4 :                     throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
     385             :                 } else {
     386           0 :                     throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered is incorrect. "
     387             :                                                                         "It contains a null character (ie - a zero byte). "
     388             :                                                                         "If the passphrase was set with a version of this software prior to 23.0, "
     389             :                                                                         "please try again with only the characters up to — but not including — "
     390             :                                                                         "the first null character. If this is successful, please set a new "
     391             :                                                                         "passphrase to avoid this issue in the future.");
     392             :                 }
     393             :             }
     394           8 :         }
     395             : 
     396          64 :         if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
     397          28 :             pwallet->SetupDescriptorScriptPubKeyMans(mnemonic, mnemonic_passphrase);
     398          28 :         } else {
     399          36 :             auto spk_man = pwallet->GetLegacyScriptPubKeyMan();
     400          36 :             if (!spk_man) {
     401           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Error: Legacy ScriptPubKeyMan is not available");
     402             :             }
     403             : 
     404          36 :             if (pwallet->IsCrypted()) {
     405          12 :                 pwallet->WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
     406           6 :                         spk_man->GenerateNewHDChain(mnemonic, mnemonic_passphrase, encryption_key);
     407           6 :                         return true;
     408           0 :                     });
     409           6 :             } else {
     410          30 :                 spk_man->GenerateNewHDChain(mnemonic, mnemonic_passphrase);
     411             :             }
     412             :         }
     413             : 
     414          64 :         if (pwallet->IsCrypted()) {
     415             :             // Relock encrypted wallet
     416           8 :             pwallet->Lock();
     417          64 :         } else if (!wallet_passphrase.empty()) {
     418             :             // Encrypt non-encrypted wallet
     419           4 :             if (!pwallet->EncryptWallet(wallet_passphrase)) {
     420           0 :                 throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Failed to encrypt HD wallet");
     421             :             }
     422           4 :         }
     423          72 :     } // pwallet->cs_wallet
     424             : 
     425             :     // If you are generating new mnemonic it is assumed that the addresses have never gotten a transaction before, so you don't need to rescan for transactions
     426          64 :     bool rescan = request.params[3].isNull() ? !generate_mnemonic : request.params[3].get_bool();
     427          64 :     if (rescan) {
     428          40 :         WalletRescanReserver reserver(*pwallet);
     429          40 :         if (!reserver.reserve()) {
     430           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     431             :         }
     432          40 :         CWallet::ScanResult result = pwallet->ScanForWalletTransactions(pwallet->chain().getBlockHash(0), 0, {}, reserver, /*fUpdate=*/true, /*save_progress=*/false);
     433          40 :         switch (result.status) {
     434             :         case CWallet::ScanResult::SUCCESS:
     435          40 :             break;
     436             :         case CWallet::ScanResult::FAILURE:
     437           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Rescan failed. Potentially corrupted data files.");
     438             :         case CWallet::ScanResult::USER_ABORT:
     439           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
     440             :             // no default case, so the compiler can warn about missing cases
     441             :         }
     442          40 :     }
     443             : 
     444             :     // Check if the passphrase has a null character (see #27067 for details)
     445          64 :     if (!mnemonic_passphrase_has_null) {
     446          60 :         return "Make sure that you have backup of your mnemonic.";
     447             :     } else {
     448           4 :         return "Make sure that you have backup of your mnemonic. "
     449             :                "Your mnemonic passphrase contains a null character (ie - a zero byte). "
     450             :                "If the passphrase was created with a version of this software prior to 23.0, "
     451             :                "please try again with only the characters up to — but not including — "
     452             :                "the first null character. If this is successful, please set a new "
     453             :                "passphrase to avoid this issue in the future.";
     454             :     }
     455          80 : },
     456             :     };
     457           0 : }
     458             : 
     459        3365 : static RPCHelpMan loadwallet()
     460             : {
     461        6730 :     return RPCHelpMan{"loadwallet",
     462        3365 :                 "\nLoads a wallet from a wallet file or directory."
     463             :                 "\nNote that all wallet command-line options used when starting dashd will be"
     464             :                 "\napplied to the new wallet (eg, rescan, etc).\n",
     465       10095 :                 {
     466        3365 :                     {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
     467        3365 :                     {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
     468             :                 },
     469        3365 :                 RPCResult{
     470        3365 :                     RPCResult::Type::OBJ, "", "",
     471       10095 :                     {
     472        3365 :                         {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
     473        3365 :                         {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
     474             :                     }
     475             :                 },
     476        3365 :                 RPCExamples{
     477        3365 :                     HelpExampleCli("loadwallet", "\"test.dat\"")
     478        3365 :             + HelpExampleRpc("loadwallet", "\"test.dat\"")
     479             :                 },
     480        3834 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     481             : {
     482         469 :     WalletContext& context = EnsureWalletContext(request.context);
     483         469 :     const std::string name(request.params[0].get_str());
     484             : 
     485         469 :     DatabaseOptions options;
     486             :     DatabaseStatus status;
     487         469 :     ReadDatabaseArgs(*context.args, options);
     488         469 :     options.require_existing = true;
     489         469 :     bilingual_str error;
     490         469 :     std::vector<bilingual_str> warnings;
     491         469 :     std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
     492             : 
     493             :     {
     494         469 :         LOCK(context.wallets_mutex);
     495        2439 :         if (std::any_of(context.wallets.begin(), context.wallets.end(), [&name](const auto& wallet) { return wallet->GetName() == name; })) {
     496           6 :             throw JSONRPCError(RPC_WALLET_ALREADY_LOADED, "Wallet \"" + name + "\" is already loaded.");
     497             :         }
     498         469 :     }
     499             : 
     500         463 :     std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings);
     501             : 
     502         463 :     HandleWalletError(wallet, status, error);
     503             : 
     504         409 :     UniValue obj(UniValue::VOBJ);
     505         409 :     obj.pushKV("name", wallet->GetName());
     506         409 :     obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
     507             : 
     508         409 :     return obj;
     509         475 : },
     510             :     };
     511           0 : }
     512             : 
     513        2926 : static RPCHelpMan setwalletflag()
     514             : {
     515        2926 :             std::string flags;
     516       23408 :             for (auto& it : WALLET_FLAG_MAP)
     517       20482 :                 if (it.second & MUTABLE_WALLET_FLAGS)
     518        2926 :                     flags += (flags == "" ? "" : ", ") + it.first;
     519             : 
     520        5852 :     return RPCHelpMan{"setwalletflag",
     521        2926 :                 "\nChange the state of the given wallet flag for a wallet.\n",
     522        8778 :                 {
     523        2926 :                     {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
     524        2926 :                     {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
     525             :                 },
     526        2926 :                 RPCResult{
     527        2926 :                     RPCResult::Type::OBJ, "", "",
     528       11704 :                     {
     529        2926 :                         {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
     530        2926 :                         {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
     531        2926 :                         {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
     532             :                     }
     533             :                 },
     534        2926 :                 RPCExamples{
     535        2926 :                     HelpExampleCli("setwalletflag", "avoid_reuse")
     536        2926 :                   + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
     537             :                 },
     538        2956 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     539             : {
     540          30 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     541          30 :     if (!pwallet) return UniValue::VNULL;
     542             : 
     543             : 
     544          30 :     std::string flag_str = request.params[0].get_str();
     545          30 :     bool value = request.params[1].isNull() || request.params[1].get_bool();
     546             : 
     547          30 :     if (!WALLET_FLAG_MAP.count(flag_str)) {
     548           4 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
     549             :     }
     550             : 
     551          26 :     auto flag = WALLET_FLAG_MAP.at(flag_str);
     552             : 
     553          26 :     if (!(flag & MUTABLE_WALLET_FLAGS)) {
     554          10 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
     555             :     }
     556             : 
     557          16 :     UniValue res(UniValue::VOBJ);
     558             : 
     559          16 :     if (pwallet->IsWalletFlagSet(flag) == value) {
     560           8 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
     561             :     }
     562             : 
     563           8 :     res.pushKV("flag_name", flag_str);
     564           8 :     res.pushKV("flag_state", value);
     565             : 
     566           8 :     if (value) {
     567           4 :         pwallet->SetWalletFlag(flag);
     568           4 :     } else {
     569           4 :         pwallet->UnsetWalletFlag(flag);
     570             :     }
     571             : 
     572           8 :     if (flag && value && WALLET_FLAG_CAVEATS.count(flag)) {
     573           4 :         res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
     574           4 :     }
     575             : 
     576           8 :     return res;
     577          52 : },
     578             :     };
     579        2926 : }
     580             : 
     581        4094 : static RPCHelpMan createwallet()
     582             : {
     583        4094 :     return RPCHelpMan{
     584        4094 :         "createwallet",
     585        4094 :         "\nCreates and loads a new wallet.\n",
     586       36846 :         {
     587        4094 :             {"wallet_name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name for the new wallet. If this is a path, the wallet will be created at the path location."},
     588        4094 :             {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
     589        4094 :             {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using upgradetohd (by mnemonic) or sethdseed (WIF private key)."},
     590        4094 :             {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Encrypt the wallet with this passphrase."},
     591        4094 :             {"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{false}, "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
     592        4094 :             {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation."},
     593        4094 :             {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
     594        4094 :             {"external_signer", RPCArg::Type::BOOL, RPCArg::Default{false}, "Use an external signer such as a hardware wallet. Requires -signer to be configured. Wallet creation will fail if keys cannot be fetched. Requires disable_private_keys and descriptors set to true."},
     595             :         },
     596        4094 :         RPCResult{
     597        4094 :             RPCResult::Type::OBJ, "", "",
     598       12282 :             {
     599        4094 :                 {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
     600        4094 :                 {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
     601             :             }
     602             :         },
     603        4094 :         RPCExamples{
     604        4094 :             HelpExampleCli("createwallet", "\"testwallet\"")
     605        4094 :             + HelpExampleRpc("createwallet", "\"testwallet\"")
     606        4094 :             + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
     607        4094 :             + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
     608             :         },
     609        5292 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     610             : {
     611        1198 :     WalletContext& context = EnsureWalletContext(request.context);
     612        1198 :     uint64_t flags = 0;
     613        1198 :     if (!request.params[1].isNull() && request.params[1].get_bool()) {
     614         139 :         flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
     615         139 :     }
     616             : 
     617        1198 :     if (!request.params[2].isNull() && request.params[2].get_bool()) {
     618         118 :         flags |= WALLET_FLAG_BLANK_WALLET;
     619         118 :     }
     620        1198 :     SecureString passphrase;
     621        1198 :     passphrase.reserve(100);
     622        1198 :     std::vector<bilingual_str> warnings;
     623        1198 :     if (!request.params[3].isNull()) {
     624        1198 :         passphrase = std::string_view{request.params[3].get_str()};
     625        1198 :         if (passphrase.empty()) {
     626             :             // Empty string means unencrypted
     627        1170 :             warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
     628        1170 :         }
     629        1198 :     }
     630             : 
     631        1198 :     if (!request.params[4].isNull() && request.params[4].get_bool()) {
     632          10 :         flags |= WALLET_FLAG_AVOID_REUSE;
     633          10 :     }
     634        1198 :     if (!request.params[5].isNull() && request.params[6].isNull()) {
     635           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "The createwallet RPC requires specifying the 'load_on_startup' flag when param 'descriptors' is specified. Dash Core v21 introduced this requirement due to breaking changes in the createwallet RPC.");
     636             :     }
     637        1198 :     if (request.params[5].isNull() || request.params[5].get_bool()) {
     638             : #ifndef USE_SQLITE
     639             :         throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without sqlite support (required for descriptor wallets)");
     640             : #endif
     641         428 :         flags |= WALLET_FLAG_DESCRIPTORS;
     642         428 :     }
     643        1198 :     if (!request.params[7].isNull() && request.params[7].get_bool()) {
     644             : #ifdef ENABLE_EXTERNAL_SIGNER
     645          10 :         flags |= WALLET_FLAG_EXTERNAL_SIGNER;
     646             : #else
     647             :         throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
     648             : #endif
     649          10 :     }
     650             : 
     651             : #ifndef USE_BDB
     652             :     if (!(flags & WALLET_FLAG_DESCRIPTORS)) {
     653             :         throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without bdb support (required for legacy wallets)");
     654             :     }
     655             : #endif
     656        1198 :     DatabaseOptions options;
     657             :     DatabaseStatus status;
     658        1198 :     ReadDatabaseArgs(*context.args, options);
     659        1198 :     options.require_create = true;
     660        1198 :     options.create_flags = flags;
     661        1198 :     options.create_passphrase = passphrase;
     662        1198 :     bilingual_str error;
     663        1198 :     std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
     664        1198 :     const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
     665        1190 :     if (!wallet) {
     666          24 :         RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
     667          24 :         throw JSONRPCError(code, error.original);
     668             :     }
     669        1166 :     wallet->SetupLegacyScriptPubKeyMan();
     670             : 
     671        1166 :     UniValue obj(UniValue::VOBJ);
     672        1166 :     obj.pushKV("name", wallet->GetName());
     673        1166 :     obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
     674             : 
     675        1166 :     return obj;
     676        1198 : },
     677             :     };
     678           0 : }
     679             : 
     680        3284 : static RPCHelpMan unloadwallet()
     681             : {
     682        6568 :     return RPCHelpMan{"unloadwallet",
     683        3284 :                 "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
     684             :                 "Specifying the wallet name on a wallet endpoint is invalid.",
     685        9852 :                 {
     686        3284 :                     {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to unload. If provided both here and in the RPC endpoint, the two must be identical."},
     687        3284 :                     {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
     688             :                 },
     689        6568 :                 RPCResult{RPCResult::Type::OBJ, "", "", {
     690        3284 :                     {RPCResult::Type::STR, "warning", "Warning message if wallet was not unloaded cleanly."},
     691             :                 }},
     692        3284 :                 RPCExamples{
     693        3284 :                     HelpExampleCli("unloadwallet", "wallet_name")
     694        3284 :             + HelpExampleRpc("unloadwallet", "wallet_name")
     695             :                 },
     696        3672 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     697             : {
     698         388 :     std::string wallet_name;
     699         388 :     if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
     700         118 :         if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) {
     701           6 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets");
     702             :         }
     703         112 :     } else {
     704         270 :         wallet_name = request.params[0].get_str();
     705             :     }
     706             : 
     707         376 :     WalletContext& context = EnsureWalletContext(request.context);
     708         376 :     std::shared_ptr<CWallet> wallet = GetWallet(context, wallet_name);
     709         376 :     if (!wallet) {
     710          12 :         throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
     711             :     }
     712             : 
     713         364 :     std::vector<bilingual_str> warnings;
     714             :     {
     715         364 :         WalletRescanReserver reserver(*wallet);
     716         364 :         if (!reserver.reserve()) {
     717           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     718             :         }
     719             : 
     720             :         // Release the "main" shared pointer and prevent further notifications.
     721             :         // Note that any attempt to load the same wallet would fail until the wallet
     722             :         // is destroyed (see CheckUniqueFileid).
     723         364 :         std::optional<bool> load_on_start{self.MaybeArg<bool>(1)};
     724         364 :         if (!RemoveWallet(context, wallet, load_on_start, warnings)) {
     725           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
     726             :         }
     727         364 :     }
     728             : 
     729         364 :     UnloadWallet(std::move(wallet));
     730             : 
     731         364 :     UniValue result(UniValue::VOBJ);
     732         364 :     result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
     733         364 :     return result;
     734         406 : },
     735             :     };
     736           0 : }
     737             : 
     738        2900 : static RPCHelpMan wipewallettxes()
     739             : {
     740        5800 :     return RPCHelpMan{"wipewallettxes",
     741        2900 :         "\nWipe wallet transactions.\n"
     742             :         "Note: Use \"rescanblockchain\" to initiate the scanning progress and recover wallet transactions.\n",
     743        5800 :         {
     744        2900 :             {"keep_confirmed", RPCArg::Type::BOOL, RPCArg::Default{false}, "Do not wipe confirmed transactions"},
     745             :         },
     746        2900 :         RPCResult{RPCResult::Type::NONE, "", ""},
     747        2900 :         RPCExamples{
     748        2900 :             HelpExampleCli("wipewallettxes", "")
     749        2900 :     + HelpExampleRpc("wipewallettxes", "")
     750             :         },
     751        2904 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     752             : {
     753           4 :     std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     754           4 :     if (!wallet) return UniValue::VNULL;
     755           4 :     CWallet* const pwallet = wallet.get();
     756             : 
     757           4 :     WalletRescanReserver reserver(*pwallet);
     758           4 :     if (!reserver.reserve()) {
     759           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort rescan or wait.");
     760             :     }
     761             : 
     762           4 :     LOCK(pwallet->cs_wallet);
     763             : 
     764           4 :     bool keep_confirmed{false};
     765           4 :     if (!request.params[0].isNull()) {
     766           2 :         keep_confirmed = request.params[0].get_bool();
     767           2 :     }
     768             : 
     769           4 :     const size_t WALLET_SIZE{pwallet->mapWallet.size()};
     770           4 :     const size_t STEPS{20};
     771           4 :     const size_t BATCH_SIZE = std::max(WALLET_SIZE / STEPS, size_t(1000));
     772             : 
     773           4 :     pwallet->ShowProgress(strprintf("%s " + _("Wiping wallet transactions…").translated, pwallet->GetDisplayName()), 0);
     774             : 
     775          84 :     for (size_t progress = 0; progress < STEPS; ++progress) {
     776          80 :         std::vector<uint256> vHashIn;
     777          80 :         std::vector<uint256> vHashOut;
     778          80 :         size_t count{0};
     779             : 
     780        8530 :         for (auto& [txid, wtx] : pwallet->mapWallet) {
     781        4328 :             if (progress < STEPS - 1 && ++count > BATCH_SIZE) break;
     782        4328 :             if (keep_confirmed && wtx.isConfirmed()) continue;
     783         208 :             vHashIn.push_back(txid);
     784             :         }
     785             : 
     786          80 :         if (vHashIn.size() > 0 && pwallet->ZapSelectTx(vHashIn, vHashOut) != DBErrors::LOAD_OK) {
     787           0 :             pwallet->ShowProgress(strprintf("%s " + _("Wiping wallet transactions…").translated, pwallet->GetDisplayName()), 100);
     788           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Could not properly delete transactions.");
     789             :         }
     790             : 
     791          80 :         CHECK_NONFATAL(vHashOut.size() == vHashIn.size());
     792             : 
     793          80 :         if (pwallet->IsAbortingRescan() || pwallet->chain().shutdownRequested()) {
     794           0 :             pwallet->ShowProgress(strprintf("%s " + _("Wiping wallet transactions…").translated, pwallet->GetDisplayName()), 100);
     795           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Wiping was aborted by user.");
     796             :         }
     797             : 
     798          80 :         pwallet->ShowProgress(strprintf("%s " + _("Wiping wallet transactions…").translated, pwallet->GetDisplayName()), std::max(1, std::min(99, int(progress * 100 / STEPS))));
     799          80 :     }
     800             : 
     801           4 :     pwallet->ShowProgress(strprintf("%s " + _("Wiping wallet transactions…").translated, pwallet->GetDisplayName()), 100);
     802             : 
     803           4 :     return UniValue::VNULL;
     804           4 : },
     805             :     };
     806           0 : }
     807             : 
     808        2935 : static RPCHelpMan sethdseed()
     809             : {
     810        5870 :     return RPCHelpMan{"sethdseed",
     811             :                 "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
     812             :                 "HD can not be updated to a new HD seed.\n"
     813        2935 :                 "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." + HELP_REQUIRING_PASSPHRASE +
     814             :                 "Note: This command is only compatible with legacy wallets.\n",
     815        8805 :                 {
     816        2935 :                     {"newkeypool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
     817             :                                          "If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
     818             :                                          "If false, addresses from the existing keypool will be used until it has been depleted."},
     819        2935 :                     {"seed", RPCArg::Type::STR, RPCArg::DefaultHint{"random seed"}, "The WIF private key to use as the new HD seed.\n"
     820             :                                          "The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
     821             :                 },
     822        2935 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     823        2935 :                 RPCExamples{
     824        2935 :                     HelpExampleCli("sethdseed", "")
     825        2935 :             + HelpExampleCli("sethdseed", "false")
     826        2935 :             + HelpExampleCli("sethdseed", "true \"wifkey\"")
     827        2935 :             + HelpExampleRpc("sethdseed", "true, \"wifkey\"")
     828             :                 },
     829        2971 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     830             : {
     831          36 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     832          36 :     if (!pwallet) return UniValue::VNULL;
     833             : 
     834          36 :     LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
     835             : 
     836          34 :     if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     837           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
     838             :     }
     839             : 
     840          34 :     LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
     841             : 
     842             :     // Do not do anything to non-HD wallets
     843          34 :     if (!pwallet->CanSupportFeature(FEATURE_HD)) {
     844           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
     845             :     }
     846          34 :     if (pwallet->IsHDEnabled()) {
     847           3 :         throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed. The wallet already has a seed");
     848             :     }
     849             : 
     850          31 :     EnsureWalletIsUnlocked(*pwallet);
     851             : 
     852          31 :     bool flush_key_pool = true;
     853          31 :     if (!request.params[0].isNull()) {
     854          28 :         flush_key_pool = request.params[0].get_bool();
     855          25 :     }
     856             : 
     857          28 :     if (request.params[1].isNull()) {
     858           5 :         spk_man.GenerateNewHDChain("", "");
     859           5 :     } else {
     860          23 :         CKey key = DecodeSecret(request.params[1].get_str());
     861          20 :         if (!key.IsValid()) {
     862           3 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
     863             :         }
     864          17 :         if (HaveKey(spk_man, key)) {
     865           3 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
     866             :         }
     867          14 :         CHDChain newHdChain;
     868          14 :         if (!newHdChain.SetSeed(SecureVector(key.begin(), key.end()), true)) {
     869           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key: SetSeed failed");
     870             :         }
     871          14 :         if (!spk_man.AddHDChainSingle(newHdChain)) {
     872           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key: AddHDChainSingle failed");
     873             :         }
     874             :         // add default account
     875          14 :         newHdChain.AddAccount();
     876          20 :     }
     877             : 
     878          19 :     if (flush_key_pool) spk_man.NewKeyPool();
     879             : 
     880          19 :     return UniValue::VNULL;
     881          45 : },
     882             :     };
     883           0 : }
     884             : 
     885        2896 : static RPCHelpMan upgradewallet()
     886             : {
     887        5792 :     return RPCHelpMan{"upgradewallet",
     888        2896 :         "\nUpgrade the wallet. Upgrades to the latest version if no version number is specified.\n"
     889             :         "New keys may be generated and a new wallet backup will need to be made.\n"
     890             :         "Consider using RPC upgradetohd instead upgradewallet if you have BIP39 mnemonic or want to set a wallet passphrase also (encrypt wallet).",
     891        5792 :         {
     892        2896 :             {"version", RPCArg::Type::NUM, RPCArg::Default{int{FEATURE_LATEST}}, "The version number to upgrade to. Default is the latest wallet version."}
     893             :         },
     894        2896 :         RPCResult{
     895        2896 :             RPCResult::Type::OBJ, "", "",
     896       17376 :             {
     897        2896 :                 {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
     898        2896 :                 {RPCResult::Type::NUM, "previous_version", "Version of wallet before this operation"},
     899        2896 :                 {RPCResult::Type::NUM, "current_version", "Version of wallet after this operation"},
     900        2896 :                 {RPCResult::Type::STR, "result", /*optional=*/true, "Description of result, if no error"},
     901        2896 :                 {RPCResult::Type::STR, "error", /*optional=*/true, "Error message (if there is one)"}
     902             :             },
     903             :         },
     904        2896 :         RPCExamples{
     905        2896 :             HelpExampleCli("upgradewallet", "120200")
     906        2896 :             + HelpExampleRpc("upgradewallet", "120200")
     907             :         },
     908        2896 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     909             : {
     910           0 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     911           0 :     if (!pwallet) return UniValue::VNULL;
     912             : 
     913           0 :     RPCTypeCheck(request.params, {UniValue::VNUM}, true);
     914             : 
     915           0 :     EnsureWalletIsUnlocked(*pwallet);
     916             : 
     917           0 :     int version = 0;
     918           0 :     if (!request.params[0].isNull()) {
     919           0 :         version = request.params[0].getInt<int>();
     920           0 :     }
     921           0 :     bilingual_str error;
     922           0 :     const int previous_version{pwallet->GetVersion()};
     923           0 :     const bool wallet_upgraded{pwallet->UpgradeWallet(version, error)};
     924           0 :     const int current_version{pwallet->GetVersion()};
     925           0 :     std::string result;
     926             : 
     927           0 :     if (wallet_upgraded) {
     928           0 :         if (previous_version == current_version) {
     929           0 :             result = "Already at latest version. Wallet version unchanged.";
     930           0 :         } else {
     931           0 :             result = strprintf("Wallet upgraded successfully from version %i to version %i.", previous_version, current_version);
     932             :         }
     933           0 :     }
     934             : 
     935           0 :     UniValue obj(UniValue::VOBJ);
     936           0 :     obj.pushKV("wallet_name", pwallet->GetName());
     937           0 :     obj.pushKV("previous_version", previous_version);
     938           0 :     obj.pushKV("current_version", current_version);
     939           0 :     if (!result.empty()) {
     940           0 :         obj.pushKV("result", result);
     941           0 :     } else {
     942           0 :         CHECK_NONFATAL(!error.empty());
     943           0 :         obj.pushKV("error", error.original);
     944             :     }
     945           0 :     return obj;
     946           0 : },
     947             :     };
     948           0 : }
     949             : 
     950        3000 : RPCHelpMan simulaterawtransaction()
     951             : {
     952        6000 :     return RPCHelpMan{"simulaterawtransaction",
     953        3000 :         "\nCalculate the balance change resulting in the signing and broadcasting of the given transaction(s).\n",
     954        9000 :         {
     955        6000 :             {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "An array of hex strings of raw transactions.\n",
     956        6000 :                 {
     957        3000 :                     {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
     958             :                 },
     959             :             },
     960        6000 :             {"options", RPCArg::Type::OBJ_USER_KEYS, RPCArg::Optional::OMITTED_NAMED_ARG, "Options",
     961        6000 :                 {
     962        3000 :                     {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see RPC importaddress)"},
     963             :                 },
     964             :             },
     965             :         },
     966        3000 :         RPCResult{
     967        3000 :             RPCResult::Type::OBJ, "", "",
     968        6000 :             {
     969        3000 :                 {RPCResult::Type::STR_AMOUNT, "balance_change", "The wallet balance change (negative means decrease)."},
     970             :             }
     971             :         },
     972        3000 :         RPCExamples{
     973        3000 :             HelpExampleCli("simulaterawtransaction", "[\"myhex\"]")
     974        3000 :             + HelpExampleRpc("simulaterawtransaction", "[\"myhex\"]")
     975             :         },
     976        3104 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     977             : {
     978         104 :     const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
     979         104 :     if (!rpc_wallet) return UniValue::VNULL;
     980         104 :     const CWallet& wallet = *rpc_wallet;
     981             : 
     982         104 :     RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ}, true);
     983             : 
     984         104 :     LOCK(wallet.cs_wallet);
     985             : 
     986         104 :     UniValue include_watchonly(UniValue::VNULL);
     987         104 :     if (request.params[1].isObject()) {
     988           0 :         UniValue options = request.params[1];
     989           0 :         RPCTypeCheckObj(options,
     990           0 :             {
     991           0 :                 {"include_watchonly", UniValueType(UniValue::VBOOL)},
     992             :             },
     993             :             true, true);
     994             : 
     995           0 :         include_watchonly = options["include_watchonly"];
     996           0 :     }
     997             : 
     998         104 :     isminefilter filter = ISMINE_SPENDABLE;
     999         104 :     if (ParseIncludeWatchonly(include_watchonly, wallet)) {
    1000          20 :         filter |= ISMINE_WATCH_ONLY;
    1001          20 :     }
    1002             : 
    1003         104 :     const auto& txs = request.params[0].get_array();
    1004         104 :     CAmount changes{0};
    1005         104 :     std::map<COutPoint, CAmount> new_utxos; // UTXO:s that were made available in transaction array
    1006         104 :     std::set<COutPoint> spent;
    1007             : 
    1008         216 :     for (size_t i = 0; i < txs.size(); ++i) {
    1009         152 :         CMutableTransaction mtx;
    1010         152 :         if (!DecodeHexTx(mtx, txs[i].get_str())) {
    1011           0 :             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Transaction hex string decoding failure.");
    1012             :         }
    1013             : 
    1014             :         // Fetch previous transactions (inputs)
    1015         152 :         std::map<COutPoint, Coin> coins;
    1016         268 :         for (const CTxIn& txin : mtx.vin) {
    1017         116 :             coins[txin.prevout]; // Create empty map entry keyed by prevout.
    1018             :         }
    1019         152 :         wallet.chain().findCoins(coins);
    1020             : 
    1021             :         // Fetch debit; we are *spending* these; if the transaction is signed and
    1022             :         // broadcast, we will lose everything in these
    1023         228 :         for (const auto& txin : mtx.vin) {
    1024         116 :             const auto& outpoint = txin.prevout;
    1025         116 :             if (spent.count(outpoint)) {
    1026          12 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction(s) are spending the same output more than once");
    1027             :             }
    1028         104 :             if (new_utxos.count(outpoint)) {
    1029          24 :                 changes -= new_utxos.at(outpoint);
    1030          24 :                 new_utxos.erase(outpoint);
    1031          24 :             } else {
    1032          80 :                 if (coins.at(outpoint).IsSpent()) {
    1033          28 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more transaction inputs are missing or have been spent already");
    1034             :                 }
    1035          52 :                 changes -= wallet.GetDebit(txin, filter);
    1036             :             }
    1037          76 :             spent.insert(outpoint);
    1038             :         }
    1039             : 
    1040             :         // Iterate over outputs; we are *receiving* these, if the wallet considers
    1041             :         // them "mine"; if the transaction is signed and broadcast, we will receive
    1042             :         // everything in these
    1043             :         // Also populate new_utxos in case these are spent in later transactions
    1044             : 
    1045         112 :         const auto& hash = mtx.GetHash();
    1046         276 :         for (size_t i = 0; i < mtx.vout.size(); ++i) {
    1047         164 :             const auto& txout = mtx.vout[i];
    1048         164 :             bool is_mine = 0 < (wallet.IsMine(txout) & filter);
    1049         164 :             changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
    1050         164 :         }
    1051         152 :     }
    1052             : 
    1053          64 :     UniValue result(UniValue::VOBJ);
    1054          64 :     result.pushKV("balance_change", ValueFromAmount(changes));
    1055             : 
    1056          64 :     return result;
    1057         144 : }
    1058             :     };
    1059           0 : }
    1060             : 
    1061        2934 : static RPCHelpMan migratewallet()
    1062             : {
    1063        5868 :     return RPCHelpMan{"migratewallet",
    1064        2934 :         "EXPERIMENTAL warning: This call may not work as expected and may be changed in future releases\n"
    1065             :         "\nMigrate the wallet to a descriptor wallet.\n"
    1066             :         "A new wallet backup will need to be made.\n"
    1067             :         "\nThe migration process will create a backup of the wallet before migrating. This backup\n"
    1068             :         "file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
    1069             :         "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
    1070             :         "\nEncrypted wallets must have the passphrase provided as an argument to this call.",
    1071        8802 :         {
    1072        2934 :             {"wallet_name", RPCArg::Type::STR, RPCArg::DefaultHint{"the wallet name from the RPC endpoint"}, "The name of the wallet to migrate. If provided both here and in the RPC endpoint, the two must be identical."},
    1073        2934 :             {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
    1074             :         },
    1075        2934 :         RPCResult{
    1076        2934 :             RPCResult::Type::OBJ, "", "",
    1077       14670 :             {
    1078        2934 :                 {RPCResult::Type::STR, "wallet_name", "The name of the primary migrated wallet"},
    1079        2934 :                 {RPCResult::Type::STR, "watchonly_name", /*optional=*/true, "The name of the migrated wallet containing the watchonly scripts"},
    1080        2934 :                 {RPCResult::Type::STR, "solvables_name", /*optional=*/true, "The name of the migrated wallet containing solvable but not watched scripts"},
    1081        2934 :                 {RPCResult::Type::STR, "backup_path", "The location of the backup of the original wallet"},
    1082             :             }
    1083             :         },
    1084        2934 :         RPCExamples{
    1085        2934 :             HelpExampleCli("migratewallet", "")
    1086        2934 :             + HelpExampleRpc("migratewallet", "")
    1087             :         },
    1088        2972 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
    1089             :         {
    1090          38 :             std::string wallet_name;
    1091          38 :             if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
    1092          32 :                 if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) {
    1093           2 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets");
    1094             :                 }
    1095          30 :             } else {
    1096           6 :                 if (request.params[0].isNull()) {
    1097           2 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Either RPC endpoint wallet or wallet_name parameter must be provided");
    1098             :                 }
    1099           4 :                 wallet_name = request.params[0].get_str();
    1100             :             }
    1101             : 
    1102          34 :             SecureString wallet_pass;
    1103          34 :             wallet_pass.reserve(100);
    1104          34 :             if (!request.params[1].isNull()) {
    1105           6 :                 wallet_pass = std::string_view{request.params[1].get_str()};
    1106           6 :             }
    1107             : 
    1108          34 :             WalletContext& context = EnsureWalletContext(request.context);
    1109          34 :             util::Result<MigrationResult> res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context);
    1110          34 :             if (!res) {
    1111           8 :                 throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
    1112             :             }
    1113             : 
    1114          26 :             UniValue r{UniValue::VOBJ};
    1115          26 :             r.pushKV("wallet_name", res->wallet_name);
    1116          26 :             if (res->watchonly_wallet) {
    1117           4 :                 r.pushKV("watchonly_name", res->watchonly_wallet->GetName());
    1118           4 :             }
    1119          26 :             if (res->solvables_wallet) {
    1120           2 :                 r.pushKV("solvables_name", res->solvables_wallet->GetName());
    1121           2 :             }
    1122          26 :             r.pushKV("backup_path", fs::PathToString(res->backup_path));
    1123             : 
    1124          26 :             return r;
    1125          50 :         },
    1126             :     };
    1127           0 : }
    1128             : 
    1129             : // addresses
    1130             : RPCHelpMan getaddressinfo();
    1131             : RPCHelpMan getnewaddress();
    1132             : RPCHelpMan getrawchangeaddress();
    1133             : RPCHelpMan setlabel();
    1134             : RPCHelpMan listaddressgroupings();
    1135             : RPCHelpMan addmultisigaddress();
    1136             : RPCHelpMan keypoolrefill();
    1137             : RPCHelpMan newkeypool();
    1138             : RPCHelpMan getaddressesbylabel();
    1139             : RPCHelpMan listlabels();
    1140             : #ifdef ENABLE_EXTERNAL_SIGNER
    1141             : RPCHelpMan walletdisplayaddress();
    1142             : #endif // ENABLE_EXTERNAL_SIGNER
    1143             : 
    1144             : // backup
    1145             : RPCHelpMan dumpprivkey();
    1146             : RPCHelpMan importprivkey();
    1147             : RPCHelpMan importaddress();
    1148             : RPCHelpMan importpubkey();
    1149             : RPCHelpMan dumpwallet();
    1150             : RPCHelpMan importwallet();
    1151             : RPCHelpMan importprunedfunds();
    1152             : RPCHelpMan removeprunedfunds();
    1153             : RPCHelpMan importmulti();
    1154             : RPCHelpMan importdescriptors();
    1155             : RPCHelpMan listdescriptors();
    1156             : RPCHelpMan backupwallet();
    1157             : RPCHelpMan restorewallet();
    1158             : RPCHelpMan dumphdinfo();
    1159             : RPCHelpMan importelectrumwallet();
    1160             : 
    1161             : // coins
    1162             : RPCHelpMan getreceivedbyaddress();
    1163             : RPCHelpMan getreceivedbylabel();
    1164             : RPCHelpMan listaddressbalances();
    1165             : RPCHelpMan getbalance();
    1166             : RPCHelpMan getunconfirmedbalance();
    1167             : RPCHelpMan lockunspent();
    1168             : RPCHelpMan listlockunspent();
    1169             : RPCHelpMan getbalances();
    1170             : RPCHelpMan listunspent();
    1171             : 
    1172             : // encryption
    1173             : RPCHelpMan walletpassphrase();
    1174             : RPCHelpMan walletpassphrasechange();
    1175             : RPCHelpMan walletlock();
    1176             : RPCHelpMan encryptwallet();
    1177             : 
    1178             : // spend
    1179             : RPCHelpMan sendtoaddress();
    1180             : RPCHelpMan sendmany();
    1181             : RPCHelpMan settxfee();
    1182             : RPCHelpMan fundrawtransaction();
    1183             : RPCHelpMan send();
    1184             : RPCHelpMan sendall();
    1185             : RPCHelpMan walletprocesspsbt();
    1186             : RPCHelpMan walletcreatefundedpsbt();
    1187             : RPCHelpMan signrawtransactionwithwallet();
    1188             : 
    1189             : // signmessage
    1190             : RPCHelpMan signmessage();
    1191             : 
    1192             : // transactions
    1193             : RPCHelpMan listreceivedbyaddress();
    1194             : RPCHelpMan listreceivedbylabel();
    1195             : RPCHelpMan listtransactions();
    1196             : RPCHelpMan listsinceblock();
    1197             : RPCHelpMan gettransaction();
    1198             : RPCHelpMan abandontransaction();
    1199             : RPCHelpMan rescanblockchain();
    1200             : RPCHelpMan abortrescan();
    1201             : 
    1202        1450 : Span<const CRPCCommand> GetWalletRPCCommands()
    1203             : {
    1204      105130 :     static const CRPCCommand commands[]{
    1205        1440 :         {"rawtransactions", &fundrawtransaction},
    1206        1440 :         {"wallet", &abandontransaction},
    1207        1440 :         {"wallet", &abortrescan},
    1208        1440 :         {"wallet", &addmultisigaddress},
    1209        1440 :         {"wallet", &backupwallet},
    1210        1440 :         {"wallet", &createwallet},
    1211        1440 :         {"wallet", &restorewallet},
    1212        1440 :         {"wallet", &dumphdinfo},
    1213        1440 :         {"wallet", &dumpprivkey},
    1214        1440 :         {"wallet", &dumpwallet},
    1215        1440 :         {"wallet", &encryptwallet},
    1216        1440 :         {"wallet", &getaddressesbylabel},
    1217        1440 :         {"wallet", &getaddressinfo},
    1218        1440 :         {"wallet", &getbalance},
    1219        1440 :         {"wallet", &getnewaddress},
    1220        1440 :         {"wallet", &getrawchangeaddress},
    1221        1440 :         {"wallet", &getreceivedbyaddress},
    1222        1440 :         {"wallet", &getreceivedbylabel},
    1223        1440 :         {"wallet", &gettransaction},
    1224        1440 :         {"wallet", &getunconfirmedbalance},
    1225        1440 :         {"wallet", &getbalances},
    1226        1440 :         {"wallet", &getwalletinfo},
    1227        1440 :         {"wallet", &importaddress},
    1228        1440 :         {"wallet", &importelectrumwallet},
    1229        1440 :         {"wallet", &importdescriptors},
    1230        1440 :         {"wallet", &importmulti},
    1231        1440 :         {"wallet", &importprivkey},
    1232        1440 :         {"wallet", &importprunedfunds},
    1233        1440 :         {"wallet", &importpubkey},
    1234        1440 :         {"wallet", &importwallet},
    1235        1440 :         {"wallet", &keypoolrefill},
    1236        1440 :         {"wallet", &listaddressbalances},
    1237        1440 :         {"wallet", &listaddressgroupings},
    1238        1440 :         {"wallet", &listdescriptors},
    1239        1440 :         {"wallet", &listlabels},
    1240        1440 :         {"wallet", &listlockunspent},
    1241        1440 :         {"wallet", &listreceivedbyaddress},
    1242        1440 :         {"wallet", &listreceivedbylabel},
    1243        1440 :         {"wallet", &listsinceblock},
    1244        1440 :         {"wallet", &listtransactions},
    1245        1440 :         {"wallet", &listunspent},
    1246        1440 :         {"wallet", &listwalletdir},
    1247        1440 :         {"wallet", &listwallets},
    1248        1440 :         {"wallet", &loadwallet},
    1249        1440 :         {"wallet", &lockunspent},
    1250        1440 :         {"wallet", &migratewallet},
    1251        1440 :         {"wallet", &newkeypool},
    1252        1440 :         {"wallet", &removeprunedfunds},
    1253        1440 :         {"wallet", &rescanblockchain},
    1254        1440 :         {"wallet", &send},
    1255        1440 :         {"wallet", &sendmany},
    1256        1440 :         {"wallet", &sendtoaddress},
    1257        1440 :         {"wallet", &sethdseed},
    1258        1440 :         {"wallet", &setcoinjoinrounds},
    1259        1440 :         {"wallet", &setcoinjoinamount},
    1260        1440 :         {"wallet", &setlabel},
    1261        1440 :         {"wallet", &settxfee},
    1262        1440 :         {"wallet", &setwalletflag},
    1263        1440 :         {"wallet", &signmessage},
    1264        1440 :         {"wallet", &signrawtransactionwithwallet},
    1265        1440 :         {"wallet", &simulaterawtransaction},
    1266        1440 :         {"wallet", &sendall},
    1267        1440 :         {"wallet", &unloadwallet},
    1268        1440 :         {"wallet", &upgradewallet},
    1269        1440 :         {"wallet", &upgradetohd},
    1270             : #ifdef ENABLE_EXTERNAL_SIGNER
    1271        1440 :         {"wallet", &walletdisplayaddress},
    1272             : #endif // ENABLE_EXTERNAL_SIGNER
    1273        1440 :         {"wallet", &walletlock},
    1274        1440 :         {"wallet", &walletpassphrasechange},
    1275        1440 :         {"wallet", &walletpassphrase},
    1276        1440 :         {"wallet", &walletprocesspsbt},
    1277        1440 :         {"wallet", &walletcreatefundedpsbt},
    1278        1440 :         {"wallet", &wipewallettxes},
    1279             :     };
    1280        1450 :     return commands;
    1281           0 : }
    1282             : } // namespace wallet

Generated by: LCOV version 1.16