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