Bitcoin Core 28.99.0
P2P Digital Currency
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages Concepts
rpc.cpp
Go to the documentation of this file.
1// Copyright (c) 2021-2022 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 <base58.h>
6#include <key.h>
7#include <key_io.h>
8#include <primitives/block.h>
10#include <psbt.h>
11#include <rpc/client.h>
12#include <rpc/request.h>
13#include <rpc/server.h>
14#include <span.h>
15#include <streams.h>
17#include <test/fuzz/fuzz.h>
18#include <test/fuzz/util.h>
20#include <tinyformat.h>
21#include <uint256.h>
22#include <univalue.h>
23#include <util/strencodings.h>
24#include <util/string.h>
25#include <util/time.h>
26
27#include <algorithm>
28#include <cassert>
29#include <cstdint>
30#include <cstdlib>
31#include <exception>
32#include <iostream>
33#include <memory>
34#include <optional>
35#include <stdexcept>
36#include <vector>
37enum class ChainType;
38
39using util::Join;
40using util::ToString;
41
42namespace {
43struct RPCFuzzTestingSetup : public TestingSetup {
44 RPCFuzzTestingSetup(const ChainType chain_type, TestOpts opts) : TestingSetup{chain_type, opts}
45 {
46 }
47
48 void CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments)
49 {
50 JSONRPCRequest request;
51 request.context = &m_node;
52 request.strMethod = rpc_method;
53 try {
54 request.params = RPCConvertValues(rpc_method, arguments);
55 } catch (const std::runtime_error&) {
56 return;
57 }
58 tableRPC.execute(request);
59 }
60
61 std::vector<std::string> GetRPCCommands() const
62 {
63 return tableRPC.listCommands();
64 }
65};
66
67RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
68std::string g_limit_to_rpc_command;
69
70// RPC commands which are not appropriate for fuzzing: such as RPC commands
71// reading or writing to a filename passed as an RPC parameter, RPC commands
72// resulting in network activity, etc.
73const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
74 "addconnection", // avoid DNS lookups
75 "addnode", // avoid DNS lookups
76 "addpeeraddress", // avoid DNS lookups
77 "dumptxoutset", // avoid writing to disk
78 "dumpwallet", // avoid writing to disk
79 "enumeratesigners",
80 "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
81 "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
82 "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
83 "gettxoutproof", // avoid prohibitively slow execution
84 "importmempool", // avoid reading from disk
85 "importwallet", // avoid reading from disk
86 "loadtxoutset", // avoid reading from disk
87 "loadwallet", // avoid reading from disk
88 "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
89 "setban", // avoid DNS lookups
90 "stop", // avoid shutdown state
91};
92
93// RPC commands which are safe for fuzzing.
94const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
95 "analyzepsbt",
96 "clearbanned",
97 "combinepsbt",
98 "combinerawtransaction",
99 "converttopsbt",
100 "createmultisig",
101 "createpsbt",
102 "createrawtransaction",
103 "decodepsbt",
104 "decoderawtransaction",
105 "decodescript",
106 "deriveaddresses",
107 "descriptorprocesspsbt",
108 "disconnectnode",
109 "echo",
110 "echojson",
111 "estimaterawfee",
112 "estimatesmartfee",
113 "finalizepsbt",
114 "generate",
115 "generateblock",
116 "getaddednodeinfo",
117 "getaddrmaninfo",
118 "getbestblockhash",
119 "getblock",
120 "getblockchaininfo",
121 "getblockcount",
122 "getblockfilter",
123 "getblockfrompeer", // when no peers are connected, no p2p message is sent
124 "getblockhash",
125 "getblockheader",
126 "getblockstats",
127 "getblocktemplate",
128 "getchaintips",
129 "getchainstates",
130 "getchaintxstats",
131 "getconnectioncount",
132 "getdeploymentinfo",
133 "getdescriptoractivity",
134 "getdescriptorinfo",
135 "getdifficulty",
136 "getindexinfo",
137 "getmemoryinfo",
138 "getmempoolancestors",
139 "getmempooldescendants",
140 "getmempoolentry",
141 "getmempoolinfo",
142 "getmininginfo",
143 "getnettotals",
144 "getnetworkhashps",
145 "getnetworkinfo",
146 "getnodeaddresses",
147 "getorphantxs",
148 "getpeerinfo",
149 "getprioritisedtransactions",
150 "getrawaddrman",
151 "getrawmempool",
152 "getrawtransaction",
153 "getrpcinfo",
154 "gettxout",
155 "gettxoutsetinfo",
156 "gettxspendingprevout",
157 "help",
158 "invalidateblock",
159 "joinpsbts",
160 "listbanned",
161 "logging",
162 "mockscheduler",
163 "ping",
164 "preciousblock",
165 "prioritisetransaction",
166 "pruneblockchain",
167 "reconsiderblock",
168 "scanblocks",
169 "scantxoutset",
170 "sendmsgtopeer", // when no peers are connected, no p2p message is sent
171 "sendrawtransaction",
172 "setmocktime",
173 "setnetworkactive",
174 "signmessagewithprivkey",
175 "signrawtransactionwithkey",
176 "submitblock",
177 "submitheader",
178 "submitpackage",
179 "syncwithvalidationinterfacequeue",
180 "testmempoolaccept",
181 "uptime",
182 "utxoupdatepsbt",
183 "validateaddress",
184 "verifychain",
185 "verifymessage",
186 "verifytxoutproof",
187 "waitforblock",
188 "waitforblockheight",
189 "waitfornewblock",
190};
191
192std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
193{
194 const size_t max_string_length = 4096;
195 const size_t max_base58_bytes_length{64};
196 std::string r;
197 CallOneOf(
198 fuzzed_data_provider,
199 [&] {
200 // string argument
201 r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
202 },
203 [&] {
204 // base64 argument
205 r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
206 },
207 [&] {
208 // hex argument
209 r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
210 },
211 [&] {
212 // bool argument
213 r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
214 },
215 [&] {
216 // range argument
217 r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
218 },
219 [&] {
220 // integral argument (int64_t)
221 r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
222 },
223 [&] {
224 // integral argument (uint64_t)
225 r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
226 },
227 [&] {
228 // floating point argument
229 r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
230 },
231 [&] {
232 // tx destination argument
233 r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
234 },
235 [&] {
236 // uint160 argument
237 r = ConsumeUInt160(fuzzed_data_provider).ToString();
238 },
239 [&] {
240 // uint256 argument
241 r = ConsumeUInt256(fuzzed_data_provider).ToString();
242 },
243 [&] {
244 // base32 argument
245 r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
246 },
247 [&] {
248 // base58 argument
249 r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
250 },
251 [&] {
252 // base58 argument with checksum
253 r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
254 },
255 [&] {
256 // hex encoded block
257 std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
258 if (!opt_block) {
259 good_data = false;
260 return;
261 }
262 DataStream data_stream{};
263 data_stream << TX_WITH_WITNESS(*opt_block);
264 r = HexStr(data_stream);
265 },
266 [&] {
267 // hex encoded block header
268 std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
269 if (!opt_block_header) {
270 good_data = false;
271 return;
272 }
273 DataStream data_stream{};
274 data_stream << *opt_block_header;
275 r = HexStr(data_stream);
276 },
277 [&] {
278 // hex encoded tx
279 std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
280 if (!opt_tx) {
281 good_data = false;
282 return;
283 }
284 DataStream data_stream;
285 auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
286 data_stream << allow_witness(*opt_tx);
287 r = HexStr(data_stream);
288 },
289 [&] {
290 // base64 encoded psbt
291 std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
292 if (!opt_psbt) {
293 good_data = false;
294 return;
295 }
296 DataStream data_stream{};
297 data_stream << *opt_psbt;
298 r = EncodeBase64(data_stream);
299 },
300 [&] {
301 // base58 encoded key
302 CKey key = ConsumePrivateKey(fuzzed_data_provider);
303 if (!key.IsValid()) {
304 good_data = false;
305 return;
306 }
307 r = EncodeSecret(key);
308 },
309 [&] {
310 // hex encoded pubkey
311 CKey key = ConsumePrivateKey(fuzzed_data_provider);
312 if (!key.IsValid()) {
313 good_data = false;
314 return;
315 }
316 r = HexStr(key.GetPubKey());
317 });
318 return r;
319}
320
321std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
322{
323 std::vector<std::string> scalar_arguments;
324 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
325 {
326 scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
327 }
328 return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
329}
330
331std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
332{
333 return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
334}
335
336RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
337{
338 static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
340 return setup.get();
341}
342}; // namespace
343
345{
346 rpc_testing_setup = InitializeRPCFuzzTestingSetup();
347 const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
348 for (const std::string& rpc_command : supported_rpc_commands) {
349 const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
350 const bool not_safe_for_fuzzing = std::find(RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end();
351 if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
352 std::cerr << "Error: RPC command \"" << rpc_command << "\" not found in RPC_COMMANDS_SAFE_FOR_FUZZING or RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
353 std::terminate();
354 }
355 if (safe_for_fuzzing && not_safe_for_fuzzing) {
356 std::cerr << "Error: RPC command \"" << rpc_command << "\" found in *both* RPC_COMMANDS_SAFE_FOR_FUZZING and RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
357 std::terminate();
358 }
359 }
360 const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
361 if (limit_to_rpc_command_env != nullptr) {
362 g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
363 }
364}
365
367{
369 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
370 bool good_data{true};
371 SetMockTime(ConsumeTime(fuzzed_data_provider));
372 const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
373 if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
374 return;
375 }
376 const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
377 if (!safe_for_fuzzing) {
378 return;
379 }
380 std::vector<std::string> arguments;
381 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
382 {
383 arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
384 }
385 try {
386 rpc_testing_setup->CallRPC(rpc_command, arguments);
387 } catch (const UniValue& json_rpc_error) {
388 const std::string error_msg{json_rpc_error.find_value("message").get_str()};
389 if (error_msg.starts_with("Internal bug detected")) {
390 // Only allow the intentional internal bug
391 assert(error_msg.find("trigger_internal_bug") != std::string::npos);
392 }
393 }
394}
std::string EncodeBase58(Span< const unsigned char > input)
Why base-58 instead of standard base-64 encoding?
Definition: base58.cpp:89
std::string EncodeBase58Check(Span< const unsigned char > input)
Encode a byte span into a base58-encoded string, including checksum.
Definition: base58.cpp:137
static UniValue CallRPC(BaseRequestHandler *rh, const std::string &strMethod, const std::vector< std::string > &args, const std::optional< std::string > &rpcwallet={})
ChainType
Definition: chaintype.h:11
An encapsulated private key.
Definition: key.h:35
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:123
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:182
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition: server.cpp:521
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:484
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:147
std::string ConsumeRandomLengthString(size_t max_length)
UniValue params
Definition: request.h:40
std::string strMethod
Definition: request.h:39
std::any context
Definition: request.h:45
const std::string & get_str() const
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:233
std::string ToString() const
Definition: uint256.cpp:47
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:366
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:29
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:231
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:294
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:233
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:192
static constexpr TransactionSerParams TX_NO_WITNESS
Definition: transaction.h:196
static constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:195
void initialize_rpc()
Definition: rpc.cpp:344
FUZZ_TARGET(rpc,.init=initialize_rpc)
Definition: rpc.cpp:366
void SetRPCWarmupFinished()
Definition: server.cpp:326
CRPCTable tableRPC
Definition: server.cpp:573
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(Span{std::forward< V >(v)}))
Like the Span constructor, but for (const) unsigned char member types only.
Definition: span.h:297
node::NodeContext m_node
Definition: setup_common.h:65
Testing setup that configures a complete environment.
Definition: setup_common.h:120
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition: util.cpp:230
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:184
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:171
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:162
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition: random.cpp:19
@ ZEROS
Seed with a compile time constant of zeros.
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:35
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1165
std::string EncodeBase64(Span< const unsigned char > input)
std::string EncodeBase32(Span< const unsigned char > input, bool pad)
Base32 encode.
assert(!tx.IsCoinBase())