LCOV - code coverage report
Current view: top level - src/wallet/rpc - wallet.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 85.3 % 598 510
Test Date: 2025-02-22 05:08:25 Functions: 92.9 % 28 26
Branches: 47.9 % 2936 1407

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2010 Satoshi Nakamoto
       2                 :             : // Copyright (c) 2009-2022 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 <core_io.h>
       9                 :             : #include <key_io.h>
      10                 :             : #include <rpc/server.h>
      11                 :             : #include <rpc/util.h>
      12                 :             : #include <util/translation.h>
      13                 :             : #include <wallet/context.h>
      14                 :             : #include <wallet/receive.h>
      15                 :             : #include <wallet/rpc/wallet.h>
      16                 :             : #include <wallet/rpc/util.h>
      17                 :             : #include <wallet/wallet.h>
      18                 :             : #include <wallet/walletutil.h>
      19                 :             : 
      20                 :             : #include <optional>
      21                 :             : 
      22                 :             : #include <univalue.h>
      23                 :             : 
      24                 :             : 
      25                 :             : namespace wallet {
      26                 :             : 
      27                 :             : static const std::map<uint64_t, std::string> WALLET_FLAG_CAVEATS{
      28                 :             :     {WALLET_FLAG_AVOID_REUSE,
      29                 :             :      "You need to rescan the blockchain in order to correctly mark used "
      30                 :             :      "destinations in the past. Until this is done, some destinations may "
      31                 :             :      "be considered unused, even if the opposite is the case."},
      32                 :             : };
      33                 :             : 
      34                 :             : /** Checks if a CKey is in the given CWallet compressed or otherwise*/
      35                 :           0 : bool HaveKey(const SigningProvider& wallet, const CKey& key)
      36                 :             : {
      37                 :           0 :     CKey key2;
      38   [ #  #  #  #  :           0 :     key2.Set(key.begin(), key.end(), !key.IsCompressed());
                   #  # ]
      39   [ #  #  #  #  :           0 :     return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
      40                 :           0 : }
      41                 :             : 
      42                 :        1368 : static RPCHelpMan getwalletinfo()
      43                 :             : {
      44                 :        1368 :     return RPCHelpMan{"getwalletinfo",
      45                 :             :                 "Returns an object containing various wallet state info.\n",
      46                 :             :                 {},
      47                 :           0 :                 RPCResult{
      48                 :             :                     RPCResult::Type::OBJ, "", "",
      49                 :             :                     {
      50                 :             :                         {
      51                 :             :                         {RPCResult::Type::STR, "walletname", "the wallet name"},
      52                 :             :                         {RPCResult::Type::NUM, "walletversion", "the wallet version"},
      53                 :             :                         {RPCResult::Type::STR, "format", "the database format (bdb or sqlite)"},
      54                 :             :                         {RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
      55                 :             :                         {RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
      56                 :             :                         {RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
      57                 :             :                         {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
      58         [ +  - ]:        2736 :                         {RPCResult::Type::NUM_TIME, "keypoololdest", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only."},
      59                 :             :                         {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
      60                 :             :                         {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)"},
      61         [ +  - ]:        2736 :                         {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)"},
      62         [ +  - ]:        2736 :                         {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kvB"},
      63                 :             :                         {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "the Hash160 of the HD seed (only present when HD is enabled)"},
      64                 :             :                         {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
      65                 :             :                         {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
      66                 :             :                         {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
      67                 :             :                         {
      68                 :             :                             {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
      69                 :             :                             {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
      70                 :             :                         }, /*skip_type_check=*/true},
      71                 :             :                         {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for output script management"},
      72                 :             :                         {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
      73                 :             :                         {RPCResult::Type::BOOL, "blank", "Whether this wallet intentionally does not contain any keys, scripts, or descriptors"},
      74                 :             :                         {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."},
      75                 :             :                         RESULT_LAST_PROCESSED_BLOCK,
      76                 :             :                     }},
      77   [ +  -  +  -  :       45144 :                 },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  +  +  +  -  
                -  -  - ]
      78                 :        1368 :                 RPCExamples{
      79   [ +  -  +  -  :        2736 :                     HelpExampleCli("getwalletinfo", "")
                   +  - ]
      80   [ +  -  +  -  :        5472 :             + HelpExampleRpc("getwalletinfo", "")
             +  -  +  - ]
      81         [ +  - ]:        1368 :                 },
      82                 :         582 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      83                 :             : {
      84         [ -  + ]:         582 :     const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
      85         [ -  + ]:         560 :     if (!pwallet) return UniValue::VNULL;
      86                 :             : 
      87                 :             :     // Make sure the results are valid at least up to the most recent block
      88                 :             :     // the user could have gotten from another RPC command prior to now
      89         [ +  - ]:         560 :     pwallet->BlockUntilSyncedToCurrentChain();
      90                 :             : 
      91         [ +  - ]:         560 :     LOCK(pwallet->cs_wallet);
      92                 :             : 
      93                 :         560 :     UniValue obj(UniValue::VOBJ);
      94                 :             : 
      95         [ +  - ]:         560 :     size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
      96         [ +  - ]:         560 :     const auto bal = GetBalance(*pwallet);
      97   [ +  -  +  -  :        1120 :     obj.pushKV("walletname", pwallet->GetName());
                   +  - ]
      98   [ +  -  +  -  :        1120 :     obj.pushKV("walletversion", pwallet->GetVersion());
             +  -  +  - ]
      99   [ +  -  +  -  :        1120 :     obj.pushKV("format", pwallet->GetDatabase().Format());
             +  -  +  - ]
     100   [ +  -  +  -  :        1120 :     obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
                   +  - ]
     101   [ +  -  +  -  :        1120 :     obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
                   +  - ]
     102   [ +  -  +  -  :        1120 :     obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
                   +  - ]
     103   [ +  -  +  -  :        1120 :     obj.pushKV("txcount",       (int)pwallet->mapWallet.size());
                   +  - ]
     104         [ +  - ]:         560 :     const auto kp_oldest = pwallet->GetOldestKeyPoolTime();
     105         [ -  + ]:         560 :     if (kp_oldest.has_value()) {
     106   [ #  #  #  #  :           0 :         obj.pushKV("keypoololdest", kp_oldest.value());
                   #  # ]
     107                 :             :     }
     108   [ +  -  +  -  :        1120 :     obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
                   +  - ]
     109                 :             : 
     110         [ +  - ]:         560 :     LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
     111         [ -  + ]:         560 :     if (spk_man) {
     112                 :           0 :         CKeyID seed_id = spk_man->GetHDChain().seed_id;
     113         [ #  # ]:           0 :         if (!seed_id.IsNull()) {
     114   [ #  #  #  #  :           0 :             obj.pushKV("hdseedid", seed_id.GetHex());
             #  #  #  # ]
     115                 :             :         }
     116                 :             :     }
     117                 :             : 
     118   [ +  -  +  - ]:         560 :     if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
     119   [ +  -  +  -  :        1120 :         obj.pushKV("keypoolsize_hd_internal",   (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
             +  -  +  - ]
     120                 :             :     }
     121   [ +  -  +  + ]:         560 :     if (pwallet->IsCrypted()) {
     122   [ +  -  +  -  :          48 :         obj.pushKV("unlocked_until", pwallet->nRelockTime);
                   +  - ]
     123                 :             :     }
     124   [ +  -  +  -  :        1120 :     obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
                   +  - ]
     125   [ +  -  +  -  :        1120 :     obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
             +  -  +  - ]
     126   [ +  -  +  -  :        1120 :     obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
             +  -  +  - ]
     127         [ -  + ]:         560 :     if (pwallet->IsScanning()) {
     128                 :           0 :         UniValue scanning(UniValue::VOBJ);
     129   [ #  #  #  #  :           0 :         scanning.pushKV("duration", Ticks<std::chrono::seconds>(pwallet->ScanningDuration()));
                   #  # ]
     130   [ #  #  #  #  :           0 :         scanning.pushKV("progress", pwallet->ScanningProgress());
                   #  # ]
     131   [ #  #  #  # ]:           0 :         obj.pushKV("scanning", std::move(scanning));
     132                 :           0 :     } else {
     133   [ +  -  +  -  :        1120 :         obj.pushKV("scanning", false);
                   +  - ]
     134                 :             :     }
     135   [ +  -  +  -  :        1120 :     obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
             +  -  +  - ]
     136   [ +  -  +  -  :        1120 :     obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
             +  -  +  - ]
     137   [ +  -  +  -  :        1120 :     obj.pushKV("blank", pwallet->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
             +  -  +  - ]
     138         [ +  + ]:         560 :     if (int64_t birthtime = pwallet->GetBirthTime(); birthtime != UNKNOWN_TIME) {
     139   [ +  -  +  -  :        1018 :         obj.pushKV("birthtime", birthtime);
                   +  - ]
     140                 :             :     }
     141                 :             : 
     142         [ +  - ]:         560 :     AppendLastProcessedBlock(obj, *pwallet);
     143                 :         560 :     return obj;
     144         [ +  - ]:        1680 : },
     145   [ +  -  +  -  :        8208 :     };
             +  -  +  - ]
     146   [ +  -  +  -  :       31464 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  -  
                -  -  - ]
     147                 :             : 
     148                 :         796 : static RPCHelpMan listwalletdir()
     149                 :             : {
     150                 :         796 :     return RPCHelpMan{"listwalletdir",
     151                 :             :                 "Returns a list of wallets in the wallet directory.\n",
     152                 :             :                 {},
     153                 :           0 :                 RPCResult{
     154                 :             :                     RPCResult::Type::OBJ, "", "",
     155                 :             :                     {
     156                 :             :                         {RPCResult::Type::ARR, "wallets", "",
     157                 :             :                         {
     158                 :             :                             {RPCResult::Type::OBJ, "", "",
     159                 :             :                             {
     160                 :             :                                 {RPCResult::Type::STR, "name", "The wallet name"},
     161                 :             :                             }},
     162                 :             :                         }},
     163                 :             :                     }
     164   [ +  -  +  -  :        5572 :                 },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  +  
          +  +  +  -  -  
             -  -  -  - ]
     165                 :         796 :                 RPCExamples{
     166   [ +  -  +  -  :        1592 :                     HelpExampleCli("listwalletdir", "")
                   +  - ]
     167   [ +  -  +  -  :        3184 :             + HelpExampleRpc("listwalletdir", "")
             +  -  +  - ]
     168         [ +  - ]:         796 :                 },
     169                 :          10 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     170                 :             : {
     171                 :          10 :     UniValue wallets(UniValue::VARR);
     172   [ +  -  +  -  :          87 :     for (const auto& [path, _] : ListDatabases(GetWalletDir())) {
                   +  + ]
     173                 :          67 :         UniValue wallet(UniValue::VOBJ);
     174   [ +  -  +  -  :         134 :         wallet.pushKV("name", path.utf8string());
             +  -  +  - ]
     175         [ +  - ]:          67 :         wallets.push_back(std::move(wallet));
     176                 :          77 :     }
     177                 :             : 
     178                 :          10 :     UniValue result(UniValue::VOBJ);
     179   [ +  -  +  - ]:          20 :     result.pushKV("wallets", std::move(wallets));
     180                 :          10 :     return result;
     181                 :          10 : },
     182   [ +  -  +  -  :        4776 :     };
             +  -  +  - ]
     183   [ +  -  +  -  :        3184 : }
             +  -  +  - ]
     184                 :             : 
     185                 :         863 : static RPCHelpMan listwallets()
     186                 :             : {
     187                 :         863 :     return RPCHelpMan{"listwallets",
     188                 :             :                 "Returns a list of currently loaded wallets.\n"
     189                 :             :                 "For full information on the wallet, use \"getwalletinfo\"\n",
     190                 :             :                 {},
     191                 :           0 :                 RPCResult{
     192                 :             :                     RPCResult::Type::ARR, "", "",
     193                 :             :                     {
     194                 :             :                         {RPCResult::Type::STR, "walletname", "the wallet name"},
     195                 :             :                     }
     196   [ +  -  +  -  :        2589 :                 },
          +  -  +  -  +  
          -  +  -  +  -  
             +  +  -  - ]
     197                 :         863 :                 RPCExamples{
     198   [ +  -  +  -  :        1726 :                     HelpExampleCli("listwallets", "")
                   +  - ]
     199   [ +  -  +  -  :        3452 :             + HelpExampleRpc("listwallets", "")
             +  -  +  - ]
     200         [ +  - ]:         863 :                 },
     201                 :          77 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     202                 :             : {
     203                 :          77 :     UniValue obj(UniValue::VARR);
     204                 :             : 
     205         [ +  - ]:          77 :     WalletContext& context = EnsureWalletContext(request.context);
     206   [ +  -  +  + ]:         526 :     for (const std::shared_ptr<CWallet>& wallet : GetWallets(context)) {
     207         [ +  - ]:         449 :         LOCK(wallet->cs_wallet);
     208   [ +  -  +  -  :         449 :         obj.push_back(wallet->GetName());
                   +  - ]
     209                 :         526 :     }
     210                 :             : 
     211                 :          77 :     return obj;
     212                 :           0 : },
     213   [ +  -  +  -  :        5178 :     };
             +  -  +  - ]
     214   [ +  -  +  - ]:        1726 : }
     215                 :             : 
     216                 :         941 : static RPCHelpMan loadwallet()
     217                 :             : {
     218                 :         941 :     return RPCHelpMan{"loadwallet",
     219                 :             :                 "\nLoads a wallet from a wallet file or directory."
     220                 :             :                 "\nNote that all wallet command-line options used when starting bitcoind will be"
     221                 :             :                 "\napplied to the new wallet.\n",
     222                 :             :                 {
     223         [ +  - ]:         941 :                     {"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."},
     224         [ +  - ]:         941 :                     {"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."},
     225                 :             :                 },
     226                 :           0 :                 RPCResult{
     227                 :             :                     RPCResult::Type::OBJ, "", "",
     228                 :             :                     {
     229                 :             :                         {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
     230                 :             :                         {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to loading the wallet.",
     231                 :             :                         {
     232                 :             :                             {RPCResult::Type::STR, "", ""},
     233                 :             :                         }},
     234                 :             :                     }
     235   [ +  -  +  -  :        5646 :                 },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     236                 :         941 :                 RPCExamples{
     237                 :             :                     "\nLoad wallet from the wallet dir:\n"
     238   [ +  -  +  -  :        1882 :                     + HelpExampleCli("loadwallet", "\"walletname\"")
             +  -  +  - ]
     239   [ +  -  +  -  :        3764 :                     + HelpExampleRpc("loadwallet", "\"walletname\"")
             +  -  +  - ]
     240                 :         941 :                     + "\nLoad wallet using absolute path (Unix):\n"
     241   [ +  -  +  -  :        3764 :                     + HelpExampleCli("loadwallet", "\"/path/to/walletname/\"")
             +  -  +  - ]
     242   [ +  -  +  -  :        3764 :                     + HelpExampleRpc("loadwallet", "\"/path/to/walletname/\"")
             +  -  +  - ]
     243                 :         941 :                     + "\nLoad wallet using absolute path (Windows):\n"
     244   [ +  -  +  -  :        3764 :                     + HelpExampleCli("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
             +  -  +  - ]
     245   [ +  -  +  -  :        3764 :                     + HelpExampleRpc("loadwallet", "\"DriveLetter:\\path\\to\\walletname\\\"")
             +  -  +  - ]
     246         [ +  - ]:         941 :                 },
     247                 :         155 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     248                 :             : {
     249                 :         155 :     WalletContext& context = EnsureWalletContext(request.context);
     250                 :         155 :     const std::string name(request.params[0].get_str());
     251                 :             : 
     252         [ +  - ]:         155 :     DatabaseOptions options;
     253                 :         155 :     DatabaseStatus status;
     254         [ +  - ]:         155 :     ReadDatabaseArgs(*context.args, options);
     255                 :         155 :     options.require_existing = true;
     256         [ +  - ]:         155 :     bilingual_str error;
     257                 :         155 :     std::vector<bilingual_str> warnings;
     258   [ +  -  +  +  :         155 :     std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
             +  -  +  - ]
     259                 :             : 
     260                 :         155 :     {
     261         [ +  - ]:         155 :         LOCK(context.wallets_mutex);
     262   [ +  +  -  +  :         474 :         if (std::any_of(context.wallets.begin(), context.wallets.end(), [&name](const auto& wallet) { return wallet->GetName() == name; })) {
          -  +  -  +  -  
          +  -  +  -  +  
                   +  + ]
     263   [ +  -  +  - ]:           6 :             throw JSONRPCError(RPC_WALLET_ALREADY_LOADED, "Wallet \"" + name + "\" is already loaded.");
     264                 :             :         }
     265                 :           2 :     }
     266                 :             : 
     267         [ +  - ]:         153 :     std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings);
     268                 :             : 
     269   [ +  +  +  + ]:         306 :     HandleWalletError(wallet, status, error);
     270                 :             : 
     271                 :         134 :     UniValue obj(UniValue::VOBJ);
     272   [ +  -  +  -  :         268 :     obj.pushKV("name", wallet->GetName());
                   +  - ]
     273         [ +  - ]:         134 :     PushWarnings(warnings, obj);
     274                 :             : 
     275         [ +  - ]:         134 :     return obj;
     276                 :         331 : },
     277   [ +  -  +  -  :       11292 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  -  
                      - ]
     278   [ +  -  +  -  :        8469 : }
          +  -  +  -  +  
          -  +  -  +  -  
             -  -  -  - ]
     279                 :             : 
     280                 :         794 : static RPCHelpMan setwalletflag()
     281                 :             : {
     282                 :         794 :             std::string flags;
     283         [ +  + ]:        6352 :             for (auto& it : WALLET_FLAG_MAP)
     284         [ +  + ]:        5558 :                 if (it.second & MUTABLE_WALLET_FLAGS)
     285   [ -  +  +  - ]:        1588 :                     flags += (flags == "" ? "" : ", ") + it.first;
     286                 :             : 
     287         [ +  - ]:         794 :     return RPCHelpMan{"setwalletflag",
     288                 :             :                 "\nChange the state of the given wallet flag for a wallet.\n",
     289                 :             :                 {
     290         [ +  - ]:        1588 :                     {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
     291         [ +  - ]:        1588 :                     {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
     292                 :             :                 },
     293                 :           0 :                 RPCResult{
     294                 :             :                     RPCResult::Type::OBJ, "", "",
     295                 :             :                     {
     296                 :             :                         {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
     297                 :             :                         {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
     298                 :             :                         {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
     299                 :             :                     }
     300   [ +  -  +  -  :        3970 :                 },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  -  
                      - ]
     301                 :         794 :                 RPCExamples{
     302   [ +  -  +  -  :        1588 :                     HelpExampleCli("setwalletflag", "avoid_reuse")
                   +  - ]
     303   [ +  -  +  -  :        3176 :                   + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
             +  -  +  - ]
     304         [ +  - ]:         794 :                 },
     305                 :           8 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     306                 :             : {
     307                 :           8 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     308         [ -  + ]:           8 :     if (!pwallet) return UniValue::VNULL;
     309                 :             : 
     310   [ +  -  +  -  :           8 :     std::string flag_str = request.params[0].get_str();
                   +  - ]
     311   [ +  -  +  +  :           8 :     bool value = request.params[1].isNull() || request.params[1].get_bool();
          +  -  +  -  +  
                      + ]
     312                 :             : 
     313         [ +  + ]:           8 :     if (!WALLET_FLAG_MAP.count(flag_str)) {
     314   [ +  -  +  - ]:           2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
     315                 :             :     }
     316                 :             : 
     317         [ +  - ]:           7 :     auto flag = WALLET_FLAG_MAP.at(flag_str);
     318                 :             : 
     319         [ +  + ]:           7 :     if (!(flag & MUTABLE_WALLET_FLAGS)) {
     320   [ +  -  +  - ]:           6 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
     321                 :             :     }
     322                 :             : 
     323                 :           4 :     UniValue res(UniValue::VOBJ);
     324                 :             : 
     325   [ +  -  +  + ]:           4 :     if (pwallet->IsWalletFlagSet(flag) == value) {
     326   [ +  +  +  -  :           5 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
                   +  - ]
     327                 :             :     }
     328                 :             : 
     329   [ +  -  +  -  :           4 :     res.pushKV("flag_name", flag_str);
                   +  - ]
     330   [ +  -  +  -  :           4 :     res.pushKV("flag_state", value);
                   +  - ]
     331                 :             : 
     332         [ +  + ]:           2 :     if (value) {
     333         [ +  - ]:           1 :         pwallet->SetWalletFlag(flag);
     334                 :             :     } else {
     335         [ +  - ]:           1 :         pwallet->UnsetWalletFlag(flag);
     336                 :             :     }
     337                 :             : 
     338   [ +  -  +  +  :           2 :     if (flag && value && WALLET_FLAG_CAVEATS.count(flag)) {
                   +  - ]
     339   [ +  -  +  -  :           2 :         res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
             +  -  +  - ]
     340                 :             :     }
     341                 :             : 
     342                 :           2 :     return res;
     343                 :          12 : },
     344   [ +  -  +  -  :       11116 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                +  -  - ]
     345   [ +  -  +  -  :        7940 : }
          +  -  +  -  +  
          -  +  -  +  -  
             -  -  -  - ]
     346                 :             : 
     347                 :        1289 : static RPCHelpMan createwallet()
     348                 :             : {
     349                 :        1289 :     return RPCHelpMan{
     350                 :             :         "createwallet",
     351                 :             :         "\nCreates and loads a new wallet.\n",
     352                 :             :         {
     353         [ +  - ]:        1289 :             {"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."},
     354         [ +  - ]:        2578 :             {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
     355         [ +  - ]:        2578 :             {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
     356         [ +  - ]:        1289 :             {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Encrypt the wallet with this passphrase."},
     357         [ +  - ]:        2578 :             {"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."},
     358         [ +  - ]:        2578 :             {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation."
     359                 :             :                                                                        " Setting to \"false\" will create a legacy wallet; This is only possible with the -deprecatedrpc=create_bdb setting because, the legacy wallet type is being deprecated and"
     360                 :             :                                                                        " support for creating and opening legacy wallets will be removed in the future."},
     361         [ +  - ]:        1289 :             {"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."},
     362         [ +  - ]:        2578 :             {"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."},
     363                 :             :         },
     364                 :           0 :         RPCResult{
     365                 :             :             RPCResult::Type::OBJ, "", "",
     366                 :             :             {
     367                 :             :                 {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."},
     368                 :             :                 {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to creating and loading the wallet.",
     369                 :             :                 {
     370                 :             :                     {RPCResult::Type::STR, "", ""},
     371                 :             :                 }},
     372                 :             :             }
     373   [ +  -  +  -  :        7734 :         },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     374                 :        1289 :         RPCExamples{
     375   [ +  -  +  -  :        2578 :             HelpExampleCli("createwallet", "\"testwallet\"")
                   +  - ]
     376   [ +  -  +  -  :        5156 :             + HelpExampleRpc("createwallet", "\"testwallet\"")
             +  -  +  - ]
     377   [ +  -  +  -  :       10312 :             + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
          +  -  +  -  +  
                +  -  - ]
     378   [ +  -  +  -  :       10312 :             + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
          +  -  +  -  +  
                +  -  - ]
     379         [ +  - ]:        1289 :         },
     380                 :         503 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     381                 :             : {
     382                 :         503 :     WalletContext& context = EnsureWalletContext(request.context);
     383                 :         503 :     uint64_t flags = 0;
     384   [ +  +  +  + ]:         503 :     if (!request.params[1].isNull() && request.params[1].get_bool()) {
     385                 :             :         flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
     386                 :             :     }
     387                 :             : 
     388   [ +  +  +  + ]:         503 :     if (!request.params[2].isNull() && request.params[2].get_bool()) {
     389                 :         163 :         flags |= WALLET_FLAG_BLANK_WALLET;
     390                 :             :     }
     391         [ +  - ]:         503 :     SecureString passphrase;
     392         [ +  - ]:         503 :     passphrase.reserve(100);
     393                 :         503 :     std::vector<bilingual_str> warnings;
     394   [ +  -  +  - ]:         503 :     if (!request.params[3].isNull()) {
     395   [ +  -  +  -  :         503 :         passphrase = std::string_view{request.params[3].get_str()};
                   +  - ]
     396         [ +  + ]:         503 :         if (passphrase.empty()) {
     397                 :             :             // Empty string means unencrypted
     398   [ +  -  +  -  :         982 :             warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
                   +  - ]
     399                 :             :         }
     400                 :             :     }
     401                 :             : 
     402   [ +  -  +  +  :         503 :     if (!request.params[4].isNull() && request.params[4].get_bool()) {
          +  -  +  -  +  
                      + ]
     403                 :           3 :         flags |= WALLET_FLAG_AVOID_REUSE;
     404                 :             :     }
     405   [ +  -  +  + ]:         503 :     if (self.Arg<bool>("descriptors")) {
     406                 :             : #ifndef USE_SQLITE
     407                 :             :         throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without sqlite support (required for descriptor wallets)");
     408                 :             : #endif
     409                 :         502 :         flags |= WALLET_FLAG_DESCRIPTORS;
     410                 :             :     } else {
     411   [ +  -  +  -  :           1 :         if (!context.chain->rpcEnableDeprecated("create_bdb")) {
                   -  + ]
     412         [ #  # ]:           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "BDB wallet creation is deprecated and will be removed in a future release."
     413   [ #  #  #  # ]:           0 :                                                  " In this release it can be re-enabled temporarily with the -deprecatedrpc=create_bdb setting.");
     414                 :             :         }
     415                 :             :     }
     416   [ +  -  +  +  :         503 :     if (!request.params[7].isNull() && request.params[7].get_bool()) {
          +  -  +  -  +  
                      + ]
     417                 :             : #ifdef ENABLE_EXTERNAL_SIGNER
     418                 :           5 :         flags |= WALLET_FLAG_EXTERNAL_SIGNER;
     419                 :             : #else
     420                 :             :         throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
     421                 :             : #endif
     422                 :             :     }
     423                 :             : 
     424                 :             : #ifndef USE_BDB
     425         [ +  + ]:         503 :     if (!(flags & WALLET_FLAG_DESCRIPTORS)) {
     426   [ +  -  +  - ]:           2 :         throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without bdb support (required for legacy wallets)");
     427                 :             :     }
     428                 :             : #endif
     429                 :             : 
     430         [ +  - ]:         502 :     DatabaseOptions options;
     431                 :         502 :     DatabaseStatus status;
     432         [ +  - ]:         502 :     ReadDatabaseArgs(*context.args, options);
     433                 :         502 :     options.require_create = true;
     434                 :         502 :     options.create_flags = flags;
     435         [ +  - ]:         502 :     options.create_passphrase = passphrase;
     436         [ +  - ]:         502 :     bilingual_str error;
     437   [ +  -  +  +  :         502 :     std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
             +  -  +  - ]
     438   [ +  -  +  -  :         502 :     const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
                   +  + ]
     439         [ +  + ]:         500 :     if (!wallet) {
     440         [ +  - ]:           9 :         RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
     441         [ +  - ]:           9 :         throw JSONRPCError(code, error.original);
     442                 :             :     }
     443                 :             : 
     444                 :         491 :     UniValue obj(UniValue::VOBJ);
     445   [ +  -  +  -  :         982 :     obj.pushKV("name", wallet->GetName());
                   +  - ]
     446         [ +  - ]:         491 :     PushWarnings(warnings, obj);
     447                 :             : 
     448         [ +  - ]:         491 :     return obj;
     449                 :        1014 : },
     450   [ +  -  +  -  :       51560 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
             -  +  +  -  
                      - ]
     451   [ +  -  +  -  :       29647 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  -  
          -  -  -  -  -  
                      - ]
     452                 :             : 
     453                 :        1067 : static RPCHelpMan unloadwallet()
     454                 :             : {
     455                 :        1067 :     return RPCHelpMan{"unloadwallet",
     456                 :             :                 "Unloads the wallet referenced by the request endpoint, otherwise unloads the wallet specified in the argument.\n"
     457                 :             :                 "Specifying the wallet name on a wallet endpoint is invalid.",
     458                 :             :                 {
     459         [ +  - ]:        2134 :                     {"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."},
     460         [ +  - ]:        1067 :                     {"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."},
     461                 :             :                 },
     462                 :           0 :                 RPCResult{RPCResult::Type::OBJ, "", "", {
     463                 :             :                     {RPCResult::Type::ARR, "warnings", /*optional=*/true, "Warning messages, if any, related to unloading the wallet.",
     464                 :             :                     {
     465                 :             :                         {RPCResult::Type::STR, "", ""},
     466                 :             :                     }},
     467   [ +  -  +  -  :        5335 :                 }},
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  +  +  +  
             -  -  -  - ]
     468                 :        1067 :                 RPCExamples{
     469   [ +  -  +  -  :        2134 :                     HelpExampleCli("unloadwallet", "wallet_name")
                   +  - ]
     470   [ +  -  +  -  :        4268 :             + HelpExampleRpc("unloadwallet", "wallet_name")
             +  -  +  - ]
     471         [ +  - ]:        1067 :                 },
     472                 :         281 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     473                 :             : {
     474         [ +  - ]:         281 :     std::string wallet_name;
     475   [ +  -  +  + ]:         281 :     if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
     476   [ +  -  +  +  :         181 :         if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) {
          +  -  +  -  +  
                      + ]
     477   [ +  -  +  - ]:           4 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets");
     478                 :             :         }
     479                 :             :     } else {
     480   [ +  -  +  +  :         100 :         wallet_name = request.params[0].get_str();
                   +  - ]
     481                 :             :     }
     482                 :             : 
     483         [ +  - ]:         277 :     WalletContext& context = EnsureWalletContext(request.context);
     484         [ +  - ]:         277 :     std::shared_ptr<CWallet> wallet = GetWallet(context, wallet_name);
     485         [ +  + ]:         277 :     if (!wallet) {
     486   [ +  -  +  - ]:           8 :         throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
     487                 :             :     }
     488                 :             : 
     489                 :         273 :     std::vector<bilingual_str> warnings;
     490                 :         273 :     {
     491                 :         273 :         WalletRescanReserver reserver(*wallet);
     492         [ -  + ]:         273 :         if (!reserver.reserve()) {
     493   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
     494                 :             :         }
     495                 :             : 
     496                 :             :         // Release the "main" shared pointer and prevent further notifications.
     497                 :             :         // Note that any attempt to load the same wallet would fail until the wallet
     498                 :             :         // is destroyed (see CheckUniqueFileid).
     499         [ +  - ]:         273 :         std::optional<bool> load_on_start{self.MaybeArg<bool>("load_on_startup")};
     500   [ +  -  -  + ]:         273 :         if (!RemoveWallet(context, wallet, load_on_start, warnings)) {
     501   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
     502                 :             :         }
     503                 :         273 :     }
     504                 :             : 
     505         [ +  - ]:         273 :     WaitForDeleteWallet(std::move(wallet));
     506                 :             : 
     507                 :         273 :     UniValue result(UniValue::VOBJ);
     508         [ +  - ]:         273 :     PushWarnings(warnings, result);
     509                 :             : 
     510                 :         546 :     return result;
     511         [ -  + ]:         277 : },
     512   [ +  -  +  -  :       14938 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  -  
                      - ]
     513   [ +  -  +  -  :        8536 : }
          +  -  +  -  +  
             -  +  -  -  
                      - ]
     514                 :             : 
     515                 :         787 : static RPCHelpMan sethdseed()
     516                 :             : {
     517                 :         787 :     return RPCHelpMan{"sethdseed",
     518                 :             :                 "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
     519                 :             :                 "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
     520         [ +  - ]:        1574 :                 "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." + HELP_REQUIRING_PASSPHRASE +
     521                 :         787 :                 "Note: This command is only compatible with legacy wallets.\n",
     522                 :             :                 {
     523         [ +  - ]:        1574 :                     {"newkeypool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
     524                 :             :                                          "If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
     525                 :             :                                          "If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
     526                 :             :                                          "keypool will be used until it has been depleted."},
     527         [ +  - ]:        1574 :                     {"seed", RPCArg::Type::STR, RPCArg::DefaultHint{"random seed"}, "The WIF private key to use as the new HD seed.\n"
     528                 :             :                                          "The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
     529                 :             :                 },
     530   [ +  -  +  -  :        1574 :                 RPCResult{RPCResult::Type::NONE, "", ""},
                   +  - ]
     531                 :         787 :                 RPCExamples{
     532   [ +  -  +  -  :        1574 :                     HelpExampleCli("sethdseed", "")
                   +  - ]
     533   [ +  -  +  -  :        3148 :             + HelpExampleCli("sethdseed", "false")
             +  -  +  - ]
     534   [ +  -  +  -  :        3148 :             + HelpExampleCli("sethdseed", "true \"wifkey\"")
             +  -  +  - ]
     535   [ +  -  +  -  :        3148 :             + HelpExampleRpc("sethdseed", "true, \"wifkey\"")
             +  -  +  - ]
     536         [ +  - ]:         787 :                 },
     537                 :           1 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     538                 :             : {
     539                 :           1 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     540         [ -  + ]:           1 :     if (!pwallet) return UniValue::VNULL;
     541                 :             : 
     542         [ -  + ]:           1 :     LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
     543                 :             : 
     544   [ #  #  #  # ]:           0 :     if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
     545   [ #  #  #  # ]:           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
     546                 :             :     }
     547                 :             : 
     548   [ #  #  #  # ]:           0 :     LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
     549                 :             : 
     550                 :             :     // Do not do anything to non-HD wallets
     551   [ #  #  #  # ]:           0 :     if (!pwallet->CanSupportFeature(FEATURE_HD)) {
     552   [ #  #  #  # ]:           0 :         throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
     553                 :             :     }
     554                 :             : 
     555         [ #  # ]:           0 :     EnsureWalletIsUnlocked(*pwallet);
     556                 :             : 
     557                 :           0 :     bool flush_key_pool = true;
     558   [ #  #  #  # ]:           0 :     if (!request.params[0].isNull()) {
     559   [ #  #  #  # ]:           0 :         flush_key_pool = request.params[0].get_bool();
     560                 :             :     }
     561                 :             : 
     562         [ #  # ]:           0 :     CPubKey master_pub_key;
     563   [ #  #  #  # ]:           0 :     if (request.params[1].isNull()) {
     564         [ #  # ]:           0 :         master_pub_key = spk_man.GenerateNewSeed();
     565                 :             :     } else {
     566   [ #  #  #  #  :           0 :         CKey key = DecodeSecret(request.params[1].get_str());
                   #  # ]
     567         [ #  # ]:           0 :         if (!key.IsValid()) {
     568   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
     569                 :             :         }
     570                 :             : 
     571   [ #  #  #  # ]:           0 :         if (HaveKey(spk_man, key)) {
     572   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
     573                 :             :         }
     574                 :             : 
     575         [ #  # ]:           0 :         master_pub_key = spk_man.DeriveNewSeed(key);
     576                 :           0 :     }
     577                 :             : 
     578         [ #  # ]:           0 :     spk_man.SetHDSeed(master_pub_key);
     579   [ #  #  #  # ]:           0 :     if (flush_key_pool) spk_man.NewKeyPool();
     580                 :             : 
     581         [ #  # ]:           0 :     return UniValue::VNULL;
     582         [ #  # ]:           0 : },
     583   [ +  -  +  -  :       14166 :     };
          +  -  +  -  +  
          -  +  -  +  -  
             +  +  -  - ]
     584   [ +  -  +  -  :        3935 : }
             +  -  -  - ]
     585                 :             : 
     586                 :         786 : static RPCHelpMan upgradewallet()
     587                 :             : {
     588                 :         786 :     return RPCHelpMan{"upgradewallet",
     589                 :             :         "\nUpgrade the wallet. Upgrades to the latest version if no version number is specified.\n"
     590                 :             :         "New keys may be generated and a new wallet backup will need to be made.",
     591                 :             :         {
     592         [ +  - ]:        1572 :             {"version", RPCArg::Type::NUM, RPCArg::Default{int{FEATURE_LATEST}}, "The version number to upgrade to. Default is the latest wallet version."}
     593                 :             :         },
     594                 :           0 :         RPCResult{
     595                 :             :             RPCResult::Type::OBJ, "", "",
     596                 :             :             {
     597                 :             :                 {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
     598                 :             :                 {RPCResult::Type::NUM, "previous_version", "Version of wallet before this operation"},
     599                 :             :                 {RPCResult::Type::NUM, "current_version", "Version of wallet after this operation"},
     600                 :             :                 {RPCResult::Type::STR, "result", /*optional=*/true, "Description of result, if no error"},
     601                 :             :                 {RPCResult::Type::STR, "error", /*optional=*/true, "Error message (if there is one)"}
     602                 :             :             },
     603   [ +  -  +  -  :        5502 :         },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                +  -  - ]
     604                 :         786 :         RPCExamples{
     605   [ +  -  +  -  :        1572 :             HelpExampleCli("upgradewallet", "169900")
                   +  - ]
     606   [ +  -  +  -  :        3144 :             + HelpExampleRpc("upgradewallet", "169900")
             +  -  +  - ]
     607         [ +  - ]:         786 :         },
     608                 :           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     609                 :             : {
     610                 :           0 :     std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     611         [ #  # ]:           0 :     if (!pwallet) return UniValue::VNULL;
     612                 :             : 
     613         [ #  # ]:           0 :     EnsureWalletIsUnlocked(*pwallet);
     614                 :             : 
     615                 :           0 :     int version = 0;
     616   [ #  #  #  # ]:           0 :     if (!request.params[0].isNull()) {
     617   [ #  #  #  # ]:           0 :         version = request.params[0].getInt<int>();
     618                 :             :     }
     619         [ #  # ]:           0 :     bilingual_str error;
     620         [ #  # ]:           0 :     const int previous_version{pwallet->GetVersion()};
     621         [ #  # ]:           0 :     const bool wallet_upgraded{pwallet->UpgradeWallet(version, error)};
     622         [ #  # ]:           0 :     const int current_version{pwallet->GetVersion()};
     623         [ #  # ]:           0 :     std::string result;
     624                 :             : 
     625         [ #  # ]:           0 :     if (wallet_upgraded) {
     626         [ #  # ]:           0 :         if (previous_version == current_version) {
     627         [ #  # ]:           0 :             result = "Already at latest version. Wallet version unchanged.";
     628                 :             :         } else {
     629         [ #  # ]:           0 :             result = strprintf("Wallet upgraded successfully from version %i to version %i.", previous_version, current_version);
     630                 :             :         }
     631                 :             :     }
     632                 :             : 
     633                 :           0 :     UniValue obj(UniValue::VOBJ);
     634   [ #  #  #  #  :           0 :     obj.pushKV("wallet_name", pwallet->GetName());
                   #  # ]
     635   [ #  #  #  #  :           0 :     obj.pushKV("previous_version", previous_version);
                   #  # ]
     636   [ #  #  #  #  :           0 :     obj.pushKV("current_version", current_version);
                   #  # ]
     637         [ #  # ]:           0 :     if (!result.empty()) {
     638   [ #  #  #  #  :           0 :         obj.pushKV("result", result);
                   #  # ]
     639                 :             :     } else {
     640         [ #  # ]:           0 :         CHECK_NONFATAL(!error.empty());
     641   [ #  #  #  #  :           0 :         obj.pushKV("error", error.original);
                   #  # ]
     642                 :             :     }
     643                 :           0 :     return obj;
     644                 :           0 : },
     645   [ +  -  +  -  :        8646 :     };
          +  -  +  -  +  
          -  +  -  +  +  
                   -  - ]
     646   [ +  -  +  -  :        7074 : }
          +  -  +  -  +  
          -  +  -  +  -  
             +  -  -  - ]
     647                 :             : 
     648                 :         812 : RPCHelpMan simulaterawtransaction()
     649                 :             : {
     650                 :         812 :     return RPCHelpMan{"simulaterawtransaction",
     651                 :             :         "\nCalculate the balance change resulting in the signing and broadcasting of the given transaction(s).\n",
     652                 :             :         {
     653         [ +  - ]:         812 :             {"rawtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "An array of hex strings of raw transactions.\n",
     654                 :             :                 {
     655         [ +  - ]:         812 :                     {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
     656                 :             :                 },
     657                 :             :             },
     658         [ +  - ]:         812 :             {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
     659                 :             :                 {
     660         [ +  - ]:        1624 :                     {"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see RPC importaddress)"},
     661                 :             :                 },
     662                 :             :             },
     663                 :             :         },
     664                 :           0 :         RPCResult{
     665                 :             :             RPCResult::Type::OBJ, "", "",
     666                 :             :             {
     667                 :             :                 {RPCResult::Type::STR_AMOUNT, "balance_change", "The wallet balance change (negative means decrease)."},
     668                 :             :             }
     669   [ +  -  +  -  :        2436 :         },
          +  -  +  -  +  
          -  +  -  +  -  
             +  +  -  - ]
     670                 :         812 :         RPCExamples{
     671   [ +  -  +  -  :        1624 :             HelpExampleCli("simulaterawtransaction", "[\"myhex\"]")
                   +  - ]
     672   [ +  -  +  -  :        3248 :             + HelpExampleRpc("simulaterawtransaction", "[\"myhex\"]")
             +  -  +  - ]
     673         [ +  - ]:         812 :         },
     674                 :          26 :     [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     675                 :             : {
     676         [ -  + ]:          26 :     const std::shared_ptr<const CWallet> rpc_wallet = GetWalletForJSONRPCRequest(request);
     677         [ -  + ]:          26 :     if (!rpc_wallet) return UniValue::VNULL;
     678         [ +  - ]:          26 :     const CWallet& wallet = *rpc_wallet;
     679                 :             : 
     680         [ +  - ]:          26 :     LOCK(wallet.cs_wallet);
     681                 :             : 
     682                 :          26 :     UniValue include_watchonly(UniValue::VNULL);
     683   [ +  -  -  + ]:          26 :     if (request.params[1].isObject()) {
     684   [ #  #  #  # ]:           0 :         UniValue options = request.params[1];
     685   [ #  #  #  #  :           0 :         RPCTypeCheckObj(options,
                   #  # ]
     686                 :             :             {
     687         [ #  # ]:           0 :                 {"include_watchonly", UniValueType(UniValue::VBOOL)},
     688                 :             :             },
     689                 :             :             true, true);
     690                 :             : 
     691   [ #  #  #  #  :           0 :         include_watchonly = options["include_watchonly"];
                   #  # ]
     692                 :           0 :     }
     693                 :             : 
     694                 :          26 :     isminefilter filter = ISMINE_SPENDABLE;
     695   [ +  -  +  + ]:          26 :     if (ParseIncludeWatchonly(include_watchonly, wallet)) {
     696                 :           5 :         filter |= ISMINE_WATCH_ONLY;
     697                 :             :     }
     698                 :             : 
     699   [ +  -  +  - ]:          26 :     const auto& txs = request.params[0].get_array();
     700                 :          26 :     CAmount changes{0};
     701                 :          26 :     std::map<COutPoint, CAmount> new_utxos; // UTXO:s that were made available in transaction array
     702                 :          26 :     std::set<COutPoint> spent;
     703                 :             : 
     704         [ +  + ]:          54 :     for (size_t i = 0; i < txs.size(); ++i) {
     705         [ +  - ]:          38 :         CMutableTransaction mtx;
     706   [ +  -  +  -  :          38 :         if (!DecodeHexTx(mtx, txs[i].get_str(), /* try_no_witness */ true, /* try_witness */ true)) {
             +  -  -  + ]
     707   [ #  #  #  # ]:           0 :             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Transaction hex string decoding failure.");
     708                 :             :         }
     709                 :             : 
     710                 :             :         // Fetch previous transactions (inputs)
     711                 :          38 :         std::map<COutPoint, Coin> coins;
     712         [ +  + ]:          67 :         for (const CTxIn& txin : mtx.vin) {
     713         [ +  - ]:          29 :             coins[txin.prevout]; // Create empty map entry keyed by prevout.
     714                 :             :         }
     715         [ +  - ]:          38 :         wallet.chain().findCoins(coins);
     716                 :             : 
     717                 :             :         // Fetch debit; we are *spending* these; if the transaction is signed and
     718                 :             :         // broadcast, we will lose everything in these
     719         [ +  + ]:          57 :         for (const auto& txin : mtx.vin) {
     720                 :          29 :             const auto& outpoint = txin.prevout;
     721         [ +  + ]:          29 :             if (spent.count(outpoint)) {
     722   [ +  -  +  - ]:           6 :                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Transaction(s) are spending the same output more than once");
     723                 :             :             }
     724         [ +  + ]:          26 :             if (new_utxos.count(outpoint)) {
     725         [ +  - ]:           6 :                 changes -= new_utxos.at(outpoint);
     726                 :           6 :                 new_utxos.erase(outpoint);
     727                 :             :             } else {
     728   [ +  -  +  + ]:          20 :                 if (coins.at(outpoint).IsSpent()) {
     729   [ +  -  +  - ]:          14 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more transaction inputs are missing or have been spent already");
     730                 :             :                 }
     731         [ +  - ]:          13 :                 changes -= wallet.GetDebit(txin, filter);
     732                 :             :             }
     733         [ +  - ]:          19 :             spent.insert(outpoint);
     734                 :             :         }
     735                 :             : 
     736                 :             :         // Iterate over outputs; we are *receiving* these, if the wallet considers
     737                 :             :         // them "mine"; if the transaction is signed and broadcast, we will receive
     738                 :             :         // everything in these
     739                 :             :         // Also populate new_utxos in case these are spent in later transactions
     740                 :             : 
     741         [ +  - ]:          28 :         const auto& hash = mtx.GetHash();
     742         [ +  + ]:          69 :         for (size_t i = 0; i < mtx.vout.size(); ++i) {
     743         [ +  - ]:          41 :             const auto& txout = mtx.vout[i];
     744         [ +  - ]:          41 :             bool is_mine = 0 < (wallet.IsMine(txout) & filter);
     745   [ +  +  +  - ]:          41 :             changes += new_utxos[COutPoint(hash, i)] = is_mine ? txout.nValue : 0;
     746                 :             :         }
     747                 :          76 :     }
     748                 :             : 
     749                 :          16 :     UniValue result(UniValue::VOBJ);
     750   [ +  -  +  -  :          32 :     result.pushKV("balance_change", ValueFromAmount(changes));
                   +  - ]
     751                 :             : 
     752                 :          16 :     return result;
     753   [ -  -  -  -  :          78 : }
                   +  - ]
     754   [ +  -  +  -  :       16240 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          +  +  +  +  +  
          -  -  -  -  -  
                      - ]
     755   [ +  -  +  -  :        8932 : }
          +  -  +  -  +  
          -  +  -  +  -  
                   -  - ]
     756                 :             : 
     757                 :         823 : static RPCHelpMan migratewallet()
     758                 :             : {
     759                 :         823 :     return RPCHelpMan{"migratewallet",
     760                 :             :         "\nMigrate the wallet to a descriptor wallet.\n"
     761                 :             :         "A new wallet backup will need to be made.\n"
     762                 :             :         "\nThe migration process will create a backup of the wallet before migrating. This backup\n"
     763                 :             :         "file will be named <wallet name>-<timestamp>.legacy.bak and can be found in the directory\n"
     764                 :             :         "for this wallet. In the event of an incorrect migration, the backup can be restored using restorewallet."
     765                 :             :         "\nEncrypted wallets must have the passphrase provided as an argument to this call.\n"
     766                 :             :         "\nThis RPC may take a long time to complete. Increasing the RPC client timeout is recommended.",
     767                 :             :         {
     768         [ +  - ]:        1646 :             {"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."},
     769         [ +  - ]:         823 :             {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The wallet passphrase"},
     770                 :             :         },
     771                 :           0 :         RPCResult{
     772                 :             :             RPCResult::Type::OBJ, "", "",
     773                 :             :             {
     774                 :             :                 {RPCResult::Type::STR, "wallet_name", "The name of the primary migrated wallet"},
     775                 :             :                 {RPCResult::Type::STR, "watchonly_name", /*optional=*/true, "The name of the migrated wallet containing the watchonly scripts"},
     776                 :             :                 {RPCResult::Type::STR, "solvables_name", /*optional=*/true, "The name of the migrated wallet containing solvable but not watched scripts"},
     777                 :             :                 {RPCResult::Type::STR, "backup_path", "The location of the backup of the original wallet"},
     778                 :             :             }
     779   [ +  -  +  -  :        4938 :         },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  +  
                   -  - ]
     780                 :         823 :         RPCExamples{
     781   [ +  -  +  -  :        1646 :             HelpExampleCli("migratewallet", "")
                   +  - ]
     782   [ +  -  +  -  :        3292 :             + HelpExampleRpc("migratewallet", "")
             +  -  +  - ]
     783         [ +  - ]:         823 :         },
     784                 :          37 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     785                 :             :         {
     786         [ +  - ]:          37 :             std::string wallet_name;
     787   [ +  -  +  + ]:          37 :             if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
     788   [ +  -  +  +  :           2 :                 if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) {
          +  -  +  -  +  
                      - ]
     789   [ +  -  +  - ]:           2 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets");
     790                 :             :                 }
     791                 :             :             } else {
     792   [ +  -  +  + ]:          35 :                 if (request.params[0].isNull()) {
     793   [ +  -  +  - ]:           2 :                     throw JSONRPCError(RPC_INVALID_PARAMETER, "Either RPC endpoint wallet or wallet_name parameter must be provided");
     794                 :             :                 }
     795   [ +  -  +  -  :          34 :                 wallet_name = request.params[0].get_str();
                   +  - ]
     796                 :             :             }
     797                 :             : 
     798         [ +  - ]:          35 :             SecureString wallet_pass;
     799         [ +  - ]:          35 :             wallet_pass.reserve(100);
     800   [ +  -  +  + ]:          35 :             if (!request.params[1].isNull()) {
     801   [ +  -  +  -  :           3 :                 wallet_pass = std::string_view{request.params[1].get_str()};
                   +  - ]
     802                 :             :             }
     803                 :             : 
     804         [ +  - ]:          35 :             WalletContext& context = EnsureWalletContext(request.context);
     805         [ +  - ]:          35 :             util::Result<MigrationResult> res = MigrateLegacyToDescriptor(wallet_name, wallet_pass, context);
     806         [ +  + ]:          35 :             if (!res) {
     807   [ +  -  +  - ]:          14 :                 throw JSONRPCError(RPC_WALLET_ERROR, util::ErrorString(res).original);
     808                 :             :             }
     809                 :             : 
     810                 :          28 :             UniValue r{UniValue::VOBJ};
     811   [ +  -  +  -  :          56 :             r.pushKV("wallet_name", res->wallet_name);
                   +  - ]
     812         [ +  + ]:          28 :             if (res->watchonly_wallet) {
     813   [ +  -  +  -  :          20 :                 r.pushKV("watchonly_name", res->watchonly_wallet->GetName());
                   +  - ]
     814                 :             :             }
     815         [ +  + ]:          28 :             if (res->solvables_wallet) {
     816   [ +  -  +  -  :          10 :                 r.pushKV("solvables_name", res->solvables_wallet->GetName());
                   +  - ]
     817                 :             :             }
     818   [ +  -  +  -  :          56 :             r.pushKV("backup_path", res->backup_path.utf8string());
             +  -  +  - ]
     819                 :             : 
     820                 :          28 :             return r;
     821                 :          42 :         },
     822   [ +  -  +  -  :       11522 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  -  
                      - ]
     823   [ +  -  +  -  :        8230 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  -  -  
                      - ]
     824                 :             : 
     825                 :         819 : RPCHelpMan gethdkeys()
     826                 :             : {
     827                 :         819 :     return RPCHelpMan{
     828                 :             :         "gethdkeys",
     829                 :             :         "\nList all BIP 32 HD keys in the wallet and which descriptors use them.\n",
     830                 :             :         {
     831         [ +  - ]:         819 :             {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
     832         [ +  - ]:        1638 :                 {"active_only", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show the keys for only active descriptors"},
     833         [ +  - ]:        1638 :                 {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private keys"}
     834                 :             :             }},
     835                 :             :         },
     836                 :           0 :         RPCResult{RPCResult::Type::ARR, "", "", {
     837                 :             :             {
     838                 :             :                 {RPCResult::Type::OBJ, "", "", {
     839                 :             :                     {RPCResult::Type::STR, "xpub", "The extended public key"},
     840                 :             :                     {RPCResult::Type::BOOL, "has_private", "Whether the wallet has the private key for this xpub"},
     841                 :             :                     {RPCResult::Type::STR, "xprv", /*optional=*/true, "The extended private key if \"private\" is true"},
     842                 :             :                     {RPCResult::Type::ARR, "descriptors", "Array of descriptor objects that use this HD key",
     843                 :             :                     {
     844                 :             :                         {RPCResult::Type::OBJ, "", "", {
     845                 :             :                             {RPCResult::Type::STR, "desc", "Descriptor string representation"},
     846                 :             :                             {RPCResult::Type::BOOL, "active", "Whether this descriptor is currently used to generate new addresses"},
     847                 :             :                         }},
     848                 :             :                     }},
     849                 :             :                 }},
     850                 :             :             }
     851   [ +  -  +  -  :       10647 :         }},
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  +  
          +  +  +  +  +  
          -  -  -  -  -  
                -  -  - ]
     852                 :         819 :         RPCExamples{
     853   [ +  -  +  -  :        1638 :             HelpExampleCli("gethdkeys", "") + HelpExampleRpc("gethdkeys", "")
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     854   [ +  -  +  -  :        8190 :             + HelpExampleCliNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}}) + HelpExampleRpcNamed("gethdkeys", {{"active_only", "true"}, {"private", "true"}})
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  +  +  
             +  -  -  -  
                      - ]
     855         [ +  - ]:         819 :         },
     856                 :          33 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     857                 :             :         {
     858         [ -  + ]:          33 :             const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     859         [ -  + ]:          33 :             if (!wallet) return UniValue::VNULL;
     860                 :             : 
     861   [ +  -  -  + ]:          33 :             if (!wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
     862   [ #  #  #  # ]:           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "gethdkeys is not available for non-descriptor wallets");
     863                 :             :             }
     864                 :             : 
     865         [ +  - ]:          33 :             LOCK(wallet->cs_wallet);
     866                 :             : 
     867   [ +  -  +  +  :          33 :             UniValue options{request.params[0].isNull() ? UniValue::VOBJ : request.params[0]};
             +  -  +  - ]
     868   [ +  -  +  +  :          66 :             const bool active_only{options.exists("active_only") ? options["active_only"].get_bool() : false};
          +  -  +  -  +  
                      - ]
     869   [ +  -  +  +  :          66 :             const bool priv{options.exists("private") ? options["private"].get_bool() : false};
          +  -  +  -  +  
                      - ]
     870         [ +  + ]:          33 :             if (priv) {
     871         [ +  + ]:           9 :                 EnsureWalletIsUnlocked(*wallet);
     872                 :             :             }
     873                 :             : 
     874                 :             : 
     875         [ +  + ]:          32 :             std::set<ScriptPubKeyMan*> spkms;
     876         [ +  + ]:          32 :             if (active_only) {
     877         [ +  - ]:          12 :                 spkms = wallet->GetActiveScriptPubKeyMans();
     878                 :             :             } else {
     879         [ +  - ]:          52 :                 spkms = wallet->GetAllScriptPubKeyMans();
     880                 :             :             }
     881                 :             : 
     882                 :          32 :             std::map<CExtPubKey, std::set<std::tuple<std::string, bool, bool>>> wallet_xpubs;
     883                 :          32 :             std::map<CExtPubKey, CExtKey> wallet_xprvs;
     884         [ +  + ]:         264 :             for (auto* spkm : spkms) {
     885         [ +  - ]:         232 :                 auto* desc_spkm{dynamic_cast<DescriptorScriptPubKeyMan*>(spkm)};
     886         [ +  - ]:         232 :                 CHECK_NONFATAL(desc_spkm);
     887         [ +  - ]:         232 :                 LOCK(desc_spkm->cs_desc_man);
     888         [ +  - ]:         232 :                 WalletDescriptor w_desc = desc_spkm->GetWalletDescriptor();
     889                 :             : 
     890                 :             :                 // Retrieve the pubkeys from the descriptor
     891         [ +  - ]:         232 :                 std::set<CPubKey> desc_pubkeys;
     892                 :         232 :                 std::set<CExtPubKey> desc_xpubs;
     893         [ +  - ]:         232 :                 w_desc.descriptor->GetPubKeys(desc_pubkeys, desc_xpubs);
     894         [ +  + ]:         457 :                 for (const CExtPubKey& xpub : desc_xpubs) {
     895         [ +  - ]:         225 :                     std::string desc_str;
     896         [ +  - ]:         225 :                     bool ok = desc_spkm->GetDescriptorString(desc_str, false);
     897         [ +  - ]:         225 :                     CHECK_NONFATAL(ok);
     898   [ +  -  +  -  :         225 :                     wallet_xpubs[xpub].emplace(desc_str, wallet->IsActiveScriptPubKeyMan(*spkm), desc_spkm->HasPrivKey(xpub.pubkey.GetID()));
          +  -  +  -  +  
                      - ]
     899   [ +  +  +  -  :         225 :                     if (std::optional<CKey> key = priv ? desc_spkm->GetKey(xpub.pubkey.GetID()) : std::nullopt) {
             +  -  +  + ]
     900   [ +  -  +  - ]:          64 :                         wallet_xprvs[xpub] = CExtKey(xpub, *key);
     901                 :         225 :                     }
     902                 :         225 :                 }
     903         [ +  - ]:         464 :             }
     904                 :             : 
     905                 :          32 :             UniValue response(UniValue::VARR);
     906         [ +  + ]:          66 :             for (const auto& [xpub, descs] : wallet_xpubs) {
     907                 :          34 :                 bool has_xprv = false;
     908                 :          34 :                 UniValue descriptors(UniValue::VARR);
     909         [ +  + ]:         259 :                 for (const auto& [desc, active, has_priv] : descs) {
     910                 :         225 :                     UniValue d(UniValue::VOBJ);
     911   [ +  -  +  -  :         450 :                     d.pushKV("desc", desc);
                   +  - ]
     912   [ +  -  +  -  :         450 :                     d.pushKV("active", active);
                   +  - ]
     913                 :         225 :                     has_xprv |= has_priv;
     914                 :             : 
     915         [ +  - ]:         225 :                     descriptors.push_back(std::move(d));
     916                 :         225 :                 }
     917                 :          34 :                 UniValue xpub_info(UniValue::VOBJ);
     918   [ +  -  +  -  :          68 :                 xpub_info.pushKV("xpub", EncodeExtPubKey(xpub));
             +  -  +  - ]
     919   [ +  -  +  -  :          68 :                 xpub_info.pushKV("has_private", has_xprv);
                   +  - ]
     920         [ +  + ]:          34 :                 if (priv) {
     921   [ +  -  +  -  :          16 :                     xpub_info.pushKV("xprv", EncodeExtKey(wallet_xprvs.at(xpub)));
          +  -  +  -  +  
                      - ]
     922                 :             :                 }
     923   [ +  -  +  - ]:          68 :                 xpub_info.pushKV("descriptors", std::move(descriptors));
     924                 :             : 
     925         [ +  - ]:          34 :                 response.push_back(std::move(xpub_info));
     926                 :          34 :             }
     927                 :             : 
     928                 :          32 :             return response;
     929         [ +  - ]:          98 :         },
     930   [ +  -  +  -  :       15561 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  +  
          +  +  -  -  -  
                      - ]
     931   [ +  -  +  -  :       14742 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          -  -  -  -  -  
             -  -  -  -  
                      - ]
     932                 :             : 
     933                 :         799 : static RPCHelpMan createwalletdescriptor()
     934                 :             : {
     935                 :         799 :     return RPCHelpMan{"createwalletdescriptor",
     936                 :             :         "Creates the wallet's descriptor for the given address type. "
     937                 :             :         "The address type must be one that the wallet does not already have a descriptor for."
     938         [ +  - ]:        1598 :         + HELP_REQUIRING_PASSPHRASE,
     939                 :             :         {
     940         [ +  - ]:         799 :             {"type", RPCArg::Type::STR, RPCArg::Optional::NO, "The address type the descriptor will produce. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
     941         [ +  - ]:         799 :             {"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "", {
     942         [ +  - ]:        1598 :                 {"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)"},
     943         [ +  - ]:        1598 :                 {"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"},
     944                 :             :             }},
     945                 :             :         },
     946                 :           0 :         RPCResult{
     947                 :             :             RPCResult::Type::OBJ, "", "",
     948                 :             :             {
     949                 :             :                 {RPCResult::Type::ARR, "descs", "The public descriptors that were added to the wallet",
     950                 :             :                     {{RPCResult::Type::STR, "", ""}}
     951                 :             :                 }
     952                 :             :             },
     953   [ +  -  +  -  :        3995 :         },
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  +  +  +  
             -  -  -  - ]
     954                 :         799 :         RPCExamples{
     955   [ +  -  +  -  :        1598 :             HelpExampleCli("createwalletdescriptor", "bech32m")
                   +  - ]
     956   [ +  -  +  -  :        3196 :             + HelpExampleRpc("createwalletdescriptor", "bech32m")
             +  -  +  - ]
     957         [ +  - ]:         799 :         },
     958                 :          13 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     959                 :             :         {
     960                 :          13 :             std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
     961         [ -  + ]:          13 :             if (!pwallet) return UniValue::VNULL;
     962                 :             : 
     963                 :             :             //  Make sure wallet is a descriptor wallet
     964   [ +  -  -  + ]:          13 :             if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
     965   [ #  #  #  # ]:           0 :                 throw JSONRPCError(RPC_WALLET_ERROR, "createwalletdescriptor is not available for non-descriptor wallets");
     966                 :             :             }
     967                 :             : 
     968   [ +  -  +  -  :          13 :             std::optional<OutputType> output_type = ParseOutputType(request.params[0].get_str());
                   +  - ]
     969         [ +  + ]:          13 :             if (!output_type) {
     970   [ +  -  +  -  :           2 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
             +  -  +  - ]
     971                 :             :             }
     972                 :             : 
     973   [ +  -  +  +  :          12 :             UniValue options{request.params[1].isNull() ? UniValue::VOBJ : request.params[1]};
             +  -  +  - ]
     974   [ +  -  +  -  :          12 :             UniValue internal_only{options["internal"]};
                   +  - ]
     975   [ +  -  +  -  :          12 :             UniValue hdkey{options["hdkey"]};
                   +  - ]
     976                 :             : 
     977                 :          12 :             std::vector<bool> internals;
     978         [ +  + ]:          12 :             if (internal_only.isNull()) {
     979         [ +  - ]:          10 :                 internals.push_back(false);
     980         [ +  - ]:          10 :                 internals.push_back(true);
     981                 :             :             } else {
     982   [ +  -  +  - ]:           2 :                 internals.push_back(internal_only.get_bool());
     983                 :             :             }
     984                 :             : 
     985         [ +  - ]:          12 :             LOCK(pwallet->cs_wallet);
     986         [ +  + ]:          12 :             EnsureWalletIsUnlocked(*pwallet);
     987                 :             : 
     988         [ +  + ]:          11 :             CExtPubKey xpub;
     989         [ +  + ]:          11 :             if (hdkey.isNull()) {
     990         [ +  - ]:           7 :                 std::set<CExtPubKey> active_xpubs = pwallet->GetActiveHDPubKeys();
     991         [ +  + ]:           7 :                 if (active_xpubs.size() != 1) {
     992   [ +  -  +  - ]:           4 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use from active descriptors. Please specify with 'hdkey'");
     993                 :             :                 }
     994                 :           5 :                 xpub = *active_xpubs.begin();
     995                 :           7 :             } else {
     996   [ +  -  +  - ]:           4 :                 xpub = DecodeExtPubKey(hdkey.get_str());
     997         [ +  + ]:           4 :                 if (!xpub.pubkey.IsValid()) {
     998   [ +  -  +  - ]:           2 :                     throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to parse HD key. Please provide a valid xpub");
     999                 :             :                 }
    1000                 :             :             }
    1001                 :             : 
    1002   [ +  -  +  - ]:           8 :             std::optional<CKey> key = pwallet->GetKey(xpub.pubkey.GetID());
    1003         [ +  + ]:           8 :             if (!key) {
    1004   [ +  -  +  -  :           2 :                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Private key for %s is not known", EncodeExtPubKey(xpub)));
                   +  - ]
    1005                 :             :             }
    1006         [ +  - ]:           7 :             CExtKey active_hdkey(xpub, *key);
    1007                 :             : 
    1008                 :           7 :             std::vector<std::reference_wrapper<DescriptorScriptPubKeyMan>> spkms;
    1009         [ +  - ]:           7 :             WalletBatch batch{pwallet->GetDatabase()};
    1010   [ +  -  -  + ]:          43 :             for (bool internal : internals) {
    1011         [ +  - ]:          12 :                 WalletDescriptor w_desc = GenerateWalletDescriptor(xpub, *output_type, internal);
    1012         [ +  - ]:          12 :                 uint256 w_id = DescriptorID(*w_desc.descriptor);
    1013   [ +  -  +  + ]:          12 :                 if (!pwallet->GetScriptPubKeyMan(w_id)) {
    1014   [ +  -  +  - ]:          10 :                     spkms.emplace_back(pwallet->SetupDescriptorScriptPubKeyMan(batch, active_hdkey, *output_type, internal));
    1015                 :             :                 }
    1016                 :          12 :             }
    1017         [ +  + ]:           7 :             if (spkms.empty()) {
    1018   [ +  -  +  - ]:           2 :                 throw JSONRPCError(RPC_WALLET_ERROR, "Descriptor already exists");
    1019                 :             :             }
    1020                 :             : 
    1021                 :             :             // Fetch each descspkm from the wallet in order to get the descriptor strings
    1022                 :           6 :             UniValue descs{UniValue::VARR};
    1023         [ +  + ]:          16 :             for (const auto& spkm : spkms) {
    1024         [ +  - ]:          10 :                 std::string desc_str;
    1025         [ +  - ]:          10 :                 bool ok = spkm.get().GetDescriptorString(desc_str, false);
    1026         [ +  - ]:          10 :                 CHECK_NONFATAL(ok);
    1027   [ +  -  +  - ]:          10 :                 descs.push_back(desc_str);
    1028                 :          10 :             }
    1029                 :           6 :             UniValue out{UniValue::VOBJ};
    1030   [ +  -  +  - ]:          12 :             out.pushKV("descs", std::move(descs));
    1031                 :           6 :             return out;
    1032         [ +  - ]:          53 :         }
    1033   [ +  -  +  -  :       19176 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  +  +  +  -  
                -  -  - ]
    1034   [ +  -  +  -  :        9588 : }
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  -  -  
                      - ]
    1035                 :             : 
    1036                 :             : // addresses
    1037                 :             : RPCHelpMan getaddressinfo();
    1038                 :             : RPCHelpMan getnewaddress();
    1039                 :             : RPCHelpMan getrawchangeaddress();
    1040                 :             : RPCHelpMan setlabel();
    1041                 :             : RPCHelpMan listaddressgroupings();
    1042                 :             : RPCHelpMan addmultisigaddress();
    1043                 :             : RPCHelpMan keypoolrefill();
    1044                 :             : RPCHelpMan newkeypool();
    1045                 :             : RPCHelpMan getaddressesbylabel();
    1046                 :             : RPCHelpMan listlabels();
    1047                 :             : #ifdef ENABLE_EXTERNAL_SIGNER
    1048                 :             : RPCHelpMan walletdisplayaddress();
    1049                 :             : #endif // ENABLE_EXTERNAL_SIGNER
    1050                 :             : 
    1051                 :             : // backup
    1052                 :             : RPCHelpMan dumpprivkey();
    1053                 :             : RPCHelpMan importprivkey();
    1054                 :             : RPCHelpMan importaddress();
    1055                 :             : RPCHelpMan importpubkey();
    1056                 :             : RPCHelpMan dumpwallet();
    1057                 :             : RPCHelpMan importwallet();
    1058                 :             : RPCHelpMan importprunedfunds();
    1059                 :             : RPCHelpMan removeprunedfunds();
    1060                 :             : RPCHelpMan importmulti();
    1061                 :             : RPCHelpMan importdescriptors();
    1062                 :             : RPCHelpMan listdescriptors();
    1063                 :             : RPCHelpMan backupwallet();
    1064                 :             : RPCHelpMan restorewallet();
    1065                 :             : 
    1066                 :             : // coins
    1067                 :             : RPCHelpMan getreceivedbyaddress();
    1068                 :             : RPCHelpMan getreceivedbylabel();
    1069                 :             : RPCHelpMan getbalance();
    1070                 :             : RPCHelpMan getunconfirmedbalance();
    1071                 :             : RPCHelpMan lockunspent();
    1072                 :             : RPCHelpMan listlockunspent();
    1073                 :             : RPCHelpMan getbalances();
    1074                 :             : RPCHelpMan listunspent();
    1075                 :             : 
    1076                 :             : // encryption
    1077                 :             : RPCHelpMan walletpassphrase();
    1078                 :             : RPCHelpMan walletpassphrasechange();
    1079                 :             : RPCHelpMan walletlock();
    1080                 :             : RPCHelpMan encryptwallet();
    1081                 :             : 
    1082                 :             : // spend
    1083                 :             : RPCHelpMan sendtoaddress();
    1084                 :             : RPCHelpMan sendmany();
    1085                 :             : RPCHelpMan settxfee();
    1086                 :             : RPCHelpMan fundrawtransaction();
    1087                 :             : RPCHelpMan bumpfee();
    1088                 :             : RPCHelpMan psbtbumpfee();
    1089                 :             : RPCHelpMan send();
    1090                 :             : RPCHelpMan sendall();
    1091                 :             : RPCHelpMan walletprocesspsbt();
    1092                 :             : RPCHelpMan walletcreatefundedpsbt();
    1093                 :             : RPCHelpMan signrawtransactionwithwallet();
    1094                 :             : 
    1095                 :             : // signmessage
    1096                 :             : RPCHelpMan signmessage();
    1097                 :             : 
    1098                 :             : // transactions
    1099                 :             : RPCHelpMan listreceivedbyaddress();
    1100                 :             : RPCHelpMan listreceivedbylabel();
    1101                 :             : RPCHelpMan listtransactions();
    1102                 :             : RPCHelpMan listsinceblock();
    1103                 :             : RPCHelpMan gettransaction();
    1104                 :             : RPCHelpMan abandontransaction();
    1105                 :             : RPCHelpMan rescanblockchain();
    1106                 :             : RPCHelpMan abortrescan();
    1107                 :             : 
    1108                 :         404 : Span<const CRPCCommand> GetWalletRPCCommands()
    1109                 :             : {
    1110                 :         404 :     static const CRPCCommand commands[]{
    1111                 :             :         {"rawtransactions", &fundrawtransaction},
    1112                 :             :         {"wallet", &abandontransaction},
    1113                 :             :         {"wallet", &abortrescan},
    1114                 :             :         {"wallet", &addmultisigaddress},
    1115                 :             :         {"wallet", &backupwallet},
    1116                 :             :         {"wallet", &bumpfee},
    1117                 :             :         {"wallet", &psbtbumpfee},
    1118                 :             :         {"wallet", &createwallet},
    1119                 :             :         {"wallet", &createwalletdescriptor},
    1120                 :             :         {"wallet", &restorewallet},
    1121                 :             :         {"wallet", &dumpprivkey},
    1122                 :             :         {"wallet", &dumpwallet},
    1123                 :             :         {"wallet", &encryptwallet},
    1124                 :             :         {"wallet", &getaddressesbylabel},
    1125                 :             :         {"wallet", &getaddressinfo},
    1126                 :             :         {"wallet", &getbalance},
    1127                 :             :         {"wallet", &gethdkeys},
    1128                 :             :         {"wallet", &getnewaddress},
    1129                 :             :         {"wallet", &getrawchangeaddress},
    1130                 :             :         {"wallet", &getreceivedbyaddress},
    1131                 :             :         {"wallet", &getreceivedbylabel},
    1132                 :             :         {"wallet", &gettransaction},
    1133                 :             :         {"wallet", &getunconfirmedbalance},
    1134                 :             :         {"wallet", &getbalances},
    1135                 :             :         {"wallet", &getwalletinfo},
    1136                 :             :         {"wallet", &importaddress},
    1137                 :             :         {"wallet", &importdescriptors},
    1138                 :             :         {"wallet", &importmulti},
    1139                 :             :         {"wallet", &importprivkey},
    1140                 :             :         {"wallet", &importprunedfunds},
    1141                 :             :         {"wallet", &importpubkey},
    1142                 :             :         {"wallet", &importwallet},
    1143                 :             :         {"wallet", &keypoolrefill},
    1144                 :             :         {"wallet", &listaddressgroupings},
    1145                 :             :         {"wallet", &listdescriptors},
    1146                 :             :         {"wallet", &listlabels},
    1147                 :             :         {"wallet", &listlockunspent},
    1148                 :             :         {"wallet", &listreceivedbyaddress},
    1149                 :             :         {"wallet", &listreceivedbylabel},
    1150                 :             :         {"wallet", &listsinceblock},
    1151                 :             :         {"wallet", &listtransactions},
    1152                 :             :         {"wallet", &listunspent},
    1153                 :             :         {"wallet", &listwalletdir},
    1154                 :             :         {"wallet", &listwallets},
    1155                 :             :         {"wallet", &loadwallet},
    1156                 :             :         {"wallet", &lockunspent},
    1157                 :             :         {"wallet", &migratewallet},
    1158                 :             :         {"wallet", &newkeypool},
    1159                 :             :         {"wallet", &removeprunedfunds},
    1160                 :             :         {"wallet", &rescanblockchain},
    1161                 :             :         {"wallet", &send},
    1162                 :             :         {"wallet", &sendmany},
    1163                 :             :         {"wallet", &sendtoaddress},
    1164                 :             :         {"wallet", &sethdseed},
    1165                 :             :         {"wallet", &setlabel},
    1166                 :             :         {"wallet", &settxfee},
    1167                 :             :         {"wallet", &setwalletflag},
    1168                 :             :         {"wallet", &signmessage},
    1169                 :             :         {"wallet", &signrawtransactionwithwallet},
    1170                 :             :         {"wallet", &simulaterawtransaction},
    1171                 :             :         {"wallet", &sendall},
    1172                 :             :         {"wallet", &unloadwallet},
    1173                 :             :         {"wallet", &upgradewallet},
    1174                 :             :         {"wallet", &walletcreatefundedpsbt},
    1175                 :             : #ifdef ENABLE_EXTERNAL_SIGNER
    1176                 :             :         {"wallet", &walletdisplayaddress},
    1177                 :             : #endif // ENABLE_EXTERNAL_SIGNER
    1178                 :             :         {"wallet", &walletlock},
    1179                 :             :         {"wallet", &walletpassphrase},
    1180                 :             :         {"wallet", &walletpassphrasechange},
    1181                 :             :         {"wallet", &walletprocesspsbt},
    1182   [ +  +  +  -  :         404 :     };
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                -  -  - ]
    1183                 :         404 :     return commands;
    1184                 :             : }
    1185                 :             : } // namespace wallet
        

Generated by: LCOV version 2.0-1