Bitcoin Core  26.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 <core_io.h>
7 #include <key.h>
8 #include <key_io.h>
9 #include <node/context.h>
10 #include <primitives/block.h>
11 #include <primitives/transaction.h>
12 #include <psbt.h>
13 #include <rpc/blockchain.h>
14 #include <rpc/client.h>
15 #include <rpc/request.h>
16 #include <rpc/server.h>
17 #include <rpc/util.h>
18 #include <span.h>
19 #include <streams.h>
21 #include <test/fuzz/fuzz.h>
22 #include <test/fuzz/util.h>
23 #include <test/util/setup_common.h>
24 #include <tinyformat.h>
25 #include <univalue.h>
26 #include <util/chaintype.h>
27 #include <util/strencodings.h>
28 #include <util/string.h>
29 #include <util/time.h>
30 
31 #include <cstdint>
32 #include <iostream>
33 #include <memory>
34 #include <optional>
35 #include <stdexcept>
36 #include <string>
37 #include <vector>
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)
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);
253  if (!opt_block) {
254  return;
255  }
257  data_stream << *opt_block;
258  r = HexStr(data_stream);
259  },
260  [&] {
261  // hex encoded block header
262  std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
263  if (!opt_block_header) {
264  return;
265  }
266  DataStream data_stream{};
267  data_stream << *opt_block_header;
268  r = HexStr(data_stream);
269  },
270  [&] {
271  // hex encoded tx
272  std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
273  if (!opt_tx) {
274  return;
275  }
277  data_stream << *opt_tx;
278  r = HexStr(data_stream);
279  },
280  [&] {
281  // base64 encoded psbt
282  std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
283  if (!opt_psbt) {
284  return;
285  }
287  data_stream << *opt_psbt;
288  r = EncodeBase64(data_stream);
289  },
290  [&] {
291  // base58 encoded key
292  CKey key = ConsumePrivateKey(fuzzed_data_provider);
293  if (!key.IsValid()) {
294  return;
295  }
296  r = EncodeSecret(key);
297  },
298  [&] {
299  // hex encoded pubkey
300  CKey key = ConsumePrivateKey(fuzzed_data_provider);
301  if (!key.IsValid()) {
302  return;
303  }
304  r = HexStr(key.GetPubKey());
305  });
306  return r;
307 }
308 
309 std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
310 {
311  std::vector<std::string> scalar_arguments;
312  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
313  scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
314  }
315  return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
316 }
317 
318 std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
319 {
320  return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
321 }
322 
323 RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
324 {
325  static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
327  return setup.get();
328 }
329 }; // namespace
330 
332 {
333  rpc_testing_setup = InitializeRPCFuzzTestingSetup();
334  const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
335  for (const std::string& rpc_command : supported_rpc_commands) {
336  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();
337  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();
338  if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
339  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";
340  std::terminate();
341  }
342  if (safe_for_fuzzing && not_safe_for_fuzzing) {
343  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";
344  std::terminate();
345  }
346  }
347  const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
348  if (limit_to_rpc_command_env != nullptr) {
349  g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
350  }
351 }
352 
354 {
355  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
356  SetMockTime(ConsumeTime(fuzzed_data_provider));
357  const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
358  if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
359  return;
360  }
361  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();
362  if (!safe_for_fuzzing) {
363  return;
364  }
365  std::vector<std::string> arguments;
366  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
367  arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
368  }
369  try {
370  rpc_testing_setup->CallRPC(rpc_command, arguments);
371  } catch (const UniValue& json_rpc_error) {
372  const std::string error_msg{json_rpc_error.find_value("message").get_str()};
373  // Once c++20 is allowed, starts_with can be used.
374  // if (error_msg.starts_with("Internal bug detected")) {
375  if (0 == error_msg.rfind("Internal bug detected", 0)) {
376  // Only allow the intentional internal bug
377  assert(error_msg.find("trigger_internal_bug") != std::string::npos);
378  }
379  }
380 }
static const int SERIALIZE_TRANSACTION_NO_WITNESS
A flag that is ORed into the protocol version to designate that a transaction should be (un)serialize...
Definition: transaction.h:32
std::any context
Definition: request.h:38
assert(!tx.IsCoinBase())
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1162
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:188
void initialize_rpc()
Definition: rpc.cpp:331
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:18
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:87
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:348
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:509
const UniValue & find_value(std::string_view key) const
Definition: univalue.cpp:233
std::string strMethod
Definition: request.h:32
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:81
void SetRPCWarmupFinished()
Definition: server.cpp:337
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:109
CRPCTable tableRPC
Definition: server.cpp:606
ChainType
Definition: chaintype.h:11
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
UniValue params
Definition: request.h:33
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:192
std::string ConsumeRandomLengthString(size_t max_length)
std::string ToString() const
Definition: uint256.cpp:55
std::string EncodeBase58Check(Span< const unsigned char > input)
Encode a byte span into a base58-encoded string, including checksum.
Definition: base58.cpp:135
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:35
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:68
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(Span
Like the Span constructor, but for (const) unsigned char member types only.
Definition: span.h:281
FUZZ_TARGET(rpc,.init=initialize_rpc)
Definition: rpc.cpp:353
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:287
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:36
An encapsulated private key.
Definition: key.h:32
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:161
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:170
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition: server.cpp:546
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:227
node::NodeContext m_node
Definition: setup_common.h:50
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:167
Testing setup that configures a complete environment.
Definition: setup_common.h:77
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition: util.cpp:210
bool IsValid() const
Check whether this private key is valid.
Definition: key.h:119
std::string EncodeBase32(Span< const unsigned char > input, bool pad)
Base32 encode.