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