Branch data Line data Source code
# 1 : : // Copyright (c) 2010 Satoshi Nakamoto
# 2 : : // Copyright (c) 2009-2021 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 <core_io.h>
# 7 : : #include <key_io.h>
# 8 : : #include <rpc/server.h>
# 9 : : #include <rpc/util.h>
# 10 : : #include <util/translation.h>
# 11 : : #include <wallet/context.h>
# 12 : : #include <wallet/receive.h>
# 13 : : #include <wallet/rpc/wallet.h>
# 14 : : #include <wallet/rpc/util.h>
# 15 : : #include <wallet/wallet.h>
# 16 : :
# 17 : : #include <optional>
# 18 : :
# 19 : : #include <univalue.h>
# 20 : :
# 21 : :
# 22 : : namespace wallet {
# 23 : : /** Checks if a CKey is in the given CWallet compressed or otherwise*/
# 24 : : bool HaveKey(const SigningProvider& wallet, const CKey& key)
# 25 : 8 : {
# 26 : 8 : CKey key2;
# 27 : 8 : key2.Set(key.begin(), key.end(), !key.IsCompressed());
# 28 [ + + ][ - + ]: 8 : return wallet.HaveKey(key.GetPubKey().GetID()) || wallet.HaveKey(key2.GetPubKey().GetID());
# 29 : 8 : }
# 30 : :
# 31 : : static RPCHelpMan getwalletinfo()
# 32 : 2598 : {
# 33 : 2598 : return RPCHelpMan{"getwalletinfo",
# 34 : 2598 : "Returns an object containing various wallet state info.\n",
# 35 : 2598 : {},
# 36 : 2598 : RPCResult{
# 37 : 2598 : RPCResult::Type::OBJ, "", "",
# 38 : 2598 : {
# 39 : 2598 : {
# 40 : 2598 : {RPCResult::Type::STR, "walletname", "the wallet name"},
# 41 : 2598 : {RPCResult::Type::NUM, "walletversion", "the wallet version"},
# 42 : 2598 : {RPCResult::Type::STR, "format", "the database format (bdb or sqlite)"},
# 43 : 2598 : {RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
# 44 : 2598 : {RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
# 45 : 2598 : {RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
# 46 : 2598 : {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
# 47 : 2598 : {RPCResult::Type::NUM_TIME, "keypoololdest", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool. Legacy wallets only."},
# 48 : 2598 : {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
# 49 : 2598 : {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)"},
# 50 : 2598 : {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)"},
# 51 : 2598 : {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kvB"},
# 52 : 2598 : {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "the Hash160 of the HD seed (only present when HD is enabled)"},
# 53 : 2598 : {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
# 54 : 2598 : {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
# 55 : 2598 : {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
# 56 : 2598 : {
# 57 : 2598 : {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
# 58 : 2598 : {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
# 59 : 2598 : }, /*skip_type_check=*/true},
# 60 : 2598 : {RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for scriptPubKey management"},
# 61 : 2598 : {RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
# 62 : 2598 : }},
# 63 : 2598 : },
# 64 : 2598 : RPCExamples{
# 65 : 2598 : HelpExampleCli("getwalletinfo", "")
# 66 : 2598 : + HelpExampleRpc("getwalletinfo", "")
# 67 : 2598 : },
# 68 : 2598 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 69 : 2598 : {
# 70 : 1014 : const std::shared_ptr<const CWallet> pwallet = GetWalletForJSONRPCRequest(request);
# 71 [ - + ]: 1014 : if (!pwallet) return NullUniValue;
# 72 : :
# 73 : : // Make sure the results are valid at least up to the most recent block
# 74 : : // the user could have gotten from another RPC command prior to now
# 75 : 1014 : pwallet->BlockUntilSyncedToCurrentChain();
# 76 : :
# 77 : 1014 : LOCK(pwallet->cs_wallet);
# 78 : :
# 79 : 1014 : UniValue obj(UniValue::VOBJ);
# 80 : :
# 81 : 1014 : size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
# 82 : 1014 : const auto bal = GetBalance(*pwallet);
# 83 : 1014 : obj.pushKV("walletname", pwallet->GetName());
# 84 : 1014 : obj.pushKV("walletversion", pwallet->GetVersion());
# 85 : 1014 : obj.pushKV("format", pwallet->GetDatabase().Format());
# 86 : 1014 : obj.pushKV("balance", ValueFromAmount(bal.m_mine_trusted));
# 87 : 1014 : obj.pushKV("unconfirmed_balance", ValueFromAmount(bal.m_mine_untrusted_pending));
# 88 : 1014 : obj.pushKV("immature_balance", ValueFromAmount(bal.m_mine_immature));
# 89 : 1014 : obj.pushKV("txcount", (int)pwallet->mapWallet.size());
# 90 : 1014 : const auto kp_oldest = pwallet->GetOldestKeyPoolTime();
# 91 [ + + ]: 1014 : if (kp_oldest.has_value()) {
# 92 : 651 : obj.pushKV("keypoololdest", kp_oldest.value());
# 93 : 651 : }
# 94 : 1014 : obj.pushKV("keypoolsize", (int64_t)kpExternalSize);
# 95 : :
# 96 : 1014 : LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan();
# 97 [ + + ]: 1014 : if (spk_man) {
# 98 : 651 : CKeyID seed_id = spk_man->GetHDChain().seed_id;
# 99 [ + + ]: 651 : if (!seed_id.IsNull()) {
# 100 : 570 : obj.pushKV("hdseedid", seed_id.GetHex());
# 101 : 570 : }
# 102 : 651 : }
# 103 : :
# 104 [ + + ]: 1014 : if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
# 105 : 991 : obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize));
# 106 : 991 : }
# 107 [ + + ]: 1014 : if (pwallet->IsCrypted()) {
# 108 : 31 : obj.pushKV("unlocked_until", pwallet->nRelockTime);
# 109 : 31 : }
# 110 : 1014 : obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK()));
# 111 : 1014 : obj.pushKV("private_keys_enabled", !pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS));
# 112 : 1014 : obj.pushKV("avoid_reuse", pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE));
# 113 [ - + ]: 1014 : if (pwallet->IsScanning()) {
# 114 : 0 : UniValue scanning(UniValue::VOBJ);
# 115 : 0 : scanning.pushKV("duration", pwallet->ScanningDuration() / 1000);
# 116 : 0 : scanning.pushKV("progress", pwallet->ScanningProgress());
# 117 : 0 : obj.pushKV("scanning", scanning);
# 118 : 1014 : } else {
# 119 : 1014 : obj.pushKV("scanning", false);
# 120 : 1014 : }
# 121 : 1014 : obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
# 122 : 1014 : obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
# 123 : 1014 : return obj;
# 124 : 1014 : },
# 125 : 2598 : };
# 126 : 2598 : }
# 127 : :
# 128 : : static RPCHelpMan listwalletdir()
# 129 : 1597 : {
# 130 : 1597 : return RPCHelpMan{"listwalletdir",
# 131 : 1597 : "Returns a list of wallets in the wallet directory.\n",
# 132 : 1597 : {},
# 133 : 1597 : RPCResult{
# 134 : 1597 : RPCResult::Type::OBJ, "", "",
# 135 : 1597 : {
# 136 : 1597 : {RPCResult::Type::ARR, "wallets", "",
# 137 : 1597 : {
# 138 : 1597 : {RPCResult::Type::OBJ, "", "",
# 139 : 1597 : {
# 140 : 1597 : {RPCResult::Type::STR, "name", "The wallet name"},
# 141 : 1597 : }},
# 142 : 1597 : }},
# 143 : 1597 : }
# 144 : 1597 : },
# 145 : 1597 : RPCExamples{
# 146 : 1597 : HelpExampleCli("listwalletdir", "")
# 147 : 1597 : + HelpExampleRpc("listwalletdir", "")
# 148 : 1597 : },
# 149 : 1597 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 150 : 1597 : {
# 151 : 13 : UniValue wallets(UniValue::VARR);
# 152 [ + + ]: 88 : for (const auto& path : ListDatabases(GetWalletDir())) {
# 153 : 88 : UniValue wallet(UniValue::VOBJ);
# 154 : 88 : wallet.pushKV("name", path.u8string());
# 155 : 88 : wallets.push_back(wallet);
# 156 : 88 : }
# 157 : :
# 158 : 13 : UniValue result(UniValue::VOBJ);
# 159 : 13 : result.pushKV("wallets", wallets);
# 160 : 13 : return result;
# 161 : 13 : },
# 162 : 1597 : };
# 163 : 1597 : }
# 164 : :
# 165 : : static RPCHelpMan listwallets()
# 166 : 1697 : {
# 167 : 1697 : return RPCHelpMan{"listwallets",
# 168 : 1697 : "Returns a list of currently loaded wallets.\n"
# 169 : 1697 : "For full information on the wallet, use \"getwalletinfo\"\n",
# 170 : 1697 : {},
# 171 : 1697 : RPCResult{
# 172 : 1697 : RPCResult::Type::ARR, "", "",
# 173 : 1697 : {
# 174 : 1697 : {RPCResult::Type::STR, "walletname", "the wallet name"},
# 175 : 1697 : }
# 176 : 1697 : },
# 177 : 1697 : RPCExamples{
# 178 : 1697 : HelpExampleCli("listwallets", "")
# 179 : 1697 : + HelpExampleRpc("listwallets", "")
# 180 : 1697 : },
# 181 : 1697 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 182 : 1697 : {
# 183 : 113 : UniValue obj(UniValue::VARR);
# 184 : :
# 185 : 113 : WalletContext& context = EnsureWalletContext(request.context);
# 186 [ + + ]: 386 : for (const std::shared_ptr<CWallet>& wallet : GetWallets(context)) {
# 187 : 386 : LOCK(wallet->cs_wallet);
# 188 : 386 : obj.push_back(wallet->GetName());
# 189 : 386 : }
# 190 : :
# 191 : 113 : return obj;
# 192 : 113 : },
# 193 : 1697 : };
# 194 : 1697 : }
# 195 : :
# 196 : : static RPCHelpMan loadwallet()
# 197 : 1803 : {
# 198 : 1803 : return RPCHelpMan{"loadwallet",
# 199 : 1803 : "\nLoads a wallet from a wallet file or directory."
# 200 : 1803 : "\nNote that all wallet command-line options used when starting bitcoind will be"
# 201 : 1803 : "\napplied to the new wallet.\n",
# 202 : 1803 : {
# 203 : 1803 : {"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
# 204 : 1803 : {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
# 205 : 1803 : },
# 206 : 1803 : RPCResult{
# 207 : 1803 : RPCResult::Type::OBJ, "", "",
# 208 : 1803 : {
# 209 : 1803 : {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
# 210 : 1803 : {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
# 211 : 1803 : }
# 212 : 1803 : },
# 213 : 1803 : RPCExamples{
# 214 : 1803 : HelpExampleCli("loadwallet", "\"test.dat\"")
# 215 : 1803 : + HelpExampleRpc("loadwallet", "\"test.dat\"")
# 216 : 1803 : },
# 217 : 1803 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 218 : 1803 : {
# 219 : 219 : WalletContext& context = EnsureWalletContext(request.context);
# 220 : 219 : const std::string name(request.params[0].get_str());
# 221 : :
# 222 : 219 : DatabaseOptions options;
# 223 : 219 : DatabaseStatus status;
# 224 : 219 : ReadDatabaseArgs(*context.args, options);
# 225 : 219 : options.require_existing = true;
# 226 : 219 : bilingual_str error;
# 227 : 219 : std::vector<bilingual_str> warnings;
# 228 [ + + ]: 219 : std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
# 229 : 219 : std::shared_ptr<CWallet> const wallet = LoadWallet(context, name, load_on_start, options, status, error, warnings);
# 230 : :
# 231 : 219 : HandleWalletError(wallet, status, error);
# 232 : :
# 233 : 219 : UniValue obj(UniValue::VOBJ);
# 234 : 219 : obj.pushKV("name", wallet->GetName());
# 235 : 219 : obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
# 236 : :
# 237 : 219 : return obj;
# 238 : 219 : },
# 239 : 1803 : };
# 240 : 1803 : }
# 241 : :
# 242 : : static RPCHelpMan setwalletflag()
# 243 : 1597 : {
# 244 : 1597 : std::string flags = "";
# 245 [ + + ]: 1597 : for (auto& it : WALLET_FLAG_MAP)
# 246 [ + + ]: 11179 : if (it.second & MUTABLE_WALLET_FLAGS)
# 247 [ + - ]: 1597 : flags += (flags == "" ? "" : ", ") + it.first;
# 248 : :
# 249 : 1597 : return RPCHelpMan{"setwalletflag",
# 250 : 1597 : "\nChange the state of the given wallet flag for a wallet.\n",
# 251 : 1597 : {
# 252 : 1597 : {"flag", RPCArg::Type::STR, RPCArg::Optional::NO, "The name of the flag to change. Current available flags: " + flags},
# 253 : 1597 : {"value", RPCArg::Type::BOOL, RPCArg::Default{true}, "The new state."},
# 254 : 1597 : },
# 255 : 1597 : RPCResult{
# 256 : 1597 : RPCResult::Type::OBJ, "", "",
# 257 : 1597 : {
# 258 : 1597 : {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
# 259 : 1597 : {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
# 260 : 1597 : {RPCResult::Type::STR, "warnings", /*optional=*/true, "Any warnings associated with the change"},
# 261 : 1597 : }
# 262 : 1597 : },
# 263 : 1597 : RPCExamples{
# 264 : 1597 : HelpExampleCli("setwalletflag", "avoid_reuse")
# 265 : 1597 : + HelpExampleRpc("setwalletflag", "\"avoid_reuse\"")
# 266 : 1597 : },
# 267 : 1597 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 268 : 1597 : {
# 269 : 13 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
# 270 [ - + ]: 13 : if (!pwallet) return NullUniValue;
# 271 : :
# 272 : 13 : std::string flag_str = request.params[0].get_str();
# 273 [ + + ][ + + ]: 13 : bool value = request.params[1].isNull() || request.params[1].get_bool();
# 274 : :
# 275 [ - + ]: 13 : if (!WALLET_FLAG_MAP.count(flag_str)) {
# 276 : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unknown wallet flag: %s", flag_str));
# 277 : 0 : }
# 278 : :
# 279 : 13 : auto flag = WALLET_FLAG_MAP.at(flag_str);
# 280 : :
# 281 [ + + ]: 13 : if (!(flag & MUTABLE_WALLET_FLAGS)) {
# 282 : 5 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is immutable: %s", flag_str));
# 283 : 5 : }
# 284 : :
# 285 : 8 : UniValue res(UniValue::VOBJ);
# 286 : :
# 287 [ + + ]: 8 : if (pwallet->IsWalletFlagSet(flag) == value) {
# 288 [ + + ]: 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Wallet flag is already set to %s: %s", value ? "true" : "false", flag_str));
# 289 : 4 : }
# 290 : :
# 291 : 4 : res.pushKV("flag_name", flag_str);
# 292 : 4 : res.pushKV("flag_state", value);
# 293 : :
# 294 [ + + ]: 4 : if (value) {
# 295 : 2 : pwallet->SetWalletFlag(flag);
# 296 : 2 : } else {
# 297 : 2 : pwallet->UnsetWalletFlag(flag);
# 298 : 2 : }
# 299 : :
# 300 [ + + ][ + - ]: 4 : if (flag && value && WALLET_FLAG_CAVEATS.count(flag)) {
# [ + + ][ + - ]
# 301 : 2 : res.pushKV("warnings", WALLET_FLAG_CAVEATS.at(flag));
# 302 : 2 : }
# 303 : :
# 304 : 4 : return res;
# 305 : 8 : },
# 306 : 1597 : };
# 307 : 1597 : }
# 308 : :
# 309 : : static RPCHelpMan createwallet()
# 310 : 2054 : {
# 311 : 2054 : return RPCHelpMan{
# 312 : 2054 : "createwallet",
# 313 : 2054 : "\nCreates and loads a new wallet.\n",
# 314 : 2054 : {
# 315 : 2054 : {"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."},
# 316 : 2054 : {"disable_private_keys", RPCArg::Type::BOOL, RPCArg::Default{false}, "Disable the possibility of private keys (only watchonlys are possible in this mode)."},
# 317 : 2054 : {"blank", RPCArg::Type::BOOL, RPCArg::Default{false}, "Create a blank wallet. A blank wallet has no keys or HD seed. One can be set using sethdseed."},
# 318 : 2054 : {"passphrase", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Encrypt the wallet with this passphrase."},
# 319 : 2054 : {"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."},
# 320 : 2054 : {"descriptors", RPCArg::Type::BOOL, RPCArg::Default{true}, "Create a native descriptor wallet. The wallet will use descriptors internally to handle address creation."
# 321 : 2054 : " Setting to \"false\" will create a legacy wallet; however, the legacy wallet type is being deprecated and"
# 322 : 2054 : " support for creating and opening legacy wallets will be removed in the future."},
# 323 : 2054 : {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
# 324 : 2054 : {"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."},
# 325 : 2054 : },
# 326 : 2054 : RPCResult{
# 327 : 2054 : RPCResult::Type::OBJ, "", "",
# 328 : 2054 : {
# 329 : 2054 : {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."},
# 330 : 2054 : {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
# 331 : 2054 : }
# 332 : 2054 : },
# 333 : 2054 : RPCExamples{
# 334 : 2054 : HelpExampleCli("createwallet", "\"testwallet\"")
# 335 : 2054 : + HelpExampleRpc("createwallet", "\"testwallet\"")
# 336 : 2054 : + HelpExampleCliNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
# 337 : 2054 : + HelpExampleRpcNamed("createwallet", {{"wallet_name", "descriptors"}, {"avoid_reuse", true}, {"descriptors", true}, {"load_on_startup", true}})
# 338 : 2054 : },
# 339 : 2054 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 340 : 2054 : {
# 341 : 470 : WalletContext& context = EnsureWalletContext(request.context);
# 342 : 470 : uint64_t flags = 0;
# 343 [ + + ][ + + ]: 470 : if (!request.params[1].isNull() && request.params[1].get_bool()) {
# 344 : 60 : flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
# 345 : 60 : }
# 346 : :
# 347 [ + + ][ + + ]: 470 : if (!request.params[2].isNull() && request.params[2].get_bool()) {
# 348 : 50 : flags |= WALLET_FLAG_BLANK_WALLET;
# 349 : 50 : }
# 350 : 470 : SecureString passphrase;
# 351 : 470 : passphrase.reserve(100);
# 352 : 470 : std::vector<bilingual_str> warnings;
# 353 [ + + ]: 470 : if (!request.params[3].isNull()) {
# 354 : 468 : passphrase = request.params[3].get_str().c_str();
# 355 [ + + ]: 468 : if (passphrase.empty()) {
# 356 : : // Empty string means unencrypted
# 357 : 454 : warnings.emplace_back(Untranslated("Empty string given as passphrase, wallet will not be encrypted."));
# 358 : 454 : }
# 359 : 468 : }
# 360 : :
# 361 [ + + ][ + + ]: 470 : if (!request.params[4].isNull() && request.params[4].get_bool()) {
# 362 : 5 : flags |= WALLET_FLAG_AVOID_REUSE;
# 363 : 5 : }
# 364 [ - + ][ + + ]: 470 : if (request.params[5].isNull() || request.params[5].get_bool()) {
# 365 : : #ifndef USE_SQLITE
# 366 : : throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without sqlite support (required for descriptor wallets)");
# 367 : : #endif
# 368 : 200 : flags |= WALLET_FLAG_DESCRIPTORS;
# 369 : 200 : }
# 370 [ + + ][ + + ]: 470 : if (!request.params[7].isNull() && request.params[7].get_bool()) {
# 371 : 4 : #ifdef ENABLE_EXTERNAL_SIGNER
# 372 : 4 : flags |= WALLET_FLAG_EXTERNAL_SIGNER;
# 373 : : #else
# 374 : : throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without external signing support (required for external signing)");
# 375 : : #endif
# 376 : 4 : }
# 377 : :
# 378 : : #ifndef USE_BDB
# 379 : : if (!(flags & WALLET_FLAG_DESCRIPTORS)) {
# 380 : : throw JSONRPCError(RPC_WALLET_ERROR, "Compiled without bdb support (required for legacy wallets)");
# 381 : : }
# 382 : : #endif
# 383 : :
# 384 : 470 : DatabaseOptions options;
# 385 : 470 : DatabaseStatus status;
# 386 : 470 : ReadDatabaseArgs(*context.args, options);
# 387 : 470 : options.require_create = true;
# 388 : 470 : options.create_flags = flags;
# 389 : 470 : options.create_passphrase = passphrase;
# 390 : 470 : bilingual_str error;
# 391 [ + + ]: 470 : std::optional<bool> load_on_start = request.params[6].isNull() ? std::nullopt : std::optional<bool>(request.params[6].get_bool());
# 392 : 470 : const std::shared_ptr<CWallet> wallet = CreateWallet(context, request.params[0].get_str(), load_on_start, options, status, error, warnings);
# 393 [ + + ]: 470 : if (!wallet) {
# 394 [ - + ]: 12 : RPCErrorCode code = status == DatabaseStatus::FAILED_ENCRYPT ? RPC_WALLET_ENCRYPTION_FAILED : RPC_WALLET_ERROR;
# 395 : 12 : throw JSONRPCError(code, error.original);
# 396 : 12 : }
# 397 : :
# 398 : 458 : UniValue obj(UniValue::VOBJ);
# 399 : 458 : obj.pushKV("name", wallet->GetName());
# 400 : 458 : obj.pushKV("warning", Join(warnings, Untranslated("\n")).original);
# 401 : :
# 402 : 458 : return obj;
# 403 : 470 : },
# 404 : 2054 : };
# 405 : 2054 : }
# 406 : :
# 407 : : static RPCHelpMan unloadwallet()
# 408 : 1763 : {
# 409 : 1763 : return RPCHelpMan{"unloadwallet",
# 410 : 1763 : "Unloads the wallet referenced by the request endpoint otherwise unloads the wallet specified in the argument.\n"
# 411 : 1763 : "Specifying the wallet name on a wallet endpoint is invalid.",
# 412 : 1763 : {
# 413 : 1763 : {"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."},
# 414 : 1763 : {"load_on_startup", RPCArg::Type::BOOL, RPCArg::Optional::OMITTED_NAMED_ARG, "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
# 415 : 1763 : },
# 416 : 1763 : RPCResult{RPCResult::Type::OBJ, "", "", {
# 417 : 1763 : {RPCResult::Type::STR, "warning", "Warning message if wallet was not unloaded cleanly."},
# 418 : 1763 : }},
# 419 : 1763 : RPCExamples{
# 420 : 1763 : HelpExampleCli("unloadwallet", "wallet_name")
# 421 : 1763 : + HelpExampleRpc("unloadwallet", "wallet_name")
# 422 : 1763 : },
# 423 : 1763 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 424 : 1763 : {
# 425 : 179 : std::string wallet_name;
# 426 [ + + ]: 179 : if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
# 427 [ + + ][ + + ]: 45 : if (!(request.params[0].isNull() || request.params[0].get_str() == wallet_name)) {
# 428 : 3 : throw JSONRPCError(RPC_INVALID_PARAMETER, "RPC endpoint wallet and wallet_name parameter specify different wallets");
# 429 : 3 : }
# 430 : 134 : } else {
# 431 : 134 : wallet_name = request.params[0].get_str();
# 432 : 134 : }
# 433 : :
# 434 : 176 : WalletContext& context = EnsureWalletContext(request.context);
# 435 : 176 : std::shared_ptr<CWallet> wallet = GetWallet(context, wallet_name);
# 436 [ + + ]: 176 : if (!wallet) {
# 437 : 6 : throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
# 438 : 6 : }
# 439 : :
# 440 : : // Release the "main" shared pointer and prevent further notifications.
# 441 : : // Note that any attempt to load the same wallet would fail until the wallet
# 442 : : // is destroyed (see CheckUniqueFileid).
# 443 : 170 : std::vector<bilingual_str> warnings;
# 444 [ + + ]: 170 : std::optional<bool> load_on_start = request.params[1].isNull() ? std::nullopt : std::optional<bool>(request.params[1].get_bool());
# 445 [ - + ]: 170 : if (!RemoveWallet(context, wallet, load_on_start, warnings)) {
# 446 : 0 : throw JSONRPCError(RPC_MISC_ERROR, "Requested wallet already unloaded");
# 447 : 0 : }
# 448 : :
# 449 : 170 : UnloadWallet(std::move(wallet));
# 450 : :
# 451 : 170 : UniValue result(UniValue::VOBJ);
# 452 : 170 : result.pushKV("warning", Join(warnings, Untranslated("\n")).original);
# 453 : 170 : return result;
# 454 : 170 : },
# 455 : 1763 : };
# 456 : 1763 : }
# 457 : :
# 458 : : static RPCHelpMan sethdseed()
# 459 : 1605 : {
# 460 : 1605 : return RPCHelpMan{"sethdseed",
# 461 : 1605 : "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n"
# 462 : 1605 : "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n"
# 463 : 1605 : "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed." +
# 464 : 1605 : HELP_REQUIRING_PASSPHRASE,
# 465 : 1605 : {
# 466 : 1605 : {"newkeypool", RPCArg::Type::BOOL, RPCArg::Default{true}, "Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n"
# 467 : 1605 : "If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n"
# 468 : 1605 : "If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n"
# 469 : 1605 : "keypool will be used until it has been depleted."},
# 470 : 1605 : {"seed", RPCArg::Type::STR, RPCArg::DefaultHint{"random seed"}, "The WIF private key to use as the new HD seed.\n"
# 471 : 1605 : "The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1"},
# 472 : 1605 : },
# 473 : 1605 : RPCResult{RPCResult::Type::NONE, "", ""},
# 474 : 1605 : RPCExamples{
# 475 : 1605 : HelpExampleCli("sethdseed", "")
# 476 : 1605 : + HelpExampleCli("sethdseed", "false")
# 477 : 1605 : + HelpExampleCli("sethdseed", "true \"wifkey\"")
# 478 : 1605 : + HelpExampleRpc("sethdseed", "true, \"wifkey\"")
# 479 : 1605 : },
# 480 : 1605 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 481 : 1605 : {
# 482 : 20 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
# 483 [ - + ]: 20 : if (!pwallet) return NullUniValue;
# 484 : :
# 485 : 20 : LegacyScriptPubKeyMan& spk_man = EnsureLegacyScriptPubKeyMan(*pwallet, true);
# 486 : :
# 487 [ - + ]: 20 : if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
# 488 : 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed to a wallet with private keys disabled");
# 489 : 0 : }
# 490 : :
# 491 : 20 : LOCK2(pwallet->cs_wallet, spk_man.cs_KeyStore);
# 492 : :
# 493 : : // Do not do anything to non-HD wallets
# 494 [ - + ]: 20 : if (!pwallet->CanSupportFeature(FEATURE_HD)) {
# 495 : 0 : throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set an HD seed on a non-HD wallet. Use the upgradewallet RPC in order to upgrade a non-HD wallet to HD");
# 496 : 0 : }
# 497 : :
# 498 : 20 : EnsureWalletIsUnlocked(*pwallet);
# 499 : :
# 500 : 20 : bool flush_key_pool = true;
# 501 [ + + ]: 20 : if (!request.params[0].isNull()) {
# 502 : 14 : flush_key_pool = request.params[0].get_bool();
# 503 : 14 : }
# 504 : :
# 505 : 20 : CPubKey master_pub_key;
# 506 [ + + ]: 20 : if (request.params[1].isNull()) {
# 507 : 8 : master_pub_key = spk_man.GenerateNewSeed();
# 508 : 12 : } else {
# 509 : 12 : CKey key = DecodeSecret(request.params[1].get_str());
# 510 [ + + ]: 12 : if (!key.IsValid()) {
# 511 : 1 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
# 512 : 1 : }
# 513 : :
# 514 [ + + ]: 11 : if (HaveKey(spk_man, key)) {
# 515 : 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)");
# 516 : 2 : }
# 517 : :
# 518 : 9 : master_pub_key = spk_man.DeriveNewSeed(key);
# 519 : 9 : }
# 520 : :
# 521 : 17 : spk_man.SetHDSeed(master_pub_key);
# 522 [ + + ]: 17 : if (flush_key_pool) spk_man.NewKeyPool();
# 523 : :
# 524 : 17 : return NullUniValue;
# 525 : 20 : },
# 526 : 1605 : };
# 527 : 1605 : }
# 528 : :
# 529 : : static RPCHelpMan upgradewallet()
# 530 : 1584 : {
# 531 : 1584 : return RPCHelpMan{"upgradewallet",
# 532 : 1584 : "\nUpgrade the wallet. Upgrades to the latest version if no version number is specified.\n"
# 533 : 1584 : "New keys may be generated and a new wallet backup will need to be made.",
# 534 : 1584 : {
# 535 : 1584 : {"version", RPCArg::Type::NUM, RPCArg::Default{FEATURE_LATEST}, "The version number to upgrade to. Default is the latest wallet version."}
# 536 : 1584 : },
# 537 : 1584 : RPCResult{
# 538 : 1584 : RPCResult::Type::OBJ, "", "",
# 539 : 1584 : {
# 540 : 1584 : {RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
# 541 : 1584 : {RPCResult::Type::NUM, "previous_version", "Version of wallet before this operation"},
# 542 : 1584 : {RPCResult::Type::NUM, "current_version", "Version of wallet after this operation"},
# 543 : 1584 : {RPCResult::Type::STR, "result", /*optional=*/true, "Description of result, if no error"},
# 544 : 1584 : {RPCResult::Type::STR, "error", /*optional=*/true, "Error message (if there is one)"}
# 545 : 1584 : },
# 546 : 1584 : },
# 547 : 1584 : RPCExamples{
# 548 : 1584 : HelpExampleCli("upgradewallet", "169900")
# 549 : 1584 : + HelpExampleRpc("upgradewallet", "169900")
# 550 : 1584 : },
# 551 : 1584 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
# 552 : 1584 : {
# 553 : 0 : std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
# 554 [ # # ]: 0 : if (!pwallet) return NullUniValue;
# 555 : :
# 556 : 0 : RPCTypeCheck(request.params, {UniValue::VNUM}, true);
# 557 : :
# 558 : 0 : EnsureWalletIsUnlocked(*pwallet);
# 559 : :
# 560 : 0 : int version = 0;
# 561 [ # # ]: 0 : if (!request.params[0].isNull()) {
# 562 : 0 : version = request.params[0].get_int();
# 563 : 0 : }
# 564 : 0 : bilingual_str error;
# 565 : 0 : const int previous_version{pwallet->GetVersion()};
# 566 : 0 : const bool wallet_upgraded{pwallet->UpgradeWallet(version, error)};
# 567 : 0 : const int current_version{pwallet->GetVersion()};
# 568 : 0 : std::string result;
# 569 : :
# 570 [ # # ]: 0 : if (wallet_upgraded) {
# 571 [ # # ]: 0 : if (previous_version == current_version) {
# 572 : 0 : result = "Already at latest version. Wallet version unchanged.";
# 573 : 0 : } else {
# 574 : 0 : result = strprintf("Wallet upgraded successfully from version %i to version %i.", previous_version, current_version);
# 575 : 0 : }
# 576 : 0 : }
# 577 : :
# 578 : 0 : UniValue obj(UniValue::VOBJ);
# 579 : 0 : obj.pushKV("wallet_name", pwallet->GetName());
# 580 : 0 : obj.pushKV("previous_version", previous_version);
# 581 : 0 : obj.pushKV("current_version", current_version);
# 582 [ # # ]: 0 : if (!result.empty()) {
# 583 : 0 : obj.pushKV("result", result);
# 584 : 0 : } else {
# 585 [ # # ]: 0 : CHECK_NONFATAL(!error.empty());
# 586 : 0 : obj.pushKV("error", error.original);
# 587 : 0 : }
# 588 : 0 : return obj;
# 589 : 0 : },
# 590 : 1584 : };
# 591 : 1584 : }
# 592 : :
# 593 : : // addresses
# 594 : : RPCHelpMan getaddressinfo();
# 595 : : RPCHelpMan getnewaddress();
# 596 : : RPCHelpMan getrawchangeaddress();
# 597 : : RPCHelpMan setlabel();
# 598 : : RPCHelpMan listaddressgroupings();
# 599 : : RPCHelpMan addmultisigaddress();
# 600 : : RPCHelpMan keypoolrefill();
# 601 : : RPCHelpMan newkeypool();
# 602 : : RPCHelpMan getaddressesbylabel();
# 603 : : RPCHelpMan listlabels();
# 604 : : #ifdef ENABLE_EXTERNAL_SIGNER
# 605 : : RPCHelpMan walletdisplayaddress();
# 606 : : #endif // ENABLE_EXTERNAL_SIGNER
# 607 : :
# 608 : : // backup
# 609 : : RPCHelpMan dumpprivkey();
# 610 : : RPCHelpMan importprivkey();
# 611 : : RPCHelpMan importaddress();
# 612 : : RPCHelpMan importpubkey();
# 613 : : RPCHelpMan dumpwallet();
# 614 : : RPCHelpMan importwallet();
# 615 : : RPCHelpMan importprunedfunds();
# 616 : : RPCHelpMan removeprunedfunds();
# 617 : : RPCHelpMan importmulti();
# 618 : : RPCHelpMan importdescriptors();
# 619 : : RPCHelpMan listdescriptors();
# 620 : : RPCHelpMan backupwallet();
# 621 : : RPCHelpMan restorewallet();
# 622 : :
# 623 : : // coins
# 624 : : RPCHelpMan getreceivedbyaddress();
# 625 : : RPCHelpMan getreceivedbylabel();
# 626 : : RPCHelpMan getbalance();
# 627 : : RPCHelpMan getunconfirmedbalance();
# 628 : : RPCHelpMan lockunspent();
# 629 : : RPCHelpMan listlockunspent();
# 630 : : RPCHelpMan getbalances();
# 631 : : RPCHelpMan listunspent();
# 632 : :
# 633 : : // encryption
# 634 : : RPCHelpMan walletpassphrase();
# 635 : : RPCHelpMan walletpassphrasechange();
# 636 : : RPCHelpMan walletlock();
# 637 : : RPCHelpMan encryptwallet();
# 638 : :
# 639 : : // spend
# 640 : : RPCHelpMan sendtoaddress();
# 641 : : RPCHelpMan sendmany();
# 642 : : RPCHelpMan settxfee();
# 643 : : RPCHelpMan fundrawtransaction();
# 644 : : RPCHelpMan bumpfee();
# 645 : : RPCHelpMan psbtbumpfee();
# 646 : : RPCHelpMan send();
# 647 : : RPCHelpMan sendall();
# 648 : : RPCHelpMan walletprocesspsbt();
# 649 : : RPCHelpMan walletcreatefundedpsbt();
# 650 : : RPCHelpMan signrawtransactionwithwallet();
# 651 : :
# 652 : : // signmessage
# 653 : : RPCHelpMan signmessage();
# 654 : :
# 655 : : // transactions
# 656 : : RPCHelpMan listreceivedbyaddress();
# 657 : : RPCHelpMan listreceivedbylabel();
# 658 : : RPCHelpMan listtransactions();
# 659 : : RPCHelpMan listsinceblock();
# 660 : : RPCHelpMan gettransaction();
# 661 : : RPCHelpMan abandontransaction();
# 662 : : RPCHelpMan rescanblockchain();
# 663 : : RPCHelpMan abortrescan();
# 664 : :
# 665 : : Span<const CRPCCommand> GetWalletRPCCommands()
# 666 : 799 : {
# 667 : : // clang-format off
# 668 : 799 : static const CRPCCommand commands[] =
# 669 : 799 : { // category actor (function)
# 670 : : // ------------------ ------------------------
# 671 : 799 : { "rawtransactions", &fundrawtransaction, },
# 672 : 799 : { "wallet", &abandontransaction, },
# 673 : 799 : { "wallet", &abortrescan, },
# 674 : 799 : { "wallet", &addmultisigaddress, },
# 675 : 799 : { "wallet", &backupwallet, },
# 676 : 799 : { "wallet", &bumpfee, },
# 677 : 799 : { "wallet", &psbtbumpfee, },
# 678 : 799 : { "wallet", &createwallet, },
# 679 : 799 : { "wallet", &restorewallet, },
# 680 : 799 : { "wallet", &dumpprivkey, },
# 681 : 799 : { "wallet", &dumpwallet, },
# 682 : 799 : { "wallet", &encryptwallet, },
# 683 : 799 : { "wallet", &getaddressesbylabel, },
# 684 : 799 : { "wallet", &getaddressinfo, },
# 685 : 799 : { "wallet", &getbalance, },
# 686 : 799 : { "wallet", &getnewaddress, },
# 687 : 799 : { "wallet", &getrawchangeaddress, },
# 688 : 799 : { "wallet", &getreceivedbyaddress, },
# 689 : 799 : { "wallet", &getreceivedbylabel, },
# 690 : 799 : { "wallet", &gettransaction, },
# 691 : 799 : { "wallet", &getunconfirmedbalance, },
# 692 : 799 : { "wallet", &getbalances, },
# 693 : 799 : { "wallet", &getwalletinfo, },
# 694 : 799 : { "wallet", &importaddress, },
# 695 : 799 : { "wallet", &importdescriptors, },
# 696 : 799 : { "wallet", &importmulti, },
# 697 : 799 : { "wallet", &importprivkey, },
# 698 : 799 : { "wallet", &importprunedfunds, },
# 699 : 799 : { "wallet", &importpubkey, },
# 700 : 799 : { "wallet", &importwallet, },
# 701 : 799 : { "wallet", &keypoolrefill, },
# 702 : 799 : { "wallet", &listaddressgroupings, },
# 703 : 799 : { "wallet", &listdescriptors, },
# 704 : 799 : { "wallet", &listlabels, },
# 705 : 799 : { "wallet", &listlockunspent, },
# 706 : 799 : { "wallet", &listreceivedbyaddress, },
# 707 : 799 : { "wallet", &listreceivedbylabel, },
# 708 : 799 : { "wallet", &listsinceblock, },
# 709 : 799 : { "wallet", &listtransactions, },
# 710 : 799 : { "wallet", &listunspent, },
# 711 : 799 : { "wallet", &listwalletdir, },
# 712 : 799 : { "wallet", &listwallets, },
# 713 : 799 : { "wallet", &loadwallet, },
# 714 : 799 : { "wallet", &lockunspent, },
# 715 : 799 : { "wallet", &newkeypool, },
# 716 : 799 : { "wallet", &removeprunedfunds, },
# 717 : 799 : { "wallet", &rescanblockchain, },
# 718 : 799 : { "wallet", &send, },
# 719 : 799 : { "wallet", &sendmany, },
# 720 : 799 : { "wallet", &sendtoaddress, },
# 721 : 799 : { "wallet", &sethdseed, },
# 722 : 799 : { "wallet", &setlabel, },
# 723 : 799 : { "wallet", &settxfee, },
# 724 : 799 : { "wallet", &setwalletflag, },
# 725 : 799 : { "wallet", &signmessage, },
# 726 : 799 : { "wallet", &signrawtransactionwithwallet, },
# 727 : 799 : { "wallet", &sendall, },
# 728 : 799 : { "wallet", &unloadwallet, },
# 729 : 799 : { "wallet", &upgradewallet, },
# 730 : 799 : { "wallet", &walletcreatefundedpsbt, },
# 731 : 799 : #ifdef ENABLE_EXTERNAL_SIGNER
# 732 : 799 : { "wallet", &walletdisplayaddress, },
# 733 : 799 : #endif // ENABLE_EXTERNAL_SIGNER
# 734 : 799 : { "wallet", &walletlock, },
# 735 : 799 : { "wallet", &walletpassphrase, },
# 736 : 799 : { "wallet", &walletpassphrasechange, },
# 737 : 799 : { "wallet", &walletprocesspsbt, },
# 738 : 799 : };
# 739 : : // clang-format on
# 740 : 799 : return commands;
# 741 : 799 : }
# 742 : : } // namespace wallet
|