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