Bitcoin Core 31.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
rpc.cpp
Go to the documentation of this file.
1// Copyright (c) 2021-present 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 <key.h>
7#include <key_io.h>
8#include <primitives/block.h>
10#include <psbt.h>
11#include <rpc/client.h>
12#include <rpc/request.h>
13#include <rpc/server.h>
14#include <span.h>
15#include <streams.h>
17#include <test/fuzz/fuzz.h>
18#include <test/fuzz/util.h>
20#include <tinyformat.h>
21#include <uint256.h>
22#include <univalue.h>
23#include <util/strencodings.h>
24#include <util/string.h>
25#include <util/time.h>
26
27#include <algorithm>
28#include <cassert>
29#include <cstdint>
30#include <cstdlib>
31#include <exception>
32#include <iostream>
33#include <memory>
34#include <optional>
35#include <stdexcept>
36#include <vector>
37enum class ChainType;
38
39using util::Join;
40using util::ToString;
41
42namespace {
43struct RPCFuzzTestingSetup : public TestingSetup {
44 RPCFuzzTestingSetup(const ChainType chain_type, TestOpts opts) : TestingSetup{chain_type, opts}
45 {
46 }
47
48 void CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments)
49 {
50 JSONRPCRequest request;
51 request.context = &m_node;
52 request.strMethod = rpc_method;
53 try {
54 request.params = RPCConvertValues(rpc_method, arguments);
55 } catch (const std::runtime_error&) {
56 return;
57 }
58 tableRPC.execute(request);
59 }
60
61 std::vector<std::string> GetRPCCommands() const
62 {
63 return tableRPC.listCommands();
64 }
65};
66
67RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
68std::string g_limit_to_rpc_command;
69
70// RPC commands which are not appropriate for fuzzing: such as RPC commands
71// reading or writing to a filename passed as an RPC parameter, RPC commands
72// resulting in network activity, etc.
73const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
74 "addconnection", // avoid DNS lookups
75 "addnode", // avoid DNS lookups
76 "addpeeraddress", // avoid DNS lookups
77 "dumptxoutset", // avoid writing to disk
78 "enumeratesigners",
79 "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
80 "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
81 "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
82 "gettxoutproof", // avoid prohibitively slow execution
83 "importmempool", // avoid reading from disk
84 "loadtxoutset", // avoid reading from disk
85 "loadwallet", // avoid reading from disk
86 "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
87 "setban", // avoid DNS lookups
88 "stop", // avoid shutdown state
89};
90
91// RPC commands which are safe for fuzzing.
92const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
93 "abortprivatebroadcast",
94 "analyzepsbt",
95 "clearbanned",
96 "combinepsbt",
97 "combinerawtransaction",
98 "converttopsbt",
99 "createmultisig",
100 "createpsbt",
101 "createrawtransaction",
102 "decodepsbt",
103 "decoderawtransaction",
104 "decodescript",
105 "deriveaddresses",
106 "descriptorprocesspsbt",
107 "disconnectnode",
108 "echo",
109 "echojson",
110 "estimaterawfee",
111 "estimatesmartfee",
112 "finalizepsbt",
113 "generate",
114 "generateblock",
115 "getaddednodeinfo",
116 "getaddrmaninfo",
117 "getbestblockhash",
118 "getblock",
119 "getblockchaininfo",
120 "getblockcount",
121 "getblockfilter",
122 "getblockfrompeer", // when no peers are connected, no p2p message is sent
123 "getblockhash",
124 "getblockheader",
125 "getblockstats",
126 "getblocktemplate",
127 "getchaintips",
128 "getchainstates",
129 "getchaintxstats",
130 "getconnectioncount",
131 "getdeploymentinfo",
132 "getdescriptoractivity",
133 "getdescriptorinfo",
134 "getdifficulty",
135 "getindexinfo",
136 "getmemoryinfo",
137 "getmempoolancestors",
138 "getmempooldescendants",
139 "getmempoolentry",
140 "getmempoolfeeratediagram",
141 "getmempoolcluster",
142 "getmempoolinfo",
143 "getmininginfo",
144 "getnettotals",
145 "getnetworkhashps",
146 "getnetworkinfo",
147 "getnodeaddresses",
148 "getorphantxs",
149 "getpeerinfo",
150 "getprioritisedtransactions",
151 "getprivatebroadcastinfo",
152 "getrawaddrman",
153 "getrawmempool",
154 "getrawtransaction",
155 "getrpcinfo",
156 "gettxout",
157 "gettxoutsetinfo",
158 "gettxspendingprevout",
159 "help",
160 "invalidateblock",
161 "joinpsbts",
162 "listbanned",
163 "logging",
164 "mockscheduler",
165 "ping",
166 "preciousblock",
167 "prioritisetransaction",
168 "pruneblockchain",
169 "reconsiderblock",
170 "scanblocks",
171 "scantxoutset",
172 "sendmsgtopeer", // when no peers are connected, no p2p message is sent
173 "sendrawtransaction",
174 "setmocktime",
175 "setnetworkactive",
176 "signmessagewithprivkey",
177 "signrawtransactionwithkey",
178 "submitblock",
179 "submitheader",
180 "submitpackage",
181 "syncwithvalidationinterfacequeue",
182 "testmempoolaccept",
183 "uptime",
184 "utxoupdatepsbt",
185 "validateaddress",
186 "verifychain",
187 "verifymessage",
188 "verifytxoutproof",
189 "waitforblock",
190 "waitforblockheight",
191 "waitfornewblock",
192};
193
194std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
195{
196 const size_t max_string_length = 4096;
197 const size_t max_base58_bytes_length{64};
198 std::string r;
199 CallOneOf(
200 fuzzed_data_provider,
201 [&] {
202 // string argument
203 r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
204 },
205 [&] {
206 // base64 argument
207 r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
208 },
209 [&] {
210 // hex argument
211 r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
212 },
213 [&] {
214 // bool argument
215 r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
216 },
217 [&] {
218 // range argument
219 r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
220 },
221 [&] {
222 // integral argument (int64_t)
223 r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
224 },
225 [&] {
226 // integral argument (uint64_t)
227 r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
228 },
229 [&] {
230 // floating point argument
231 r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
232 },
233 [&] {
234 // tx destination argument
235 r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
236 },
237 [&] {
238 // uint160 argument
239 r = ConsumeUInt160(fuzzed_data_provider).ToString();
240 },
241 [&] {
242 // uint256 argument
243 r = ConsumeUInt256(fuzzed_data_provider).ToString();
244 },
245 [&] {
246 // base32 argument
247 r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
248 },
249 [&] {
250 // base58 argument
251 r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
252 },
253 [&] {
254 // base58 argument with checksum
255 r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
256 },
257 [&] {
258 // hex encoded block
259 std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
260 if (!opt_block) {
261 good_data = false;
262 return;
263 }
264 DataStream data_stream{};
265 data_stream << TX_WITH_WITNESS(*opt_block);
266 r = HexStr(data_stream);
267 },
268 [&] {
269 // hex encoded block header
270 std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
271 if (!opt_block_header) {
272 good_data = false;
273 return;
274 }
275 DataStream data_stream{};
276 data_stream << *opt_block_header;
277 r = HexStr(data_stream);
278 },
279 [&] {
280 // hex encoded tx
281 std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
282 if (!opt_tx) {
283 good_data = false;
284 return;
285 }
286 DataStream data_stream;
287 auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
288 data_stream << allow_witness(*opt_tx);
289 r = HexStr(data_stream);
290 },
291 [&] {
292 // base64 encoded psbt
293 std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
294 if (!opt_psbt) {
295 good_data = false;
296 return;
297 }
298 DataStream data_stream{};
299 data_stream << *opt_psbt;
300 r = EncodeBase64(data_stream);
301 },
302 [&] {
303 // base58 encoded key
304 CKey key = ConsumePrivateKey(fuzzed_data_provider);
305 if (!key.IsValid()) {
306 good_data = false;
307 return;
308 }
309 r = EncodeSecret(key);
310 },
311 [&] {
312 // hex encoded pubkey
313 CKey key = ConsumePrivateKey(fuzzed_data_provider);
314 if (!key.IsValid()) {
315 good_data = false;
316 return;
317 }
318 r = HexStr(key.GetPubKey());
319 });
320 return r;
321}
322
323std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
324{
325 std::vector<std::string> scalar_arguments;
326 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
327 {
328 scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
329 }
330 return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
331}
332
333std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
334{
335 return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
336}
337
338RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
339{
342 return setup.get();
343}
344}; // namespace
345
347{
348 rpc_testing_setup = InitializeRPCFuzzTestingSetup();
349 const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
350 for (const std::string& rpc_command : supported_rpc_commands) {
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 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();
353 if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
354 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";
355 std::terminate();
356 }
357 if (safe_for_fuzzing && not_safe_for_fuzzing) {
358 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";
359 std::terminate();
360 }
361 }
362 const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
363 if (limit_to_rpc_command_env != nullptr) {
364 g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
365 }
366}
367
369{
371 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
372 bool good_data{true};
373 SetMockTime(ConsumeTime(fuzzed_data_provider));
374 const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
375 if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
376 return;
377 }
378 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();
379 if (!safe_for_fuzzing) {
380 return;
381 }
382 std::vector<std::string> arguments;
383 LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
384 {
385 arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
386 }
387 try {
388 std::optional<test_only_CheckFailuresAreExceptionsNotAborts> maybe_mock{};
389 if (rpc_command == "echo") {
390 // Avoid aborting fuzzing for this specific test-only RPC with an
391 // intentional trigger_internal_bug
392 maybe_mock.emplace();
393 }
394 rpc_testing_setup->CallRPC(rpc_command, arguments);
395 } catch (const UniValue& json_rpc_error) {
396 const std::string error_msg{json_rpc_error.find_value("message").get_str()};
397 if (error_msg.starts_with("Internal bug detected")) {
398 // Only allow the intentional internal bug
399 assert(error_msg.find("trigger_internal_bug") != std::string::npos);
400 }
401 }
402}
std::string EncodeBase58(std::span< const unsigned char > input)
Why base-58 instead of standard base-64 encoding?
Definition base58.cpp:89
std::string EncodeBase58Check(std::span< const unsigned char > input)
Encode a byte span into a base58-encoded string, including checksum.
Definition base58.cpp:137
static UniValue CallRPC(BaseRequestHandler *rh, const std::string &strMethod, const std::vector< std::string > &args, const std::optional< std::string > &rpcwallet={})
ChainType
Definition chaintype.h:11
An encapsulated private key.
Definition key.h:36
bool IsValid() const
Check whether this private key is valid.
Definition key.h:124
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition key.cpp:183
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition server.cpp:519
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition server.cpp:482
Double ended buffer combining vector and stream-like interfaces.
Definition streams.h:133
std::string ConsumeRandomLengthString(size_t max_length)
UniValue params
Definition request.h:57
std::string strMethod
Definition request.h:56
std::any context
Definition request.h:62
const std::string & get_str() const
const UniValue & find_value(std::string_view key) const
Definition univalue.cpp:232
std::string ToString() const
Definition uint256.cpp:21
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert command lines arguments to params object when -named is disabled.
Definition client.cpp:435
#define FUZZ_TARGET(...)
Definition fuzz.h:35
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition fuzz.h:22
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition hex_base.cpp:30
std::string EncodeSecret(const CKey &key)
Definition key_io.cpp:231
std::string EncodeDestination(const CTxDestination &dest)
Definition key_io.cpp:294
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition string.h:246
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition string.h:205
static constexpr TransactionSerParams TX_NO_WITNESS
static constexpr TransactionSerParams TX_WITH_WITNESS
void initialize_rpc()
Definition rpc.cpp:346
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition string.h:246
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition string.h:205
void SetRPCWarmupFinished()
Definition server.cpp:324
CRPCTable tableRPC
Definition server.cpp:544
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
constexpr auto MakeUCharSpan(const V &v) -> decltype(UCharSpanCast(std::span{v}))
Like the std::span constructor, but for (const) unsigned char member types only.
Definition span.h:111
Testing setup that configures a complete environment.
NodeSeconds ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition util.cpp:34
CKey ConsumePrivateKey(FuzzedDataProvider &fuzzed_data_provider, std::optional< bool > compressed) noexcept
Definition util.cpp:235
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.cpp:189
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.h:167
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition util.h:35
std::optional< T > ConsumeDeserializable(FuzzedDataProvider &fuzzed_data_provider, const P &params, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition util.h:100
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.h:158
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition random.cpp:19
@ ZEROS
Seed with a compile time constant of zeros.
Definition random.h:19
static int setup(void)
Definition tests.c:7808
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
std::string EncodeBase32(std::span< const unsigned char > input, bool pad)
Base32 encode.
std::string EncodeBase64(std::span< const unsigned char > input)
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition time.cpp:44
assert(!tx.IsCoinBase())