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