Coverage Report

Created: 2026-09-02 14:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/rpc/util.cpp
Line
Count
Source
1
// Copyright (c) 2017-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 <chain.h>
6
#include <common/args.h>
7
#include <common/messages.h>
8
#include <common/types.h>
9
#include <consensus/amount.h>
10
#include <core_io.h>
11
#include <key_io.h>
12
#include <node/types.h>
13
#include <outputtype.h>
14
#include <pow.h>
15
#include <rpc/util.h>
16
#include <script/descriptor.h>
17
#include <script/interpreter.h>
18
#include <script/signingprovider.h>
19
#include <script/solver.h>
20
#include <tinyformat.h>
21
#include <uint256.h>
22
#include <univalue.h>
23
#include <util/bip32.h>
24
#include <util/check.h>
25
#include <util/result.h>
26
#include <util/strencodings.h>
27
#include <util/string.h>
28
#include <util/translation.h>
29
30
#include <algorithm>
31
#include <iterator>
32
#include <string_view>
33
#include <tuple>
34
#include <utility>
35
36
using common::PSBTError;
37
using common::PSBTErrorString;
38
using common::TransactionErrorString;
39
using node::TransactionError;
40
using util::Join;
41
using util::SplitString;
42
using util::TrimString;
43
44
const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
45
const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"};
46
47
std::string GetAllOutputTypes()
48
57.9k
{
49
57.9k
    std::vector<std::string> ret;
50
57.9k
    using U = std::underlying_type_t<TxoutType>;
51
695k
    for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::WITNESS_UNKNOWN; ++i) {
52
637k
        ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i)));
53
637k
    }
54
57.9k
    return Join(ret, ", ");
55
57.9k
}
56
57
void RPCTypeCheckObj(const UniValue& o,
58
    const std::map<std::string, UniValueType>& typesExpected,
59
    bool fAllowNull,
60
    bool fStrict)
61
2.21k
{
62
22.1k
    for (const auto& t : typesExpected) {
63
22.1k
        const UniValue& v = o.find_value(t.first);
64
22.1k
        if (!fAllowNull && v.isNull())
65
11
            throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
66
67
22.1k
        if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull())))
68
27
            throw JSONRPCError(RPC_TYPE_ERROR, strprintf("JSON value of type %s for field %s is not of expected type %s", uvTypeName(v.type()),  t.first, uvTypeName(t.second.type)));
69
22.1k
    }
70
71
2.17k
    if (fStrict)
72
1.22k
    {
73
1.22k
        for (const std::string& k : o.getKeys())
74
2.41k
        {
75
2.41k
            if (!typesExpected.contains(k))
76
5
            {
77
5
                std::string err = strprintf("Unexpected key %s", k);
78
5
                throw JSONRPCError(RPC_TYPE_ERROR, err);
79
5
            }
80
2.41k
        }
81
1.22k
    }
82
2.17k
}
83
84
int ParseVerbosity(const UniValue& arg, int default_verbosity, bool allow_bool)
85
8.11k
{
86
8.11k
    if (!arg.isNull()) {
87
6.47k
        if (arg.isBool()) {
88
4.79k
            if (!allow_bool) {
89
2
                throw JSONRPCError(RPC_TYPE_ERROR, "Verbosity was boolean but only integer allowed");
90
2
            }
91
4.79k
            return arg.get_bool(); // true = 1
92
4.79k
        } else {
93
1.67k
            return arg.getInt<int>();
94
1.67k
        }
95
6.47k
    }
96
1.64k
    return default_verbosity;
97
8.11k
}
98
99
CAmount AmountFromValue(const UniValue& value, int decimals)
100
54.0k
{
101
54.0k
    if (!value.isNum() && !value.isStr())
102
12
        throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
103
53.9k
    int64_t amount;
104
53.9k
    if (!ParseFixedPoint(value.getValStr(), decimals, &amount))
105
83
        throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
106
53.9k
    if (!MoneyRange(amount))
107
12
        throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
108
53.9k
    return amount;
109
53.9k
}
110
111
CFeeRate ParseFeeRate(const UniValue& json)
112
37.4k
{
113
37.4k
    CAmount val{AmountFromValue(json)};
114
37.4k
    if (val >= COIN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Fee rates larger than or equal to 1BTC/kvB are not accepted");
115
37.4k
    return CFeeRate{val};
116
37.4k
}
117
118
uint256 ParseHashV(const UniValue& v, std::string_view name)
119
31.2k
{
120
31.2k
    const std::string& strHex(v.get_str());
121
31.2k
    if (auto rv{uint256::FromHex(strHex)}) return *rv;
122
29
    if (auto expected_len{uint256::size() * 2}; strHex.length() != expected_len) {
123
16
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", name, expected_len, strHex.length(), strHex));
124
16
    }
125
13
    throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be hexadecimal string (not '%s')", name, strHex));
126
29
}
127
uint256 ParseHashO(const UniValue& o, std::string_view strKey)
128
6.56k
{
129
6.56k
    return ParseHashV(o.find_value(strKey), strKey);
130
6.56k
}
131
std::vector<unsigned char> ParseHexV(const UniValue& v, std::string_view name)
132
948
{
133
948
    std::string strHex;
134
948
    if (v.isStr())
135
948
        strHex = v.get_str();
136
948
    if (!IsHex(strHex))
137
4
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be hexadecimal string (not '%s')", name, strHex));
138
944
    return ParseHex(strHex);
139
948
}
140
std::vector<unsigned char> ParseHexO(const UniValue& o, std::string_view strKey)
141
600
{
142
600
    return ParseHexV(o.find_value(strKey), strKey);
143
600
}
144
145
namespace {
146
147
/**
148
 * Quote an argument for shell.
149
 *
150
 * @note This is intended for help, not for security-sensitive purposes.
151
 */
152
std::string ShellQuote(const std::string& s)
153
2.54k
{
154
2.54k
    std::string result;
155
2.54k
    result.reserve(s.size() * 2);
156
218k
    for (const char ch: s) {
157
218k
        if (ch == '\'') {
158
1
            result += "'\''";
159
218k
        } else {
160
218k
            result += ch;
161
218k
        }
162
218k
    }
163
2.54k
    return "'" + result + "'";
164
2.54k
}
165
166
/**
167
 * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing.
168
 *
169
 * @note This is intended for help, not for security-sensitive purposes.
170
 */
171
std::string ShellQuoteIfNeeded(const std::string& s)
172
15.7k
{
173
103k
    for (const char ch: s) {
174
103k
        if (ch == ' ' || ch == '\'' || ch == '"') {
175
2.54k
            return ShellQuote(s);
176
2.54k
        }
177
103k
    }
178
179
13.1k
    return s;
180
15.7k
}
181
182
}
183
184
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
185
750k
{
186
750k
    return "> bitcoin-cli " + methodname + " " + args + "\n";
187
750k
}
188
189
std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args)
190
6.67k
{
191
6.67k
    std::string result = "> bitcoin-cli -named " + methodname;
192
15.7k
    for (const auto& argpair: args) {
193
15.7k
        const auto& value = argpair.second.isStr()
194
15.7k
                ? argpair.second.get_str()
195
15.7k
                : argpair.second.write();
196
15.7k
        result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value);
197
15.7k
    }
198
6.67k
    result += "\n";
199
6.67k
    return result;
200
6.67k
}
201
202
std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
203
488k
{
204
488k
    return "> curl --user myusername --data-binary '{\"jsonrpc\": \"2.0\", \"id\": \"curltest\", "
205
488k
        "\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: application/json' http://127.0.0.1:8332/\n";
206
488k
}
207
208
std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args)
209
4.13k
{
210
4.13k
    UniValue params(UniValue::VOBJ);
211
10.6k
    for (const auto& param: args) {
212
10.6k
        params.pushKV(param.first, param.second);
213
10.6k
    }
214
215
4.13k
    return "> curl --user myusername --data-binary '{\"jsonrpc\": \"2.0\", \"id\": \"curltest\", "
216
4.13k
           "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: application/json' http://127.0.0.1:8332/\n";
217
4.13k
}
218
219
// Converts a hex string to a public key if possible
220
CPubKey HexToPubKey(const std::string& hex_in)
221
568
{
222
568
    if (!IsHex(hex_in)) {
223
1
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + hex_in + "\" must be a hex string");
224
1
    }
225
567
    if (hex_in.length() != 66 && hex_in.length() != 130) {
226
1
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + hex_in + "\" must have a length of either 33 or 65 bytes");
227
1
    }
228
566
    CPubKey vchPubKey(ParseHex(hex_in));
229
566
    if (!vchPubKey.IsFullyValid()) {
230
0
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey \"" + hex_in + "\" must be cryptographically valid.");
231
0
    }
232
566
    return vchPubKey;
233
566
}
234
235
// Creates a multisig address from a given list of public keys, number of signatures required, and the address type
236
CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FlatSigningProvider& keystore, CScript& script_out)
237
83
{
238
    // Gather public keys
239
83
    if (required < 1) {
240
0
        throw JSONRPCError(RPC_INVALID_PARAMETER, "a multisignature address must require at least one key to redeem");
241
0
    }
242
83
    if ((int)pubkeys.size() < required) {
243
0
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("not enough keys supplied (got %u keys, but need at least %d to redeem)", pubkeys.size(), required));
244
0
    }
245
83
    if (pubkeys.size() > MAX_PUBKEYS_PER_MULTISIG) {
246
2
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Number of keys involved in the multisignature address creation > %d\nReduce the number", MAX_PUBKEYS_PER_MULTISIG));
247
2
    }
248
249
81
    script_out = GetScriptForMultisig(required, pubkeys);
250
251
    // Check if any keys are uncompressed. If so, the type is legacy
252
480
    for (const CPubKey& pk : pubkeys) {
253
480
        if (!pk.IsCompressed()) {
254
18
            type = OutputType::LEGACY;
255
18
            break;
256
18
        }
257
480
    }
258
259
81
    if (type == OutputType::LEGACY && script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
260
1
        throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
261
1
    }
262
263
    // Make the address
264
80
    CTxDestination dest = AddAndGetDestinationForScript(keystore, script_out, type);
265
266
80
    return dest;
267
81
}
268
269
class DescribeAddressVisitor
270
{
271
public:
272
    explicit DescribeAddressVisitor() = default;
273
274
    UniValue operator()(const CNoDestination& dest) const
275
0
    {
276
0
        return UniValue(UniValue::VOBJ);
277
0
    }
278
279
    UniValue operator()(const PubKeyDestination& dest) const
280
0
    {
281
0
        return UniValue(UniValue::VOBJ);
282
0
    }
283
284
    UniValue operator()(const PKHash& keyID) const
285
133
    {
286
133
        UniValue obj(UniValue::VOBJ);
287
133
        obj.pushKV("isscript", false);
288
133
        obj.pushKV("iswitness", false);
289
133
        return obj;
290
133
    }
291
292
    UniValue operator()(const ScriptHash& scriptID) const
293
134
    {
294
134
        UniValue obj(UniValue::VOBJ);
295
134
        obj.pushKV("isscript", true);
296
134
        obj.pushKV("iswitness", false);
297
134
        return obj;
298
134
    }
299
300
    UniValue operator()(const WitnessV0KeyHash& id) const
301
498
    {
302
498
        UniValue obj(UniValue::VOBJ);
303
498
        obj.pushKV("isscript", false);
304
498
        obj.pushKV("iswitness", true);
305
498
        obj.pushKV("witness_version", 0);
306
498
        obj.pushKV("witness_program", HexStr(id));
307
498
        return obj;
308
498
    }
309
310
    UniValue operator()(const WitnessV0ScriptHash& id) const
311
43
    {
312
43
        UniValue obj(UniValue::VOBJ);
313
43
        obj.pushKV("isscript", true);
314
43
        obj.pushKV("iswitness", true);
315
43
        obj.pushKV("witness_version", 0);
316
43
        obj.pushKV("witness_program", HexStr(id));
317
43
        return obj;
318
43
    }
319
320
    UniValue operator()(const WitnessV1Taproot& tap) const
321
119
    {
322
119
        UniValue obj(UniValue::VOBJ);
323
119
        obj.pushKV("isscript", true);
324
119
        obj.pushKV("iswitness", true);
325
119
        obj.pushKV("witness_version", 1);
326
119
        obj.pushKV("witness_program", HexStr(tap));
327
119
        return obj;
328
119
    }
329
330
    UniValue operator()(const PayToAnchor& anchor) const
331
1
    {
332
1
        UniValue obj(UniValue::VOBJ);
333
1
        obj.pushKV("isscript", true);
334
1
        obj.pushKV("iswitness", true);
335
1
        return obj;
336
1
    }
337
338
    UniValue operator()(const WitnessUnknown& id) const
339
5
    {
340
5
        UniValue obj(UniValue::VOBJ);
341
5
        obj.pushKV("iswitness", true);
342
5
        obj.pushKV("witness_version", id.GetWitnessVersion());
343
5
        obj.pushKV("witness_program", HexStr(id.GetWitnessProgram()));
344
5
        return obj;
345
5
    }
346
};
347
348
UniValue DescribeAddress(const CTxDestination& dest)
349
933
{
350
933
    return std::visit(DescribeAddressVisitor(), dest);
351
933
}
352
353
/**
354
 * Returns a sighash value corresponding to the passed in argument.
355
 *
356
 * @pre The sighash argument should be string or null.
357
*/
358
std::optional<int> ParseSighashString(const UniValue& sighash)
359
1.07k
{
360
1.07k
    if (sighash.isNull()) {
361
989
        return std::nullopt;
362
989
    }
363
85
    const auto result{SighashFromStr(sighash.get_str())};
364
85
    if (!result) {
365
4
        throw JSONRPCError(RPC_INVALID_PARAMETER, util::ErrorString(result).original);
366
4
    }
367
81
    return result.value();
368
85
}
369
370
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
371
352
{
372
352
    const int target{value.getInt<int>()};
373
352
    const unsigned int unsigned_target{static_cast<unsigned int>(target)};
374
352
    if (target < 1 || unsigned_target > max_target) {
375
31
        throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u and %u", 1, max_target));
376
31
    }
377
321
    return unsigned_target;
378
352
}
379
380
RPCErrorCode RPCErrorFromPSBTError(PSBTError err)
381
15
{
382
15
    switch (err) {
383
0
        case PSBTError::UNSUPPORTED:
384
0
            return RPC_INVALID_PARAMETER;
385
14
        case PSBTError::SIGHASH_MISMATCH:
386
14
            return RPC_DESERIALIZATION_ERROR;
387
1
        default: break;
388
15
    }
389
1
    return RPC_TRANSACTION_ERROR;
390
15
}
391
392
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
393
4.43k
{
394
4.43k
    switch (terr) {
395
4.41k
        case TransactionError::MEMPOOL_REJECTED:
396
4.41k
            return RPC_TRANSACTION_REJECTED;
397
3
        case TransactionError::ALREADY_IN_UTXO_SET:
398
3
            return RPC_VERIFY_ALREADY_IN_UTXO_SET;
399
5
        case TransactionError::PRIVATE_BROADCAST_FULL:
400
5
            return RPC_LIMIT_EXCEEDED;
401
12
        default: break;
402
4.43k
    }
403
12
    return RPC_TRANSACTION_ERROR;
404
4.43k
}
405
406
UniValue JSONRPCPSBTError(PSBTError err)
407
15
{
408
15
    return JSONRPCError(RPCErrorFromPSBTError(err), PSBTErrorString(err).original);
409
15
}
410
411
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string)
412
4.43k
{
413
4.43k
    if (err_string.length() > 0) {
414
4.42k
        return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
415
4.42k
    } else {
416
15
        return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr).original);
417
15
    }
418
4.43k
}
419
420
/**
421
 * A pair of strings that can be aligned (through padding) with other Sections
422
 * later on
423
 */
424
struct Section {
425
    Section(const std::string& left, const std::string& right)
426
20.9k
        : m_left{left}, m_right{right} {}
427
    std::string m_left;
428
    const std::string m_right;
429
};
430
431
/**
432
 * Keeps track of RPCArgs by transforming them into sections for the purpose
433
 * of serializing everything to a single string
434
 */
435
struct Sections {
436
    std::vector<Section> m_sections;
437
    size_t m_max_pad{0};
438
439
    void PushSection(const Section& s)
440
18.9k
    {
441
18.9k
        m_max_pad = std::max(m_max_pad, s.m_left.size());
442
18.9k
        m_sections.push_back(s);
443
18.9k
    }
444
445
    /**
446
     * Recursive helper to translate an RPCArg into sections
447
     */
448
    // NOLINTNEXTLINE(misc-no-recursion)
449
    void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
450
3.24k
    {
451
3.24k
        const auto indent = std::string(current_indent, ' ');
452
3.24k
        const auto indent_next = std::string(current_indent + 2, ' ');
453
3.24k
        const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
454
3.24k
        const bool is_top_level_arg{outer_type == OuterType::NONE}; // True on the first recursion
455
456
3.24k
        switch (arg.m_type) {
457
477
        case RPCArg::Type::STR_HEX:
458
1.33k
        case RPCArg::Type::STR:
459
1.93k
        case RPCArg::Type::NUM:
460
2.08k
        case RPCArg::Type::AMOUNT:
461
2.14k
        case RPCArg::Type::RANGE:
462
2.62k
        case RPCArg::Type::BOOL:
463
2.69k
        case RPCArg::Type::OBJ_NAMED_PARAMS: {
464
2.69k
            if (is_top_level_arg) return; // Nothing more to do for non-recursive types on first recursion
465
703
            auto left = indent;
466
703
            if (arg.m_opts.type_str.size() != 0 && push_name) {
467
3
                left += "\"" + arg.GetName() + "\": " + arg.m_opts.type_str.at(0);
468
700
            } else {
469
700
                left += push_name ? arg.ToStringObj(/*oneline=*/false) : arg.ToString(/*oneline=*/false);
470
700
            }
471
703
            left += ",";
472
703
            PushSection({left, arg.ToDescriptionString(/*is_named_arg=*/push_name)});
473
703
            break;
474
2.69k
        }
475
176
        case RPCArg::Type::OBJ:
476
216
        case RPCArg::Type::OBJ_USER_KEYS: {
477
216
            const auto right = is_top_level_arg ? "" : arg.ToDescriptionString(/*is_named_arg=*/push_name);
478
216
            PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right});
479
500
            for (const auto& arg_inner : arg.m_inner) {
480
500
                Push(arg_inner, current_indent + 2, OuterType::OBJ);
481
500
            }
482
216
            if (arg.m_type != RPCArg::Type::OBJ) {
483
40
                PushSection({indent_next + "...", ""});
484
40
            }
485
216
            PushSection({indent + "}" + (is_top_level_arg ? "" : ","), ""});
486
216
            break;
487
176
        }
488
332
        case RPCArg::Type::ARR: {
489
332
            auto left = indent;
490
332
            left += push_name ? "\"" + arg.GetName() + "\": " : "";
491
332
            left += "[";
492
332
            const auto right = is_top_level_arg ? "" : arg.ToDescriptionString(/*is_named_arg=*/push_name);
493
332
            PushSection({left, right});
494
437
            for (const auto& arg_inner : arg.m_inner) {
495
437
                Push(arg_inner, current_indent + 2, OuterType::ARR);
496
437
            }
497
332
            PushSection({indent_next + "...", ""});
498
332
            PushSection({indent + "]" + (is_top_level_arg ? "" : ","), ""});
499
332
            break;
500
176
        }
501
3.24k
        } // no default case, so the compiler can warn about missing cases
502
3.24k
    }
503
504
    /**
505
     * Concatenate all sections with proper padding
506
     */
507
    std::string ToString() const
508
3.54k
    {
509
3.54k
        std::string ret;
510
3.54k
        const size_t pad = m_max_pad + 4;
511
20.9k
        for (const auto& s : m_sections) {
512
            // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
513
            // brace like {, }, [, or ]
514
20.9k
            CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
515
20.9k
            if (s.m_right.empty()) {
516
5.57k
                ret += s.m_left;
517
5.57k
                ret += "\n";
518
5.57k
                continue;
519
5.57k
            }
520
521
15.3k
            std::string left = s.m_left;
522
15.3k
            left.resize(pad, ' ');
523
15.3k
            ret += left;
524
525
            // Properly pad after newlines
526
15.3k
            std::string right;
527
15.3k
            size_t begin = 0;
528
15.3k
            size_t new_line_pos = s.m_right.find_first_of('\n');
529
17.2k
            while (true) {
530
17.2k
                right += s.m_right.substr(begin, new_line_pos - begin);
531
17.2k
                if (new_line_pos == std::string::npos) {
532
15.1k
                    break; //No new line
533
15.1k
                }
534
2.05k
                right += "\n" + std::string(pad, ' ');
535
2.05k
                begin = s.m_right.find_first_not_of(' ', new_line_pos + 1);
536
2.05k
                if (begin == std::string::npos) {
537
159
                    break; // Empty line
538
159
                }
539
1.89k
                new_line_pos = s.m_right.find_first_of('\n', begin + 1);
540
1.89k
            }
541
15.3k
            ret += right;
542
15.3k
            ret += "\n";
543
15.3k
        }
544
3.54k
        return ret;
545
3.54k
    }
546
};
547
548
RPCMethod::RPCMethod(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
549
19
    : RPCMethod{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
550
551
RPCMethod::RPCMethod(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
552
537k
    : m_name{std::move(name)},
553
537k
      m_fun{std::move(fun)},
554
537k
      m_description{std::move(description)},
555
537k
      m_args{std::move(args)},
556
537k
      m_results{std::move(results)},
557
537k
      m_examples{std::move(examples)}
558
537k
{
559
    // Map of parameter names and types just used to check whether the names are
560
    // unique. Parameter names always need to be unique, with the exception that
561
    // there can be pairs of POSITIONAL and NAMED parameters with the same name.
562
537k
    enum ParamType { POSITIONAL = 1, NAMED = 2, NAMED_ONLY = 4 };
563
537k
    std::map<std::string, int> param_names;
564
565
1.00M
    for (const auto& arg : m_args) {
566
1.00M
        std::vector<std::string> names = SplitString(arg.m_names, '|');
567
        // Should have unique named arguments
568
1.01M
        for (const std::string& name : names) {
569
1.01M
            auto& param_type = param_names[name];
570
1.01M
            CHECK_NONFATAL(!(param_type & POSITIONAL));
571
1.01M
            CHECK_NONFATAL(!(param_type & NAMED_ONLY));
572
1.01M
            param_type |= POSITIONAL;
573
1.01M
        }
574
1.00M
        if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {
575
118k
            for (const auto& inner : arg.m_inner) {
576
118k
                std::vector<std::string> inner_names = SplitString(inner.m_names, '|');
577
118k
                for (const std::string& inner_name : inner_names) {
578
118k
                    auto& param_type = param_names[inner_name];
579
118k
                    CHECK_NONFATAL(!(param_type & POSITIONAL) || inner.m_opts.also_positional);
580
118k
                    CHECK_NONFATAL(!(param_type & NAMED));
581
118k
                    CHECK_NONFATAL(!(param_type & NAMED_ONLY));
582
118k
                    param_type |= inner.m_opts.also_positional ? NAMED : NAMED_ONLY;
583
118k
                }
584
118k
            }
585
20.7k
        }
586
        // Default value type should match argument type only when defined
587
1.00M
        if (arg.m_fallback.index() == 2) {
588
357k
            const RPCArg::Type type = arg.m_type;
589
357k
            [&]() {
590
357k
                switch (std::get<RPCArg::Default>(arg.m_fallback).getType()) {
591
0
                case UniValue::VOBJ:
592
0
                    CHECK_NONFATAL(type == RPCArg::Type::OBJ);
593
0
                    return;
594
2.16k
                case UniValue::VARR:
595
2.16k
                    CHECK_NONFATAL(type == RPCArg::Type::ARR);
596
2.16k
                    return;
597
131k
                case UniValue::VSTR:
598
131k
                    CHECK_NONFATAL(type == RPCArg::Type::STR || type == RPCArg::Type::STR_HEX || type == RPCArg::Type::AMOUNT);
599
131k
                    return;
600
76.6k
                case UniValue::VNUM:
601
76.6k
                    CHECK_NONFATAL(type == RPCArg::Type::NUM || type == RPCArg::Type::AMOUNT || type == RPCArg::Type::RANGE);
602
76.6k
                    return;
603
147k
                case UniValue::VBOOL:
604
147k
                    CHECK_NONFATAL(type == RPCArg::Type::BOOL);
605
147k
                    return;
606
0
                case UniValue::VNULL:
607
                    // Null values are accepted in all arguments
608
0
                    return;
609
357k
                } // no default case, so the compiler can warn about missing cases
610
357k
                NONFATAL_UNREACHABLE();
611
357k
            }();
612
357k
        }
613
1.00M
    }
614
537k
}
615
616
std::string RPCResults::ToDescriptionString() const
617
1.11k
{
618
1.11k
    std::string result;
619
1.33k
    for (const auto& r : m_results) {
620
1.33k
        Sections sections;
621
1.33k
        r.ToSections(sections);
622
        // A result can be empty via HelpElisionSkip
623
1.33k
        if (sections.m_sections.empty()) continue;
624
625
1.32k
        if (r.m_cond.empty()) {
626
983
            result += "\nResult:\n";
627
983
        } else {
628
337
            result += "\nResult (" + r.m_cond + "):\n";
629
337
        }
630
1.32k
        result += sections.ToString();
631
1.32k
    }
632
1.11k
    return result;
633
1.11k
}
634
635
std::string RPCExamples::ToDescriptionString() const
636
1.11k
{
637
1.11k
    return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
638
1.11k
}
639
640
UniValue RPCMethod::HandleRequest(const JSONRPCRequest& request) const
641
203k
{
642
203k
    if (request.mode == JSONRPCRequest::GET_ARGS) {
643
350
        return GetArgMap();
644
350
    }
645
    /*
646
     * Check if the given request is valid according to this command or if
647
     * the user is asking for help information, and throw help when appropriate.
648
     */
649
203k
    if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
650
1.10k
        throw HelpResult{ToString()};
651
1.10k
    }
652
202k
    UniValue arg_mismatch{UniValue::VOBJ};
653
595k
    for (size_t i{0}; i < m_args.size(); ++i) {
654
393k
        const auto& arg{m_args.at(i)};
655
393k
        UniValue match{arg.MatchesType(request.params[i])};
656
393k
        if (!match.isTrue()) {
657
31
            arg_mismatch.pushKV(strprintf("Position %s (%s)", i + 1, arg.m_names), std::move(match));
658
31
        }
659
393k
    }
660
202k
    if (!arg_mismatch.empty()) {
661
29
        throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Wrong type passed:\n%s", arg_mismatch.write(4)));
662
29
    }
663
202k
    CHECK_NONFATAL(m_req == nullptr);
664
202k
    m_req = &request;
665
202k
    UniValue ret = m_fun(*this, request);
666
202k
    m_req = nullptr;
667
202k
    if (gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {
668
196k
        UniValue mismatch{UniValue::VARR};
669
207k
        for (const auto& res : m_results.m_results) {
670
207k
            UniValue match{res.MatchesType(ret)};
671
207k
            if (match.isTrue()) {
672
196k
                mismatch.setNull();
673
196k
                break;
674
196k
            }
675
11.5k
            mismatch.push_back(std::move(match));
676
11.5k
        }
677
196k
        if (!mismatch.isNull()) {
678
0
            std::string explain{
679
0
                mismatch.empty() ? "no possible results defined" :
680
0
                mismatch.size() == 1 ? mismatch[0].write(4) :
681
0
                mismatch.write(4)};
682
0
            throw std::runtime_error{
683
0
                STR_INTERNAL_BUG(strprintf("RPC call \"%s\" returned incorrect type:\n%s", m_name, explain)),
684
0
            };
685
0
        }
686
196k
    }
687
202k
    return ret;
688
202k
}
689
690
using CheckFn = void(const RPCArg&);
691
static const UniValue* DetailMaybeArg(CheckFn* check, const std::vector<RPCArg>& params, const JSONRPCRequest* req, size_t i)
692
49.9k
{
693
49.9k
    CHECK_NONFATAL(i < params.size());
694
49.9k
    const UniValue& arg{CHECK_NONFATAL(req)->params[i]};
695
49.9k
    const RPCArg& param{params.at(i)};
696
49.9k
    if (check) check(param);
697
698
49.9k
    if (!arg.isNull()) return &arg;
699
23.2k
    if (!std::holds_alternative<RPCArg::Default>(param.m_fallback)) return nullptr;
700
20.7k
    return &std::get<RPCArg::Default>(param.m_fallback);
701
23.2k
}
702
703
static void CheckRequiredOrDefault(const RPCArg& param)
704
45.7k
{
705
    // Must use `Arg<Type>(key)` to get the argument or its default value.
706
45.7k
    const bool required{
707
45.7k
        std::holds_alternative<RPCArg::Optional>(param.m_fallback) && RPCArg::Optional::NO == std::get<RPCArg::Optional>(param.m_fallback),
708
45.7k
    };
709
45.7k
    CHECK_NONFATAL(required || std::holds_alternative<RPCArg::Default>(param.m_fallback));
710
45.7k
}
711
712
#define TMPL_INST(check_param, ret_type, return_code)       \
713
    template <>                                             \
714
    ret_type RPCMethod::ArgValue<ret_type>(size_t i) const \
715
49.9k
    {                                                       \
716
49.9k
        const UniValue* maybe_arg{                          \
717
49.9k
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
49.9k
        };                                                  \
719
49.9k
        return return_code                                  \
720
49.9k
    }                                                       \
UniValue const* RPCMethod::ArgValue<UniValue const*>(unsigned long) const
Line
Count
Source
715
1.35k
    {                                                       \
716
1.35k
        const UniValue* maybe_arg{                          \
717
1.35k
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
1.35k
        };                                                  \
719
1.35k
        return return_code                                  \
720
1.35k
    }                                                       \
std::optional<double> RPCMethod::ArgValue<std::optional<double>>(unsigned long) const
Line
Count
Source
715
723
    {                                                       \
716
723
        const UniValue* maybe_arg{                          \
717
723
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
723
        };                                                  \
719
1.44k
        return return_code                                  \
720
723
    }                                                       \
std::optional<bool> RPCMethod::ArgValue<std::optional<bool>>(unsigned long) const
Line
Count
Source
715
777
    {                                                       \
716
777
        const UniValue* maybe_arg{                          \
717
777
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
777
        };                                                  \
719
1.55k
        return return_code                                  \
720
777
    }                                                       \
std::optional<long> RPCMethod::ArgValue<std::optional<long>>(unsigned long) const
Line
Count
Source
715
106
    {                                                       \
716
106
        const UniValue* maybe_arg{                          \
717
106
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
106
        };                                                  \
719
212
        return return_code                                  \
720
106
    }                                                       \
std::optional<std::basic_string_view<char, std::char_traits<char>>> RPCMethod::ArgValue<std::optional<std::basic_string_view<char, std::char_traits<char>>>>(unsigned long) const
Line
Count
Source
715
1.24k
    {                                                       \
716
1.24k
        const UniValue* maybe_arg{                          \
717
1.24k
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
1.24k
        };                                                  \
719
2.49k
        return return_code                                  \
720
1.24k
    }                                                       \
UniValue const& RPCMethod::ArgValue<UniValue const&>(unsigned long) const
Line
Count
Source
715
37.4k
    {                                                       \
716
37.4k
        const UniValue* maybe_arg{                          \
717
37.4k
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
37.4k
        };                                                  \
719
37.4k
        return return_code                                  \
720
37.4k
    }                                                       \
bool RPCMethod::ArgValue<bool>(unsigned long) const
Line
Count
Source
715
872
    {                                                       \
716
872
        const UniValue* maybe_arg{                          \
717
872
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
872
        };                                                  \
719
872
        return return_code                                  \
720
872
    }                                                       \
int RPCMethod::ArgValue<int>(unsigned long) const
Line
Count
Source
715
1.41k
    {                                                       \
716
1.41k
        const UniValue* maybe_arg{                          \
717
1.41k
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
1.41k
        };                                                  \
719
1.41k
        return return_code                                  \
720
1.41k
    }                                                       \
unsigned long RPCMethod::ArgValue<unsigned long>(unsigned long) const
Line
Count
Source
715
828
    {                                                       \
716
828
        const UniValue* maybe_arg{                          \
717
828
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
828
        };                                                  \
719
828
        return return_code                                  \
720
828
    }                                                       \
unsigned int RPCMethod::ArgValue<unsigned int>(unsigned long) const
Line
Count
Source
715
994
    {                                                       \
716
994
        const UniValue* maybe_arg{                          \
717
994
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
994
        };                                                  \
719
994
        return return_code                                  \
720
994
    }                                                       \
std::basic_string_view<char, std::char_traits<char>> RPCMethod::ArgValue<std::basic_string_view<char, std::char_traits<char>>>(unsigned long) const
Line
Count
Source
715
4.16k
    {                                                       \
716
4.16k
        const UniValue* maybe_arg{                          \
717
4.16k
            DetailMaybeArg(check_param, m_args, m_req, i),  \
718
4.16k
        };                                                  \
719
4.16k
        return return_code                                  \
720
4.16k
    }                                                       \
721
    void force_semicolon(ret_type)
722
723
// Optional arg (without default). Can also be called on required args, if needed.
724
TMPL_INST(nullptr, const UniValue*, maybe_arg;);
725
TMPL_INST(nullptr, std::optional<double>, maybe_arg ? std::optional{maybe_arg->get_real()} : std::nullopt;);
726
TMPL_INST(nullptr, std::optional<bool>, maybe_arg ? std::optional{maybe_arg->get_bool()} : std::nullopt;);
727
TMPL_INST(nullptr, std::optional<int64_t>, maybe_arg ? std::optional{maybe_arg->getInt<int64_t>()} : std::nullopt;);
728
TMPL_INST(nullptr, std::optional<std::string_view>, maybe_arg ? std::optional<std::string_view>{maybe_arg->get_str()} : std::nullopt;);
729
730
// Required arg or optional arg with default value.
731
TMPL_INST(CheckRequiredOrDefault, const UniValue&, *CHECK_NONFATAL(maybe_arg););
732
TMPL_INST(CheckRequiredOrDefault, bool, CHECK_NONFATAL(maybe_arg)->get_bool(););
733
TMPL_INST(CheckRequiredOrDefault, int, CHECK_NONFATAL(maybe_arg)->getInt<int>(););
734
TMPL_INST(CheckRequiredOrDefault, uint64_t, CHECK_NONFATAL(maybe_arg)->getInt<uint64_t>(););
735
TMPL_INST(CheckRequiredOrDefault, uint32_t, CHECK_NONFATAL(maybe_arg)->getInt<uint32_t>(););
736
TMPL_INST(CheckRequiredOrDefault, std::string_view, CHECK_NONFATAL(maybe_arg)->get_str(););
737
738
bool RPCMethod::IsValidNumArgs(size_t num_args) const
739
202k
{
740
202k
    size_t num_required_args = 0;
741
427k
    for (size_t n = m_args.size(); n > 0; --n) {
742
352k
        if (!m_args.at(n - 1).IsOptional()) {
743
128k
            num_required_args = n;
744
128k
            break;
745
128k
        }
746
352k
    }
747
202k
    return num_required_args <= num_args && num_args <= m_args.size();
748
202k
}
749
750
std::vector<std::pair<std::string, bool>> RPCMethod::GetArgNames() const
751
166k
{
752
166k
    std::vector<std::pair<std::string, bool>> ret;
753
166k
    ret.reserve(m_args.size());
754
304k
    for (const auto& arg : m_args) {
755
304k
        if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {
756
49.2k
            for (const auto& inner : arg.m_inner) {
757
49.2k
                ret.emplace_back(inner.m_names, /*named_only=*/true);
758
49.2k
            }
759
9.44k
        }
760
304k
        ret.emplace_back(arg.m_names, /*named_only=*/false);
761
304k
    }
762
166k
    return ret;
763
166k
}
764
765
size_t RPCMethod::GetParamIndex(std::string_view key) const
766
49.9k
{
767
49.9k
    auto it{std::find_if(
768
103k
        m_args.begin(), m_args.end(), [&key](const auto& arg) { return arg.GetName() == key;}
769
49.9k
    )};
770
771
49.9k
    CHECK_NONFATAL(it != m_args.end());  // TODO: ideally this is checked at compile time
772
49.9k
    return std::distance(m_args.begin(), it);
773
49.9k
}
774
775
std::string RPCMethod::ToString() const
776
1.11k
{
777
1.11k
    std::string ret;
778
779
    // Oneline summary
780
1.11k
    ret += m_name;
781
1.11k
    bool was_optional{false};
782
1.95k
    for (const auto& arg : m_args) {
783
1.95k
        if (arg.m_opts.hidden) break; // Any arg that follows is also hidden
784
1.94k
        const bool optional = arg.IsOptional();
785
1.94k
        ret += " ";
786
1.94k
        if (optional) {
787
1.09k
            if (!was_optional) ret += "( ";
788
1.09k
            was_optional = true;
789
1.09k
        } else {
790
850
            if (was_optional) ret += ") ";
791
850
            was_optional = false;
792
850
        }
793
1.94k
        ret += arg.ToString(/*oneline=*/true);
794
1.94k
    }
795
1.11k
    if (was_optional) ret += " )";
796
797
    // Description
798
1.11k
    CHECK_NONFATAL(!m_description.starts_with('\n'));  // Historically \n was required, but reject it for new code.
799
1.11k
    ret += "\n\n" + TrimString(m_description) + "\n";
800
801
    // Arguments
802
1.11k
    Sections sections;
803
1.11k
    Sections named_only_sections;
804
3.05k
    for (size_t i{0}; i < m_args.size(); ++i) {
805
1.95k
        const auto& arg = m_args.at(i);
806
1.95k
        if (arg.m_opts.hidden) break; // Any arg that follows is also hidden
807
808
        // Push named argument name and description
809
1.94k
        sections.m_sections.emplace_back(util::ToString(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString(/*is_named_arg=*/true));
810
1.94k
        sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size());
811
812
        // Recursively push nested args
813
1.94k
        sections.Push(arg);
814
815
        // Push named-only argument sections
816
1.94k
        if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {
817
357
            for (const auto& arg_inner : arg.m_inner) {
818
357
                named_only_sections.PushSection({arg_inner.GetFirstName(), arg_inner.ToDescriptionString(/*is_named_arg=*/true)});
819
357
                named_only_sections.Push(arg_inner);
820
357
            }
821
69
        }
822
1.94k
    }
823
824
1.11k
    if (!sections.m_sections.empty()) ret += "\nArguments:\n";
825
1.11k
    ret += sections.ToString();
826
1.11k
    if (!named_only_sections.m_sections.empty()) ret += "\nNamed Arguments:\n";
827
1.11k
    ret += named_only_sections.ToString();
828
829
    // Result
830
1.11k
    ret += m_results.ToDescriptionString();
831
832
    // Examples
833
1.11k
    ret += m_examples.ToDescriptionString();
834
835
1.11k
    return ret;
836
1.11k
}
837
838
UniValue RPCMethod::GetArgMap() const
839
350
{
840
350
    UniValue arr{UniValue::VARR};
841
842
906
    auto push_back_arg_info = [&arr](const std::string& rpc_name, int pos, const std::string& arg_name, const RPCArg::Type& type) {
843
906
        UniValue map{UniValue::VARR};
844
906
        map.push_back(rpc_name);
845
906
        map.push_back(pos);
846
906
        map.push_back(arg_name);
847
906
        map.push_back(type == RPCArg::Type::STR ||
848
906
                      type == RPCArg::Type::STR_HEX);
849
906
        arr.push_back(std::move(map));
850
906
    };
851
852
1.04k
    for (int i{0}; i < int(m_args.size()); ++i) {
853
696
        const auto& arg = m_args.at(i);
854
696
        std::vector<std::string> arg_names = SplitString(arg.m_names, '|');
855
700
        for (const auto& arg_name : arg_names) {
856
700
            push_back_arg_info(m_name, i, arg_name, arg.m_type);
857
700
            if (arg.m_type == RPCArg::Type::OBJ_NAMED_PARAMS) {
858
206
                for (const auto& inner : arg.m_inner) {
859
206
                    std::vector<std::string> inner_names = SplitString(inner.m_names, '|');
860
206
                    for (const std::string& inner_name : inner_names) {
861
206
                        push_back_arg_info(m_name, i, inner_name, inner.m_type);
862
206
                    }
863
206
                }
864
30
            }
865
700
        }
866
696
    }
867
350
    return arr;
868
350
}
869
870
static std::optional<UniValue::VType> ExpectedType(RPCArg::Type type)
871
203k
{
872
203k
    using Type = RPCArg::Type;
873
203k
    switch (type) {
874
73.1k
    case Type::STR_HEX:
875
124k
    case Type::STR: {
876
124k
        return UniValue::VSTR;
877
73.1k
    }
878
46.6k
    case Type::NUM: {
879
46.6k
        return UniValue::VNUM;
880
73.1k
    }
881
21.4k
    case Type::AMOUNT: {
882
        // VNUM or VSTR, checked inside AmountFromValue()
883
21.4k
        return std::nullopt;
884
73.1k
    }
885
80
    case Type::RANGE: {
886
        // VNUM or VARR, checked inside ParseRange()
887
80
        return std::nullopt;
888
73.1k
    }
889
3.60k
    case Type::BOOL: {
890
3.60k
        return UniValue::VBOOL;
891
73.1k
    }
892
598
    case Type::OBJ:
893
1.49k
    case Type::OBJ_NAMED_PARAMS:
894
1.58k
    case Type::OBJ_USER_KEYS: {
895
1.58k
        return UniValue::VOBJ;
896
1.49k
    }
897
5.70k
    case Type::ARR: {
898
5.70k
        return UniValue::VARR;
899
1.49k
    }
900
203k
    } // no default case, so the compiler can warn about missing cases
901
203k
    NONFATAL_UNREACHABLE();
902
203k
}
903
904
UniValue RPCArg::MatchesType(const UniValue& request) const
905
393k
{
906
393k
    if (m_opts.skip_type_check) return true;
907
382k
    if (IsOptional() && request.isNull()) return true;
908
203k
    const auto exp_type{ExpectedType(m_type)};
909
203k
    if (!exp_type) return true; // nothing to check
910
911
181k
    if (*exp_type != request.getType()) {
912
31
        return strprintf("JSON value of type %s is not of expected type %s", uvTypeName(request.getType()), uvTypeName(*exp_type));
913
31
    }
914
181k
    return true;
915
181k
}
916
917
std::string RPCArg::GetFirstName() const
918
5.83k
{
919
5.83k
    return m_names.substr(0, m_names.find('|'));
920
5.83k
}
921
922
std::string RPCArg::GetName() const
923
103k
{
924
103k
    CHECK_NONFATAL(std::string::npos == m_names.find('|'));
925
103k
    return m_names;
926
103k
}
927
928
bool RPCArg::IsOptional() const
929
737k
{
930
737k
    if (m_fallback.index() != 0) {
931
408k
        return true;
932
408k
    } else {
933
329k
        return RPCArg::Optional::NO != std::get<RPCArg::Optional>(m_fallback);
934
329k
    }
935
737k
}
936
937
std::string RPCArg::ToDescriptionString(bool is_named_arg) const
938
3.24k
{
939
3.24k
    std::string ret;
940
3.24k
    ret += "(";
941
3.24k
    if (m_opts.type_str.size() != 0) {
942
32
        ret += m_opts.type_str.at(1);
943
3.20k
    } else {
944
3.20k
        switch (m_type) {
945
477
        case Type::STR_HEX:
946
1.33k
        case Type::STR: {
947
1.33k
            ret += "string";
948
1.33k
            break;
949
477
        }
950
572
        case Type::NUM: {
951
572
            ret += "numeric";
952
572
            break;
953
477
        }
954
147
        case Type::AMOUNT: {
955
147
            ret += "numeric or string";
956
147
            break;
957
477
        }
958
60
        case Type::RANGE: {
959
60
            ret += "numeric or array";
960
60
            break;
961
477
        }
962
481
        case Type::BOOL: {
963
481
            ret += "boolean";
964
481
            break;
965
477
        }
966
176
        case Type::OBJ:
967
245
        case Type::OBJ_NAMED_PARAMS:
968
285
        case Type::OBJ_USER_KEYS: {
969
285
            ret += "json object";
970
285
            break;
971
245
        }
972
332
        case Type::ARR: {
973
332
            ret += "json array";
974
332
            break;
975
245
        }
976
3.20k
        } // no default case, so the compiler can warn about missing cases
977
3.20k
    }
978
3.24k
    if (m_fallback.index() == 1) {
979
386
        ret += ", optional, default=" + std::get<RPCArg::DefaultHint>(m_fallback);
980
2.85k
    } else if (m_fallback.index() == 2) {
981
926
        ret += ", optional, default=" + std::get<RPCArg::Default>(m_fallback).write();
982
1.92k
    } else {
983
1.92k
        switch (std::get<RPCArg::Optional>(m_fallback)) {
984
790
        case RPCArg::Optional::OMITTED: {
985
790
            if (is_named_arg) ret += ", optional"; // Default value is "null" in dicts. Otherwise,
986
            // nothing to do. Element is treated as if not present and has no default value
987
790
            break;
988
0
        }
989
1.13k
        case RPCArg::Optional::NO: {
990
1.13k
            ret += ", required";
991
1.13k
            break;
992
0
        }
993
1.92k
        } // no default case, so the compiler can warn about missing cases
994
1.92k
    }
995
3.24k
    ret += ")";
996
3.24k
    if (m_type == Type::OBJ_NAMED_PARAMS) ret += " Options object that can be used to pass named arguments, listed below.";
997
3.24k
    ret += m_description.empty() ? "" : " " + m_description;
998
3.24k
    return ret;
999
3.24k
}
1000
1001
// NOLINTNEXTLINE(misc-no-recursion)
1002
void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
1003
13.1k
{
1004
    // Indentation
1005
13.1k
    const std::string indent(current_indent, ' ');
1006
13.1k
    const std::string indent_next(current_indent + 2, ' ');
1007
1008
    // Elements in a JSON structure (dictionary or array) are separated by a comma
1009
13.1k
    const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""};
1010
1011
    // The key name if recursed into a dictionary
1012
13.1k
    const std::string maybe_key{
1013
13.1k
        outer_type == OuterType::OBJ ?
1014
10.5k
            "\"" + this->m_key_name + "\" : " :
1015
13.1k
            ""};
1016
1017
    // Format description with type
1018
13.1k
    const auto Description = [&](const std::string& type) {
1019
12.0k
        return "(" + type + (this->m_optional ? ", optional" : "") + ")" +
1020
12.0k
               (this->m_description.empty() ? "" : " " + this->m_description);
1021
12.0k
    };
1022
1023
    // Ensure at least one visible field exists when elision is used
1024
13.1k
    const auto elision_has_description{[](const std::vector<RPCResult>& inner) {
1025
3.06k
        return std::ranges::any_of(inner, [](const auto& res) {
1026
3.06k
            return !std::holds_alternative<HelpElisionSkip>(res.m_opts.print_elision);
1027
3.06k
        });
1028
3.00k
    }};
1029
1030
13.1k
    if (const auto* text = std::get_if<std::string>(&m_opts.print_elision)) {
1031
95
        sections.PushSection({indent + "..." + maybe_separator, *text});
1032
95
        return;
1033
95
    }
1034
13.0k
    if (std::holds_alternative<HelpElisionSkip>(m_opts.print_elision)) {
1035
1.00k
        return;
1036
1.00k
    }
1037
1038
12.0k
    switch (m_type) {
1039
36
    case Type::ANY: {
1040
36
        sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("any")});
1041
36
        return;
1042
0
    }
1043
139
    case Type::NONE: {
1044
139
        sections.PushSection({indent + "null" + maybe_separator, Description("json null")});
1045
139
        return;
1046
0
    }
1047
2.09k
    case Type::STR: {
1048
2.09k
        sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")});
1049
2.09k
        return;
1050
0
    }
1051
544
    case Type::STR_AMOUNT: {
1052
544
        sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
1053
544
        return;
1054
0
    }
1055
1.92k
    case Type::STR_HEX: {
1056
1.92k
        sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")});
1057
1.92k
        return;
1058
0
    }
1059
3.15k
    case Type::NUM: {
1060
3.15k
        sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
1061
3.15k
        return;
1062
0
    }
1063
357
    case Type::NUM_TIME: {
1064
357
        sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")});
1065
357
        return;
1066
0
    }
1067
728
    case Type::BOOL: {
1068
728
        sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")});
1069
728
        return;
1070
0
    }
1071
17
    case Type::ARR_FIXED:
1072
1.13k
    case Type::ARR: {
1073
1.13k
        sections.PushSection({indent + maybe_key + "[", Description("json array")});
1074
1.20k
        for (const auto& i : m_inner) {
1075
1.20k
            i.ToSections(sections, OuterType::ARR, current_indent + 2);
1076
1.20k
        }
1077
1.13k
        CHECK_NONFATAL(!m_inner.empty());
1078
1.13k
        CHECK_NONFATAL(elision_has_description(m_inner));
1079
1.13k
        if (m_type == Type::ARR && !std::holds_alternative<std::string>(m_inner.back().m_opts.print_elision)) {
1080
1.11k
            sections.PushSection({indent_next + "...", ""});
1081
1.11k
        } else {
1082
            // Remove final comma, which would be invalid JSON
1083
20
            sections.m_sections.back().m_left.pop_back();
1084
20
        }
1085
1.13k
        sections.PushSection({indent + "]" + maybe_separator, ""});
1086
1.13k
        return;
1087
17
    }
1088
213
    case Type::OBJ_DYN:
1089
1.88k
    case Type::OBJ: {
1090
1.88k
        if (m_inner.empty()) {
1091
18
            sections.PushSection({indent + maybe_key + "{}", Description("empty JSON object")});
1092
18
            return;
1093
18
        }
1094
1.86k
        CHECK_NONFATAL(elision_has_description(m_inner));
1095
1.86k
        sections.PushSection({indent + maybe_key + "{", Description("json object")});
1096
10.5k
        for (const auto& i : m_inner) {
1097
10.5k
            i.ToSections(sections, OuterType::OBJ, current_indent + 2);
1098
10.5k
        }
1099
1.86k
        if (m_type == Type::OBJ_DYN) {
1100
            // If the dictionary keys are dynamic, use three dots for continuation
1101
213
            sections.PushSection({indent_next + "...", ""});
1102
1.65k
        } else {
1103
            // Remove final comma, which would be invalid JSON
1104
1.65k
            sections.m_sections.back().m_left.pop_back();
1105
1.65k
        }
1106
1.86k
        sections.PushSection({indent + "}" + maybe_separator, ""});
1107
1.86k
        return;
1108
1.88k
    }
1109
12.0k
    } // no default case, so the compiler can warn about missing cases
1110
12.0k
    NONFATAL_UNREACHABLE();
1111
12.0k
}
1112
1113
static std::optional<UniValue::VType> ExpectedType(RPCResult::Type type)
1114
5.47M
{
1115
5.47M
    using Type = RPCResult::Type;
1116
5.47M
    switch (type) {
1117
214
    case Type::ANY: {
1118
214
        return std::nullopt;
1119
0
    }
1120
16.0k
    case Type::NONE: {
1121
16.0k
        return UniValue::VNULL;
1122
0
    }
1123
631k
    case Type::STR:
1124
2.18M
    case Type::STR_HEX: {
1125
2.18M
        return UniValue::VSTR;
1126
631k
    }
1127
1.59M
    case Type::NUM:
1128
1.86M
    case Type::STR_AMOUNT:
1129
2.08M
    case Type::NUM_TIME: {
1130
2.08M
        return UniValue::VNUM;
1131
1.86M
    }
1132
392k
    case Type::BOOL: {
1133
392k
        return UniValue::VBOOL;
1134
1.86M
    }
1135
1.54k
    case Type::ARR_FIXED:
1136
262k
    case Type::ARR: {
1137
262k
        return UniValue::VARR;
1138
1.54k
    }
1139
31.6k
    case Type::OBJ_DYN:
1140
526k
    case Type::OBJ: {
1141
526k
        return UniValue::VOBJ;
1142
31.6k
    }
1143
5.47M
    } // no default case, so the compiler can warn about missing cases
1144
5.47M
    NONFATAL_UNREACHABLE();
1145
5.47M
}
1146
1147
// NOLINTNEXTLINE(misc-no-recursion)
1148
UniValue RPCResult::MatchesType(const UniValue& result) const
1149
5.47M
{
1150
5.47M
    if (m_opts.skip_type_check) {
1151
449
        return true;
1152
449
    }
1153
1154
5.47M
    const auto exp_type = ExpectedType(m_type);
1155
5.47M
    if (!exp_type) return true; // can be any type, so nothing to check
1156
1157
5.47M
    if (*exp_type != result.getType()) {
1158
11.5k
        return strprintf("returned type is %s, but declared as %s in doc", uvTypeName(result.getType()), uvTypeName(*exp_type));
1159
11.5k
    }
1160
1161
5.46M
    if (UniValue::VARR == result.getType()) {
1162
261k
        UniValue errors(UniValue::VOBJ);
1163
1.32M
        for (size_t i{0}; i < result.get_array().size(); ++i) {
1164
            // If there are more results than documented, reuse the last doc_inner.
1165
1.06M
            const RPCResult& doc_inner{m_inner.at(std::min(m_inner.size() - 1, i))};
1166
1.06M
            UniValue match{doc_inner.MatchesType(result.get_array()[i])};
1167
1.06M
            if (!match.isTrue()) errors.pushKV(strprintf("%d", i), std::move(match));
1168
1.06M
        }
1169
261k
        if (errors.empty()) return true; // empty result array is valid
1170
404
        return errors;
1171
261k
    }
1172
1173
5.20M
    if (UniValue::VOBJ == result.getType()) {
1174
525k
        UniValue errors(UniValue::VOBJ);
1175
525k
        if (m_type == Type::OBJ_DYN) {
1176
31.6k
            const RPCResult& doc_inner{m_inner.at(0)}; // Assume all types are the same, randomly pick the first
1177
327k
            for (size_t i{0}; i < result.get_obj().size(); ++i) {
1178
296k
                UniValue match{doc_inner.MatchesType(result.get_obj()[i])};
1179
296k
                if (!match.isTrue()) errors.pushKV(result.getKeys()[i], std::move(match));
1180
296k
            }
1181
31.6k
            if (errors.empty()) return true; // empty result obj is valid
1182
5
            return errors;
1183
31.6k
        }
1184
494k
        std::set<std::string> doc_keys;
1185
4.24M
        for (const auto& doc_entry : m_inner) {
1186
4.24M
            doc_keys.insert(doc_entry.m_key_name);
1187
4.24M
        }
1188
494k
        std::map<std::string, UniValue> result_obj;
1189
494k
        result.getObjMap(result_obj);
1190
3.90M
        for (const auto& result_entry : result_obj) {
1191
3.90M
            if (!doc_keys.contains(result_entry.first)) {
1192
30
                errors.pushKV(result_entry.first, "key returned that was not in doc");
1193
30
            }
1194
3.90M
        }
1195
1196
4.24M
        for (const auto& doc_entry : m_inner) {
1197
4.24M
            const auto result_it{result_obj.find(doc_entry.m_key_name)};
1198
4.24M
            if (result_it == result_obj.end()) {
1199
333k
                if (!doc_entry.m_optional) {
1200
0
                    errors.pushKV(doc_entry.m_key_name, "key missing, despite not being optional in doc");
1201
0
                }
1202
333k
                continue;
1203
333k
            }
1204
3.90M
            UniValue match{doc_entry.MatchesType(result_it->second)};
1205
3.90M
            if (!match.isTrue()) errors.pushKV(doc_entry.m_key_name, std::move(match));
1206
3.90M
        }
1207
494k
        if (errors.empty()) return true;
1208
388
        return errors;
1209
494k
    }
1210
1211
4.67M
    return true;
1212
5.20M
}
1213
1214
void RPCResult::CheckInnerDoc() const
1215
6.58M
{
1216
6.58M
    if (m_type == Type::OBJ) {
1217
        // May or may not be empty
1218
788k
        return;
1219
788k
    }
1220
    // Everything else must either be empty or not
1221
5.80M
    const bool inner_needed{m_type == Type::ARR || m_type == Type::ARR_FIXED || m_type == Type::OBJ_DYN};
1222
5.80M
    CHECK_NONFATAL(inner_needed != m_inner.empty());
1223
5.80M
}
1224
1225
// NOLINTNEXTLINE(misc-no-recursion)
1226
std::string RPCArg::ToStringObj(const bool oneline) const
1227
841
{
1228
841
    std::string res;
1229
841
    res += "\"";
1230
841
    res += GetFirstName();
1231
841
    if (oneline) {
1232
398
        res += "\":";
1233
443
    } else {
1234
443
        res += "\": ";
1235
443
    }
1236
841
    switch (m_type) {
1237
138
    case Type::STR:
1238
138
        return res + "\"str\"";
1239
259
    case Type::STR_HEX:
1240
259
        return res + "\"hex\"";
1241
211
    case Type::NUM:
1242
211
        return res + "n";
1243
69
    case Type::RANGE:
1244
69
        return res + "n or [n,n]";
1245
98
    case Type::AMOUNT:
1246
98
        return res + "amount";
1247
48
    case Type::BOOL:
1248
48
        return res + "bool";
1249
18
    case Type::ARR:
1250
18
        res += "[";
1251
27
        for (const auto& i : m_inner) {
1252
27
            res += i.ToString(oneline) + ",";
1253
27
        }
1254
18
        return res + "...]";
1255
0
    case Type::OBJ:
1256
0
    case Type::OBJ_NAMED_PARAMS:
1257
0
    case Type::OBJ_USER_KEYS:
1258
        // Currently unused, so avoid writing dead code
1259
0
        NONFATAL_UNREACHABLE();
1260
841
    } // no default case, so the compiler can warn about missing cases
1261
841
    NONFATAL_UNREACHABLE();
1262
841
}
1263
1264
// NOLINTNEXTLINE(misc-no-recursion)
1265
std::string RPCArg::ToString(const bool oneline) const
1266
2.49k
{
1267
2.49k
    if (oneline && !m_opts.oneline_description.empty()) {
1268
86
        if (m_opts.oneline_description[0] == '\"' && m_type != Type::STR_HEX && m_type != Type::STR && gArgs.GetBoolArg("-rpcdoccheck", DEFAULT_RPC_DOC_CHECK)) {
1269
0
            throw std::runtime_error{
1270
0
                STR_INTERNAL_BUG(strprintf("non-string RPC arg \"%s\" quotes oneline_description:\n%s",
1271
0
                    m_names, m_opts.oneline_description)
1272
0
                )};
1273
0
        }
1274
86
        return m_opts.oneline_description;
1275
86
    }
1276
1277
2.40k
    switch (m_type) {
1278
399
    case Type::STR_HEX:
1279
1.22k
    case Type::STR: {
1280
1.22k
        return "\"" + GetFirstName() + "\"";
1281
399
    }
1282
398
    case Type::NUM:
1283
407
    case Type::RANGE:
1284
469
    case Type::AMOUNT:
1285
788
    case Type::BOOL: {
1286
788
        return GetFirstName();
1287
469
    }
1288
116
    case Type::OBJ:
1289
146
    case Type::OBJ_NAMED_PARAMS:
1290
180
    case Type::OBJ_USER_KEYS: {
1291
        // NOLINTNEXTLINE(misc-no-recursion)
1292
398
        const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
1293
180
        if (m_type == Type::OBJ) {
1294
116
            return "{" + res + "}";
1295
116
        } else {
1296
64
            return "{" + res + ",...}";
1297
64
        }
1298
180
    }
1299
210
    case Type::ARR: {
1300
210
        std::string res;
1301
259
        for (const auto& i : m_inner) {
1302
259
            res += i.ToString(oneline) + ",";
1303
259
        }
1304
210
        return "[" + res + "...]";
1305
180
    }
1306
2.40k
    } // no default case, so the compiler can warn about missing cases
1307
2.40k
    NONFATAL_UNREACHABLE();
1308
2.40k
}
1309
1310
static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
1311
301
{
1312
301
    if (value.isNum()) {
1313
86
        return {0, value.getInt<int64_t>()};
1314
86
    }
1315
215
    if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) {
1316
215
        int64_t low = value[0].getInt<int64_t>();
1317
215
        int64_t high = value[1].getInt<int64_t>();
1318
215
        if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end");
1319
211
        return {low, high};
1320
215
    }
1321
0
    throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
1322
215
}
1323
1324
std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
1325
301
{
1326
301
    int64_t low, high;
1327
301
    std::tie(low, high) = ParseRange(value);
1328
301
    if (low < 0) {
1329
4
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
1330
4
    }
1331
297
    if ((high >> 31) != 0) {
1332
6
        throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
1333
6
    }
1334
291
    if (high >= low + 1000000) {
1335
4
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
1336
4
    }
1337
287
    return {low, high};
1338
291
}
1339
1340
std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider, const bool expand_priv)
1341
1.65k
{
1342
1.65k
    std::string desc_str;
1343
1.65k
    std::pair<int64_t, int64_t> range = {0, 1000};
1344
1.65k
    if (scanobject.isStr()) {
1345
1.54k
        desc_str = scanobject.get_str();
1346
1.54k
    } else if (scanobject.isObject()) {
1347
109
        const UniValue& desc_uni{scanobject.find_value("desc")};
1348
109
        if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
1349
109
        desc_str = desc_uni.get_str();
1350
109
        const UniValue& range_uni{scanobject.find_value("range")};
1351
109
        if (!range_uni.isNull()) {
1352
100
            range = ParseDescriptorRange(range_uni);
1353
100
        }
1354
109
    } else {
1355
0
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
1356
0
    }
1357
1358
1.65k
    std::string error;
1359
1.65k
    auto descs = Parse(desc_str, provider, error);
1360
1.65k
    if (descs.empty()) {
1361
1
        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
1362
1
    }
1363
1.65k
    if (!descs.at(0)->IsRange()) {
1364
1.55k
        range.first = 0;
1365
1.55k
        range.second = 0;
1366
1.55k
    }
1367
1.65k
    std::vector<CScript> ret;
1368
23.4k
    for (int64_t i = range.first; i <= range.second; ++i) {
1369
21.7k
        for (const auto& desc : descs) {
1370
21.7k
            std::vector<CScript> scripts;
1371
21.7k
            if (!desc->Expand(i, provider, scripts, provider)) {
1372
0
                throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
1373
0
            }
1374
21.7k
            if (expand_priv) {
1375
2.11k
                desc->ExpandPrivate(/*pos=*/i, provider, /*out=*/provider);
1376
2.11k
            }
1377
21.7k
            std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
1378
21.7k
        }
1379
21.7k
    }
1380
1.65k
    return ret;
1381
1.65k
}
1382
1383
std::vector<uint32_t> ParsePathBIP32(const std::string& path)
1384
30
{
1385
30
    std::vector<uint32_t> out;
1386
30
    if (!ParseHDKeypath(path, out)) {
1387
3
        throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid BIP32 keypath");
1388
3
    }
1389
27
    return out;
1390
30
}
1391
1392
/** Convert a vector of bilingual strings to a UniValue::VARR containing their original untranslated values. */
1393
[[nodiscard]] static UniValue BilingualStringsToUniValue(const std::vector<bilingual_str>& bilingual_strings)
1394
11
{
1395
11
    CHECK_NONFATAL(!bilingual_strings.empty());
1396
11
    UniValue result{UniValue::VARR};
1397
11
    for (const auto& s : bilingual_strings) {
1398
11
        result.push_back(s.original);
1399
11
    }
1400
11
    return result;
1401
11
}
1402
1403
void PushWarnings(const UniValue& warnings, UniValue& obj)
1404
844
{
1405
844
    if (warnings.empty()) return;
1406
390
    obj.pushKV("warnings", warnings);
1407
390
}
1408
1409
void PushWarnings(const std::vector<bilingual_str>& warnings, UniValue& obj)
1410
1.10k
{
1411
1.10k
    if (warnings.empty()) return;
1412
11
    obj.pushKV("warnings", BilingualStringsToUniValue(warnings));
1413
11
}
1414
1415
55.4k
std::vector<RPCResult> ScriptPubKeyDoc() {
1416
55.4k
    return
1417
55.4k
         {
1418
55.4k
             {RPCResult::Type::STR, "asm", "Disassembly of the output script"},
1419
55.4k
             {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
1420
55.4k
             {RPCResult::Type::STR_HEX, "hex", "The raw output script bytes, hex-encoded"},
1421
55.4k
             {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
1422
55.4k
             {RPCResult::Type::STR, "type", "The type (one of: " + GetAllOutputTypes() + ")"},
1423
55.4k
         };
1424
55.4k
}
1425
1426
uint256 GetTarget(const CBlockIndex& blockindex, const uint256 pow_limit)
1427
22.8k
{
1428
22.8k
    arith_uint256 target{*CHECK_NONFATAL(DeriveTarget(blockindex.nBits, pow_limit))};
1429
22.8k
    return ArithToUint256(target);
1430
22.8k
}
1431
1432
std::vector<RPCResult> ElideGroup(std::vector<RPCResult> fields, std::string summary)
1433
35.3k
{
1434
35.3k
    if (fields.empty()) return fields;
1435
35.3k
    std::vector<RPCResult> result;
1436
35.3k
    result.reserve(fields.size());
1437
276k
    for (size_t i = 0; i < fields.size(); ++i) {
1438
241k
        RPCResultOptions opts = fields[i].m_opts;
1439
241k
        if (i == 0) {
1440
35.3k
            opts.print_elision = summary;
1441
205k
        } else {
1442
205k
            opts.print_elision = HelpElisionSkip{};
1443
205k
        }
1444
241k
        result.emplace_back(fields[i], std::move(opts));
1445
241k
    }
1446
35.3k
    return result;
1447
35.3k
}