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  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  "getmempoolinfo",
132  "getmininginfo",
133  "getnettotals",
134  "getnetworkhashps",
135  "getnetworkinfo",
136  "getnodeaddresses",
137  "getpeerinfo",
138  "getrawmempool",
139  "getrawtransaction",
140  "getrpcinfo",
141  "gettxout",
142  "gettxoutsetinfo",
143  "help",
144  "invalidateblock",
145  "joinpsbts",
146  "listbanned",
147  "logging",
148  "mockscheduler",
149  "ping",
150  "preciousblock",
151  "pruneblockchain",
152  "reconsiderblock",
153  "scantxoutset",
154  "sendrawtransaction",
155  "setmocktime",
156  "setnetworkactive",
157  "signmessagewithprivkey",
158  "signrawtransactionwithkey",
159  "submitblock",
160  "submitheader",
161  "syncwithvalidationinterfacequeue",
162  "testmempoolaccept",
163  "uptime",
164  "utxoupdatepsbt",
165  "validateaddress",
166  "verifychain",
167  "verifymessage",
168  "verifytxoutproof",
169  "waitforblock",
170  "waitforblockheight",
171  "waitfornewblock",
172 };
173 
174 std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
175 {
176  const size_t max_string_length = 4096;
177  const size_t max_base58_bytes_length{64};
178  std::string r;
179  CallOneOf(
180  fuzzed_data_provider,
181  [&] {
182  // string argument
183  r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
184  },
185  [&] {
186  // base64 argument
187  r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
188  },
189  [&] {
190  // hex argument
191  r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
192  },
193  [&] {
194  // bool argument
195  r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
196  },
197  [&] {
198  // range argument
199  r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
200  },
201  [&] {
202  // integral argument (int64_t)
203  r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
204  },
205  [&] {
206  // integral argument (uint64_t)
207  r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
208  },
209  [&] {
210  // floating point argument
211  r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
212  },
213  [&] {
214  // tx destination argument
215  r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
216  },
217  [&] {
218  // uint160 argument
219  r = ConsumeUInt160(fuzzed_data_provider).ToString();
220  },
221  [&] {
222  // uint256 argument
223  r = ConsumeUInt256(fuzzed_data_provider).ToString();
224  },
225  [&] {
226  // base32 argument
227  r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
228  },
229  [&] {
230  // base58 argument
231  r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
232  },
233  [&] {
234  // base58 argument with checksum
235  r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
236  },
237  [&] {
238  // hex encoded block
239  std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
240  if (!opt_block) {
241  return;
242  }
244  data_stream << *opt_block;
245  r = HexStr(data_stream);
246  },
247  [&] {
248  // hex encoded block header
249  std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
250  if (!opt_block_header) {
251  return;
252  }
254  data_stream << *opt_block_header;
255  r = HexStr(data_stream);
256  },
257  [&] {
258  // hex encoded tx
259  std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
260  if (!opt_tx) {
261  return;
262  }
264  data_stream << *opt_tx;
265  r = HexStr(data_stream);
266  },
267  [&] {
268  // base64 encoded psbt
269  std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
270  if (!opt_psbt) {
271  return;
272  }
274  data_stream << *opt_psbt;
275  r = EncodeBase64(data_stream);
276  },
277  [&] {
278  // base58 encoded key
279  const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
280  CKey key;
281  key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
282  if (!key.IsValid()) {
283  return;
284  }
285  r = EncodeSecret(key);
286  },
287  [&] {
288  // hex encoded pubkey
289  const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
290  CKey key;
291  key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
292  if (!key.IsValid()) {
293  return;
294  }
295  r = HexStr(key.GetPubKey());
296  });
297  return r;
298 }
299 
300 std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
301 {
302  std::vector<std::string> scalar_arguments;
303  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
304  scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
305  }
306  return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
307 }
308 
309 std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
310 {
311  return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
312 }
313 
314 RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
315 {
316  static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
318  return setup.get();
319 }
320 }; // namespace
321 
323 {
324  rpc_testing_setup = InitializeRPCFuzzTestingSetup();
325  const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
326  for (const std::string& rpc_command : supported_rpc_commands) {
327  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();
328  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();
329  if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
330  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";
331  std::terminate();
332  }
333  if (safe_for_fuzzing && not_safe_for_fuzzing) {
334  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";
335  std::terminate();
336  }
337  }
338  const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
339  if (limit_to_rpc_command_env != nullptr) {
340  g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
341  }
342 }
343 
345 {
346  FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
347  SetMockTime(ConsumeTime(fuzzed_data_provider));
348  const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
349  if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
350  return;
351  }
352  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();
353  if (!safe_for_fuzzing) {
354  return;
355  }
356  std::vector<std::string> arguments;
357  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
358  arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
359  }
360  try {
361  rpc_testing_setup->CallRPC(rpc_command, arguments);
362  } catch (const UniValue& json_rpc_error) {
363  const std::string error_msg{find_value(json_rpc_error, "message").get_str()};
364  // Once c++20 is allowed, starts_with can be used.
365  // if (error_msg.starts_with("Internal bug detected")) {
366  if (0 == error_msg.rfind("Internal bug detected", 0)) {
367  // Only allow the intentional internal bug
368  assert(error_msg.find("trigger_internal_bug") != std::string::npos);
369  }
370  }
371 }
ConsumeUInt256
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:195
block.h
MakeUCharSpan
constexpr auto MakeUCharSpan(V &&v) -> decltype(UCharSpanCast(Span
Like the Span constructor, but for (const) unsigned char member types only.
Definition: span.h:285
ToString
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:87
BasicTestingSetup::m_node
node::NodeContext m_node
Definition: setup_common.h:85
assert
assert(!tx.IsCoinBase())
JSONRPCRequest::context
std::any context
Definition: request.h:38
key_io.h
streams.h
setup_common.h
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:246
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:417
UniValue
Definition: univalue.h:17
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:281
UniValue::get_str
const std::string & get_str() const
Definition: univalue_get.cpp:98
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:216
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:707
CallOneOf
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:82
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:186
base_blob::ToString
std::string ToString() const
Definition: uint256.cpp:64
LIMITED_WHILE
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:18
request.h
EncodeBase32
std::string EncodeBase32(Span< const unsigned char > input, bool pad)
Base32 encode.
Definition: strencodings.cpp:200
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
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:107
CDataStream
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:184
base58.h
SER_NETWORK
@ SER_NETWORK
Definition: serialize.h:131
initialize_rpc
void initialize_rpc()
Definition: rpc.cpp:322
JSONRPCRequest
Definition: request.h:28
blockchain.h
find_value
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:236
FuzzedDataProvider::ConsumeBool
bool ConsumeBool()
Definition: FuzzedDataProvider.h:288
FUZZ_TARGET_INIT
FUZZ_TARGET_INIT(rpc, initialize_rpc)
Definition: rpc.cpp:344
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:511
client.h
EncodeDestination
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:276
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