Coverage Report

Created: 2026-04-29 19:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/wallet/rpc/util.cpp
Line
Count
Source
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 <optional>
15
#include <string_view>
16
#include <univalue.h>
17
18
namespace wallet {
19
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
20
const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
21
22
1.71k
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
23
1.71k
    bool can_avoid_reuse = wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
24
1.71k
    bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
25
26
1.71k
    if (avoid_reuse && !can_avoid_reuse) {
27
0
        throw JSONRPCError(RPC_WALLET_ERROR, "wallet does not have the \"avoid reuse\" feature enabled");
28
0
    }
29
30
1.71k
    return avoid_reuse;
31
1.71k
}
32
33
std::string EnsureUniqueWalletName(const JSONRPCRequest& request, std::optional<std::string_view> wallet_name)
34
354
{
35
354
    if (auto endpoint_wallet{GetWalletNameFromJSONRPCRequest(request)}) {
36
        // wallet endpoint was used
37
193
        if (wallet_name && *wallet_name != *endpoint_wallet) {
38
6
            throw JSONRPCError(RPC_INVALID_PARAMETER,
39
6
                "The RPC endpoint wallet and the wallet name parameter specify different wallets");
40
6
        }
41
187
        return *endpoint_wallet;
42
193
    }
43
44
    // Not a wallet endpoint; parameter must be provided
45
161
    if (!wallet_name) {
46
5
        throw JSONRPCError(RPC_INVALID_PARAMETER,
47
5
            "Either the RPC endpoint wallet or the wallet name parameter must be provided");
48
5
    }
49
50
156
    return std::string{*wallet_name};
51
161
}
52
53
std::optional<std::string> GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request)
54
19.7k
{
55
19.7k
    if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) {
56
        // wallet endpoint was used
57
14.8k
        return UrlDecode(std::string_view{request.URI}.substr(WALLET_ENDPOINT_BASE.size()));
58
14.8k
    }
59
4.92k
    return std::nullopt;
60
19.7k
}
61
62
std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
63
19.3k
{
64
19.3k
    CHECK_NONFATAL(request.mode == JSONRPCRequest::EXECUTE);
65
19.3k
    WalletContext& context = EnsureWalletContext(request.context);
66
67
19.3k
    if (auto wallet_name{GetWalletNameFromJSONRPCRequest(request)}) {
68
14.6k
        std::shared_ptr<CWallet> pwallet{GetWallet(context, *wallet_name)};
69
14.6k
        if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded");
70
14.6k
        return pwallet;
71
14.6k
    }
72
73
4.76k
    size_t count{0};
74
4.76k
    auto wallet = GetDefaultWallet(context, count);
75
4.76k
    if (wallet) return wallet;
76
77
23
    if (count == 0) {
78
11
        throw JSONRPCError(
79
11
            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)");
80
11
    }
81
12
    throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
82
12
        "Multiple wallets are loaded. Please select which wallet to use by requesting the RPC through the /wallet/<walletname> URI path.");
83
23
}
84
85
void EnsureWalletIsUnlocked(const CWallet& wallet)
86
4.19k
{
87
4.19k
    if (wallet.IsLocked()) {
88
15
        throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
89
15
    }
90
4.19k
}
91
92
WalletContext& EnsureWalletContext(const std::any& context)
93
20.6k
{
94
20.6k
    auto wallet_context = util::AnyPtr<WalletContext>(context);
95
20.6k
    if (!wallet_context) {
96
0
        throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet context not found");
97
0
    }
98
20.6k
    return *wallet_context;
99
20.6k
}
100
101
std::string LabelFromValue(const UniValue& value)
102
11.8k
{
103
11.8k
    static const std::string empty_string;
104
11.8k
    if (value.isNull()) return empty_string;
105
106
338
    const std::string& label{value.get_str()};
107
338
    if (label == "*")
108
6
        throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name");
109
332
    return label;
110
338
}
111
112
void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry)
113
36.3k
{
114
36.3k
    UniValue parent_descs(UniValue::VARR);
115
36.3k
    for (const auto& desc: wallet.GetWalletDescriptors(script_pubkey)) {
116
36.3k
        std::string desc_str;
117
36.3k
        FlatSigningProvider dummy_provider;
118
36.3k
        if (!CHECK_NONFATAL(desc.descriptor->ToNormalizedString(dummy_provider, desc_str, &desc.cache))) continue;
119
36.3k
        parent_descs.push_back(desc_str);
120
36.3k
    }
121
36.3k
    entry.pushKV("parent_descs", std::move(parent_descs));
122
36.3k
}
123
124
void HandleWalletError(const std::shared_ptr<CWallet>& wallet, DatabaseStatus& status, bilingual_str& error)
125
779
{
126
779
    if (!wallet) {
127
        // Map bad format to not found, since bad format is returned when the
128
        // wallet directory exists, but doesn't contain a data file.
129
44
        RPCErrorCode code = RPC_WALLET_ERROR;
130
44
        switch (status) {
131
2
            case DatabaseStatus::FAILED_NOT_FOUND:
132
9
            case DatabaseStatus::FAILED_BAD_FORMAT:
133
15
            case DatabaseStatus::FAILED_LEGACY_DISABLED:
134
15
                code = RPC_WALLET_NOT_FOUND;
135
15
                break;
136
0
            case DatabaseStatus::FAILED_ALREADY_LOADED:
137
0
                code = RPC_WALLET_ALREADY_LOADED;
138
0
                break;
139
2
            case DatabaseStatus::FAILED_ALREADY_EXISTS:
140
2
                code = RPC_WALLET_ALREADY_EXISTS;
141
2
                break;
142
3
            case DatabaseStatus::FAILED_NEW_UNNAMED:
143
4
            case DatabaseStatus::FAILED_INVALID_BACKUP_FILE:
144
4
                code = RPC_INVALID_PARAMETER;
145
4
                break;
146
0
            case DatabaseStatus::FAILED_ENCRYPT:
147
0
                code = RPC_WALLET_ENCRYPTION_FAILED;
148
0
                break;
149
23
            default: // RPC_WALLET_ERROR is returned for all other cases.
150
23
                break;
151
44
        }
152
44
        throw JSONRPCError(code, error.original);
153
44
    }
154
779
}
155
156
void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet)
157
1.55k
{
158
1.55k
    AssertLockHeld(wallet.cs_wallet);
159
1.55k
    UniValue lastprocessedblock{UniValue::VOBJ};
160
1.55k
    lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
161
1.55k
    lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
162
1.55k
    entry.pushKV("lastprocessedblock", std::move(lastprocessedblock));
163
1.55k
}
164
165
} // namespace wallet