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>
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>
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
38namespace {
39struct 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
59RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
60std::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.
65const 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.
85const 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
168std::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
294std::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
303std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
304{
305 return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
306}
307
308RPCFuzzTestingSetup* 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}
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={})
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
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:489
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:451
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
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:240
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:196
std::string EncodeDestination(const CTxDestination &dest)
Definition: key_io.cpp:256
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
void initialize_rpc()
Definition: rpc.cpp:316
FUZZ_TARGET_INIT(rpc, initialize_rpc)
Definition: rpc.cpp:338
@ SER_NETWORK
Definition: serialize.h:138
void SetRPCWarmupFinished()
Definition: server.cpp:337
CRPCTable tableRPC
Definition: server.cpp:548
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
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.
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
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:87
NodeContext m_node
Definition: setup_common.h:78
Testing setup that configures a complete environment.
Definition: setup_common.h:99
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:227
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:310
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:153
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:40
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:144
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:101
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12