Bitcoin Core 29.99.0
P2P Digital Currency
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 "enumeratesigners",
79 "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
80 "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
81 "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
82 "gettxoutproof", // avoid prohibitively slow execution
83 "importmempool", // avoid reading from disk
84 "loadtxoutset", // avoid reading from disk
85 "loadwallet", // avoid reading from disk
86 "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
87 "setban", // avoid DNS lookups
88 "stop", // avoid shutdown state
89};
90
91// RPC commands which are safe for fuzzing.
92const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
93 "analyzepsbt",
94 "clearbanned",
95 "combinepsbt",
96 "combinerawtransaction",
97 "converttopsbt",
98 "createmultisig",
99 "createpsbt",
100 "createrawtransaction",
101 "decodepsbt",
102 "decoderawtransaction",
103 "decodescript",
104 "deriveaddresses",
105 "descriptorprocesspsbt",
106 "disconnectnode",
107 "echo",
108 "echojson",
109 "estimaterawfee",
110 "estimatesmartfee",
111 "finalizepsbt",
112 "generate",
113 "generateblock",
114 "getaddednodeinfo",
115 "getaddrmaninfo",
116 "getbestblockhash",
117 "getblock",
118 "getblockchaininfo",
119 "getblockcount",
120 "getblockfilter",
121 "getblockfrompeer", // when no peers are connected, no p2p message is sent
122 "getblockhash",
123 "getblockheader",
124 "getblockstats",
125 "getblocktemplate",
126 "getchaintips",
127 "getchainstates",
128 "getchaintxstats",
129 "getconnectioncount",
130 "getdeploymentinfo",
131 "getdescriptoractivity",
132 "getdescriptorinfo",
133 "getdifficulty",
134 "getindexinfo",
135 "getmemoryinfo",
136 "getmempoolancestors",
137 "getmempooldescendants",
138 "getmempoolentry",
139 "getmempoolinfo",
140 "getmininginfo",
141 "getnettotals",
142 "getnetworkhashps",
143 "getnetworkinfo",
144 "getnodeaddresses",
145 "getorphantxs",
146 "getpeerinfo",
147 "getprioritisedtransactions",
148 "getrawaddrman",
149 "getrawmempool",
150 "getrawtransaction",
151 "getrpcinfo",
152 "gettxout",
153 "gettxoutsetinfo",
154 "gettxspendingprevout",
155 "help",
156 "invalidateblock",
157 "joinpsbts",
158 "listbanned",
159 "logging",
160 "mockscheduler",
161 "ping",
162 "preciousblock",
163 "prioritisetransaction",
164 "pruneblockchain",
165 "reconsiderblock",
166 "scanblocks",
167 "scantxoutset",
168 "sendmsgtopeer", // when no peers are connected, no p2p message is sent
169 "sendrawtransaction",
170 "setmocktime",
171 "setnetworkactive",
172 "signmessagewithprivkey",
173 "signrawtransactionwithkey",
174 "submitblock",
175 "submitheader",
176 "submitpackage",
177 "syncwithvalidationinterfacequeue",
178 "testmempoolaccept",
179 "uptime",
180 "utxoupdatepsbt",
181 "validateaddress",
182 "verifychain",
183 "verifymessage",
184 "verifytxoutproof",
185 "waitforblock",
186 "waitforblockheight",
187 "waitfornewblock",
188};
189
190std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
191{
192 const size_t max_string_length = 4096;
193 const size_t max_base58_bytes_length{64};
194 std::string r;
195 CallOneOf(
196 fuzzed_data_provider,
197 [&] {
198 // string argument
199 r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
200 },
201 [&] {
202 // base64 argument
203 r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
204 },
205 [&] {
206 // hex argument
207 r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
208 },
209 [&] {
210 // bool argument
211 r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
212 },
213 [&] {
214 // range argument
215 r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
216 },
217 [&] {
218 // integral argument (int64_t)
219 r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
220 },
221 [&] {
222 // integral argument (uint64_t)
223 r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
224 },
225 [&] {
226 // floating point argument
227 r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
228 },
229 [&] {
230 // tx destination argument
231 r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
232 },
233 [&] {
234 // uint160 argument
235 r = ConsumeUInt160(fuzzed_data_provider).ToString();
236 },
237 [&] {
238 // uint256 argument
239 r = ConsumeUInt256(fuzzed_data_provider).ToString();
240 },
241 [&] {
242 // base32 argument
243 r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
244 },
245 [&] {
246 // base58 argument
247 r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
248 },
249 [&] {
250 // base58 argument with checksum
251 r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
252 },
253 [&] {
254 // hex encoded block
255 std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
256 if (!opt_block) {
257 good_data = false;
258 return;
259 }
260 DataStream data_stream{};
261 data_stream << TX_WITH_WITNESS(*opt_block);
262 r = HexStr(data_stream);
263 },
264 [&] {
265 // hex encoded block header
266 std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
267 if (!opt_block_header) {
268 good_data = false;
269 return;
270 }
271 DataStream data_stream{};
272 data_stream << *opt_block_header;
273 r = HexStr(data_stream);
274 },
275 [&] {
276 // hex encoded tx
277 std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
278 if (!opt_tx) {
279 good_data = false;
280 return;
281 }
282 DataStream data_stream;
283 auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
284 data_stream << allow_witness(*opt_tx);
285 r = HexStr(data_stream);
286 },
287 [&] {
288 // base64 encoded psbt
289 std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
290 if (!opt_psbt) {
291 good_data = false;
292 return;
293 }
294 DataStream data_stream{};
295 data_stream << *opt_psbt;
296 r = EncodeBase64(data_stream);
297 },
298 [&] {
299 // base58 encoded key
300 CKey key = ConsumePrivateKey(fuzzed_data_provider);
301 if (!key.IsValid()) {
302 good_data = false;
303 return;
304 }
305 r = EncodeSecret(key);
306 },
307 [&] {
308 // hex encoded pubkey
309 CKey key = ConsumePrivateKey(fuzzed_data_provider);
310 if (!key.IsValid()) {
311 good_data = false;
312 return;
313 }
314 r = HexStr(key.GetPubKey());
315 });
316 return r;
317}
318
319std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
320{
321 std::vector<std::string> scalar_arguments;
322 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
323 {
324 scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
325 }
326 return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
327}
328
329std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
330{
331 return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
332}
333
334RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
335{
336 static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
338 return setup.get();
339}
340}; // namespace
341
343{
344 rpc_testing_setup = InitializeRPCFuzzTestingSetup();
345 const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
346 for (const std::string& rpc_command : supported_rpc_commands) {
347 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();
348 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();
349 if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
350 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";
351 std::terminate();
352 }
353 if (safe_for_fuzzing && not_safe_for_fuzzing) {
354 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";
355 std::terminate();
356 }
357 }
358 const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
359 if (limit_to_rpc_command_env != nullptr) {
360 g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
361 }
362}
363
365{
367 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
368 bool good_data{true};
369 SetMockTime(ConsumeTime(fuzzed_data_provider));
370 const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
371 if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
372 return;
373 }
374 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();
375 if (!safe_for_fuzzing) {
376 return;
377 }
378 std::vector<std::string> arguments;
379 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
380 {
381 arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
382 }
383 try {
384 rpc_testing_setup->CallRPC(rpc_command, arguments);
385 } catch (const UniValue& json_rpc_error) {
386 const std::string error_msg{json_rpc_error.find_value("message").get_str()};
387 if (error_msg.starts_with("Internal bug detected")) {
388 // Only allow the intentional internal bug
389 assert(error_msg.find("trigger_internal_bug") != std::string::npos);
390 }
391 }
392}
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::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:21
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:356
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
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: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:342
FUZZ_TARGET(rpc,.init=initialize_rpc)
Definition: rpc.cpp:364
void SetRPCWarmupFinished()
Definition: server.cpp:326
CRPCTable tableRPC
Definition: server.cpp:573
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:66
Testing setup that configures a complete environment.
Definition: setup_common.h:121
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.
#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)
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:40
assert(!tx.IsCoinBase())