Bitcoin Core  27.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>
19 #include <test/util/setup_common.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>
37 enum class ChainType;
38 
39 using util::Join;
40 using util::ToString;
41 
42 namespace {
43 struct 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 
67 RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
68 std::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.
73 const 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.
94 const 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  "getdescriptorinfo",
134  "getdifficulty",
135  "getindexinfo",
136  "getmemoryinfo",
137  "getmempoolancestors",
138  "getmempooldescendants",
139  "getmempoolentry",
140  "getmempoolinfo",
141  "getmininginfo",
142  "getnettotals",
143  "getnetworkhashps",
144  "getnetworkinfo",
145  "getnodeaddresses",
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 
190 std::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 
319 std::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 
329 std::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 
334 RPCFuzzTestingSetup* 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 {
366  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
367  bool good_data{true};
368  SetMockTime(ConsumeTime(fuzzed_data_provider));
369  const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
370  if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
371  return;
372  }
373  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();
374  if (!safe_for_fuzzing) {
375  return;
376  }
377  std::vector<std::string> arguments;
378  LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
379  {
380  arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
381  }
382  try {
383  rpc_testing_setup->CallRPC(rpc_command, arguments);
384  } catch (const UniValue& json_rpc_error) {
385  const std::string error_msg{json_rpc_error.find_value("message").get_str()};
386  if (error_msg.starts_with("Internal bug detected")) {
387  // Only allow the intentional internal bug
388  assert(error_msg.find("trigger_internal_bug") != std::string::npos);
389  }
390  }
391 }
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={})
node::NodeContext m_node
Definition: bitcoin-gui.cpp:37
ChainType
Definition: chaintype.h:11
An encapsulated private key.
Definition: key.h:33
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:121
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:538
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:501
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:360
#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:227
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:287
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:148
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:107
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:343
CRPCTable tableRPC
Definition: server.cpp:590
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:304
Testing setup that configures a complete environment.
Definition: setup_common.h:94
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:227
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:184
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:169
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:160
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:32
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1161
std::string EncodeBase64(Span< const unsigned char > input)
std::string EncodeBase32(Span< const unsigned char > input, bool pad)
Base32 encode.
assert(!tx.IsCoinBase())