Bitcoin Core  24.1.0
P2P Digital Currency
rpc.cpp
Go to the documentation of this file.
1 // Copyright (c) 2021 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  "scantxoutset",
155  "sendrawtransaction",
156  "setmocktime",
157  "setnetworkactive",
158  "signmessagewithprivkey",
159  "signrawtransactionwithkey",
160  "submitblock",
161  "submitheader",
162  "submitpackage",
163  "syncwithvalidationinterfacequeue",
164  "testmempoolaccept",
165  "uptime",
166  "utxoupdatepsbt",
167  "validateaddress",
168  "verifychain",
169  "verifymessage",
170  "verifytxoutproof",
171  "waitforblock",
172  "waitforblockheight",
173  "waitfornewblock",
174 };
175 
176 std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
177 {
178  const size_t max_string_length = 4096;
179  const size_t max_base58_bytes_length{64};
180  std::string r;
181  CallOneOf(
182  fuzzed_data_provider,
183  [&] {
184  // string argument
185  r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
186  },
187  [&] {
188  // base64 argument
189  r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
190  },
191  [&] {
192  // hex argument
193  r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
194  },
195  [&] {
196  // bool argument
197  r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
198  },
199  [&] {
200  // range argument
201  r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
202  },
203  [&] {
204  // integral argument (int64_t)
205  r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
206  },
207  [&] {
208  // integral argument (uint64_t)
209  r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
210  },
211  [&] {
212  // floating point argument
213  r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
214  },
215  [&] {
216  // tx destination argument
217  r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
218  },
219  [&] {
220  // uint160 argument
221  r = ConsumeUInt160(fuzzed_data_provider).ToString();
222  },
223  [&] {
224  // uint256 argument
225  r = ConsumeUInt256(fuzzed_data_provider).ToString();
226  },
227  [&] {
228  // base32 argument
229  r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
230  },
231  [&] {
232  // base58 argument
233  r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
234  },
235  [&] {
236  // base58 argument with checksum
237  r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
238  },
239  [&] {
240  // hex encoded block
241  std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
242  if (!opt_block) {
243  return;
244  }
246  data_stream << *opt_block;
247  r = HexStr(data_stream);
248  },
249  [&] {
250  // hex encoded block header
251  std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
252  if (!opt_block_header) {
253  return;
254  }
256  data_stream << *opt_block_header;
257  r = HexStr(data_stream);
258  },
259  [&] {
260  // hex encoded tx
261  std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
262  if (!opt_tx) {
263  return;
264  }
266  data_stream << *opt_tx;
267  r = HexStr(data_stream);
268  },
269  [&] {
270  // base64 encoded psbt
271  std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
272  if (!opt_psbt) {
273  return;
274  }
276  data_stream << *opt_psbt;
277  r = EncodeBase64(data_stream);
278  },
279  [&] {
280  // base58 encoded key
281  const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
282  CKey key;
283  key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
284  if (!key.IsValid()) {
285  return;
286  }
287  r = EncodeSecret(key);
288  },
289  [&] {
290  // hex encoded pubkey
291  const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
292  CKey key;
293  key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
294  if (!key.IsValid()) {
295  return;
296  }
297  r = HexStr(key.GetPubKey());
298  });
299  return r;
300 }
301 
302 std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
303 {
304  std::vector<std::string> scalar_arguments;
305  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
306  scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
307  }
308  return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
309 }
310 
311 std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
312 {
313  return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
314 }
315 
316 RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
317 {
318  static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
320  return setup.get();
321 }
322 }; // namespace
323 
325 {
326  rpc_testing_setup = InitializeRPCFuzzTestingSetup();
327  const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
328  for (const std::string& rpc_command : supported_rpc_commands) {
329  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();
330  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();
331  if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
332  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";
333  std::terminate();
334  }
335  if (safe_for_fuzzing && not_safe_for_fuzzing) {
336  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";
337  std::terminate();
338  }
339  }
340  const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
341  if (limit_to_rpc_command_env != nullptr) {
342  g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
343  }
344 }
345 
347 {
348  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
349  SetMockTime(ConsumeTime(fuzzed_data_provider));
350  const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
351  if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
352  return;
353  }
354  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();
355  if (!safe_for_fuzzing) {
356  return;
357  }
358  std::vector<std::string> arguments;
359  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
360  arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
361  }
362  try {
363  rpc_testing_setup->CallRPC(rpc_command, arguments);
364  } catch (const UniValue& json_rpc_error) {
365  const std::string error_msg{find_value(json_rpc_error, "message").get_str()};
366  // Once c++20 is allowed, starts_with can be used.
367  // if (error_msg.starts_with("Internal bug detected")) {
368  if (0 == error_msg.rfind("Internal bug detected", 0)) {
369  // Only allow the intentional internal bug
370  assert(error_msg.find("trigger_internal_bug") != std::string::npos);
371  }
372  }
373 }
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:31
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:1164
void initialize_rpc()
Definition: rpc.cpp:324
#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
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:185
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:233
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:255
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:447
std::string strMethod
Definition: request.h:32
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:91
void SetRPCWarmupFinished()
Definition: server.cpp:334
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:109
CRPCTable tableRPC
Definition: server.cpp:544
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={})
std::string ConsumeRandomLengthString(size_t max_length)
std::string ToString() const
Definition: uint256.cpp:64
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< T > ConsumeBytes(size_t num_bytes)
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:307
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:285
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:276
FUZZ_TARGET_INIT(rpc, initialize_rpc)
Definition: rpc.cpp:346
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:89
An encapsulated private key.
Definition: key.h:26
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:193
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:202
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition: server.cpp:485
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:216
node::NodeContext m_node
Definition: setup_common.h:84
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:443
Testing setup that configures a complete environment.
Definition: setup_common.h:109
std::string EncodeBase32(Span< const unsigned char > input, bool pad)
Base32 encode.