// Copyright (c) 2011-present The Limenka developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include namespace wallet { static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"}; bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) { bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE); bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool(); if (avoid_reuse && !can_avoid_reuse) { throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled"); } return avoid_reuse; } /** Used by RPC commands that have an include_watchonly parameter. * We default to true for watchonly wallets if include_watchonly isn't * explicitly set. */ bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet) { if (include_watchonly.isNull()) { // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); } // otherwise return whatever include_watchonly was set to return include_watchonly.get_bool(); } std::string EnsureUniqueWalletName(const JSONRPCRequest& request, const std::string* wallet_name) { std::string endpoint_wallet; if (GetWalletNameFromJSONRPCRequest(request, endpoint_wallet)) { // wallet endpoint was used if (wallet_name && *wallet_name != endpoint_wallet) { throw JSONRPCError(RPC_INVALID_PARAMETER, "The RPC endpoint wallet and the wallet name parameter specify different wallets"); } return endpoint_wallet; } // Not a wallet endpoint; parameter must be provided if (!wallet_name) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Either the RPC endpoint wallet or the wallet name parameter must be provided"); } return *wallet_name; } bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name) { if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) { // wallet endpoint was used wallet_name = UrlDecode(std::string_view{request.URI}.substr(WALLET_ENDPOINT_BASE.size())); return true; } return false; } std::shared_ptr GetWalletForJSONRPCRequest(const JSONRPCRequest& request) { CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE); WalletContext& context = EnsureWalletContext(request.context); bool have_wallet_restriction; std::string authorized_wallet_name; have_wallet_restriction = GetWalletRestrictionFromJSONRPCRequest(request, authorized_wallet_name); bool have_requested_wallet; std::string requested_wallet_name; have_requested_wallet = GetWalletNameFromJSONRPCRequest(request, requested_wallet_name); std::shared_ptr pwallet; size_t count{0}; if (!have_wallet_restriction) { // Any wallet is permitted; select by endpoint, or use the sole wallet if (have_requested_wallet) { pwallet = GetWallet(context, requested_wallet_name); } else { auto wallet = GetDefaultWallet(context, count); if (wallet) pwallet = wallet; } } else if (authorized_wallet_name == "-") { // Block wallet access always } else if ((!have_requested_wallet) || requested_wallet_name == authorized_wallet_name) { // Select specifically the authorized wallet pwallet = GetWallet(context, authorized_wallet_name); } if (pwallet) { return pwallet; } if (have_requested_wallet) { throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); } if (have_wallet_restriction ? (authorized_wallet_name == "-" || !GetWallet(context, authorized_wallet_name)) : (count == 0) ) { throw JSONRPCError( RPC_WALLET_NOT_FOUND, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)"); } throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED, "Multiple wallets are loaded. Please select which wallet to use by requesting the RPC through the /wallet/ URI path."); } void EnsureWalletIsUnlocked(const CWallet& wallet) { if (wallet.IsLocked()) { throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); } } WalletContext& EnsureWalletContext(const std::any& context) { auto wallet_context = util::AnyPtr(context); if (!wallet_context) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found"); } return *wallet_context; } // also_create should only be set to true only when the RPC is expected to add things to a blank wallet and make it no longer blank LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create) { LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan(); if (!spk_man && also_create) { spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan(); } if (!spk_man) { throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command"); } return *spk_man; } const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet) { const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan(); if (!spk_man) { throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command"); } return *spk_man; } std::string LabelFromValue(const UniValue& value) { static const std::string empty_string; if (value.isNull()) return empty_string; const std::string& label{value.get_str()}; if (label == "*") throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name"); return label; } void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry) { UniValue parent_descs(UniValue::VARR); for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) { parent_descs.push_back(desc.descriptor->ToString()); } entry.pushKV("parent_descs", std::move(parent_descs)); } void HandleWalletError(const std::shared_ptr wallet, DatabaseStatus& status, bilingual_str& error) { if (!wallet) { // Map bad format to not found, since bad format is returned when the // wallet directory exists, but doesn't contain a data file. RPCErrorCode code = RPC_WALLET_ERROR; switch (status) { case DatabaseStatus::FAILED_NOT_FOUND: case DatabaseStatus::FAILED_BAD_FORMAT: code = RPC_WALLET_NOT_FOUND; break; case DatabaseStatus::FAILED_ALREADY_LOADED: code = RPC_WALLET_ALREADY_LOADED; break; case DatabaseStatus::FAILED_ALREADY_EXISTS: code = RPC_WALLET_ALREADY_EXISTS; break; case DatabaseStatus::FAILED_INVALID_BACKUP_FILE: code = RPC_INVALID_PARAMETER; break; default: // RPC_WALLET_ERROR is returned for all other cases. break; } throw JSONRPCError(code, error.original); } } void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet) { AssertLockHeld(wallet.cs_wallet); UniValue lastprocessedblock{UniValue::VOBJ}; lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex()); lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight()); entry.pushKV("lastprocessedblock", std::move(lastprocessedblock)); } } // namespace wallet