Bitcoin Core  29.1.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  "getdescriptoractivity",
134  "getdescriptorinfo",
135  "getdifficulty",
136  "getindexinfo",
137  "getmemoryinfo",
138  "getmempoolancestors",
139  "getmempooldescendants",
140  "getmempoolentry",
141  "getmempoolinfo",
142  "getmininginfo",
143  "getnettotals",
144  "getnetworkhashps",
145  "getnetworkinfo",
146  "getnodeaddresses",
147  "getorphantxs",
148  "getpeerinfo",
149  "getprioritisedtransactions",
150  "getrawaddrman",
151  "getrawmempool",
152  "getrawtransaction",
153  "getrpcinfo",
154  "gettxout",
155  "gettxoutsetinfo",
156  "gettxspendingprevout",
157  "help",
158  "invalidateblock",
159  "joinpsbts",
160  "listbanned",
161  "logging",
162  "mockscheduler",
163  "ping",
164  "preciousblock",
165  "prioritisetransaction",
166  "pruneblockchain",
167  "reconsiderblock",
168  "scanblocks",
169  "scantxoutset",
170  "sendmsgtopeer", // when no peers are connected, no p2p message is sent
171  "sendrawtransaction",
172  "setmocktime",
173  "setnetworkactive",
174  "signmessagewithprivkey",
175  "signrawtransactionwithkey",
176  "submitblock",
177  "submitheader",
178  "submitpackage",
179  "syncwithvalidationinterfacequeue",
180  "testmempoolaccept",
181  "uptime",
182  "utxoupdatepsbt",
183  "validateaddress",
184  "verifychain",
185  "verifymessage",
186  "verifytxoutproof",
187  "waitforblock",
188  "waitforblockheight",
189  "waitfornewblock",
190 };
191 
192 std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
193 {
194  const size_t max_string_length = 4096;
195  const size_t max_base58_bytes_length{64};
196  std::string r;
197  CallOneOf(
198  fuzzed_data_provider,
199  [&] {
200  // string argument
201  r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
202  },
203  [&] {
204  // base64 argument
205  r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
206  },
207  [&] {
208  // hex argument
209  r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
210  },
211  [&] {
212  // bool argument
213  r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
214  },
215  [&] {
216  // range argument
217  r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
218  },
219  [&] {
220  // integral argument (int64_t)
221  r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
222  },
223  [&] {
224  // integral argument (uint64_t)
225  r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
226  },
227  [&] {
228  // floating point argument
229  r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
230  },
231  [&] {
232  // tx destination argument
233  r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
234  },
235  [&] {
236  // uint160 argument
237  r = ConsumeUInt160(fuzzed_data_provider).ToString();
238  },
239  [&] {
240  // uint256 argument
241  r = ConsumeUInt256(fuzzed_data_provider).ToString();
242  },
243  [&] {
244  // base32 argument
245  r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
246  },
247  [&] {
248  // base58 argument
249  r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
250  },
251  [&] {
252  // base58 argument with checksum
253  r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
254  },
255  [&] {
256  // hex encoded block
257  std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
258  if (!opt_block) {
259  good_data = false;
260  return;
261  }
262  DataStream data_stream{};
263  data_stream << TX_WITH_WITNESS(*opt_block);
264  r = HexStr(data_stream);
265  },
266  [&] {
267  // hex encoded block header
268  std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
269  if (!opt_block_header) {
270  good_data = false;
271  return;
272  }
273  DataStream data_stream{};
274  data_stream << *opt_block_header;
275  r = HexStr(data_stream);
276  },
277  [&] {
278  // hex encoded tx
279  std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
280  if (!opt_tx) {
281  good_data = false;
282  return;
283  }
284  DataStream data_stream;
285  auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
286  data_stream << allow_witness(*opt_tx);
287  r = HexStr(data_stream);
288  },
289  [&] {
290  // base64 encoded psbt
291  std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
292  if (!opt_psbt) {
293  good_data = false;
294  return;
295  }
296  DataStream data_stream{};
297  data_stream << *opt_psbt;
298  r = EncodeBase64(data_stream);
299  },
300  [&] {
301  // base58 encoded key
302  CKey key = ConsumePrivateKey(fuzzed_data_provider);
303  if (!key.IsValid()) {
304  good_data = false;
305  return;
306  }
307  r = EncodeSecret(key);
308  },
309  [&] {
310  // hex encoded pubkey
311  CKey key = ConsumePrivateKey(fuzzed_data_provider);
312  if (!key.IsValid()) {
313  good_data = false;
314  return;
315  }
316  r = HexStr(key.GetPubKey());
317  });
318  return r;
319 }
320 
321 std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
322 {
323  std::vector<std::string> scalar_arguments;
324  LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
325  {
326  scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
327  }
328  return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
329 }
330 
331 std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
332 {
333  return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
334 }
335 
336 RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
337 {
338  static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
340  return setup.get();
341 }
342 }; // namespace
343 
345 {
346  rpc_testing_setup = InitializeRPCFuzzTestingSetup();
347  const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
348  for (const std::string& rpc_command : supported_rpc_commands) {
349  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();
350  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();
351  if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
352  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";
353  std::terminate();
354  }
355  if (safe_for_fuzzing && not_safe_for_fuzzing) {
356  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";
357  std::terminate();
358  }
359  }
360  const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
361  if (limit_to_rpc_command_env != nullptr) {
362  g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
363  }
364 }
365 
367 {
369  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
370  bool good_data{true};
371  SetMockTime(ConsumeTime(fuzzed_data_provider));
372  const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
373  if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
374  return;
375  }
376  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();
377  if (!safe_for_fuzzing) {
378  return;
379  }
380  std::vector<std::string> arguments;
381  LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
382  {
383  arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
384  }
385  try {
386  rpc_testing_setup->CallRPC(rpc_command, arguments);
387  } catch (const UniValue& json_rpc_error) {
388  const std::string error_msg{json_rpc_error.find_value("message").get_str()};
389  if (error_msg.starts_with("Internal bug detected")) {
390  // Only allow the intentional internal bug
391  assert(error_msg.find("trigger_internal_bug") != std::string::npos);
392  }
393  }
394 }
std::any context
Definition: request.h:45
assert(!tx.IsCoinBase())
node::NodeContext m_node
Definition: bitcoin-gui.cpp:42
#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:182
void initialize_rpc()
Definition: rpc.cpp:344
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
std::string EncodeBase64(Span< const unsigned char > input)
const std::string & get_str() const
std::string EncodeBase58(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 positional arguments to command-specific RPC representation.
Definition: client.cpp:366
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:484
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:233
std::string strMethod
Definition: request.h:39
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:40
void SetRPCWarmupFinished()
Definition: server.cpp:326
CRPCTable tableRPC
Definition: server.cpp:573
ChainType
Definition: chaintype.h:11
UniValue params
Definition: request.h:40
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:146
std::string ConsumeRandomLengthString(size_t max_length)
std::string ToString() const
Definition: uint256.cpp:47
std::string EncodeBase58Check(Span< const unsigned char > input)
Encode a byte span into a base58-encoded string, including checksum.
Definition: base58.cpp:137
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition: random.cpp:19
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
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
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(Span
Like the Span constructor, but for (const) unsigned char member types only.
Definition: span.h:296
FUZZ_TARGET(rpc,.init=initialize_rpc)
Definition: rpc.cpp:366
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:34
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:162
Seed with a compile time constant of zeros.
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:171
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition: server.cpp:521
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:192
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:184
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:233
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition: util.cpp:230
static constexpr TransactionSerParams TX_NO_WITNESS
Definition: transaction.h:196
static constexpr TransactionSerParams TX_WITH_WITNESS
Definition: transaction.h:195
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:123
std::string EncodeBase32(Span< const unsigned char > input, bool pad)
Base32 encode.