Coverage Report

Created: 2025-02-21 14:37

/root/bitcoin/src/wallet/rpc/util.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2011-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <wallet/rpc/util.h>
6
7
#include <common/url.h>
8
#include <rpc/util.h>
9
#include <util/any.h>
10
#include <util/translation.h>
11
#include <wallet/context.h>
12
#include <wallet/wallet.h>
13
14
#include <string_view>
15
#include <univalue.h>
16
17
namespace wallet {
18
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
19
const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
20
21
0
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
22
0
    bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
23
0
    bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
24
25
0
    if (avoid_reuse && !can_avoid_reuse) {
26
0
        throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
27
0
    }
28
29
0
    return avoid_reuse;
30
0
}
31
32
/** Used by RPC commands that have an include_watchonly parameter.
33
 *  We default to true for watchonly wallets if include_watchonly isn't
34
 *  explicitly set.
35
 */
36
bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
37
0
{
38
0
    if (include_watchonly.isNull()) {
39
        // if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
40
0
        return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
41
0
    }
42
43
    // otherwise return whatever include_watchonly was set to
44
0
    return include_watchonly.get_bool();
45
0
}
46
47
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
48
0
{
49
0
    if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) {
50
        // wallet endpoint was used
51
0
        wallet_name = UrlDecode(std::string_view{request.URI}.substr(WALLET_ENDPOINT_BASE.size()));
52
0
        return true;
53
0
    }
54
0
    return false;
55
0
}
56
57
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
58
0
{
59
0
    CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
60
0
    WalletContext& context = EnsureWalletContext(request.context);
61
62
0
    std::string wallet_name;
63
0
    if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) {
64
0
        std::shared_ptr<CWallet> pwallet = GetWallet(context, wallet_name);
65
0
        if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
66
0
        return pwallet;
67
0
    }
68
69
0
    size_t count{0};
70
0
    auto wallet = GetDefaultWallet(context, count);
71
0
    if (wallet) return wallet;
72
73
0
    if (count == 0) {
74
0
        throw JSONRPCError(
75
0
            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)");
76
0
    }
77
0
    throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
78
0
        "Multiple wallets are loaded. Please select which wallet to use by requesting the RPC through the /wallet/<walletname> URI path.");
79
0
}
80
81
void EnsureWalletIsUnlocked(const CWallet& wallet)
82
0
{
83
0
    if (wallet.IsLocked()) {
84
0
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
85
0
    }
86
0
}
87
88
WalletContext& EnsureWalletContext(const std::any& context)
89
0
{
90
0
    auto wallet_context = util::AnyPtr<WalletContext>(context);
91
0
    if (!wallet_context) {
92
0
        throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
93
0
    }
94
0
    return *wallet_context;
95
0
}
96
97
// 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
98
LegacyScriptPubKeyMan& EnsureLegacyScriptPubKeyMan(CWallet& wallet, bool also_create)
99
0
{
100
0
    LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
101
0
    if (!spk_man && also_create) {
102
0
        spk_man = wallet.GetOrCreateLegacyScriptPubKeyMan();
103
0
    }
104
0
    if (!spk_man) {
105
0
        throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
106
0
    }
107
0
    return *spk_man;
108
0
}
109
110
const LegacyScriptPubKeyMan& EnsureConstLegacyScriptPubKeyMan(const CWallet& wallet)
111
0
{
112
0
    const LegacyScriptPubKeyMan* spk_man = wallet.GetLegacyScriptPubKeyMan();
113
0
    if (!spk_man) {
114
0
        throw JSONRPCError(RPC_WALLET_ERROR, "Only legacy wallets are supported by this command");
115
0
    }
116
0
    return *spk_man;
117
0
}
118
119
std::string LabelFromValue(const UniValue& value)
120
0
{
121
0
    static const std::string empty_string;
122
0
    if (value.isNull()) return empty_string;
123
124
0
    const std::string& label{value.get_str()};
125
0
    if (label == "*")
126
0
        throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
127
0
    return label;
128
0
}
129
130
void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry)
131
0
{
132
0
    UniValue parent_descs(UniValue::VARR);
133
0
    for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) {
134
0
        parent_descs.push_back(desc.descriptor->ToString());
135
0
    }
136
0
    entry.pushKV("parent_descs", std::move(parent_descs));
137
0
}
138
139
void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error)
140
0
{
141
0
    if (!wallet) {
142
        // Map bad format to not found, since bad format is returned when the
143
        // wallet directory exists, but doesn't contain a data file.
144
0
        RPCErrorCode code = RPC_WALLET_ERROR;
145
0
        switch (status) {
146
0
            case DatabaseStatus::FAILED_NOT_FOUND:
147
0
            case DatabaseStatus::FAILED_BAD_FORMAT:
148
0
                code = RPC_WALLET_NOT_FOUND;
149
0
                break;
150
0
            case DatabaseStatus::FAILED_ALREADY_LOADED:
151
0
                code = RPC_WALLET_ALREADY_LOADED;
152
0
                break;
153
0
            case DatabaseStatus::FAILED_ALREADY_EXISTS:
154
0
                code = RPC_WALLET_ALREADY_EXISTS;
155
0
                break;
156
0
            case DatabaseStatus::FAILED_INVALID_BACKUP_FILE:
157
0
                code = RPC_INVALID_PARAMETER;
158
0
                break;
159
0
            default: // RPC_WALLET_ERROR is returned for all other cases.
160
0
                break;
161
0
        }
162
0
        throw JSONRPCError(code, error.original);
163
0
    }
164
0
}
165
166
void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet)
167
0
{
168
0
    AssertLockHeld(wallet.cs_wallet);
169
0
    UniValue lastprocessedblock{UniValue::VOBJ};
170
0
    lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
171
0
    lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
172
0
    entry.pushKV("lastprocessedblock", std::move(lastprocessedblock));
173
0
}
174
175
} // namespace wallet