LCOV - code coverage report
Current view: top level - src/wallet/rpc - wallet.cpp (source / functions) Coverage Total Hit
Test: fuzz_coverage.info Lines: 0.0 % 698 0
Test Date: 2026-07-11 06:27:17 Functions: 0.0 % 27 0
Branches: 0.0 % 2530 0

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

Generated by: LCOV version 2.0-1