Bitcoin Core  31.0.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>
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  "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.
92 const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
93  "abortprivatebroadcast",
94  "analyzepsbt",
95  "clearbanned",
96  "combinepsbt",
97  "combinerawtransaction",
98  "converttopsbt",
99  "createmultisig",
100  "createpsbt",
101  "createrawtransaction",
102  "decodepsbt",
103  "decoderawtransaction",
104  "decodescript",
105  "deriveaddresses",
106  "descriptorprocesspsbt",
107  "disconnectnode",
108  "echo",
109  "echojson",
110  "estimaterawfee",
111  "estimatesmartfee",
112  "finalizepsbt",
113  "generate",
114  "generateblock",
115  "getaddednodeinfo",
116  "getaddrmaninfo",
117  "getbestblockhash",
118  "getblock",
119  "getblockchaininfo",
120  "getblockcount",
121  "getblockfilter",
122  "getblockfrompeer", // when no peers are connected, no p2p message is sent
123  "getblockhash",
124  "getblockheader",
125  "getblockstats",
126  "getblocktemplate",
127  "getchaintips",
128  "getchainstates",
129  "getchaintxstats",
130  "getconnectioncount",
131  "getdeploymentinfo",
132  "getdescriptoractivity",
133  "getdescriptorinfo",
134  "getdifficulty",
135  "getindexinfo",
136  "getmemoryinfo",
137  "getmempoolancestors",
138  "getmempooldescendants",
139  "getmempoolentry",
140  "getmempoolfeeratediagram",
141  "getmempoolcluster",
142  "getmempoolinfo",
143  "getmininginfo",
144  "getnettotals",
145  "getnetworkhashps",
146  "getnetworkinfo",
147  "getnodeaddresses",
148  "getorphantxs",
149  "getpeerinfo",
150  "getprioritisedtransactions",
151  "getprivatebroadcastinfo",
152  "getrawaddrman",
153  "getrawmempool",
154  "getrawtransaction",
155  "getrpcinfo",
156  "gettxout",
157  "gettxoutsetinfo",
158  "gettxspendingprevout",
159  "help",
160  "invalidateblock",
161  "joinpsbts",
162  "listbanned",
163  "logging",
164  "mockscheduler",
165  "ping",
166  "preciousblock",
167  "prioritisetransaction",
168  "pruneblockchain",
169  "reconsiderblock",
170  "scanblocks",
171  "scantxoutset",
172  "sendmsgtopeer", // when no peers are connected, no p2p message is sent
173  "sendrawtransaction",
174  "setmocktime",
175  "setnetworkactive",
176  "signmessagewithprivkey",
177  "signrawtransactionwithkey",
178  "submitblock",
179  "submitheader",
180  "submitpackage",
181  "syncwithvalidationinterfacequeue",
182  "testmempoolaccept",
183  "uptime",
184  "utxoupdatepsbt",
185  "validateaddress",
186  "verifychain",
187  "verifymessage",
188  "verifytxoutproof",
189  "waitforblock",
190  "waitforblockheight",
191  "waitfornewblock",
192 };
193 
194 std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
195 {
196  const size_t max_string_length = 4096;
197  const size_t max_base58_bytes_length{64};
198  std::string r;
199  CallOneOf(
201  [&] {
202  // string argument
203  r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
204  },
205  [&] {
206  // base64 argument
208  },
209  [&] {
210  // hex argument
211  r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
212  },
213  [&] {
214  // bool argument
215  r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
216  },
217  [&] {
218  // range argument
219  r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
220  },
221  [&] {
222  // integral argument (int64_t)
224  },
225  [&] {
226  // integral argument (uint64_t)
228  },
229  [&] {
230  // floating point argument
232  },
233  [&] {
234  // tx destination argument
236  },
237  [&] {
238  // uint160 argument
240  },
241  [&] {
242  // uint256 argument
244  },
245  [&] {
246  // base32 argument
248  },
249  [&] {
250  // base58 argument
252  },
253  [&] {
254  // base58 argument with checksum
256  },
257  [&] {
258  // hex encoded block
259  std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
260  if (!opt_block) {
261  good_data = false;
262  return;
263  }
264  DataStream data_stream{};
265  data_stream << TX_WITH_WITNESS(*opt_block);
266  r = HexStr(data_stream);
267  },
268  [&] {
269  // hex encoded block header
270  std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
271  if (!opt_block_header) {
272  good_data = false;
273  return;
274  }
275  DataStream data_stream{};
276  data_stream << *opt_block_header;
277  r = HexStr(data_stream);
278  },
279  [&] {
280  // hex encoded tx
281  std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
282  if (!opt_tx) {
283  good_data = false;
284  return;
285  }
286  DataStream data_stream;
287  auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
288  data_stream << allow_witness(*opt_tx);
289  r = HexStr(data_stream);
290  },
291  [&] {
292  // base64 encoded psbt
293  std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
294  if (!opt_psbt) {
295  good_data = false;
296  return;
297  }
298  DataStream data_stream{};
299  data_stream << *opt_psbt;
300  r = EncodeBase64(data_stream);
301  },
302  [&] {
303  // base58 encoded key
305  if (!key.IsValid()) {
306  good_data = false;
307  return;
308  }
309  r = EncodeSecret(key);
310  },
311  [&] {
312  // hex encoded pubkey
314  if (!key.IsValid()) {
315  good_data = false;
316  return;
317  }
318  r = HexStr(key.GetPubKey());
319  });
320  return r;
321 }
322 
323 std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
324 {
325  std::vector<std::string> scalar_arguments;
326  LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
327  {
328  scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
329  }
330  return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
331 }
332 
333 std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
334 {
335  return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
336 }
337 
338 RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
339 {
340  static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
342  return setup.get();
343 }
344 }; // namespace
345 
347 {
348  rpc_testing_setup = InitializeRPCFuzzTestingSetup();
349  const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
350  for (const std::string& rpc_command : supported_rpc_commands) {
351  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();
352  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();
353  if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
354  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";
355  std::terminate();
356  }
357  if (safe_for_fuzzing && not_safe_for_fuzzing) {
358  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";
359  std::terminate();
360  }
361  }
362  const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
363  if (limit_to_rpc_command_env != nullptr) {
364  g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
365  }
366 }
367 
369 {
371  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
372  bool good_data{true};
374  const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
375  if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
376  return;
377  }
378  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();
379  if (!safe_for_fuzzing) {
380  return;
381  }
382  std::vector<std::string> arguments;
383  LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
384  {
385  arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
386  }
387  try {
388  std::optional<test_only_CheckFailuresAreExceptionsNotAborts> maybe_mock{};
389  if (rpc_command == "echo") {
390  // Avoid aborting fuzzing for this specific test-only RPC with an
391  // intentional trigger_internal_bug
392  maybe_mock.emplace();
393  }
394  rpc_testing_setup->CallRPC(rpc_command, arguments);
395  } catch (const UniValue& json_rpc_error) {
396  const std::string error_msg{json_rpc_error.find_value("message").get_str()};
397  if (error_msg.starts_with("Internal bug detected")) {
398  // Only allow the intentional internal bug
399  assert(error_msg.find("trigger_internal_bug") != std::string::npos);
400  }
401  }
402 }
std::any context
Definition: request.h:62
constexpr auto MakeUCharSpan(const V &v) -> decltype(UCharSpanCast(std::span
Like the std::span constructor, but for (const) unsigned char member types only.
Definition: span.h:111
assert(!tx.IsCoinBase())
node::NodeContext m_node
Definition: bitcoin-gui.cpp:43
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:183
std::string EncodeBase32(std::span< const unsigned char > input, bool pad)
Base32 encode.
void initialize_rpc()
Definition: rpc.cpp:346
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
const std::string & get_str() const
std::string EncodeBase64(std::span< const unsigned char > input)
std::string EncodeBase58(std::span< const unsigned char > input)
Why base-58 instead of standard base-64 encoding?
Definition: base58.cpp:89
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:435
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:482
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:232
std::string strMethod
Definition: request.h:56
void SetRPCWarmupFinished()
Definition: server.cpp:324
CRPCTable tableRPC
Definition: server.cpp:544
ChainType
Definition: chaintype.h:11
UniValue params
Definition: request.h:57
static UniValue CallRPC(BaseRequestHandler *rh, const std::string &strMethod, const std::vector< std::string > &args, const std::optional< std::string > &rpcwallet={})
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:132
std::string ConsumeRandomLengthString(size_t max_length)
std::string ToString() const
Definition: uint256.cpp:21
std::string EncodeBase58Check(std::span< const unsigned char > input)
Encode a byte span into a base58-encoded string, including checksum.
Definition: base58.cpp:137
NodeSeconds ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition: random.cpp:19
FuzzedDataProvider & fuzzed_data_provider
Definition: fees.cpp:38
static int setup(void)
Definition: tests.c:7808
FUZZ_TARGET(rpc,.init=initialize_rpc)
Definition: rpc.cpp:368
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:44
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:294
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
An encapsulated private key.
Definition: key.h:35
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:158
Seed with a compile time constant of zeros.
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:167
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition: server.cpp:519
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:231
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:205
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
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:189
Testing setup that configures a complete environment.
Definition: setup_common.h:121
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:246
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition: util.cpp:235
static constexpr TransactionSerParams TX_NO_WITNESS
Definition: transaction.h:181
static constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:180
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:124