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