LCOV - code coverage report
Current view: top level - src/wallet/rpc - wallet.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 97.8 % 767 750
Test Date: 2026-08-23 07:40:03 Functions: 100.0 % 29 29
Branches: 51.6 % 2826 1458

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 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 <bitcoin-build-config.h> // IWYU pragma: keep
       7                 :             : 
       8                 :             : #include <wallet/rpc/wallet.h>
       9                 :             : 
      10                 :             : #include <coins.h>
      11                 :             : #include <core_io.h>
      12                 :             : #include <key.h>
      13                 :             : #include <key_io.h>
      14                 :             : #include <rpc/server.h>
      15                 :             : #include <rpc/util.h>
      16                 :             : #include <univalue.h>
      17                 :             : #include <util/bip32.h>
      18                 :             : #include <util/translation.h>
      19                 :             : #include <wallet/context.h>
      20                 :             : #include <wallet/export.h>
      21                 :             : #include <wallet/receive.h>
      22                 :             : #include <wallet/rpc/util.h>
      23                 :             : #include <wallet/wallet.h>
      24                 :             : #include <wallet/walletutil.h>
      25                 :             : 
      26                 :             : #include <algorithm>
      27                 :             : #include <optional>
      28                 :             : #include <string_view>
      29                 :             : 
      30                 :             : 
      31                 :             : namespace wallet {
      32                 :             : 
      33                 :             : using HDPubKeyMap = CWallet::HDPubKeyMap;
      34                 :             : using HDKeyFilter = CWallet::HDKeyFilter;
      35                 :             : 
      36                 :             : static const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{
      37                 :             :     {WALLET_FLAG_AVOID_REUSE,
      38                 :             :      "You need to rescan the blockchain in order to correctly mark used "
      39                 :             :      "destinations in the past. Until this is done, some destinations may "
      40                 :             :      "be considered unused, even if the opposite is the case."},
      41                 :             : };
      42                 :             : 
      43                 :        1297 : static RPCMethod getwalletinfo()
      44                 :             : {
      45                 :        1297 :     return RPCMethod{"getwalletinfo",
      46         [ +  - ]:        2594 :                 "Returns an object containing various wallet state info.\n",
      47                 :             :                 {},
      48         [ +  - ]:        2594 :                 RPCResult{
      49         [ +  - ]:        2594 :                     RPCResult::Type::OBJ, "", "",
      50                 :             :                     {
      51                 :             :                         {
      52   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::STR, "walletname", "the wallet name"},
      53   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::NUM, "walletversion", "(DEPRECATED) only related to unsupported legacy wallet, returns the latest version 169900 for backwards compatibility"},
      54   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::STR, "format", "the database format (only sqlite)"},
      55   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
      56   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
      57   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::NUM, "keypoolsize_hd_internal", /*optional=*/true, "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
      58   [ +  -  +  - ]:        2594 :                         {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)"},
      59   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
      60   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
      61   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
      62                 :             :                         {
      63   [ +  -  +  - ]:        2594 :                             {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
      64   [ +  -  +  - ]:        2594 :                             {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
      65                 :             :                         }, {.skip_type_check=true}, },
      66   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for output script management"},
      67   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
      68   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::BOOL, "blank", "Whether this wallet intentionally does not contain any keys, scripts, or descriptors"},
      69   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::NUM_TIME, "birthtime", /*optional=*/true, "The start time for blocks scanning. It could be modified by (re)importing any descriptor with an earlier timestamp."},
      70   [ +  -  +  - ]:        2594 :                         {RPCResult::Type::ARR, "flags", "The flags currently set on the wallet",
      71                 :             :                         {
      72   [ +  -  +  - ]:        2594 :                             {RPCResult::Type::STR, "flag", "The name of the flag"},
      73                 :             :                         }},
      74                 :             :                         RESULT_LAST_PROCESSED_BLOCK,
      75                 :             :                     }},
      76   [ +  -  +  -  :       53177 :                 },
          +  -  +  -  +  
          +  +  +  +  +  
          -  -  -  -  -  
                      - ]
      77                 :        1297 :                 RPCExamples{
      78   [ +  -  +  -  :        2594 :                     HelpExampleCli("getwalletinfo", "")
                   +  - ]
      79   [ +  -  +  -  :        5188 :             + HelpExampleRpc("getwalletinfo", "")
             +  -  +  - ]
      80         [ +  - ]:        1297 :                 },
      81                 :        1297 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
      82                 :             : {
      83         [ -  + ]:         440 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
      84         [ -  + ]:         430 :     if (!pwallet) return UniValue::VNULL;
      85                 :             : 
      86                 :             :     // Make sure the results are valid at least up to the most recent block
      87                 :             :     // the user could have gotten from another RPC command prior to now
      88         [ +  - ]:         430 :     pwallet->BlockUntilSyncedToCurrentChain();
      89                 :             : 
      90         [ +  - ]:         430 :     LOCK(pwallet->cs_wallet);
      91                 :             : 
      92                 :         430 :     UniValue obj(UniValue::VOBJ);
      93                 :             : 
      94                 :         430 :     const int latest_legacy_wallet_minversion{169900};
      95                 :             : 
      96         [ +  - ]:         430 :     size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
      97   [ +  -  +  -  :         860 :     obj.pushKV("walletname", pwallet->GetName());
                   +  - ]
      98   [ +  -  +  -  :         860 :     obj.pushKV("walletversion", latest_legacy_wallet_minversion);
                   +  - ]
      99   [ +  -  +  -  :         860 :     obj.pushKV("format", pwallet->GetDatabase().Format());
             +  -  +  - ]
     100   [ +  -  +  -  :         860 :     obj.pushKV("txcount", pwallet->mapWallet.size());
                   +  - ]
     101   [ +  -  +  -  :         860 :     obj.pushKV("keypoolsize", kpExternalSize);
                   +  - ]
     102   [ +  -  +  -  :         860 :     obj.pushKV("keypoolsize_hd_internal", pwallet->GetKeyPoolSize() - kpExternalSize);
             +  -  +  - ]
     103                 :             : 
     104   [ +  -  +  + ]:         430 :     if (pwallet->HasEncryptionKeys()) {
     105   [ +  -  +  -  :          68 :         obj.pushKV("unlocked_until", pwallet->nRelockTime);
                   +  - ]
     106                 :             :     }
     107   [ +  -  +  -  :         860 :     obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
             +  -  +  - ]
     108   [ +  -  +  -  :         860 :     obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
             +  -  +  - ]
     109         [ -  + ]:         430 :     if (pwallet->IsScanning()) {
     110                 :           0 :         UniValue scanning(UniValue::VOBJ);
     111   [ #  #  #  #  :           0 :         scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->ScanningDuration()));
                   #  # ]
     112   [ #  #  #  #  :           0 :         scanning.pushKV("progress", pwallet->ScanningProgress());
                   #  # ]
     113   [ #  #  #  # ]:           0 :         obj.pushKV("scanning", std::move(scanning));
     114                 :           0 :     } else {
     115   [ +  -  +  -  :         860 :         obj.pushKV("scanning", false);
                   +  - ]
     116                 :             :     }
     117   [ +  -  +  -  :         860 :     obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
             +  -  +  - ]
     118   [ +  -  +  -  :         860 :     obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
             +  -  +  - ]
     119   [ +  -  +  -  :         860 :     obj.pushKV("blank", pwallet->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
             +  -  +  - ]
     120         [ +  + ]:         430 :     if (int64_t birthtime = pwallet->GetBirthTime(); birthtime != UNKNOWN_TIME) {
     121   [ +  -  +  -  :         782 :         obj.pushKV("birthtime", birthtime);
                   +  - ]
     122                 :             :     }
     123                 :             : 
     124                 :             :     // Push known flags
     125                 :         430 :     UniValue flags(UniValue::VARR);
     126         [ +  - ]:         430 :     uint64_t wallet_flags = pwallet->GetWalletFlags();
     127         [ +  + ]:       27950 :     for (uint64_t i = 0; i < 64; ++i) {
     128                 :       27520 :         uint64_t flag = uint64_t{1} << i;
     129         [ +  + ]:       27520 :         if (flag & wallet_flags) {
     130         [ +  - ]:        1086 :             if (flag & KNOWN_WALLET_FLAGS) {
     131   [ +  -  +  -  :        1086 :                 flags.push_back(WALLET_FLAG_TO_STRING.at(WalletFlags{flag}));
                   +  - ]
     132                 :             :             } else {
     133   [ #  #  #  #  :           0 :                 flags.push_back(strprintf("unknown_flag_%u", i));
                   #  # ]
     134                 :             :             }
     135                 :             :         }
     136                 :             :     }
     137   [ +  -  +  -  :         860 :     obj.pushKV("flags", flags);
                   +  - ]
     138                 :             : 
     139         [ +  - ]:         430 :     AppendLastProcessedBlock(obj, *pwallet);
     140                 :         430 :     return obj;
     141         [ +  - ]:        1290 : },
     142   [ +  -  +  - ]:        5188 :     };
     143   [ +  -  +  -  :       46692 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  -  
                -  -  - ]
     144                 :             : 
     145                 :         931 : static RPCMethod listwalletdir()
     146                 :             : {
     147                 :         931 :     return RPCMethod{"listwalletdir",
     148         [ +  - ]:        1862 :                 "Returns a list of wallets in the wallet directory.\n",
     149                 :             :                 {},
     150         [ +  - ]:        1862 :                 RPCResult{
     151         [ +  - ]:        1862 :                     RPCResult::Type::OBJ, "", "",
     152                 :             :                     {
     153   [ +  -  +  - ]:        1862 :                         {RPCResult::Type::ARR, "wallets", "",
     154                 :             :                         {
     155   [ +  -  +  - ]:        1862 :                             {RPCResult::Type::OBJ, "", "",
     156                 :             :                             {
     157   [ +  -  +  - ]:        1862 :                                 {RPCResult::Type::STR, "name", "The wallet name"},
     158   [ +  -  +  - ]:        1862 :                                 {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
     159                 :             :                                 {
     160   [ +  -  +  - ]:        1862 :                                     {RPCResult::Type::STR, "", ""},
     161                 :             :                                 }},
     162                 :             :                             }},
     163                 :             :                         }},
     164                 :             :                     }
     165   [ +  -  +  -  :       13965 :                 },
          +  -  +  -  +  
          -  +  +  +  +  
          +  +  +  +  -  
          -  -  -  -  -  
                   -  - ]
     166                 :         931 :                 RPCExamples{
     167   [ +  -  +  -  :        1862 :                     HelpExampleCli("listwalletdir", "")
                   +  - ]
     168   [ +  -  +  -  :        3724 :             + HelpExampleRpc("listwalletdir", "")
             +  -  +  - ]
     169         [ +  - ]:         931 :                 },
     170                 :         931 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     171                 :             : {
     172                 :          74 :     UniValue wallets(UniValue::VARR);
     173   [ +  -  +  -  :        1715 :     for (const auto& [path, db_type] : ListDatabases(GetWalletDir())) {
                   +  + ]
     174                 :        1567 :         UniValue wallet(UniValue::VOBJ);
     175   [ +  -  +  -  :        3134 :         wallet.pushKV("name", path.utf8string());
             +  -  +  - ]
     176                 :        1567 :                 UniValue warnings(UniValue::VARR);
     177         [ +  + ]:        1567 :         if (db_type == "bdb") {
     178   [ +  -  +  - ]:          82 :             warnings.push_back("This wallet is a legacy wallet and will need to be migrated with migratewallet before it can be loaded");
     179                 :             :         }
     180   [ +  -  +  -  :        3134 :         wallet.pushKV("warnings", warnings);
                   +  - ]
     181         [ +  - ]:        1567 :         wallets.push_back(std::move(wallet));
     182                 :        1641 :     }
     183                 :             : 
     184                 :          74 :     UniValue result(UniValue::VOBJ);
     185   [ +  -  +  - ]:         148 :     result.pushKV("wallets", std::move(wallets));
     186                 :          74 :     return result;
     187                 :          74 : },
     188   [ +  -  +  - ]:        3724 :     };
     189   [ +  -  +  -  :        9310 : }
          +  -  +  -  +  
                -  -  - ]
     190                 :             : 
     191                 :         927 : static RPCMethod listwallets()
     192                 :             : {
     193                 :         927 :     return RPCMethod{"listwallets",
     194         [ +  - ]:        1854 :                 "Returns a list of currently loaded wallets.\n"
     195                 :             :                 "For full information on the wallet, use \"getwalletinfo\"\n",
     196                 :             :                 {},
     197         [ +  - ]:        1854 :                 RPCResult{
     198         [ +  - ]:        1854 :                     RPCResult::Type::ARR, "", "",
     199                 :             :                     {
     200   [ +  -  +  - ]:        1854 :                         {RPCResult::Type::STR, "walletname", "the wallet name"},
     201                 :             :                     }
     202   [ +  -  +  -  :        3708 :                 },
             +  +  -  - ]
     203                 :         927 :                 RPCExamples{
     204   [ +  -  +  -  :        1854 :                     HelpExampleCli("listwallets", "")
                   +  - ]
     205   [ +  -  +  -  :        3708 :             + HelpExampleRpc("listwallets", "")
             +  -  +  - ]
     206         [ +  - ]:         927 :                 },
     207                 :         927 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     208                 :             : {
     209                 :          70 :     UniValue obj(UniValue::VARR);
     210                 :             : 
     211         [ +  - ]:          70 :     WalletContext& context = EnsureWalletContext(request.context);
     212   [ +  -  +  + ]:         609 :     for (const std::shared_ptr<CWallet>& wallet : GetWallets(context)) {
     213         [ +  - ]:         539 :         LOCK(wallet->cs_wallet);
     214   [ +  -  +  -  :         539 :         obj.push_back(wallet->GetName());
                   +  - ]
     215                 :         609 :     }
     216                 :             : 
     217                 :          70 :     return obj;
     218                 :           0 : },
     219   [ +  -  +  - ]:        3708 :     };
     220         [ +  - ]:        1854 : }
     221                 :             : 
     222                 :        1019 : static RPCMethod loadwallet()
     223                 :             : {
     224                 :        1019 :     return RPCMethod{
     225                 :        1019 :         "loadwallet",
     226         [ +  - ]:        2038 :         "Loads a wallet from a wallet file or directory."
     227                 :             :                 "\nNote that all wallet command-line options used when starting bitcoind will be"
     228                 :             :                 "\napplied to the new wallet.\n",
     229                 :             :                 {
     230   [ +  -  +  - ]:        2038 :                     {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The path to the directory of the wallet to be loaded, either absolute or relative to the \"wallets\" directory. The \"wallets\" directory is set by the -walletdir option and defaults to the \"wallets\" folder within the data directory."},
     231   [ +  -  +  - ]:        2038 :                     {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
     232                 :             :                 },
     233         [ +  - ]:        2038 :                 RPCResult{
     234   [ +  -  +  - ]:        2038 :                     RPCResult::Type::OBJ, "", "",
     235                 :             :                     {
     236   [ +  -  +  - ]:        2038 :                         {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
     237   [ +  -  +  - ]:        2038 :                         {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
     238                 :             :                         {
     239   [ +  -  +  - ]:        2038 :                             {RPCResult::Type::STR, "", ""},
     240                 :             :                         }},
     241                 :             :                     }
     242   [ +  -  +  -  :        9171 :                 },
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     243                 :        1019 :                 RPCExamples{
     244                 :             :                     "\nLoad wallet from the wallet dir:\n"
     245   [ +  -  +  -  :        2038 :                     + HelpExampleCli("loadwallet", "\"walletname\"")
             +  -  +  - ]
     246   [ +  -  +  -  :        4076 :                     + HelpExampleRpc("loadwallet", "\"walletname\"")
             +  -  +  - ]
     247                 :        1019 :                     + "\nLoad wallet using absolute path (Unix):\n"
     248   [ +  -  +  -  :        4076 :                     + HelpExampleCli("loadwallet", "\"/path/to/walletname/\"")
             +  -  +  - ]
     249   [ +  -  +  -  :        4076 :                     + HelpExampleRpc("loadwallet", "\"/path/to/walletname/\"")
             +  -  +  - ]
     250                 :        1019 :                     + "\nLoad wallet using absolute path (Windows):\n"
     251   [ +  -  +  -  :        4076 :                     + HelpExampleCli("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
             +  -  +  - ]
     252   [ +  -  +  -  :        4076 :                     + HelpExampleRpc("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
             +  -  +  - ]
     253         [ +  - ]:        1019 :                 },
     254                 :        1019 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     255                 :             : {
     256                 :         162 :     WalletContext& context = EnsureWalletContext(request.context);
     257         [ -  + ]:         162 :     const std::string name(request.params[0].get_str());
     258                 :             : 
     259         [ +  - ]:         162 :     DatabaseOptions options;
     260                 :         162 :     DatabaseStatus status;
     261         [ +  - ]:         162 :     ReadDatabaseArgs(*context.args, options);
     262                 :         162 :     options.require_existing = true;
     263         [ +  - ]:         162 :     bilingual_str error;
     264                 :         162 :     std::vector<bilingual_str> warnings;
     265   [ +  -  +  +  :         162 :     std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
             +  -  +  - ]
     266                 :             : 
     267                 :         162 :     {
     268         [ +  - ]:         162 :         LOCK(context.wallets_mutex);
     269   [ +  +  +  + ]:         837 :         if (std::any_of(context.wallets.begin(), context.wallets.end(), [&name](const auto& wallet) { return wallet->GetName() == name; })) {
     270   [ +  -  +  - ]:           6 :             throw JSONRPCError(RPC_WALLET_ALREADY_LOADED, "Wallet \"" + name + "\" is already loaded.");
     271                 :             :         }
     272                 :           2 :     }
     273                 :             : 
     274         [ +  - ]:         160 :     std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings);
     275                 :             : 
     276         [ +  + ]:         160 :     HandleWalletError(wallet, status, error);
     277                 :             : 
     278                 :         142 :     UniValue obj(UniValue::VOBJ);
     279   [ +  -  +  -  :         284 :     obj.pushKV("name", wallet->GetName());
                   +  - ]
     280         [ +  - ]:         142 :     PushWarnings(warnings, obj);
     281                 :             : 
     282         [ +  - ]:         142 :     return obj;
     283                 :         344 : },
     284   [ +  -  +  -  :        6114 :     };
             +  +  -  - ]
     285   [ +  -  +  -  :       10190 : }
          +  -  +  -  +  
             -  -  -  -  
                      - ]
     286                 :             : 
     287                 :         865 : static RPCMethod setwalletflag()
     288                 :             : {
     289                 :         865 :             std::string flags;
     290         [ +  + ]:        6920 :             for (auto& it : STRING_TO_WALLET_FLAG)
     291         [ +  + ]:        6055 :                 if (it.second & MUTABLE_WALLET_FLAGS)
     292   [ -  +  +  - ]:        1730 :                     flags += (flags == "" ? "" : ", ") + it.first;
     293                 :             : 
     294                 :         865 :     return RPCMethod{
     295         [ +  - ]:        1730 :         "setwalletflag",
     296         [ +  - ]:        1730 :         "Change the state of the given wallet flag for a wallet.\n",
     297                 :             :                 {
     298   [ +  -  +  - ]:        1730 :                     {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
     299   [ +  -  +  -  :        2595 :                     {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
                   +  - ]
     300                 :             :                 },
     301         [ +  - ]:        1730 :                 RPCResult{
     302   [ +  -  +  - ]:        1730 :                     RPCResult::Type::OBJ, "", "",
     303                 :             :                     {
     304   [ +  -  +  - ]:        1730 :                         {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
     305   [ +  -  +  - ]:        1730 :                         {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
     306   [ +  -  +  - ]:        1730 :                         {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
     307                 :             :                     }
     308   [ +  -  +  -  :        6920 :                 },
             +  +  -  - ]
     309                 :         865 :                 RPCExamples{
     310   [ +  -  +  -  :        1730 :                     HelpExampleCli("setwalletflag", "avoid_reuse")
                   +  - ]
     311   [ +  -  +  -  :        3460 :                   + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
             +  -  +  - ]
     312         [ +  - ]:         865 :                 },
     313                 :         865 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     314                 :             : {
     315                 :           8 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     316         [ -  + ]:           8 :     if (!pwallet) return UniValue::VNULL;
     317                 :             : 
     318   [ +  -  +  -  :           8 :     std::string flag_str = request.params[0].get_str();
                   -  + ]
     319   [ +  -  +  +  :           8 :     bool value = request.params[1].isNull() || request.params[1].get_bool();
          +  -  +  -  +  
                      + ]
     320                 :             : 
     321         [ +  + ]:           8 :     if (!STRING_TO_WALLET_FLAG.contains(flag_str)) {
     322   [ +  -  +  - ]:           2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
     323                 :             :     }
     324                 :             : 
     325         [ +  - ]:           7 :     auto flag = STRING_TO_WALLET_FLAG.at(flag_str);
     326                 :             : 
     327         [ +  + ]:           7 :     if (!(flag & MUTABLE_WALLET_FLAGS)) {
     328   [ +  -  +  - ]:           6 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
     329                 :             :     }
     330                 :             : 
     331                 :           4 :     UniValue res(UniValue::VOBJ);
     332                 :             : 
     333   [ +  -  +  + ]:           4 :     if (pwallet->IsWalletFlagSet(flag) == value) {
     334   [ +  +  +  -  :           5 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
                   +  - ]
     335                 :             :     }
     336                 :             : 
     337   [ +  -  +  -  :           4 :     res.pushKV("flag_name", flag_str);
                   +  - ]
     338   [ +  -  +  -  :           4 :     res.pushKV("flag_state", value);
                   +  - ]
     339                 :             : 
     340         [ +  + ]:           2 :     if (value) {
     341         [ +  - ]:           1 :         pwallet->SetWalletFlag(flag);
     342                 :             :     } else {
     343         [ +  - ]:           1 :         pwallet->UnsetWalletFlag(flag);
     344                 :             :     }
     345                 :             : 
     346   [ +  -  +  +  :           2 :     if (flag && value && WALLET_FLAG_CAVEATS.contains(flag)) {
                   +  - ]
     347   [ +  -  +  -  :           2 :         res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
             +  -  +  - ]
     348                 :             :     }
     349                 :             : 
     350                 :           2 :     return res;
     351                 :          12 : },
     352   [ +  -  +  -  :        4325 :     };
             +  +  -  - ]
     353   [ +  -  +  -  :        9515 : }
          +  -  +  -  +  
             -  -  -  -  
                      - ]
     354                 :             : 
     355                 :        1517 : static RPCMethod createwallet()
     356                 :             : {
     357                 :        1517 :     return RPCMethod{
     358                 :        1517 :         "createwallet",
     359         [ +  - ]:        3034 :         "Creates and loads a new wallet.\n",
     360                 :             :         {
     361   [ +  -  +  - ]:        3034 :             {"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."},
     362   [ +  -  +  -  :        4551 :             {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
                   +  - ]
     363   [ +  -  +  -  :        4551 :             {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys."},
                   +  - ]
     364   [ +  -  +  - ]:        3034 :             {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
     365   [ +  -  +  -  :        4551 :             {"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."},
                   +  - ]
     366   [ +  -  +  -  :        4551 :             {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "If set, must be \"true\""},
                   +  - ]
     367   [ +  -  +  - ]:        3034 :             {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
     368   [ +  -  +  -  :        4551 :             {"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."},
                   +  - ]
     369                 :             :         },
     370         [ +  - ]:        3034 :         RPCResult{
     371   [ +  -  +  - ]:        3034 :             RPCResult::Type::OBJ, "", "",
     372                 :             :             {
     373   [ +  -  +  - ]:        3034 :                 {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."},
     374   [ +  -  +  - ]:        3034 :                 {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to creating and loading the wallet.",
     375                 :             :                 {
     376   [ +  -  +  - ]:        3034 :                     {RPCResult::Type::STR, "", ""},
     377                 :             :                 }},
     378                 :             :             }
     379   [ +  -  +  -  :       13653 :         },
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     380                 :        1517 :         RPCExamples{
     381   [ +  -  +  -  :        3034 :             HelpExampleCli("createwallet", "\"testwallet\"")
                   +  - ]
     382   [ +  -  +  -  :        6068 :             + HelpExampleRpc("createwallet", "\"testwallet\"")
             +  -  +  - ]
     383   [ +  -  +  -  :       10619 :             + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
          +  -  +  -  +  
                +  -  - ]
     384   [ +  -  +  -  :       10619 :             + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"load_on_startup", true}})
          +  -  +  -  +  
                +  -  - ]
     385         [ +  - ]:        1517 :         },
     386                 :        1517 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     387                 :             : {
     388                 :         660 :     WalletContext& context = EnsureWalletContext(request.context);
     389                 :         660 :     uint64_t flags = 0;
     390   [ +  +  +  + ]:         660 :     if (!request.params[1].isNull() && request.params[1].get_bool()) {
     391                 :             :         flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
     392                 :             :     }
     393                 :             : 
     394   [ +  +  +  + ]:         660 :     if (!request.params[2].isNull() && request.params[2].get_bool()) {
     395                 :         175 :         flags |= WALLET_FLAG_BLANK_WALLET;
     396                 :             :     }
     397         [ +  - ]:         660 :     SecureString passphrase;
     398         [ +  - ]:         660 :     passphrase.reserve(100);
     399                 :         660 :     std::vector<bilingual_str> warnings;
     400   [ +  -  +  + ]:         660 :     if (!request.params[3].isNull()) {
     401   [ +  -  +  -  :          17 :         passphrase = std::string_view{request.params[3].get_str()};
             -  +  +  - ]
     402         [ +  + ]:          17 :         if (passphrase.empty()) {
     403                 :             :             // Empty string means unencrypted
     404   [ +  -  +  -  :           8 :             warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
                   +  - ]
     405                 :             :         }
     406                 :             :     }
     407                 :             : 
     408   [ +  -  +  +  :         660 :     if (!request.params[4].isNull() && request.params[4].get_bool()) {
          +  -  +  -  +  
                      + ]
     409                 :           4 :         flags |= WALLET_FLAG_AVOID_REUSE;
     410                 :             :     }
     411                 :         660 :     flags |= WALLET_FLAG_DESCRIPTORS;
     412   [ +  -  +  + ]:         660 :     if (!self.Arg<bool>("descriptors")) {
     413   [ +  -  +  - ]:           4 :         throw JSONRPCError(RPC_WALLET_ERROR, "descriptors argument must be set to \"true\"; it is no longer possible to create a legacy wallet.");
     414                 :             :     }
     415   [ +  -  +  +  :         658 :     if (!request.params[7].isNull() && request.params[7].get_bool()) {
          +  -  +  -  +  
                      + ]
     416                 :             : #ifdef ENABLE_EXTERNAL_SIGNER
     417                 :           6 :         flags |= WALLET_FLAG_EXTERNAL_SIGNER;
     418                 :             : #else
     419                 :             :         throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
     420                 :             : #endif
     421                 :             :     }
     422                 :             : 
     423         [ +  - ]:         658 :     DatabaseOptions options;
     424                 :         658 :     DatabaseStatus status;
     425         [ +  - ]:         658 :     ReadDatabaseArgs(*context.args, options);
     426                 :         658 :     options.require_create = true;
     427                 :         658 :     options.create_flags = flags;
     428         [ +  - ]:         658 :     options.create_passphrase = passphrase;
     429         [ +  - ]:         658 :     bilingual_str error;
     430   [ +  -  +  +  :         658 :     std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
             +  -  +  - ]
     431   [ +  -  +  -  :         658 :     const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
                   +  + ]
     432         [ +  + ]:         655 :     HandleWalletError(wallet, status, error);
     433                 :             : 
     434                 :         620 :     UniValue obj(UniValue::VOBJ);
     435   [ +  -  +  -  :        1240 :     obj.pushKV("name", wallet->GetName());
                   +  - ]
     436         [ +  - ]:         620 :     PushWarnings(warnings, obj);
     437                 :             : 
     438         [ +  - ]:         620 :     return obj;
     439                 :        1353 : },
     440   [ +  -  +  -  :       18204 :     };
             +  +  -  - ]
     441   [ +  -  +  -  :       36408 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          -  -  -  -  -  
                -  -  - ]
     442                 :             : 
     443                 :        1175 : static RPCMethod unloadwallet()
     444                 :             : {
     445                 :        1175 :     return RPCMethod{"unloadwallet",
     446         [ +  - ]:        2350 :                 "Unloads the wallet referenced by the request endpoint or the wallet_name argument.\n"
     447                 :             :                 "If both are specified, they must be identical.",
     448                 :             :                 {
     449   [ +  -  +  -  :        3525 :                     {"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."},
                   +  - ]
     450   [ +  -  +  - ]:        2350 :                     {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
     451                 :             :                 },
     452   [ +  -  +  -  :        4700 :                 RPCResult{RPCResult::Type::OBJ, "", "", {
                   +  - ]
     453   [ +  -  +  - ]:        2350 :                     {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to unloading the wallet.",
     454                 :             :                     {
     455   [ +  -  +  - ]:        2350 :                         {RPCResult::Type::STR, "", ""},
     456                 :             :                     }},
     457   [ +  -  +  -  :        8225 :                 }},
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     458                 :        1175 :                 RPCExamples{
     459   [ +  -  +  -  :        2350 :                     HelpExampleCli("unloadwallet", "wallet_name")
                   +  - ]
     460   [ +  -  +  -  :        4700 :             + HelpExampleRpc("unloadwallet", "wallet_name")
             +  -  +  - ]
     461         [ +  - ]:        1175 :                 },
     462                 :        1175 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     463                 :             : {
     464                 :         318 :     const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
     465                 :             : 
     466         [ +  - ]:         314 :     WalletContext& context = EnsureWalletContext(request.context);
     467         [ +  - ]:         314 :     std::shared_ptr<CWallet> wallet = GetWallet(context, wallet_name);
     468         [ +  + ]:         314 :     if (!wallet) {
     469   [ +  -  +  - ]:           8 :         throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
     470                 :             :     }
     471                 :             : 
     472                 :         310 :     std::vector<bilingual_str> warnings;
     473                 :         310 :     {
     474                 :         310 :         WalletRescanReserver reserver(*wallet);
     475         [ -  + ]:         310 :         if (!reserver.reserve()) {
     476   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     477                 :             :         }
     478                 :             : 
     479                 :             :         // Release the "main" shared pointer and prevent further notifications.
     480                 :             :         // Note that any attempt to load the same wallet would fail until the wallet
     481                 :             :         // is destroyed (see CheckUniqueFileid).
     482         [ +  - ]:         310 :         std::optional<bool> load_on_start{self.MaybeArg<bool>("load_on_startup")};
     483   [ +  -  -  + ]:         310 :         if (!RemoveWallet(context, wallet, load_on_start, warnings)) {
     484   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
     485                 :             :         }
     486                 :         310 :     }
     487                 :             : 
     488         [ +  - ]:         310 :     WaitForDeleteWallet(std::move(wallet));
     489                 :             : 
     490                 :         310 :     UniValue result(UniValue::VOBJ);
     491         [ +  - ]:         310 :     PushWarnings(warnings, result);
     492                 :             : 
     493                 :         620 :     return result;
     494         [ -  + ]:         314 : },
     495   [ +  -  +  -  :        7050 :     };
             +  +  -  - ]
     496   [ +  -  +  -  :        9400 : }
          +  -  +  -  -  
                      - ]
     497                 :             : 
     498                 :         883 : RPCMethod simulaterawtransaction()
     499                 :             : {
     500                 :         883 :     return RPCMethod{
     501                 :         883 :         "simulaterawtransaction",
     502         [ +  - ]:        1766 :         "Calculate the balance change resulting in the signing and broadcasting of the given transaction(s).\n",
     503                 :             :         {
     504   [ +  -  +  - ]:        1766 :             {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "An array of hex strings of raw transactions.\n",
     505                 :             :                 {
     506   [ +  -  +  - ]:        1766 :                     {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
     507                 :             :                 },
     508                 :             :             },
     509   [ +  -  +  - ]:        1766 :             {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
     510                 :             :                 {
     511   [ +  -  +  -  :        2649 :                     {"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
                   +  - ]
     512                 :             :                 },
     513                 :             :             },
     514                 :             :         },
     515         [ +  - ]:        1766 :         RPCResult{
     516   [ +  -  +  - ]:        1766 :             RPCResult::Type::OBJ, "", "",
     517                 :             :             {
     518   [ +  -  +  - ]:        1766 :                 {RPCResult::Type::STR_AMOUNT, "balance_change", "The wallet balance change (negative means decrease)."},
     519                 :             :             }
     520   [ +  -  +  -  :        3532 :         },
             +  +  -  - ]
     521                 :         883 :         RPCExamples{
     522   [ +  -  +  -  :        1766 :             HelpExampleCli("simulaterawtransaction", "[\"myhex\"]")
                   +  - ]
     523   [ +  -  +  -  :        3532 :             + HelpExampleRpc("simulaterawtransaction", "[\"myhex\"]")
             +  -  +  - ]
     524         [ +  - ]:         883 :         },
     525                 :         883 :     [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     526                 :             : {
     527         [ -  + ]:          26 :     const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
     528         [ -  + ]:          26 :     if (!rpc_wallet) return UniValue::VNULL;
     529         [ +  - ]:          26 :     const CWallet& wallet = *rpc_wallet;
     530                 :             : 
     531         [ +  - ]:          26 :     LOCK(wallet.cs_wallet);
     532                 :             : 
     533   [ +  -  +  - ]:          26 :     const auto& txs = request.params[0].get_array();
     534                 :          26 :     CAmount changes{0};
     535                 :          26 :     std::map<COutPoint, CAmount> new_utxos; // UTXO:s that were made available in transaction array
     536                 :          26 :     std::set<COutPoint> spent;
     537                 :             : 
     538   [ -  +  +  + ]:          54 :     for (size_t i = 0; i < txs.size(); ++i) {
     539         [ +  - ]:          38 :         CMutableTransaction mtx;
     540   [ +  -  +  -  :          38 :         if (!DecodeHexTx(mtx, txs[i].get_str(), /*try_no_witness=*/ true, /*try_witness=*/ true)) {
             +  -  -  + ]
     541   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Transaction hex string decoding failure.");
     542                 :             :         }
     543                 :             : 
     544                 :             :         // Fetch previous transactions (inputs)
     545                 :          38 :         std::map<COutPoint, Coin> coins;
     546         [ +  + ]:          67 :         for (const CTxIn& txin : mtx.vin) {
     547         [ +  - ]:          29 :             coins[txin.prevout]; // Create empty map entry keyed by prevout.
     548                 :             :         }
     549         [ +  - ]:          38 :         wallet.chain().findCoins(coins);
     550                 :             : 
     551                 :             :         // Fetch debit; we are *spending* these; if the transaction is signed and
     552                 :             :         // broadcast, we will lose everything in these
     553         [ +  + ]:          57 :         for (const auto& txin : mtx.vin) {
     554                 :          29 :             const auto& outpoint = txin.prevout;
     555         [ +  + ]:          29 :             if (spent.contains(outpoint)) {
     556   [ +  -  +  - ]:           6 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction(s) are spending the same output more than once");
     557                 :             :             }
     558         [ +  + ]:          26 :             if (new_utxos.contains(outpoint)) {
     559         [ +  - ]:           6 :                 changes -= new_utxos.at(outpoint);
     560                 :           6 :                 new_utxos.erase(outpoint);
     561                 :             :             } else {
     562   [ +  -  +  + ]:          20 :                 if (coins.at(outpoint).IsSpent()) {
     563   [ +  -  +  - ]:          14 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more transaction inputs are missing or have been spent already");
     564                 :             :                 }
     565         [ +  - ]:          13 :                 changes -= wallet.GetDebit(txin);
     566                 :             :             }
     567         [ +  - ]:          19 :             spent.insert(outpoint);
     568                 :             :         }
     569                 :             : 
     570                 :             :         // Iterate over outputs; we are *receiving* these, if the wallet considers
     571                 :             :         // them "mine"; if the transaction is signed and broadcast, we will receive
     572                 :             :         // everything in these
     573                 :             :         // Also populate new_utxos in case these are spent in later transactions
     574                 :             : 
     575         [ +  - ]:          28 :         const auto& hash = mtx.GetHash();
     576   [ -  +  +  + ]:          69 :         for (size_t i = 0; i < mtx.vout.size(); ++i) {
     577         [ +  - ]:          41 :             const auto& txout = mtx.vout[i];
     578         [ +  - ]:          41 :             bool is_mine = wallet.IsMine(txout);
     579   [ +  +  +  - ]:          41 :             changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
     580                 :             :         }
     581                 :          76 :     }
     582                 :             : 
     583                 :          16 :     UniValue result(UniValue::VOBJ);
     584   [ +  -  +  -  :          32 :     result.pushKV("balance_change", ValueFromAmount(changes));
                   +  - ]
     585                 :             : 
     586                 :          16 :     return result;
     587         [ +  - ]:          78 : }
     588   [ +  -  +  -  :       10596 :     };
          +  -  +  -  +  
          +  +  +  +  +  
          -  -  -  -  -  
                      - ]
     589   [ +  -  +  -  :        8830 : }
          +  -  +  -  +  
                -  -  - ]
     590                 :             : 
     591                 :         914 : static RPCMethod migratewallet()
     592                 :             : {
     593                 :         914 :     return RPCMethod{
     594                 :         914 :         "migratewallet",
     595         [ +  - ]:        1828 :         "Migrate the wallet to a descriptor wallet.\n"
     596                 :             :         "A new wallet backup will need to be made.\n"
     597                 :             :         "\nThe migration process will create a backup of the wallet before migrating. This backup\n"
     598                 :             :         "file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
     599                 :             :         "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
     600                 :             :         "\nEncrypted wallets must have the passphrase provided as an argument to this call.\n"
     601                 :             :         "\nThis RPC may take a long time to complete. Increasing the RPC client timeout is recommended.",
     602                 :             :         {
     603   [ +  -  +  -  :        2742 :             {"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."},
                   +  - ]
     604   [ +  -  +  - ]:        1828 :             {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
     605   [ +  -  +  -  :        2742 :             {"load_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "Load the wallet after migration."},
                   +  - ]
     606                 :             :         },
     607         [ +  - ]:        1828 :         RPCResult{
     608   [ +  -  +  - ]:        1828 :             RPCResult::Type::OBJ, "", "",
     609                 :             :             {
     610   [ +  -  +  - ]:        1828 :                 {RPCResult::Type::STR, "wallet_name", "The name of the primary migrated wallet"},
     611   [ +  -  +  - ]:        1828 :                 {RPCResult::Type::STR, "watchonly_name", /*optional=*/true, "The name of the migrated wallet containing the watchonly scripts"},
     612   [ +  -  +  - ]:        1828 :                 {RPCResult::Type::STR, "solvables_name", /*optional=*/true, "The name of the migrated wallet containing solvable but not watched scripts"},
     613   [ +  -  +  - ]:        1828 :                 {RPCResult::Type::STR, "backup_path", "The location of the backup of the original wallet"},
     614                 :             :             }
     615   [ +  -  +  -  :        9140 :         },
             +  +  -  - ]
     616                 :         914 :         RPCExamples{
     617   [ +  -  +  -  :        1828 :             HelpExampleCli("migratewallet", "")
                   +  - ]
     618   [ +  -  +  -  :        3656 :             + HelpExampleRpc("migratewallet", "")
             +  -  +  - ]
     619         [ +  - ]:         914 :         },
     620                 :         914 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     621                 :             :         {
     622                 :          57 :             const std::string wallet_name{EnsureUniqueWalletName(request, self.MaybeArg<std::string_view>("wallet_name"))};
     623                 :             : 
     624         [ +  - ]:          55 :             SecureString wallet_pass;
     625         [ +  - ]:          55 :             wallet_pass.reserve(100);
     626   [ +  -  +  + ]:          55 :             if (!request.params[1].isNull()) {
     627   [ +  -  +  -  :           5 :                 wallet_pass = std::string_view{request.params[1].get_str()};
             -  +  +  - ]
     628                 :             :             }
     629                 :             : 
     630         [ +  - ]:          55 :             const bool loadwallet = self.Arg<bool>("load_wallet");
     631                 :             : 
     632         [ +  - ]:          55 :             WalletContext& context = EnsureWalletContext(request.context);
     633         [ +  - ]:          55 :             util::Result<MigrationResult> res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context, loadwallet);
     634         [ +  + ]:          55 :             if (!res) {
     635   [ +  -  +  - ]:          24 :                 throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
     636                 :             :             }
     637                 :             : 
     638                 :          43 :             UniValue r{UniValue::VOBJ};
     639   [ +  -  +  -  :          86 :             r.pushKV("wallet_name", res->wallet_name);
                   +  - ]
     640         [ +  + ]:          43 :             if (res->watchonly_wallet_name.has_value()) {
     641   [ +  -  +  -  :          24 :                 r.pushKV("watchonly_name", res->watchonly_wallet_name.value());
                   +  - ]
     642                 :             :             }
     643         [ +  + ]:          43 :             if (res->solvables_wallet_name.has_value()) {
     644   [ +  -  +  -  :          12 :                 r.pushKV("solvables_name", res->solvables_wallet_name.value());
                   +  - ]
     645                 :             :             }
     646   [ +  -  +  -  :          86 :             r.pushKV("backup_path", res->backup_path.utf8string());
             +  -  +  - ]
     647                 :             : 
     648                 :          43 :             return r;
     649                 :          67 :         },
     650   [ +  -  +  -  :        6398 :     };
             +  +  -  - ]
     651   [ +  -  +  -  :       12796 : }
          +  -  +  -  +  
          -  +  -  +  -  
             -  -  -  - ]
     652                 :             : 
     653                 :         899 : RPCMethod gethdkeys()
     654                 :             : {
     655                 :         899 :     return RPCMethod{
     656                 :         899 :         "gethdkeys",
     657         [ +  - ]:        1798 :         "List all BIP 32 HD keys in the wallet and which descriptors use them.\n",
     658                 :             :         {
     659   [ +  -  +  - ]:        1798 :             {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
     660   [ +  -  +  -  :        2697 :                 {"active_only", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show the keys for only active descriptors"},
                   +  - ]
     661   [ +  -  +  -  :        2697 :                 {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private keys"}
                   +  - ]
     662                 :             :             }},
     663                 :             :         },
     664   [ +  -  +  -  :        3596 :         RPCResult{RPCResult::Type::ARR, "", "", {
                   +  - ]
     665                 :             :             {
     666   [ +  -  +  - ]:        1798 :                 {RPCResult::Type::OBJ, "", "", {
     667   [ +  -  +  - ]:        1798 :                     {RPCResult::Type::STR, "xpub", "The extended public key"},
     668   [ +  -  +  - ]:        1798 :                     {RPCResult::Type::BOOL, "has_private", "Whether the wallet has the private key for this xpub"},
     669   [ +  -  +  - ]:        1798 :                     {RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},
     670   [ +  -  +  - ]:        1798 :                     {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects that use this HD key",
     671                 :             :                     {
     672   [ +  -  +  - ]:        1798 :                         {RPCResult::Type::OBJ, "", "", {
     673   [ +  -  +  - ]:        1798 :                             {RPCResult::Type::STR, "desc", "Descriptor string public representation"},
     674   [ +  -  +  - ]:        1798 :                             {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
     675                 :             :                         }},
     676                 :             :                     }},
     677                 :             :                 }},
     678                 :             :             }
     679   [ +  -  +  -  :       18879 :         }},
          +  -  +  -  +  
          -  +  +  +  +  
          +  +  +  +  -  
          -  -  -  -  -  
                   -  - ]
     680                 :         899 :         RPCExamples{
     681   [ +  -  +  -  :        1798 :             HelpExampleCli("gethdkeys", "") + HelpExampleRpc("gethdkeys", "")
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     682   [ +  -  +  -  :        8990 :             + HelpExampleCliNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}}) + HelpExampleRpcNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}})
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     683         [ +  - ]:         899 :         },
     684                 :         899 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     685                 :             :         {
     686         [ -  + ]:          42 :             const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     687         [ -  + ]:          42 :             if (!wallet) return UniValue::VNULL;
     688                 :             : 
     689         [ +  - ]:          42 :             LOCK(wallet->cs_wallet);
     690                 :             : 
     691   [ +  -  +  +  :          42 :             UniValue options{request.params[0].isNull() ? UniValue::VOBJ : request.params[0]};
             +  -  +  - ]
     692   [ +  -  +  +  :          92 :             const bool active_only{options.exists("active_only") ? options["active_only"].get_bool() : false};
          +  -  +  -  +  
                      - ]
     693   [ +  -  +  +  :          96 :             const bool priv{options.exists("private") ? options["private"].get_bool() : false};
          +  -  +  -  +  
                      - ]
     694         [ +  + ]:          42 :             if (priv) {
     695         [ +  + ]:          12 :                 EnsureWalletIsUnlocked(*wallet);
     696                 :             :             }
     697                 :             : 
     698         [ +  + ]:          41 :             std::map<CExtPubKey, std::set<std::tuple<std::string, bool, bool>>> wallet_xpubs;
     699                 :          41 :             std::map<CExtPubKey, CExtKey> wallet_xprvs;
     700   [ +  +  +  -  :         120 :             for (const auto& [xpub, spkms] : wallet->GetHDPubKeys(active_only ? HDKeyFilter::Active : HDKeyFilter::All)) {
                   +  + ]
     701         [ +  + ]:         326 :                 for (auto* desc_spkm : spkms) {
     702         [ +  - ]:         280 :                     LOCK(desc_spkm->cs_desc_man);
     703         [ +  - ]:         280 :                     std::string desc_str;
     704         [ +  - ]:         280 :                     bool ok = desc_spkm->GetDescriptorString(desc_str, /*priv=*/false);
     705         [ +  - ]:         280 :                     CHECK_NONFATAL(ok);
     706   [ +  -  +  -  :         280 :                     wallet_xpubs[xpub].emplace(desc_str, wallet->IsActiveScriptPubKeyMan(*desc_spkm), desc_spkm->HasPrivKey(xpub.pubkey.GetID()));
          +  -  +  -  +  
                      - ]
     707   [ +  +  +  -  :         280 :                     if (std::optional<CKey> key = priv ? desc_spkm->GetKey(xpub.pubkey.GetID()) : std::nullopt) {
             +  -  +  + ]
     708   [ +  -  +  - ]:          89 :                         wallet_xprvs[xpub] = CExtKey(xpub, *key);
     709                 :         280 :                     }
     710         [ +  - ]:         560 :                 }
     711                 :           0 :             }
     712                 :             : 
     713                 :          41 :             UniValue response(UniValue::VARR);
     714         [ +  + ]:          87 :             for (const auto& [xpub, descs] : wallet_xpubs) {
     715                 :          46 :                 bool has_xprv = false;
     716                 :          46 :                 UniValue descriptors(UniValue::VARR);
     717         [ +  + ]:         326 :                 for (const auto& [desc, active, has_priv] : descs) {
     718                 :         280 :                     UniValue d(UniValue::VOBJ);
     719   [ +  -  +  -  :         560 :                     d.pushKV("desc", desc);
                   +  - ]
     720   [ +  -  +  -  :         560 :                     d.pushKV("active", active);
                   +  - ]
     721                 :         280 :                     has_xprv |= has_priv;
     722                 :             : 
     723         [ +  - ]:         280 :                     descriptors.push_back(std::move(d));
     724                 :         280 :                 }
     725                 :          46 :                 UniValue xpub_info(UniValue::VOBJ);
     726   [ +  -  +  -  :          92 :                 xpub_info.pushKV("xpub", EncodeExtPubKey(xpub));
             +  -  +  - ]
     727   [ +  -  +  -  :          92 :                 xpub_info.pushKV("has_private", has_xprv);
                   +  - ]
     728   [ +  +  +  + ]:          46 :                 if (priv && has_xprv) {
     729   [ +  -  +  -  :          22 :                     xpub_info.pushKV("xprv", EncodeExtKey(wallet_xprvs.at(xpub)));
          +  -  +  -  +  
                      - ]
     730                 :             :                 }
     731   [ +  -  +  - ]:          92 :                 xpub_info.pushKV("descriptors", std::move(descriptors));
     732                 :             : 
     733         [ +  - ]:          46 :                 response.push_back(std::move(xpub_info));
     734                 :          46 :             }
     735                 :             : 
     736                 :          41 :             return response;
     737         [ +  - ]:         125 :         },
     738   [ +  -  +  -  :        8091 :     };
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     739   [ +  -  +  -  :       21576 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  -  -  -  -  
          -  -  -  -  -  
                      - ]
     740                 :             : 
     741                 :         871 : static RPCMethod createwalletdescriptor()
     742                 :             : {
     743                 :         871 :     return RPCMethod{"createwalletdescriptor",
     744                 :             :         "Creates the wallet's descriptor for the given address type. "
     745                 :             :         "The address type must be one that the wallet does not already have a descriptor for."
     746         [ +  - ]:        1742 :         + HELP_REQUIRING_PASSPHRASE,
     747                 :             :         {
     748   [ +  -  +  -  :        1742 :             {"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are " + FormatAllOutputTypes() + "."},
                   +  - ]
     749   [ +  -  +  - ]:        1742 :             {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
     750   [ +  -  +  -  :        2613 :                 {"internal", RPCArg::Type::BOOL, RPCArg::DefaultHint{"Both external and internal will be generated unless this parameter is specified"}, "Whether to only make one descriptor that is internal (if parameter is true) or external (if parameter is false)"},
                   +  - ]
     751   [ +  -  +  -  :        2613 :                 {"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"The HD key used by all other active descriptors"}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for this descriptor's key"},
                   +  - ]
     752                 :             :             }},
     753                 :             :         },
     754         [ +  - ]:        1742 :         RPCResult{
     755   [ +  -  +  - ]:        1742 :             RPCResult::Type::OBJ, "", "",
     756                 :             :             {
     757   [ +  -  +  - ]:        1742 :                 {RPCResult::Type::ARR, "descs", "The public descriptors that were added to the wallet",
     758   [ +  -  +  - ]:        1742 :                     {{RPCResult::Type::STR, "", ""}}
     759                 :             :                 }
     760                 :             :             },
     761   [ +  -  +  -  :        6097 :         },
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     762                 :         871 :         RPCExamples{
     763   [ +  -  +  -  :        1742 :             HelpExampleCli("createwalletdescriptor", "bech32m")
                   +  - ]
     764   [ +  -  +  -  :        3484 :             + HelpExampleRpc("createwalletdescriptor", "bech32m")
             +  -  +  - ]
     765         [ +  - ]:         871 :         },
     766                 :         871 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     767                 :             :         {
     768                 :          14 :             std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     769         [ -  + ]:          14 :             if (!pwallet) return UniValue::VNULL;
     770                 :             : 
     771   [ +  -  +  -  :          14 :             std::optional<OutputType> output_type = ParseOutputType(request.params[0].get_str());
             -  +  +  - ]
     772         [ +  + ]:          14 :             if (!output_type) {
     773   [ +  -  +  -  :           2 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
             +  -  +  - ]
     774                 :             :             }
     775                 :             : 
     776   [ +  -  +  +  :          13 :             UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
             +  -  +  - ]
     777   [ +  -  +  -  :          13 :             UniValue internal_only{options["internal"]};
                   +  - ]
     778   [ +  -  +  -  :          13 :             UniValue hdkey{options["hdkey"]};
                   +  - ]
     779                 :             : 
     780                 :          13 :             std::vector<bool> internals;
     781         [ +  + ]:          13 :             if (internal_only.isNull()) {
     782         [ +  - ]:          11 :                 internals.push_back(false);
     783         [ +  - ]:          11 :                 internals.push_back(true);
     784                 :             :             } else {
     785   [ +  -  +  - ]:           2 :                 internals.push_back(internal_only.get_bool());
     786                 :             :             }
     787                 :             : 
     788         [ +  - ]:          13 :             LOCK(pwallet->cs_wallet);
     789         [ +  + ]:          13 :             EnsureWalletIsUnlocked(*pwallet);
     790                 :             : 
     791         [ +  + ]:          12 :             CExtPubKey xpub;
     792         [ +  + ]:          12 :             if (hdkey.isNull()) {
     793         [ +  - ]:           7 :                 HDPubKeyMap active_xpubs = pwallet->GetHDPubKeys(HDKeyFilter::Active);
     794         [ +  + ]:           7 :                 if (active_xpubs.size() != 1) {
     795   [ +  -  +  - ]:           4 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
     796                 :             :                 }
     797                 :           5 :                 xpub = active_xpubs.begin()->first;
     798                 :           7 :             } else {
     799   [ +  -  +  - ]:           5 :                 xpub = DecodeExtPubKey(hdkey.get_str());
     800         [ +  + ]:           5 :                 if (!xpub.pubkey.IsValid()) {
     801   [ +  -  +  - ]:           2 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
     802                 :             :                 }
     803                 :             :             }
     804                 :             : 
     805   [ +  -  +  - ]:           9 :             std::optional<CKey> key = pwallet->GetKey(xpub.pubkey.GetID());
     806         [ +  + ]:           9 :             if (!key) {
     807   [ +  -  +  -  :           2 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
                   +  - ]
     808                 :             :             }
     809         [ +  - ]:           8 :             CExtKey active_hdkey(xpub, *key);
     810                 :             : 
     811                 :           8 :             std::vector<std::reference_wrapper<DescriptorScriptPubKeyMan>> spkms;
     812         [ +  - ]:           8 :             WalletBatch batch{pwallet->GetDatabase()};
     813   [ +  -  -  +  :          44 :             for (bool internal : internals) {
                   -  + ]
     814         [ +  - ]:          14 :                 WalletDescriptor w_desc = GenerateWalletDescriptor(xpub, *output_type, internal);
     815         [ +  - ]:          14 :                 uint256 w_id = DescriptorID(*w_desc.descriptor);
     816   [ +  -  +  + ]:          14 :                 if (!pwallet->GetScriptPubKeyMan(w_id)) {
     817   [ +  -  +  - ]:          12 :                     spkms.emplace_back(pwallet->SetupDescriptorScriptPubKeyMan(batch, active_hdkey, *output_type, internal));
     818                 :             :                 }
     819                 :          14 :             }
     820         [ +  + ]:           8 :             if (spkms.empty()) {
     821   [ +  -  +  - ]:           2 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor already exists");
     822                 :             :             }
     823                 :             : 
     824                 :             :             // Fetch each descspkm from the wallet in order to get the descriptor strings
     825                 :           7 :             UniValue descs{UniValue::VARR};
     826         [ +  + ]:          19 :             for (const auto& spkm : spkms) {
     827         [ +  - ]:          12 :                 std::string desc_str;
     828         [ +  - ]:          12 :                 bool ok = spkm.get().GetDescriptorString(desc_str, false);
     829         [ +  - ]:          12 :                 CHECK_NONFATAL(ok);
     830   [ +  -  +  - ]:          12 :                 descs.push_back(desc_str);
     831                 :          12 :             }
     832                 :           7 :             UniValue out{UniValue::VOBJ};
     833   [ +  -  +  - ]:          14 :             out.pushKV("descs", std::move(descs));
     834                 :           7 :             return out;
     835         [ +  - ]:          61 :         }
     836   [ +  -  +  -  :        8710 :     };
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     837   [ +  -  +  -  :       10452 : }
          +  -  +  -  +  
          -  +  -  -  -  
                   -  - ]
     838                 :             : 
     839                 :         867 : RPCMethod addhdkey()
     840                 :             : {
     841                 :         867 :     return RPCMethod{
     842                 :         867 :         "addhdkey",
     843         [ +  - ]:        1734 :         "Add a BIP 32 HD key to the wallet that can be used with 'createwalletdescriptor'\n",
     844                 :             :         {
     845   [ +  -  +  -  :        2601 :             {"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"Automatically generated new key"}, "The BIP 32 extended private key to add. If none is provided, a randomly generated one will be added."},
                   +  - ]
     846                 :             :         },
     847         [ +  - ]:        1734 :         RPCResult{
     848   [ +  -  +  - ]:        1734 :             RPCResult::Type::OBJ, "", "",
     849                 :             :             {
     850   [ +  -  +  - ]:        1734 :                 {RPCResult::Type::STR, "xpub", "The xpub of the HD key that was added to the wallet"}
     851                 :             :             },
     852   [ +  -  +  -  :        3468 :         },
             +  +  -  - ]
     853                 :         867 :         RPCExamples{
     854   [ +  -  +  -  :        1734 :             HelpExampleCli("addhdkey", "xprv") + HelpExampleRpc("addhdkey", "xprv")
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     855         [ +  - ]:         867 :         },
     856                 :         867 :         [&](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     857                 :             :         {
     858                 :          10 :             std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
     859         [ -  + ]:          10 :             if (!wallet) return UniValue::VNULL;
     860                 :             : 
     861   [ +  -  +  + ]:          10 :             if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     862   [ +  -  +  - ]:           2 :                 throw JSONRPCError(RPC_WALLET_ERROR, "addhdkey is not available for wallets without private keys");
     863                 :             :             }
     864                 :             : 
     865         [ +  - ]:           9 :             EnsureWalletIsUnlocked(*wallet);
     866                 :             : 
     867         [ +  - ]:           9 :             CExtKey hdkey;
     868   [ +  -  +  + ]:           9 :             if (request.params[0].isNull()) {
     869                 :           6 :                 CKey seed_key = GenerateRandomKey();
     870   [ +  -  +  - ]:          12 :                 hdkey.SetSeed(seed_key);
     871                 :           6 :             } else {
     872   [ +  -  +  -  :           3 :                 hdkey = DecodeExtKey(request.params[0].get_str());
                   +  - ]
     873         [ +  + ]:           3 :                 if (!hdkey.key.IsValid()) {
     874                 :             :                     // Check if the user gave us an xpub and give a more descriptive error if so
     875   [ +  -  +  -  :           1 :                     CExtPubKey xpub = DecodeExtPubKey(request.params[0].get_str());
                   +  - ]
     876         [ +  - ]:           1 :                     if (xpub.pubkey.IsValid()) {
     877   [ +  -  +  - ]:           2 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Extended public key (xpub) provided, but extended private key (xprv) is required");
     878                 :             :                     } else {
     879   [ #  #  #  # ]:           0 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Could not parse HD key");
     880                 :             :                     }
     881                 :           1 :                 }
     882                 :             :             }
     883                 :             : 
     884         [ +  - ]:           8 :             LOCK(wallet->cs_wallet);
     885   [ +  -  +  - ]:          16 :             std::string desc_str = "unused(" + EncodeExtKey(hdkey) + ")";
     886                 :           8 :             FlatSigningProvider keys;
     887         [ -  + ]:           8 :             std::string error;
     888   [ -  +  +  - ]:           8 :             std::vector<std::unique_ptr<Descriptor>> descs = Parse(desc_str, keys, error, false);
     889         [ +  - ]:           8 :             CHECK_NONFATAL(!descs.empty());
     890   [ +  -  +  -  :           8 :             WalletDescriptor w_desc(std::move(descs.at(0)), GetTime(), 0, 0, 0);
             +  -  +  - ]
     891   [ +  -  +  + ]:           8 :             if (wallet->GetDescriptorScriptPubKeyMan(w_desc) != nullptr) {
     892   [ +  -  +  - ]:           2 :                 throw JSONRPCError(RPC_WALLET_ERROR, "HD key already exists");
     893                 :             :             }
     894                 :             : 
     895   [ +  -  +  - ]:           7 :             auto spkm = wallet->AddWalletDescriptor(w_desc, keys, /*label=*/"", /*internal=*/false);
     896         [ -  + ]:           7 :             if (!spkm) {
     897   [ #  #  #  # ]:           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(spkm).original);
     898                 :             :             }
     899                 :             : 
     900                 :           7 :             UniValue response(UniValue::VOBJ);
     901         [ +  - ]:           7 :             const DescriptorScriptPubKeyMan& desc_spkm = spkm->get();
     902         [ +  - ]:           7 :             LOCK(desc_spkm.cs_desc_man);
     903         [ +  - ]:           7 :             std::set<CPubKey> pubkeys;
     904                 :           7 :             std::set<CExtPubKey> extpubs;
     905   [ +  -  +  - ]:           7 :             desc_spkm.GetWalletDescriptor().descriptor->GetPubKeys(pubkeys, extpubs);
     906         [ +  - ]:           7 :             CHECK_NONFATAL(pubkeys.size() == 0);
     907         [ +  - ]:           7 :             CHECK_NONFATAL(extpubs.size() == 1);
     908   [ +  -  +  -  :          14 :             response.pushKV("xpub", EncodeExtPubKey(*extpubs.begin()));
             +  -  +  - ]
     909                 :             : 
     910                 :           7 :             return response;
     911   [ +  -  +  - ]:          34 :         },
     912   [ +  -  +  -  :        4335 :     };
             +  +  -  - ]
     913   [ +  -  +  - ]:        3468 : }
     914                 :             : 
     915                 :         869 : static RPCMethod exportwatchonlywallet()
     916                 :             : {
     917                 :         869 :     return RPCMethod{"exportwatchonlywallet",
     918         [ +  - ]:        1738 :         "Creates a wallet file at the specified destination containing a watchonly version "
     919                 :             :         "of the current wallet. This watchonly wallet contains the wallet's public descriptors, "
     920                 :             :         "its transactions, and address book data. Descriptors that use hardened derivation will "
     921                 :             :         "only have a limited number of derived keys included in the export due to hardened "
     922                 :             :         "derivation requiring private keys. Descriptors with unhardened derivation do not have "
     923                 :             :         "this limitation. The watchonly wallet can be imported into another node using 'restorewallet'.",
     924                 :             :         {
     925   [ +  -  +  - ]:        1738 :             {"destination", RPCArg::Type::STR, RPCArg::Optional::NO, "The path to the filename the exported watchonly wallet will be saved to"},
     926                 :             :         },
     927         [ +  - ]:        1738 :         RPCResult{
     928   [ +  -  +  - ]:        1738 :             RPCResult::Type::OBJ, "", "",
     929                 :             :             {
     930   [ +  -  +  - ]:        1738 :                 {RPCResult::Type::STR, "exported_file", "The full path that the file has been exported to"},
     931                 :             :             },
     932   [ +  -  +  -  :        3476 :         },
             +  +  -  - ]
     933                 :         869 :         RPCExamples{
     934   [ +  -  +  -  :        1738 :             HelpExampleCli("exportwatchonlywallet", "\"/path/to/export.dat\"")
                   +  - ]
     935   [ +  -  +  -  :        3476 :             + HelpExampleRpc("exportwatchonlywallet", "\"/path/to/export.dat\"")
             +  -  +  - ]
     936         [ +  - ]:         869 :         },
     937                 :         869 :         [&](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     938                 :             :         {
     939                 :          12 :             std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     940         [ -  + ]:          12 :             if (!pwallet) return UniValue::VNULL;
     941         [ +  - ]:          12 :             WalletContext& context = EnsureWalletContext(request.context);
     942                 :             : 
     943   [ +  -  +  -  :          12 :             std::string dest = request.params[0].get_str();
                   -  + ]
     944                 :             : 
     945         [ +  - ]:          12 :             LOCK(pwallet->cs_wallet);
     946         [ +  - ]:          12 :             pwallet->TopUpKeyPool();
     947   [ +  -  +  - ]:          12 :             util::Result<std::string> exported = ExportWatchOnlyWallet(*pwallet, fs::PathFromString(dest), context);
     948         [ +  + ]:          12 :             if (!exported) {
     949   [ +  -  +  - ]:          10 :                 throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(exported).original);
     950                 :             :             }
     951                 :           7 :             UniValue out{UniValue::VOBJ};
     952   [ +  -  +  -  :          14 :             out.pushKV("exported_file", *exported);
                   +  - ]
     953                 :           7 :             return out;
     954         [ +  - ]:          36 :         }
     955   [ +  -  +  -  :        4345 :     };
             +  +  -  - ]
     956   [ +  -  +  - ]:        3476 : }
     957                 :             : 
     958                 :         888 : RPCMethod derivehdkey()
     959                 :             : {
     960                 :         888 :     return RPCMethod{
     961                 :         888 :         "derivehdkey",
     962                 :             :         "Derive extended public or private key from HD key in the wallet at a given path.\n"
     963                 :             :         "Derivation uses wallet private key material.\n"
     964         [ +  - ]:        1776 :         + HELP_REQUIRING_PASSPHRASE,
     965                 :             :         {
     966   [ +  -  +  - ]:        1776 :             {"path", RPCArg::Type::STR, RPCArg::Optional::NO, "BIP 32 derivation path with at least one hardened step."},
     967   [ +  -  +  - ]:        1776 :             {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
     968   [ +  -  +  -  :        2664 :                 {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private key"},
                   +  - ]
     969   [ +  -  +  -  :        2664 :                 {"hdkey", RPCArg::Type::STR, RPCArg::DefaultHint{"Either the HD key of an unused(KEY) descriptor, or any other active descriptor."}, "The HD key that the wallet knows the private key of, listed using 'gethdkeys', to use for derivation"},
                   +  - ]
     970                 :             :             }},
     971                 :             :         },
     972         [ +  - ]:        1776 :         RPCResult{
     973   [ +  -  +  - ]:        1776 :             RPCResult::Type::OBJ, "", "", {
     974   [ +  -  +  - ]:        1776 :                 {RPCResult::Type::STR, "origin", "Fingerprint and path for use in descriptors"},
     975   [ +  -  +  - ]:        1776 :                 {RPCResult::Type::STR, "xpub", "The extended public key"},
     976   [ +  -  +  - ]:        1776 :                 {RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},
     977                 :             :             },
     978   [ +  -  +  -  :        7104 :         },
             +  +  -  - ]
     979                 :         888 :         RPCExamples{
     980   [ +  -  +  -  :        1776 :             HelpExampleCli("derivehdkey", "m/87h/0h/0h") + HelpExampleRpc("derivehdkey", "\"m/87h/0h/0h\"")
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     981   [ +  -  +  -  :        5328 :             + HelpExampleCliNamed("derivehdkey", {{"path", "m/87h/0h/0h"}, {"private", "true"}})
          +  -  +  -  +  
                +  -  - ]
     982   [ +  -  +  -  :        5328 :             + HelpExampleRpcNamed("derivehdkey", {{"path", "m/87h/0h/0h"}, {"private", "true"}})
          +  -  +  -  +  
                +  -  - ]
     983         [ +  - ]:         888 :         },
     984                 :         888 :         [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
     985                 :             :         {
     986         [ -  + ]:          31 :             const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     987         [ -  + ]:          31 :             if (!wallet) return UniValue::VNULL;
     988                 :             : 
     989   [ +  -  +  + ]:          31 :             if (wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     990                 :             :                 // Watch-only wallets can't contain unused(KEY) descriptors
     991   [ +  -  +  - ]:           2 :                 throw JSONRPCError(RPC_WALLET_ERROR, "derivehdkey is not available for watch-only wallets");
     992                 :             :             }
     993                 :             : 
     994   [ +  -  +  -  :          30 :             std::vector<uint32_t> path = ParsePathBIP32(request.params[0].get_str());
                   +  + ]
     995   [ +  -  +  +  :          27 :             UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
             +  -  +  - ]
     996   [ +  -  +  +  :          57 :             const bool priv{options.exists("private") ? options["private"].get_bool() : false};
          +  -  +  -  +  
                      - ]
     997   [ +  -  +  -  :          27 :             UniValue hdkey{options["hdkey"]};
                   +  - ]
     998   [ -  +  +  -  :          27 :             if (!HasHardenedDerivation(path)) {
                   +  + ]
     999   [ +  -  +  - ]:           4 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Derivation path requires at least one hardened step");
    1000                 :             :             }
    1001                 :             : 
    1002         [ +  - ]:          25 :             LOCK(wallet->cs_wallet);
    1003                 :             : 
    1004                 :             :             // The RPC requires a hardened derivation step, so always unlock
    1005                 :             :             // the wallet.
    1006         [ +  + ]:          25 :             EnsureWalletIsUnlocked(*wallet);
    1007                 :             : 
    1008         [ +  + ]:          23 :             CExtPubKey xpub;
    1009         [ +  + ]:          23 :             if (!hdkey.isNull()) {
    1010   [ +  -  +  - ]:           7 :                 xpub = DecodeExtPubKey(hdkey.get_str());
    1011         [ +  + ]:           7 :                 if (!xpub.pubkey.IsValid()) {
    1012   [ +  -  +  - ]:           2 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
    1013                 :             :                 }
    1014                 :             : 
    1015                 :             :                 // Accept an xpub from an active or unused(KEY) descriptor, but
    1016                 :             :                 // not from a (used) inactive one.
    1017         [ +  - ]:           6 :                 std::set<CExtPubKey> xpub_candidates;
    1018   [ +  -  +  + ]:          11 :                 for (const auto& candidate : wallet->GetHDPubKeys(HDKeyFilter::UnusedKey)) {
    1019         [ +  - ]:           5 :                     xpub_candidates.insert(candidate.first);
    1020                 :           0 :                 }
    1021   [ +  -  +  + ]:           9 :                 for (const auto& candidate : wallet->GetHDPubKeys(HDKeyFilter::Active)) {
    1022         [ +  - ]:           3 :                     xpub_candidates.insert(candidate.first);
    1023                 :           0 :                 }
    1024         [ +  + ]:           6 :                 if (!xpub_candidates.contains(xpub)) {
    1025   [ +  -  +  - ]:           2 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "HD key is not used by an active or unused(KEY) descriptor");
    1026                 :             :                 }
    1027                 :           6 :             }
    1028                 :             : 
    1029                 :             :             // If hdkey was not specified, try to look it up. First consider
    1030                 :             :             // unused(KEY) descriptors. Otherwise look for active descriptors.
    1031         [ +  + ]:          21 :             if (hdkey.isNull()) {
    1032         [ +  - ]:          16 :                 HDPubKeyMap wallet_xpubs{wallet->GetHDPubKeys(HDKeyFilter::UnusedKey)};
    1033                 :             : 
    1034         [ +  + ]:          16 :                 if (wallet_xpubs.size() > 1) {
    1035   [ +  -  +  - ]:           2 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use. Please specify with 'hdkey'");
    1036         [ +  + ]:          15 :                 } else if (wallet_xpubs.size() == 1) {
    1037                 :           9 :                     xpub = wallet_xpubs.begin()->first;
    1038                 :             :                 } else {
    1039         [ +  - ]:           6 :                     HDPubKeyMap active_xpubs = wallet->GetHDPubKeys(HDKeyFilter::Active);
    1040         [ +  + ]:           6 :                     if (active_xpubs.empty()) {
    1041   [ +  -  +  - ]:           4 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No active or unused(KEY) descriptor found");
    1042                 :             :                     }
    1043                 :             : 
    1044         [ -  + ]:           4 :                     if (active_xpubs.size() > 1) {
    1045   [ #  #  #  # ]:           0 :                         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
    1046                 :             :                     }
    1047                 :             : 
    1048                 :           4 :                     xpub = active_xpubs.begin()->first;
    1049                 :           6 :                 }
    1050                 :          16 :             }
    1051                 :             : 
    1052         [ +  - ]:          18 :             std::optional<CExtKey> xprv{wallet->GetExtKey(xpub)};
    1053         [ -  + ]:          18 :             if (!xprv) {
    1054   [ #  #  #  #  :           0 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
                   #  # ]
    1055                 :             :             }
    1056                 :             : 
    1057         [ +  - ]:          18 :             std::optional<std::pair<CExtKey, KeyOriginInfo>> child{DeriveExtKey(*xprv, path)};
    1058         [ +  + ]:          18 :             if (!child) {
    1059   [ +  -  +  - ]:           2 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unable to derive HD key at the requested path");
    1060                 :             :             }
    1061                 :             : 
    1062                 :          17 :             UniValue res{UniValue::VOBJ};
    1063                 :             : 
    1064         [ +  - ]:          17 :             const std::string fingerprint{HexStr(child->second.fingerprint)};
    1065                 :             : 
    1066   [ +  -  +  -  :          34 :             res.pushKV("origin", strprintf("[%s%s]", fingerprint, FormatHDKeypath(child->second.path)));
          +  -  +  -  +  
                      - ]
    1067   [ +  -  +  -  :          34 :             res.pushKV("xpub", EncodeExtPubKey(child->first.Neuter()));
          +  -  +  -  +  
                      - ]
    1068         [ +  + ]:          17 :             if (priv) {
    1069   [ +  -  +  -  :           4 :                 res.pushKV("xprv", EncodeExtKey(child->first));
             +  -  +  - ]
    1070                 :             :             }
    1071                 :          17 :             return res;
    1072   [ +  -  +  - ]:         106 :         },
    1073   [ +  -  +  -  :        8880 :     };
          +  -  +  +  +  
             +  -  -  -  
                      - ]
    1074   [ +  -  +  -  :       14208 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  -  
          -  -  -  -  -  
                -  -  - ]
    1075                 :             : 
    1076                 :             : // addresses
    1077                 :             : RPCMethod getaddressinfo();
    1078                 :             : RPCMethod getnewaddress();
    1079                 :             : RPCMethod getrawchangeaddress();
    1080                 :             : RPCMethod setlabel();
    1081                 :             : RPCMethod listaddressgroupings();
    1082                 :             : RPCMethod keypoolrefill();
    1083                 :             : RPCMethod getaddressesbylabel();
    1084                 :             : RPCMethod listlabels();
    1085                 :             : #ifdef ENABLE_EXTERNAL_SIGNER
    1086                 :             : RPCMethod walletdisplayaddress();
    1087                 :             : #endif // ENABLE_EXTERNAL_SIGNER
    1088                 :             : 
    1089                 :             : // backup
    1090                 :             : RPCMethod importprunedfunds();
    1091                 :             : RPCMethod removeprunedfunds();
    1092                 :             : RPCMethod importdescriptors();
    1093                 :             : RPCMethod listdescriptors();
    1094                 :             : RPCMethod backupwallet();
    1095                 :             : RPCMethod restorewallet();
    1096                 :             : 
    1097                 :             : // coins
    1098                 :             : RPCMethod getreceivedbyaddress();
    1099                 :             : RPCMethod getreceivedbylabel();
    1100                 :             : RPCMethod getbalance();
    1101                 :             : RPCMethod lockunspent();
    1102                 :             : RPCMethod listlockunspent();
    1103                 :             : RPCMethod getbalances();
    1104                 :             : RPCMethod listunspent();
    1105                 :             : 
    1106                 :             : // encryption
    1107                 :             : RPCMethod walletpassphrase();
    1108                 :             : RPCMethod walletpassphrasechange();
    1109                 :             : RPCMethod walletlock();
    1110                 :             : RPCMethod encryptwallet();
    1111                 :             : 
    1112                 :             : // spend
    1113                 :             : RPCMethod sendtoaddress();
    1114                 :             : RPCMethod sendmany();
    1115                 :             : RPCMethod fundrawtransaction();
    1116                 :             : RPCMethod bumpfee();
    1117                 :             : RPCMethod psbtbumpfee();
    1118                 :             : RPCMethod send();
    1119                 :             : RPCMethod sendall();
    1120                 :             : RPCMethod walletprocesspsbt();
    1121                 :             : RPCMethod walletcreatefundedpsbt();
    1122                 :             : RPCMethod signrawtransactionwithwallet();
    1123                 :             : 
    1124                 :             : // signmessage
    1125                 :             : RPCMethod signmessage();
    1126                 :             : 
    1127                 :             : // transactions
    1128                 :             : RPCMethod listreceivedbyaddress();
    1129                 :             : RPCMethod listreceivedbylabel();
    1130                 :             : RPCMethod listtransactions();
    1131                 :             : RPCMethod listsinceblock();
    1132                 :             : RPCMethod gettransaction();
    1133                 :             : RPCMethod abandontransaction();
    1134                 :             : RPCMethod rescanblockchain();
    1135                 :             : RPCMethod abortrescan();
    1136                 :             : 
    1137                 :         437 : std::span<const CRPCCommand> GetWalletRPCCommands()
    1138                 :             : {
    1139                 :         437 :     static const CRPCCommand commands[]{
    1140         [ +  - ]:         852 :         {"rawtransactions", &fundrawtransaction},
    1141         [ +  - ]:         852 :         {"wallet", &abandontransaction},
    1142         [ +  - ]:         852 :         {"wallet", &abortrescan},
    1143         [ +  - ]:         852 :         {"wallet", &addhdkey},
    1144         [ +  - ]:         852 :         {"wallet", &backupwallet},
    1145         [ +  - ]:         852 :         {"wallet", &bumpfee},
    1146         [ +  - ]:         852 :         {"wallet", &psbtbumpfee},
    1147         [ +  - ]:         852 :         {"wallet", &createwallet},
    1148         [ +  - ]:         852 :         {"wallet", &createwalletdescriptor},
    1149         [ +  - ]:         852 :         {"wallet", &derivehdkey},
    1150         [ +  - ]:         852 :         {"wallet", &restorewallet},
    1151         [ +  - ]:         852 :         {"wallet", &encryptwallet},
    1152         [ +  - ]:         852 :         {"wallet", &exportwatchonlywallet},
    1153         [ +  - ]:         852 :         {"wallet", &getaddressesbylabel},
    1154         [ +  - ]:         852 :         {"wallet", &getaddressinfo},
    1155         [ +  - ]:         852 :         {"wallet", &getbalance},
    1156         [ +  - ]:         852 :         {"wallet", &gethdkeys},
    1157         [ +  - ]:         852 :         {"wallet", &getnewaddress},
    1158         [ +  - ]:         852 :         {"wallet", &getrawchangeaddress},
    1159         [ +  - ]:         852 :         {"wallet", &getreceivedbyaddress},
    1160         [ +  - ]:         852 :         {"wallet", &getreceivedbylabel},
    1161         [ +  - ]:         852 :         {"wallet", &gettransaction},
    1162         [ +  - ]:         852 :         {"wallet", &getbalances},
    1163         [ +  - ]:         852 :         {"wallet", &getwalletinfo},
    1164         [ +  - ]:         852 :         {"wallet", &importdescriptors},
    1165         [ +  - ]:         852 :         {"wallet", &importprunedfunds},
    1166         [ +  - ]:         852 :         {"wallet", &keypoolrefill},
    1167         [ +  - ]:         852 :         {"wallet", &listaddressgroupings},
    1168         [ +  - ]:         852 :         {"wallet", &listdescriptors},
    1169         [ +  - ]:         852 :         {"wallet", &listlabels},
    1170         [ +  - ]:         852 :         {"wallet", &listlockunspent},
    1171         [ +  - ]:         852 :         {"wallet", &listreceivedbyaddress},
    1172         [ +  - ]:         852 :         {"wallet", &listreceivedbylabel},
    1173         [ +  - ]:         852 :         {"wallet", &listsinceblock},
    1174         [ +  - ]:         852 :         {"wallet", &listtransactions},
    1175         [ +  - ]:         852 :         {"wallet", &listunspent},
    1176         [ +  - ]:         852 :         {"wallet", &listwalletdir},
    1177         [ +  - ]:         852 :         {"wallet", &listwallets},
    1178         [ +  - ]:         852 :         {"wallet", &loadwallet},
    1179         [ +  - ]:         852 :         {"wallet", &lockunspent},
    1180         [ +  - ]:         852 :         {"wallet", &migratewallet},
    1181         [ +  - ]:         852 :         {"wallet", &removeprunedfunds},
    1182         [ +  - ]:         852 :         {"wallet", &rescanblockchain},
    1183         [ +  - ]:         852 :         {"wallet", &send},
    1184         [ +  - ]:         852 :         {"wallet", &sendmany},
    1185         [ +  - ]:         852 :         {"wallet", &sendtoaddress},
    1186         [ +  - ]:         852 :         {"wallet", &setlabel},
    1187         [ +  - ]:         852 :         {"wallet", &setwalletflag},
    1188         [ +  - ]:         852 :         {"wallet", &signmessage},
    1189         [ +  - ]:         852 :         {"wallet", &signrawtransactionwithwallet},
    1190         [ +  - ]:         852 :         {"wallet", &simulaterawtransaction},
    1191         [ +  - ]:         852 :         {"wallet", &sendall},
    1192         [ +  - ]:         852 :         {"wallet", &unloadwallet},
    1193         [ +  - ]:         852 :         {"wallet", &walletcreatefundedpsbt},
    1194                 :             : #ifdef ENABLE_EXTERNAL_SIGNER
    1195         [ +  - ]:         852 :         {"wallet", &walletdisplayaddress},
    1196                 :             : #endif // ENABLE_EXTERNAL_SIGNER
    1197         [ +  - ]:         852 :         {"wallet", &walletlock},
    1198         [ +  - ]:         852 :         {"wallet", &walletpassphrase},
    1199         [ +  - ]:         852 :         {"wallet", &walletpassphrasechange},
    1200         [ +  - ]:         852 :         {"wallet", &walletprocesspsbt},
    1201   [ +  +  +  -  :       25571 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
             -  +  -  -  
                      - ]
    1202                 :         437 :     return commands;
    1203                 :             : }
    1204                 :             : } // namespace wallet
        

Generated by: LCOV version 2.0-1