Bitcoin Core 32.1.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/request.h>
12#include <rpc/server.h>
13#include <span.h>
14#include <streams.h>
16#include <test/fuzz/fuzz.h>
17#include <test/fuzz/util.h>
19#include <test/util/time.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 <utility>
37#include <vector>
38
39enum class ChainType;
40
41namespace {
42struct RPCFuzzTestingSetup : public TestingSetup {
43 RPCFuzzTestingSetup(const ChainType chain_type, TestOpts opts) : TestingSetup{chain_type, opts}
44 {
45 }
46
47 void CallRPC(const std::string& rpc_method, UniValue&& params)
48 {
49 JSONRPCRequest request;
50 request.context = &m_node;
51 request.strMethod = rpc_method;
52 request.params = std::move(params);
54 }
55
56 std::vector<std::string> GetRPCCommands() const
57 {
58 return tableRPC.listCommands();
59 }
60};
61
62RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
63std::string g_limit_to_rpc_command;
64
65// RPC commands which are not appropriate for fuzzing: such as RPC commands
66// reading or writing to a filename passed as an RPC parameter, RPC commands
67// resulting in network activity, etc.
68const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
69 "addconnection", // avoid DNS lookups
70 "addnode", // avoid DNS lookups
71 "addpeeraddress", // avoid DNS lookups
72 "dumptxoutset", // avoid writing to disk
73 "enumeratesigners",
74 "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
75 "exportasmap", // avoid writing to disk
76 "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
77 "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
78 "gettxoutproof", // avoid prohibitively slow execution
79 "importmempool", // avoid reading from disk
80 "loadtxoutset", // avoid reading from disk
81 "loadwallet", // avoid reading from disk
82 "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
83 "setban", // avoid DNS lookups
84 "stop", // avoid shutdown state
85};
86
87// RPC commands which are safe for fuzzing.
88const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
89 "abortprivatebroadcast",
90 "analyzepsbt",
91 "clearbanned",
92 "combinepsbt",
93 "combinerawtransaction",
94 "converttopsbt",
95 "createmultisig",
96 "createpsbt",
97 "createrawtransaction",
98 "decodepsbt",
99 "decoderawtransaction",
100 "decodescript",
101 "deriveaddresses",
102 "descriptorprocesspsbt",
103 "disconnectnode",
104 "echo",
105 "echojson",
106 "estimaterawfee",
107 "estimatesmartfee",
108 "finalizepsbt",
109 "generate",
110 "generateblock",
111 "getaddednodeinfo",
112 "getaddrmaninfo",
113 "getbestblockhash",
114 "getblock",
115 "getblockchaininfo",
116 "getblockcount",
117 "getblockfilter",
118 "getblockfrompeer", // when no peers are connected, no p2p message is sent
119 "getblockhash",
120 "getblockheader",
121 "getblockstats",
122 "getblocktemplate",
123 "getchaintips",
124 "getchainstates",
125 "getchaintxstats",
126 "getconnectioncount",
127 "getdeploymentinfo",
128 "getdescriptoractivity",
129 "getdescriptorinfo",
130 "getdifficulty",
131 "getindexinfo",
132 "getmemoryinfo",
133 "getmempoolancestors",
134 "getmempooldescendants",
135 "getmempoolentry",
136 "getmempoolfeeratediagram",
137 "getmempoolcluster",
138 "getmempoolinfo",
139 "getmininginfo",
140 "getnettotals",
141 "getnetworkhashps",
142 "getnetworkinfo",
143 "getnodeaddresses",
144 "getopenrpcinfo",
145 "getorphantxs",
146 "getpeerinfo",
147 "getprioritisedtransactions",
148 "getprivatebroadcastinfo",
149 "getrawaddrman",
150 "getrawmempool",
151 "getrawtransaction",
152 "getrpcinfo",
153 "gettxout",
154 "gettxoutsetinfo",
155 "gettxspendingprevout",
156 "help",
157 "invalidateblock",
158 "joinpsbts",
159 "listbanned",
160 "logging",
161 "mockscheduler",
162 "ping",
163 "preciousblock",
164 "prioritisetransaction",
165 "pruneblockchain",
166 "reconsiderblock",
167 "rpc.discover",
168 "scanblocks",
169 "scantxoutset",
170 "sendmsgtopeer", // when no peers are connected, no p2p message is sent
171 "sendrawtransaction",
172 "setmocktime",
173 "setnetworkactive",
174 "signmessagewithprivkey",
175 "signrawtransactionwithkey",
176 "submitblock",
177 "submitheader",
178 "submitpackage",
179 "syncwithvalidationinterfacequeue",
180 "testmempoolaccept",
181 "uptime",
182 "utxoupdatepsbt",
183 "validateaddress",
184 "verifychain",
185 "verifymessage",
186 "verifytxoutproof",
187 "waitforblock",
188 "waitforblockheight",
189 "waitfornewblock",
190};
191
192UniValue ConsumeBasicRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
193{
194 const size_t max_string_length = 4096;
195 const size_t max_base58_bytes_length{64};
196 UniValue r{};
197 CallOneOf(
198 fuzzed_data_provider,
199 [&] {
200 // arbitrary JSON argument
201 if (!r.read(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))) {
202 good_data = false;
203 }
204 },
205 [&] {
206 // null argument
208 },
209 [&] {
210 // string argument
211 r = UniValue{fuzzed_data_provider.ConsumeRandomLengthString(max_string_length)};
212 },
213 [&] {
214 // base64 argument
215 r = UniValue{EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))};
216 },
217 [&] {
218 // hex argument
219 r = UniValue{HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))};
220 },
221 [&] {
222 // bool argument
223 r = UniValue{fuzzed_data_provider.ConsumeBool()};
224 },
225 [&] {
226 // integral argument (int64_t)
227 r = UniValue{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
228 },
229 [&] {
230 // integral argument (uint64_t)
231 r = UniValue{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
232 },
233 [&] {
234 // floating point argument
235 r = UniValue{fuzzed_data_provider.ConsumeFloatingPoint<double>()};
236 },
237 [&] {
238 // tx destination argument
239 r = UniValue{EncodeDestination(ConsumeTxDestination(fuzzed_data_provider))};
240 },
241 [&] {
242 // uint160 argument
243 r = UniValue{ConsumeUInt160(fuzzed_data_provider).ToString()};
244 },
245 [&] {
246 // uint256 argument
247 r = UniValue{ConsumeUInt256(fuzzed_data_provider).ToString()};
248 },
249 [&] {
250 // base32 argument
251 r = UniValue{EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length))};
252 },
253 [&] {
254 // base58 argument
255 r = UniValue{EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)))};
256 },
257 [&] {
258 // base58 argument with checksum
259 r = UniValue{EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)))};
260 },
261 [&] {
262 // hex encoded block
263 std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS);
264 if (!opt_block) {
265 good_data = false;
266 return;
267 }
268 DataStream data_stream{};
269 data_stream << TX_WITH_WITNESS(*opt_block);
270 r = UniValue{HexStr(data_stream)};
271 },
272 [&] {
273 // hex encoded block header
274 std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
275 if (!opt_block_header) {
276 good_data = false;
277 return;
278 }
279 DataStream data_stream{};
280 data_stream << *opt_block_header;
281 r = UniValue{HexStr(data_stream)};
282 },
283 [&] {
284 // hex encoded tx
285 std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
286 if (!opt_tx) {
287 good_data = false;
288 return;
289 }
290 DataStream data_stream;
291 auto allow_witness = (fuzzed_data_provider.ConsumeBool() ? TX_WITH_WITNESS : TX_NO_WITNESS);
292 data_stream << allow_witness(*opt_tx);
293 r = UniValue{HexStr(data_stream)};
294 },
295 [&] {
296 // base64 encoded psbt
297 std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializableConstructor<PartiallySignedTransaction>(fuzzed_data_provider);
298 if (!opt_psbt) {
299 good_data = false;
300 return;
301 }
302 DataStream data_stream{};
303 data_stream << *opt_psbt;
304 r = UniValue{EncodeBase64(data_stream)};
305 },
306 [&] {
307 // base58 encoded key
308 CKey key = ConsumePrivateKey(fuzzed_data_provider);
309 if (!key.IsValid()) {
310 good_data = false;
311 return;
312 }
313 r = UniValue{EncodeSecret(key)};
314 },
315 [&] {
316 // hex encoded pubkey
317 CKey key = ConsumePrivateKey(fuzzed_data_provider);
318 if (!key.IsValid()) {
319 good_data = false;
320 return;
321 }
322 r = UniValue{HexStr(key.GetPubKey())};
323 });
324 return r;
325}
326
327constexpr int MAX_RPC_ARGUMENT_NESTING{9};
328
329// NOLINTBEGIN(misc-no-recursion)
330UniValue ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data, int nesting_depth)
331{
332 if (nesting_depth == 0) {
333 return ConsumeBasicRPCArgument(fuzzed_data_provider, good_data);
334 }
335 UniValue argument{};
336 std::vector<std::function<void()>> mks{
337 [&] { argument = ConsumeBasicRPCArgument(fuzzed_data_provider, good_data); },
338 [&] {
339 argument = UniValue(UniValue::VARR);
340 LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 100) {
341 argument.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data, nesting_depth - 1));
342 }
343 },
344 [&] {
345 argument = UniValue(UniValue::VOBJ);
346 LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 100) {
347 argument.pushKV(fuzzed_data_provider.ConsumeRandomLengthString(128),
348 ConsumeRPCArgument(fuzzed_data_provider, good_data, nesting_depth - 1));
349 }
350 },
351 };
352 PickValue(fuzzed_data_provider, mks)();
353 return argument;
354}
355// NOLINTEND(misc-no-recursion)
356
357RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
358{
359 static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
361 return setup.get();
362}
363}; // namespace
364
366{
367 rpc_testing_setup = InitializeRPCFuzzTestingSetup();
368 const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
369 for (const std::string& rpc_command : supported_rpc_commands) {
370 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();
371 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();
372 if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
373 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";
374 std::terminate();
375 }
376 if (safe_for_fuzzing && not_safe_for_fuzzing) {
377 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";
378 std::terminate();
379 }
380 }
381 const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
382 if (limit_to_rpc_command_env != nullptr) {
383 g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
384 }
385}
386
388{
390 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
391 bool good_data{true};
392 FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
393 const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
394 if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
395 return;
396 }
397 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();
398 if (!safe_for_fuzzing) {
399 return;
400 }
401 UniValue arguments(UniValue::VARR);
402 LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 100) {
403 arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data, MAX_RPC_ARGUMENT_NESTING));
404 }
405 try {
406 std::optional<test_only_CheckFailuresAreExceptionsNotAborts> maybe_mock{};
407 if (rpc_command == "echo") {
408 // Avoid aborting fuzzing for this specific test-only RPC with an
409 // intentional trigger_internal_bug
410 maybe_mock.emplace();
411 }
412 rpc_testing_setup->CallRPC(rpc_command, std::move(arguments));
413 } catch (const UniValue& json_rpc_error) {
414 const std::string error_msg{json_rpc_error.find_value("message").get_str()};
415 if (error_msg.starts_with("Internal bug detected")) {
416 // Only allow the intentional internal bug
417 assert(error_msg.find("trigger_internal_bug") != std::string::npos);
418 }
419 }
420}
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
ChainType
Definition chaintype.h:12
An encapsulated private key.
Definition key.h:40
bool IsValid() const
Check whether this private key is valid.
Definition key.h:128
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition key.cpp:184
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition server.cpp:896
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition server.cpp:859
Double ended buffer combining vector and stream-like interfaces.
Definition streams.h:165
Helper to initialize the global NodeClock, let a duration elapse, and reset it after use in a test.
Definition time.h:54
std::string ConsumeRandomLengthString(size_t max_length)
void push_back(UniValue val)
Definition univalue.cpp:103
const std::string & get_str() const
const UniValue & find_value(std::string_view key) const
Definition univalue.cpp:232
bool read(std::string_view json)
Parse JSON; set value to null on failure.
UniValue()
Definition univalue.h:31
void pushKV(std::string key, UniValue val)
Definition univalue.cpp:125
std::string ToString() const
Definition uint256.cpp:21
#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
const std::string request
std::string EncodeSecret(const CKey &key)
Definition key_io.cpp:232
std::string EncodeDestination(const CTxDestination &dest)
Definition key_io.cpp:295
Definition basic.cpp:11
constexpr TransactionSerParams TX_NO_WITNESS
constexpr TransactionSerParams TX_WITH_WITNESS
void initialize_rpc()
Definition rpc.cpp:365
void SetRPCWarmupFinished()
Definition server.cpp:701
CRPCTable tableRPC
Definition server.cpp:1003
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.
SeedRandomStateForTest(SeedRand::ZEROS)
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:230
CTxDestination ConsumeTxDestination(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.cpp:184
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
Definition util.h:57
std::optional< T > ConsumeDeserializableConstructor(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition util.h:134
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.h:195
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition util.h:37
std::optional< T > ConsumeDeserializable(FuzzedDataProvider &fuzzed_data_provider, const P &params, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition util.h:106
uint160 ConsumeUInt160(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.h:186
@ ZEROS
Seed with a compile time constant of zeros.
Definition random.h:19
std::string EncodeBase32(std::span< const unsigned char > input, bool pad)
Base32 encode.
std::string EncodeBase64(std::span< const unsigned char > input)
assert(!tx.IsCoinBase())