Branch data Line data Source code
# 1 : : // Copyright (c) 2017-2021 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #include <key_io.h>
# 6 : : #include <outputtype.h>
# 7 : : #include <rpc/util.h>
# 8 : : #include <script/descriptor.h>
# 9 : : #include <script/signingprovider.h>
# 10 : : #include <tinyformat.h>
# 11 : : #include <util/strencodings.h>
# 12 : : #include <util/string.h>
# 13 : : #include <util/translation.h>
# 14 : :
# 15 : : #include <tuple>
# 16 : :
# 17 : : #include <boost/algorithm/string/classification.hpp>
# 18 : : #include <boost/algorithm/string/split.hpp>
# 19 : :
# 20 : : const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
# 21 : : const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"};
# 22 : :
# 23 : : void RPCTypeCheck(const UniValue& params,
# 24 : : const std::list<UniValueType>& typesExpected,
# 25 : : bool fAllowNull)
# 26 : 18571 : {
# 27 : 18571 : unsigned int i = 0;
# 28 [ + + ]: 39491 : for (const UniValueType& t : typesExpected) {
# 29 [ + + ]: 39491 : if (params.size() <= i)
# 30 : 5608 : break;
# 31 : :
# 32 : 33883 : const UniValue& v = params[i];
# 33 [ + + ][ + + ]: 33883 : if (!(fAllowNull && v.isNull())) {
# 34 : 32959 : RPCTypeCheckArgument(v, t);
# 35 : 32959 : }
# 36 : 33883 : i++;
# 37 : 33883 : }
# 38 : 18571 : }
# 39 : :
# 40 : : void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
# 41 : 33974 : {
# 42 [ + + ][ + + ]: 33974 : if (!typeExpected.typeAny && value.type() != typeExpected.type) {
# 43 : 14 : throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
# 44 : 14 : }
# 45 : 33974 : }
# 46 : :
# 47 : : void RPCTypeCheckObj(const UniValue& o,
# 48 : : const std::map<std::string, UniValueType>& typesExpected,
# 49 : : bool fAllowNull,
# 50 : : bool fStrict)
# 51 : 1325 : {
# 52 [ + + ]: 15029 : for (const auto& t : typesExpected) {
# 53 : 15029 : const UniValue& v = find_value(o, t.first);
# 54 [ + + ][ + + ]: 15029 : if (!fAllowNull && v.isNull())
# 55 : 18 : throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
# 56 : :
# 57 [ + + ][ + + ]: 15011 : if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
# [ + - ][ + + ]
# 58 : 48 : std::string err = strprintf("Expected type %s for %s, got %s",
# 59 : 48 : uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
# 60 : 48 : throw JSONRPCError(RPC_TYPE_ERROR, err);
# 61 : 48 : }
# 62 : 15011 : }
# 63 : :
# 64 [ + + ]: 1325 : if (fStrict)
# 65 : 740 : {
# 66 [ + + ]: 740 : for (const std::string& k : o.getKeys())
# 67 : 1558 : {
# 68 [ + + ]: 1558 : if (typesExpected.count(k) == 0)
# 69 : 10 : {
# 70 : 10 : std::string err = strprintf("Unexpected key %s", k);
# 71 : 10 : throw JSONRPCError(RPC_TYPE_ERROR, err);
# 72 : 10 : }
# 73 : 1558 : }
# 74 : 740 : }
# 75 : 1259 : }
# 76 : :
# 77 : : CAmount AmountFromValue(const UniValue& value, int decimals)
# 78 : 29750 : {
# 79 [ + + ][ + + ]: 29750 : if (!value.isNum() && !value.isStr())
# 80 : 26 : throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
# 81 : 29724 : CAmount amount;
# 82 [ + + ]: 29724 : if (!ParseFixedPoint(value.getValStr(), decimals, &amount))
# 83 : 224 : throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
# 84 [ + + ]: 29500 : if (!MoneyRange(amount))
# 85 : 23 : throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
# 86 : 29477 : return amount;
# 87 : 29477 : }
# 88 : :
# 89 : : uint256 ParseHashV(const UniValue& v, std::string strName)
# 90 : 19948 : {
# 91 : 19948 : std::string strHex(v.get_str());
# 92 [ + + ]: 19948 : if (64 != strHex.length())
# 93 : 17 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
# 94 [ + + ]: 19931 : if (!IsHex(strHex)) // Note: IsHex("") is false
# 95 : 13 : throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
# 96 : 19918 : return uint256S(strHex);
# 97 : 19918 : }
# 98 : : uint256 ParseHashO(const UniValue& o, std::string strKey)
# 99 : 7221 : {
# 100 : 7221 : return ParseHashV(find_value(o, strKey), strKey);
# 101 : 7221 : }
# 102 : : std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
# 103 : 565 : {
# 104 : 565 : std::string strHex;
# 105 [ + - ]: 565 : if (v.isStr())
# 106 : 565 : strHex = v.get_str();
# 107 [ + + ]: 565 : if (!IsHex(strHex))
# 108 : 8 : throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
# 109 : 557 : return ParseHex(strHex);
# 110 : 557 : }
# 111 : : std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
# 112 : 293 : {
# 113 : 293 : return ParseHexV(find_value(o, strKey), strKey);
# 114 : 293 : }
# 115 : :
# 116 : : namespace {
# 117 : :
# 118 : : /**
# 119 : : * Quote an argument for shell.
# 120 : : *
# 121 : : * @note This is intended for help, not for security-sensitive purposes.
# 122 : : */
# 123 : : std::string ShellQuote(const std::string& s)
# 124 : 10 : {
# 125 : 10 : std::string result;
# 126 : 10 : result.reserve(s.size() * 2);
# 127 [ + + ]: 112 : for (const char ch: s) {
# 128 [ + + ]: 112 : if (ch == '\'') {
# 129 : 2 : result += "'\''";
# 130 : 110 : } else {
# 131 : 110 : result += ch;
# 132 : 110 : }
# 133 : 112 : }
# 134 : 10 : return "'" + result + "'";
# 135 : 10 : }
# 136 : :
# 137 : : /**
# 138 : : * Shell-quotes the argument if it needs quoting, else returns it literally, to save typing.
# 139 : : *
# 140 : : * @note This is intended for help, not for security-sensitive purposes.
# 141 : : */
# 142 : : std::string ShellQuoteIfNeeded(const std::string& s)
# 143 : 6972 : {
# 144 [ + + ]: 40026 : for (const char ch: s) {
# 145 [ + + ][ + + ]: 40026 : if (ch == ' ' || ch == '\'' || ch == '"') {
# [ + + ]
# 146 : 10 : return ShellQuote(s);
# 147 : 10 : }
# 148 : 40026 : }
# 149 : :
# 150 : 6972 : return s;
# 151 : 6972 : }
# 152 : :
# 153 : : }
# 154 : :
# 155 : : std::string HelpExampleCli(const std::string& methodname, const std::string& args)
# 156 : 453117 : {
# 157 : 453117 : return "> bitcoin-cli " + methodname + " " + args + "\n";
# 158 : 453117 : }
# 159 : :
# 160 : : std::string HelpExampleCliNamed(const std::string& methodname, const RPCArgList& args)
# 161 : 1754 : {
# 162 : 1754 : std::string result = "> bitcoin-cli -named " + methodname;
# 163 [ + + ]: 6972 : for (const auto& argpair: args) {
# 164 [ + + ]: 6972 : const auto& value = argpair.second.isStr()
# 165 : 6972 : ? argpair.second.get_str()
# 166 : 6972 : : argpair.second.write();
# 167 : 6972 : result += " " + argpair.first + "=" + ShellQuoteIfNeeded(value);
# 168 : 6972 : }
# 169 : 1754 : result += "\n";
# 170 : 1754 : return result;
# 171 : 1754 : }
# 172 : :
# 173 : : std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
# 174 : 272203 : {
# 175 : 272203 : return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
# 176 : 272203 : "\"method\": \"" + methodname + "\", \"params\": [" + args + "]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
# 177 : 272203 : }
# 178 : :
# 179 : : std::string HelpExampleRpcNamed(const std::string& methodname, const RPCArgList& args)
# 180 : 1748 : {
# 181 : 1748 : UniValue params(UniValue::VOBJ);
# 182 [ + + ]: 6966 : for (const auto& param: args) {
# 183 : 6966 : params.pushKV(param.first, param.second);
# 184 : 6966 : }
# 185 : :
# 186 : 1748 : return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\": \"curltest\", "
# 187 : 1748 : "\"method\": \"" + methodname + "\", \"params\": " + params.write() + "}' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
# 188 : 1748 : }
# 189 : :
# 190 : : // Converts a hex string to a public key if possible
# 191 : : CPubKey HexToPubKey(const std::string& hex_in)
# 192 : 506 : {
# 193 [ - + ]: 506 : if (!IsHex(hex_in)) {
# 194 : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
# 195 : 0 : }
# 196 : 506 : CPubKey vchPubKey(ParseHex(hex_in));
# 197 [ - + ]: 506 : if (!vchPubKey.IsFullyValid()) {
# 198 : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key: " + hex_in);
# 199 : 0 : }
# 200 : 506 : return vchPubKey;
# 201 : 506 : }
# 202 : :
# 203 : : // Retrieves a public key for an address from the given FillableSigningProvider
# 204 : : CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in)
# 205 : 140 : {
# 206 : 140 : CTxDestination dest = DecodeDestination(addr_in);
# 207 [ - + ]: 140 : if (!IsValidDestination(dest)) {
# 208 : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: " + addr_in);
# 209 : 0 : }
# 210 : 140 : CKeyID key = GetKeyForDestination(keystore, dest);
# 211 [ - + ]: 140 : if (key.IsNull()) {
# 212 : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("%s does not refer to a key", addr_in));
# 213 : 0 : }
# 214 : 140 : CPubKey vchPubKey;
# 215 [ + + ]: 140 : if (!keystore.GetPubKey(key, vchPubKey)) {
# 216 : 2 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("no full public key for address %s", addr_in));
# 217 : 2 : }
# 218 [ - + ]: 138 : if (!vchPubKey.IsFullyValid()) {
# 219 : 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet contains an invalid public key");
# 220 : 0 : }
# 221 : 138 : return vchPubKey;
# 222 : 138 : }
# 223 : :
# 224 : : // Creates a multisig address from a given list of public keys, number of signatures required, and the address type
# 225 : : CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FillableSigningProvider& keystore, CScript& script_out)
# 226 : 216 : {
# 227 : : // Gather public keys
# 228 [ - + ]: 216 : if (required < 1) {
# 229 : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "a multisignature address must require at least one key to redeem");
# 230 : 0 : }
# 231 [ - + ]: 216 : if ((int)pubkeys.size() < required) {
# 232 : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("not enough keys supplied (got %u keys, but need at least %d to redeem)", pubkeys.size(), required));
# 233 : 0 : }
# 234 [ - + ]: 216 : if (pubkeys.size() > MAX_PUBKEYS_PER_MULTISIG) {
# 235 : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Number of keys involved in the multisignature address creation > %d\nReduce the number", MAX_PUBKEYS_PER_MULTISIG));
# 236 : 0 : }
# 237 : :
# 238 : 216 : script_out = GetScriptForMultisig(required, pubkeys);
# 239 : :
# 240 : : // Check if any keys are uncompressed. If so, the type is legacy
# 241 [ + + ]: 555 : for (const CPubKey& pk : pubkeys) {
# 242 [ + + ]: 555 : if (!pk.IsCompressed()) {
# 243 : 90 : type = OutputType::LEGACY;
# 244 : 90 : break;
# 245 : 90 : }
# 246 : 555 : }
# 247 : :
# 248 [ + + ][ - + ]: 216 : if (type == OutputType::LEGACY && script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) {
# 249 : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", script_out.size(), MAX_SCRIPT_ELEMENT_SIZE)));
# 250 : 0 : }
# 251 : :
# 252 : : // Make the address
# 253 : 216 : CTxDestination dest = AddAndGetDestinationForScript(keystore, script_out, type);
# 254 : :
# 255 : 216 : return dest;
# 256 : 216 : }
# 257 : :
# 258 : : class DescribeAddressVisitor
# 259 : : {
# 260 : : public:
# 261 : 1680 : explicit DescribeAddressVisitor() {}
# 262 : :
# 263 : : UniValue operator()(const CNoDestination& dest) const
# 264 : 0 : {
# 265 : 0 : return UniValue(UniValue::VOBJ);
# 266 : 0 : }
# 267 : :
# 268 : : UniValue operator()(const PKHash& keyID) const
# 269 : 284 : {
# 270 : 284 : UniValue obj(UniValue::VOBJ);
# 271 : 284 : obj.pushKV("isscript", false);
# 272 : 284 : obj.pushKV("iswitness", false);
# 273 : 284 : return obj;
# 274 : 284 : }
# 275 : :
# 276 : : UniValue operator()(const ScriptHash& scriptID) const
# 277 : 429 : {
# 278 : 429 : UniValue obj(UniValue::VOBJ);
# 279 : 429 : obj.pushKV("isscript", true);
# 280 : 429 : obj.pushKV("iswitness", false);
# 281 : 429 : return obj;
# 282 : 429 : }
# 283 : :
# 284 : : UniValue operator()(const WitnessV0KeyHash& id) const
# 285 : 839 : {
# 286 : 839 : UniValue obj(UniValue::VOBJ);
# 287 : 839 : obj.pushKV("isscript", false);
# 288 : 839 : obj.pushKV("iswitness", true);
# 289 : 839 : obj.pushKV("witness_version", 0);
# 290 : 839 : obj.pushKV("witness_program", HexStr(id));
# 291 : 839 : return obj;
# 292 : 839 : }
# 293 : :
# 294 : : UniValue operator()(const WitnessV0ScriptHash& id) const
# 295 : 128 : {
# 296 : 128 : UniValue obj(UniValue::VOBJ);
# 297 : 128 : obj.pushKV("isscript", true);
# 298 : 128 : obj.pushKV("iswitness", true);
# 299 : 128 : obj.pushKV("witness_version", 0);
# 300 : 128 : obj.pushKV("witness_program", HexStr(id));
# 301 : 128 : return obj;
# 302 : 128 : }
# 303 : :
# 304 : : UniValue operator()(const WitnessUnknown& id) const
# 305 : 0 : {
# 306 : 0 : UniValue obj(UniValue::VOBJ);
# 307 : 0 : obj.pushKV("iswitness", true);
# 308 : 0 : obj.pushKV("witness_version", (int)id.version);
# 309 : 0 : obj.pushKV("witness_program", HexStr(Span<const unsigned char>(id.program, id.length)));
# 310 : 0 : return obj;
# 311 : 0 : }
# 312 : : };
# 313 : :
# 314 : : UniValue DescribeAddress(const CTxDestination& dest)
# 315 : 1680 : {
# 316 : 1680 : return std::visit(DescribeAddressVisitor(), dest);
# 317 : 1680 : }
# 318 : :
# 319 : : unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
# 320 : 328 : {
# 321 : 328 : const int target{value.get_int()};
# 322 : 328 : const unsigned int unsigned_target{static_cast<unsigned int>(target)};
# 323 [ + + ][ + + ]: 328 : if (target < 1 || unsigned_target > max_target) {
# 324 : 66 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u and %u", 1, max_target));
# 325 : 66 : }
# 326 : 262 : return unsigned_target;
# 327 : 262 : }
# 328 : :
# 329 : : RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
# 330 : 4185 : {
# 331 : 4185 : switch (terr) {
# 332 [ + + ]: 4177 : case TransactionError::MEMPOOL_REJECTED:
# 333 : 4177 : return RPC_TRANSACTION_REJECTED;
# 334 [ - + ]: 0 : case TransactionError::ALREADY_IN_CHAIN:
# 335 : 0 : return RPC_TRANSACTION_ALREADY_IN_CHAIN;
# 336 [ - + ]: 0 : case TransactionError::P2P_DISABLED:
# 337 : 0 : return RPC_CLIENT_P2P_DISABLED;
# 338 [ - + ]: 0 : case TransactionError::INVALID_PSBT:
# 339 [ - + ]: 0 : case TransactionError::PSBT_MISMATCH:
# 340 : 0 : return RPC_INVALID_PARAMETER;
# 341 [ - + ]: 0 : case TransactionError::SIGHASH_MISMATCH:
# 342 : 0 : return RPC_DESERIALIZATION_ERROR;
# 343 [ + + ]: 8 : default: break;
# 344 : 8 : }
# 345 : 8 : return RPC_TRANSACTION_ERROR;
# 346 : 8 : }
# 347 : :
# 348 : : UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string)
# 349 : 4185 : {
# 350 [ + + ]: 4185 : if (err_string.length() > 0) {
# 351 : 4179 : return JSONRPCError(RPCErrorFromTransactionError(terr), err_string);
# 352 : 4179 : } else {
# 353 : 6 : return JSONRPCError(RPCErrorFromTransactionError(terr), TransactionErrorString(terr).original);
# 354 : 6 : }
# 355 : 4185 : }
# 356 : :
# 357 : : /**
# 358 : : * A pair of strings that can be aligned (through padding) with other Sections
# 359 : : * later on
# 360 : : */
# 361 : : struct Section {
# 362 : : Section(const std::string& left, const std::string& right)
# 363 : 10532 : : m_left{left}, m_right{right} {}
# 364 : : std::string m_left;
# 365 : : const std::string m_right;
# 366 : : };
# 367 : :
# 368 : : /**
# 369 : : * Keeps track of RPCArgs by transforming them into sections for the purpose
# 370 : : * of serializing everything to a single string
# 371 : : */
# 372 : : struct Sections {
# 373 : : std::vector<Section> m_sections;
# 374 : : size_t m_max_pad{0};
# 375 : :
# 376 : : void PushSection(const Section& s)
# 377 : 9135 : {
# 378 : 9135 : m_max_pad = std::max(m_max_pad, s.m_left.size());
# 379 : 9135 : m_sections.push_back(s);
# 380 : 9135 : }
# 381 : :
# 382 : : /**
# 383 : : * Recursive helper to translate an RPCArg into sections
# 384 : : */
# 385 : : void Push(const RPCArg& arg, const size_t current_indent = 5, const OuterType outer_type = OuterType::NONE)
# 386 : 2260 : {
# 387 : 2260 : const auto indent = std::string(current_indent, ' ');
# 388 : 2260 : const auto indent_next = std::string(current_indent + 2, ' ');
# 389 : 2260 : const bool push_name{outer_type == OuterType::OBJ}; // Dictionary keys must have a name
# 390 : :
# 391 [ - + ]: 2260 : switch (arg.m_type) {
# 392 [ + + ]: 286 : case RPCArg::Type::STR_HEX:
# 393 [ + + ]: 938 : case RPCArg::Type::STR:
# 394 [ + + ]: 1341 : case RPCArg::Type::NUM:
# 395 [ + + ]: 1471 : case RPCArg::Type::AMOUNT:
# 396 [ + + ]: 1496 : case RPCArg::Type::RANGE:
# 397 [ + + ]: 1903 : case RPCArg::Type::BOOL: {
# 398 [ + + ]: 1903 : if (outer_type == OuterType::NONE) return; // Nothing more to do for non-recursive types on first recursion
# 399 : 709 : auto left = indent;
# 400 [ + + ][ + - ]: 709 : if (arg.m_type_str.size() != 0 && push_name) {
# 401 : 15 : left += "\"" + arg.GetName() + "\": " + arg.m_type_str.at(0);
# 402 : 694 : } else {
# 403 [ + + ]: 694 : left += push_name ? arg.ToStringObj(/* oneline */ false) : arg.ToString(/* oneline */ false);
# 404 : 694 : }
# 405 : 709 : left += ",";
# 406 : 709 : PushSection({left, arg.ToDescriptionString()});
# 407 : 709 : break;
# 408 : 709 : }
# 409 [ + + ]: 709 : case RPCArg::Type::OBJ:
# 410 [ + + ]: 159 : case RPCArg::Type::OBJ_USER_KEYS: {
# 411 [ + + ]: 159 : const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
# 412 [ - + ]: 159 : PushSection({indent + (push_name ? "\"" + arg.GetName() + "\": " : "") + "{", right});
# 413 [ + + ]: 605 : for (const auto& arg_inner : arg.m_inner) {
# 414 : 605 : Push(arg_inner, current_indent + 2, OuterType::OBJ);
# 415 : 605 : }
# 416 [ + + ]: 159 : if (arg.m_type != RPCArg::Type::OBJ) {
# 417 : 33 : PushSection({indent_next + "...", ""});
# 418 : 33 : }
# 419 [ + + ]: 159 : PushSection({indent + "}" + (outer_type != OuterType::NONE ? "," : ""), ""});
# 420 : 159 : break;
# 421 : 126 : }
# 422 [ + + ]: 198 : case RPCArg::Type::ARR: {
# 423 : 198 : auto left = indent;
# 424 [ + + ]: 198 : left += push_name ? "\"" + arg.GetName() + "\": " : "";
# 425 : 198 : left += "[";
# 426 [ + + ]: 198 : const auto right = outer_type == OuterType::NONE ? "" : arg.ToDescriptionString();
# 427 : 198 : PushSection({left, right});
# 428 [ + + ]: 258 : for (const auto& arg_inner : arg.m_inner) {
# 429 : 258 : Push(arg_inner, current_indent + 2, OuterType::ARR);
# 430 : 258 : }
# 431 : 198 : PushSection({indent_next + "...", ""});
# 432 [ + + ]: 198 : PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""});
# 433 : 198 : break;
# 434 : 126 : }
# 435 : 2260 : } // no default case, so the compiler can warn about missing cases
# 436 : 2260 : }
# 437 : :
# 438 : : /**
# 439 : : * Concatenate all sections with proper padding
# 440 : : */
# 441 : : std::string ToString() const
# 442 : 1578 : {
# 443 : 1578 : std::string ret;
# 444 : 1578 : const size_t pad = m_max_pad + 4;
# 445 [ + + ]: 10532 : for (const auto& s : m_sections) {
# 446 : : // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
# 447 : : // brace like {, }, [, or ]
# 448 [ - + ]: 10532 : CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
# 449 [ + + ]: 10532 : if (s.m_right.empty()) {
# 450 : 2601 : ret += s.m_left;
# 451 : 2601 : ret += "\n";
# 452 : 2601 : continue;
# 453 : 2601 : }
# 454 : :
# 455 : 7931 : std::string left = s.m_left;
# 456 : 7931 : left.resize(pad, ' ');
# 457 : 7931 : ret += left;
# 458 : :
# 459 : : // Properly pad after newlines
# 460 : 7931 : std::string right;
# 461 : 7931 : size_t begin = 0;
# 462 : 7931 : size_t new_line_pos = s.m_right.find_first_of('\n');
# 463 : 9233 : while (true) {
# 464 : 9233 : right += s.m_right.substr(begin, new_line_pos - begin);
# 465 [ + + ]: 9233 : if (new_line_pos == std::string::npos) {
# 466 : 7873 : break; //No new line
# 467 : 7873 : }
# 468 : 1360 : right += "\n" + std::string(pad, ' ');
# 469 : 1360 : begin = s.m_right.find_first_not_of(' ', new_line_pos + 1);
# 470 [ + + ]: 1360 : if (begin == std::string::npos) {
# 471 : 58 : break; // Empty line
# 472 : 58 : }
# 473 : 1302 : new_line_pos = s.m_right.find_first_of('\n', begin + 1);
# 474 : 1302 : }
# 475 : 7931 : ret += right;
# 476 : 7931 : ret += "\n";
# 477 : 7931 : }
# 478 : 1578 : return ret;
# 479 : 1578 : }
# 480 : : };
# 481 : :
# 482 : : RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
# 483 : 0 : : RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
# 484 : :
# 485 : : RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
# 486 : : : m_name{std::move(name)},
# 487 : : m_fun{std::move(fun)},
# 488 : : m_description{std::move(description)},
# 489 : : m_args{std::move(args)},
# 490 : : m_results{std::move(results)},
# 491 : : m_examples{std::move(examples)}
# 492 : 315540 : {
# 493 : 315540 : std::set<std::string> named_args;
# 494 [ + + ]: 597968 : for (const auto& arg : m_args) {
# 495 : 597968 : std::vector<std::string> names;
# 496 : 597968 : boost::split(names, arg.m_names, boost::is_any_of("|"));
# 497 : : // Should have unique named arguments
# 498 [ + + ]: 604570 : for (const std::string& name : names) {
# 499 [ - + ]: 604570 : CHECK_NONFATAL(named_args.insert(name).second);
# 500 : 604570 : }
# 501 : : // Default value type should match argument type only when defined
# 502 [ + + ]: 597968 : if (arg.m_fallback.index() == 2) {
# 503 : 182101 : const RPCArg::Type type = arg.m_type;
# 504 : 182101 : switch (std::get<RPCArg::Default>(arg.m_fallback).getType()) {
# 505 [ + + ]: 1396 : case UniValue::VOBJ:
# 506 [ - + ]: 1396 : CHECK_NONFATAL(type == RPCArg::Type::OBJ);
# 507 : 1396 : break;
# 508 [ + + ]: 3170 : case UniValue::VARR:
# 509 [ - + ]: 3170 : CHECK_NONFATAL(type == RPCArg::Type::ARR);
# 510 : 3170 : break;
# 511 [ + + ]: 51053 : case UniValue::VSTR:
# 512 [ + - ][ - + ]: 51053 : CHECK_NONFATAL(type == RPCArg::Type::STR || type == RPCArg::Type::STR_HEX || type == RPCArg::Type::AMOUNT);
# [ + + ]
# 513 : 51053 : break;
# 514 [ + + ]: 51053 : case UniValue::VNUM:
# 515 [ + - ][ # # ]: 45381 : CHECK_NONFATAL(type == RPCArg::Type::NUM || type == RPCArg::Type::AMOUNT || type == RPCArg::Type::RANGE);
# [ # # ]
# 516 : 45381 : break;
# 517 [ + + ]: 81101 : case UniValue::VBOOL:
# 518 [ - + ]: 81101 : CHECK_NONFATAL(type == RPCArg::Type::BOOL);
# 519 : 81101 : break;
# 520 [ - + ]: 81101 : case UniValue::VNULL:
# 521 : : // Null values are accepted in all arguments
# 522 : 0 : break;
# 523 [ - + ]: 81101 : default:
# 524 : 0 : CHECK_NONFATAL(false);
# 525 : 0 : break;
# 526 : 182101 : }
# 527 : 182101 : }
# 528 : 597968 : }
# 529 : 315540 : }
# 530 : :
# 531 : : std::string RPCResults::ToDescriptionString() const
# 532 : 744 : {
# 533 : 744 : std::string result;
# 534 [ + + ]: 840 : for (const auto& r : m_results) {
# 535 [ + + ]: 840 : if (r.m_type == RPCResult::Type::ANY) continue; // for testing only
# 536 [ + + ]: 834 : if (r.m_cond.empty()) {
# 537 : 675 : result += "\nResult:\n";
# 538 : 675 : } else {
# 539 : 159 : result += "\nResult (" + r.m_cond + "):\n";
# 540 : 159 : }
# 541 : 834 : Sections sections;
# 542 : 834 : r.ToSections(sections);
# 543 : 834 : result += sections.ToString();
# 544 : 834 : }
# 545 : 744 : return result;
# 546 : 744 : }
# 547 : :
# 548 : : std::string RPCExamples::ToDescriptionString() const
# 549 : 744 : {
# 550 [ + + ]: 744 : return m_examples.empty() ? m_examples : "\nExamples:\n" + m_examples;
# 551 : 744 : }
# 552 : :
# 553 : : UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
# 554 : 111974 : {
# 555 [ + + ]: 111974 : if (request.mode == JSONRPCRequest::GET_ARGS) {
# 556 : 154 : return GetArgMap();
# 557 : 154 : }
# 558 : : /*
# 559 : : * Check if the given request is valid according to this command or if
# 560 : : * the user is asking for help information, and throw help when appropriate.
# 561 : : */
# 562 [ + + ][ + + ]: 111820 : if (request.mode == JSONRPCRequest::GET_HELP || !IsValidNumArgs(request.params.size())) {
# 563 : 743 : throw std::runtime_error(ToString());
# 564 : 743 : }
# 565 : 111077 : const UniValue ret = m_fun(*this, request);
# 566 [ - + ]: 111077 : CHECK_NONFATAL(std::any_of(m_results.m_results.begin(), m_results.m_results.end(), [ret](const RPCResult& res) { return res.MatchesType(ret); }));
# 567 : 111077 : return ret;
# 568 : 111077 : }
# 569 : :
# 570 : : bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
# 571 : 111124 : {
# 572 : 111124 : size_t num_required_args = 0;
# 573 [ + + ]: 222992 : for (size_t n = m_args.size(); n > 0; --n) {
# 574 [ + + ]: 178577 : if (!m_args.at(n - 1).IsOptional()) {
# 575 : 66709 : num_required_args = n;
# 576 : 66709 : break;
# 577 : 66709 : }
# 578 : 178577 : }
# 579 [ + + ][ + + ]: 111124 : return num_required_args <= num_args && num_args <= m_args.size();
# 580 : 111124 : }
# 581 : :
# 582 : : std::vector<std::string> RPCHelpMan::GetArgNames() const
# 583 : 101783 : {
# 584 : 101783 : std::vector<std::string> ret;
# 585 [ + + ]: 192677 : for (const auto& arg : m_args) {
# 586 : 192677 : ret.emplace_back(arg.m_names);
# 587 : 192677 : }
# 588 : 101783 : return ret;
# 589 : 101783 : }
# 590 : :
# 591 : : std::string RPCHelpMan::ToString() const
# 592 : 744 : {
# 593 : 744 : std::string ret;
# 594 : :
# 595 : : // Oneline summary
# 596 : 744 : ret += m_name;
# 597 : 744 : bool was_optional{false};
# 598 [ + + ]: 1402 : for (const auto& arg : m_args) {
# 599 [ + + ]: 1402 : if (arg.m_hidden) break; // Any arg that follows is also hidden
# 600 : 1397 : const bool optional = arg.IsOptional();
# 601 : 1397 : ret += " ";
# 602 [ + + ]: 1397 : if (optional) {
# 603 [ + + ]: 794 : if (!was_optional) ret += "( ";
# 604 : 794 : was_optional = true;
# 605 : 794 : } else {
# 606 [ + + ]: 603 : if (was_optional) ret += ") ";
# 607 : 603 : was_optional = false;
# 608 : 603 : }
# 609 : 1397 : ret += arg.ToString(/* oneline */ true);
# 610 : 1397 : }
# 611 [ + + ]: 744 : if (was_optional) ret += " )";
# 612 : 744 : ret += "\n";
# 613 : :
# 614 : : // Description
# 615 : 744 : ret += m_description;
# 616 : :
# 617 : : // Arguments
# 618 : 744 : Sections sections;
# 619 [ + + ]: 2141 : for (size_t i{0}; i < m_args.size(); ++i) {
# 620 : 1402 : const auto& arg = m_args.at(i);
# 621 [ + + ]: 1402 : if (arg.m_hidden) break; // Any arg that follows is also hidden
# 622 : :
# 623 [ + + ]: 1397 : if (i == 0) ret += "\nArguments:\n";
# 624 : :
# 625 : : // Push named argument name and description
# 626 : 1397 : sections.m_sections.emplace_back(::ToString(i + 1) + ". " + arg.GetFirstName(), arg.ToDescriptionString());
# 627 : 1397 : sections.m_max_pad = std::max(sections.m_max_pad, sections.m_sections.back().m_left.size());
# 628 : :
# 629 : : // Recursively push nested args
# 630 : 1397 : sections.Push(arg);
# 631 : 1397 : }
# 632 : 744 : ret += sections.ToString();
# 633 : :
# 634 : : // Result
# 635 : 744 : ret += m_results.ToDescriptionString();
# 636 : :
# 637 : : // Examples
# 638 : 744 : ret += m_examples.ToDescriptionString();
# 639 : :
# 640 : 744 : return ret;
# 641 : 744 : }
# 642 : :
# 643 : : UniValue RPCHelpMan::GetArgMap() const
# 644 : 154 : {
# 645 : 154 : UniValue arr{UniValue::VARR};
# 646 [ + + ]: 446 : for (int i{0}; i < int(m_args.size()); ++i) {
# 647 : 292 : const auto& arg = m_args.at(i);
# 648 : 292 : std::vector<std::string> arg_names;
# 649 : 292 : boost::split(arg_names, arg.m_names, boost::is_any_of("|"));
# 650 [ + + ]: 294 : for (const auto& arg_name : arg_names) {
# 651 : 294 : UniValue map{UniValue::VARR};
# 652 : 294 : map.push_back(m_name);
# 653 : 294 : map.push_back(i);
# 654 : 294 : map.push_back(arg_name);
# 655 [ + + ]: 294 : map.push_back(arg.m_type == RPCArg::Type::STR ||
# 656 [ + + ]: 294 : arg.m_type == RPCArg::Type::STR_HEX);
# 657 : 294 : arr.push_back(map);
# 658 : 294 : }
# 659 : 292 : }
# 660 : 154 : return arr;
# 661 : 154 : }
# 662 : :
# 663 : : std::string RPCArg::GetFirstName() const
# 664 : 3560 : {
# 665 : 3560 : return m_names.substr(0, m_names.find("|"));
# 666 : 3560 : }
# 667 : :
# 668 : : std::string RPCArg::GetName() const
# 669 : 55 : {
# 670 [ - + ]: 55 : CHECK_NONFATAL(std::string::npos == m_names.find("|"));
# 671 : 55 : return m_names;
# 672 : 55 : }
# 673 : :
# 674 : : bool RPCArg::IsOptional() const
# 675 : 179974 : {
# 676 [ + + ]: 179974 : if (m_fallback.index() != 0) {
# 677 : 99944 : return true;
# 678 : 99944 : } else {
# 679 : 80030 : return RPCArg::Optional::NO != std::get<RPCArg::Optional>(m_fallback);
# 680 : 80030 : }
# 681 : 179974 : }
# 682 : :
# 683 : : std::string RPCArg::ToDescriptionString() const
# 684 : 2260 : {
# 685 : 2260 : std::string ret;
# 686 : 2260 : ret += "(";
# 687 [ + + ]: 2260 : if (m_type_str.size() != 0) {
# 688 : 27 : ret += m_type_str.at(1);
# 689 : 2233 : } else {
# 690 [ - + ]: 2233 : switch (m_type) {
# 691 [ + + ]: 286 : case Type::STR_HEX:
# 692 [ + + ]: 933 : case Type::STR: {
# 693 : 933 : ret += "string";
# 694 : 933 : break;
# 695 : 286 : }
# 696 [ + + ]: 381 : case Type::NUM: {
# 697 : 381 : ret += "numeric";
# 698 : 381 : break;
# 699 : 286 : }
# 700 [ + + ]: 286 : case Type::AMOUNT: {
# 701 : 130 : ret += "numeric or string";
# 702 : 130 : break;
# 703 : 286 : }
# 704 [ + + ]: 286 : case Type::RANGE: {
# 705 : 25 : ret += "numeric or array";
# 706 : 25 : break;
# 707 : 286 : }
# 708 [ + + ]: 407 : case Type::BOOL: {
# 709 : 407 : ret += "boolean";
# 710 : 407 : break;
# 711 : 286 : }
# 712 [ + + ]: 286 : case Type::OBJ:
# 713 [ + + ]: 159 : case Type::OBJ_USER_KEYS: {
# 714 : 159 : ret += "json object";
# 715 : 159 : break;
# 716 : 126 : }
# 717 [ + + ]: 198 : case Type::ARR: {
# 718 : 198 : ret += "json array";
# 719 : 198 : break;
# 720 : 2260 : }
# 721 : 2233 : } // no default case, so the compiler can warn about missing cases
# 722 : 2233 : }
# 723 [ + + ]: 2260 : if (m_fallback.index() == 1) {
# 724 : 360 : ret += ", optional, default=" + std::get<RPCArg::DefaultHint>(m_fallback);
# 725 [ + + ]: 1900 : } else if (m_fallback.index() == 2) {
# 726 : 601 : ret += ", optional, default=" + std::get<RPCArg::Default>(m_fallback).write();
# 727 : 1299 : } else {
# 728 [ - + ]: 1299 : switch (std::get<RPCArg::Optional>(m_fallback)) {
# 729 [ + + ]: 298 : case RPCArg::Optional::OMITTED: {
# 730 : : // nothing to do. Element is treated as if not present and has no default value
# 731 : 298 : break;
# 732 : 0 : }
# 733 [ + + ]: 191 : case RPCArg::Optional::OMITTED_NAMED_ARG: {
# 734 : 191 : ret += ", optional"; // Default value is "null"
# 735 : 191 : break;
# 736 : 0 : }
# 737 [ + + ]: 810 : case RPCArg::Optional::NO: {
# 738 : 810 : ret += ", required";
# 739 : 810 : break;
# 740 : 2260 : }
# 741 : 1299 : } // no default case, so the compiler can warn about missing cases
# 742 : 1299 : }
# 743 : 2260 : ret += ")";
# 744 [ + + ]: 2260 : ret += m_description.empty() ? "" : " " + m_description;
# 745 : 2260 : return ret;
# 746 : 2260 : }
# 747 : :
# 748 : : void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const int current_indent) const
# 749 : 5687 : {
# 750 : : // Indentation
# 751 : 5687 : const std::string indent(current_indent, ' ');
# 752 : 5687 : const std::string indent_next(current_indent + 2, ' ');
# 753 : :
# 754 : : // Elements in a JSON structure (dictionary or array) are separated by a comma
# 755 [ + + ]: 5687 : const std::string maybe_separator{outer_type != OuterType::NONE ? "," : ""};
# 756 : :
# 757 : : // The key name if recursed into an dictionary
# 758 : 5687 : const std::string maybe_key{
# 759 [ + + ]: 5687 : outer_type == OuterType::OBJ ?
# 760 : 4333 : "\"" + this->m_key_name + "\" : " :
# 761 : 5687 : ""};
# 762 : :
# 763 : : // Format description with type
# 764 : 5687 : const auto Description = [&](const std::string& type) {
# 765 [ + + ]: 5636 : return "(" + type + (this->m_optional ? ", optional" : "") + ")" +
# 766 [ + + ]: 5636 : (this->m_description.empty() ? "" : " " + this->m_description);
# 767 : 5636 : };
# 768 : :
# 769 [ - + ]: 5687 : switch (m_type) {
# 770 [ + + ]: 51 : case Type::ELISION: {
# 771 : : // If the inner result is empty, use three dots for elision
# 772 : 51 : sections.PushSection({indent + "..." + maybe_separator, m_description});
# 773 : 51 : return;
# 774 : 0 : }
# 775 [ - + ]: 0 : case Type::ANY: {
# 776 : 0 : CHECK_NONFATAL(false); // Only for testing
# 777 : 0 : }
# 778 [ + + ]: 133 : case Type::NONE: {
# 779 : 133 : sections.PushSection({indent + "null" + maybe_separator, Description("json null")});
# 780 : 133 : return;
# 781 : 0 : }
# 782 [ + + ]: 982 : case Type::STR: {
# 783 : 982 : sections.PushSection({indent + maybe_key + "\"str\"" + maybe_separator, Description("string")});
# 784 : 982 : return;
# 785 : 0 : }
# 786 [ + + ]: 420 : case Type::STR_AMOUNT: {
# 787 : 420 : sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
# 788 : 420 : return;
# 789 : 0 : }
# 790 [ + + ]: 714 : case Type::STR_HEX: {
# 791 : 714 : sections.PushSection({indent + maybe_key + "\"hex\"" + maybe_separator, Description("string")});
# 792 : 714 : return;
# 793 : 0 : }
# 794 [ + + ]: 1452 : case Type::NUM: {
# 795 : 1452 : sections.PushSection({indent + maybe_key + "n" + maybe_separator, Description("numeric")});
# 796 : 1452 : return;
# 797 : 0 : }
# 798 [ + + ]: 211 : case Type::NUM_TIME: {
# 799 : 211 : sections.PushSection({indent + maybe_key + "xxx" + maybe_separator, Description("numeric")});
# 800 : 211 : return;
# 801 : 0 : }
# 802 [ + + ]: 458 : case Type::BOOL: {
# 803 : 458 : sections.PushSection({indent + maybe_key + "true|false" + maybe_separator, Description("boolean")});
# 804 : 458 : return;
# 805 : 0 : }
# 806 [ + + ]: 17 : case Type::ARR_FIXED:
# 807 [ + + ]: 477 : case Type::ARR: {
# 808 : 477 : sections.PushSection({indent + maybe_key + "[", Description("json array")});
# 809 [ + + ]: 520 : for (const auto& i : m_inner) {
# 810 : 520 : i.ToSections(sections, OuterType::ARR, current_indent + 2);
# 811 : 520 : }
# 812 [ - + ]: 477 : CHECK_NONFATAL(!m_inner.empty());
# 813 [ + + ][ + + ]: 477 : if (m_type == Type::ARR && m_inner.back().m_type != Type::ELISION) {
# 814 : 455 : sections.PushSection({indent_next + "...", ""});
# 815 : 455 : } else {
# 816 : : // Remove final comma, which would be invalid JSON
# 817 : 22 : sections.m_sections.back().m_left.pop_back();
# 818 : 22 : }
# 819 : 477 : sections.PushSection({indent + "]" + maybe_separator, ""});
# 820 : 477 : return;
# 821 : 477 : }
# 822 [ + + ]: 477 : case Type::OBJ_DYN:
# 823 [ + + ]: 789 : case Type::OBJ: {
# 824 : 789 : sections.PushSection({indent + maybe_key + "{", Description("json object")});
# 825 [ + + ]: 4333 : for (const auto& i : m_inner) {
# 826 : 4333 : i.ToSections(sections, OuterType::OBJ, current_indent + 2);
# 827 : 4333 : }
# 828 [ - + ]: 789 : CHECK_NONFATAL(!m_inner.empty());
# 829 [ + + ][ + - ]: 789 : if (m_type == Type::OBJ_DYN && m_inner.back().m_type != Type::ELISION) {
# 830 : : // If the dictionary keys are dynamic, use three dots for continuation
# 831 : 73 : sections.PushSection({indent_next + "...", ""});
# 832 : 716 : } else {
# 833 : : // Remove final comma, which would be invalid JSON
# 834 : 716 : sections.m_sections.back().m_left.pop_back();
# 835 : 716 : }
# 836 : 789 : sections.PushSection({indent + "}" + maybe_separator, ""});
# 837 : 789 : return;
# 838 : 0 : }
# 839 : 0 : } // no default case, so the compiler can warn about missing cases
# 840 : 0 : CHECK_NONFATAL(false);
# 841 : 0 : }
# 842 : :
# 843 : : bool RPCResult::MatchesType(const UniValue& result) const
# 844 : 117236 : {
# 845 [ - + ]: 117236 : switch (m_type) {
# 846 [ - + ]: 0 : case Type::ELISION: {
# 847 : 0 : return false;
# 848 : 0 : }
# 849 [ + + ]: 7 : case Type::ANY: {
# 850 : 7 : return true;
# 851 : 0 : }
# 852 [ + + ]: 12634 : case Type::NONE: {
# 853 : 12634 : return UniValue::VNULL == result.getType();
# 854 : 0 : }
# 855 [ + + ]: 17454 : case Type::STR:
# 856 [ + + ]: 42094 : case Type::STR_HEX: {
# 857 : 42094 : return UniValue::VSTR == result.getType();
# 858 : 17454 : }
# 859 [ + + ]: 17454 : case Type::NUM:
# 860 [ + + ]: 4052 : case Type::STR_AMOUNT:
# 861 [ - + ]: 4052 : case Type::NUM_TIME: {
# 862 : 4052 : return UniValue::VNUM == result.getType();
# 863 : 4052 : }
# 864 [ + + ]: 4052 : case Type::BOOL: {
# 865 : 110 : return UniValue::VBOOL == result.getType();
# 866 : 4052 : }
# 867 [ - + ]: 4052 : case Type::ARR_FIXED:
# 868 [ + + ]: 15520 : case Type::ARR: {
# 869 : 15520 : return UniValue::VARR == result.getType();
# 870 : 0 : }
# 871 [ + + ]: 648 : case Type::OBJ_DYN:
# 872 [ + + ]: 42819 : case Type::OBJ: {
# 873 : 42819 : return UniValue::VOBJ == result.getType();
# 874 : 0 : }
# 875 : 0 : } // no default case, so the compiler can warn about missing cases
# 876 : 0 : CHECK_NONFATAL(false);
# 877 : 0 : }
# 878 : :
# 879 : : std::string RPCArg::ToStringObj(const bool oneline) const
# 880 : 760 : {
# 881 : 760 : std::string res;
# 882 : 760 : res += "\"";
# 883 : 760 : res += GetFirstName();
# 884 [ + + ]: 760 : if (oneline) {
# 885 : 210 : res += "\":";
# 886 : 550 : } else {
# 887 : 550 : res += "\": ";
# 888 : 550 : }
# 889 [ - + ]: 760 : switch (m_type) {
# 890 [ + + ]: 95 : case Type::STR:
# 891 : 95 : return res + "\"str\"";
# 892 [ + + ]: 202 : case Type::STR_HEX:
# 893 : 202 : return res + "\"hex\"";
# 894 [ + + ]: 177 : case Type::NUM:
# 895 : 177 : return res + "n";
# 896 [ + + ]: 25 : case Type::RANGE:
# 897 : 25 : return res + "n or [n,n]";
# 898 [ + + ]: 136 : case Type::AMOUNT:
# 899 : 136 : return res + "amount";
# 900 [ + + ]: 125 : case Type::BOOL:
# 901 : 125 : return res + "bool";
# 902 [ - + ]: 0 : case Type::ARR:
# 903 : 0 : res += "[";
# 904 [ # # ]: 0 : for (const auto& i : m_inner) {
# 905 : 0 : res += i.ToString(oneline) + ",";
# 906 : 0 : }
# 907 : 0 : return res + "...]";
# 908 [ - + ]: 0 : case Type::OBJ:
# 909 [ - + ]: 0 : case Type::OBJ_USER_KEYS:
# 910 : : // Currently unused, so avoid writing dead code
# 911 : 0 : CHECK_NONFATAL(false);
# 912 : 760 : } // no default case, so the compiler can warn about missing cases
# 913 : 760 : CHECK_NONFATAL(false);
# 914 : 0 : }
# 915 : :
# 916 : : std::string RPCArg::ToString(const bool oneline) const
# 917 : 1710 : {
# 918 [ + + ][ + + ]: 1710 : if (oneline && !m_oneline_description.empty()) return m_oneline_description;
# 919 : :
# 920 [ - + ]: 1643 : switch (m_type) {
# 921 [ + + ]: 210 : case Type::STR_HEX:
# 922 [ + + ]: 802 : case Type::STR: {
# 923 : 802 : return "\"" + GetFirstName() + "\"";
# 924 : 210 : }
# 925 [ + + ]: 277 : case Type::NUM:
# 926 [ + + ]: 282 : case Type::RANGE:
# 927 [ + + ]: 319 : case Type::AMOUNT:
# 928 [ + + ]: 601 : case Type::BOOL: {
# 929 : 601 : return GetFirstName();
# 930 : 319 : }
# 931 [ + + ]: 319 : case Type::OBJ:
# 932 [ + + ]: 104 : case Type::OBJ_USER_KEYS: {
# 933 : 210 : const std::string res = Join(m_inner, ",", [&](const RPCArg& i) { return i.ToStringObj(oneline); });
# 934 [ + + ]: 104 : if (m_type == Type::OBJ) {
# 935 : 71 : return "{" + res + "}";
# 936 : 71 : } else {
# 937 : 33 : return "{" + res + ",...}";
# 938 : 33 : }
# 939 : 0 : }
# 940 [ + + ]: 136 : case Type::ARR: {
# 941 : 136 : std::string res;
# 942 [ + + ]: 169 : for (const auto& i : m_inner) {
# 943 : 169 : res += i.ToString(oneline) + ",";
# 944 : 169 : }
# 945 : 136 : return "[" + res + "...]";
# 946 : 0 : }
# 947 : 0 : } // no default case, so the compiler can warn about missing cases
# 948 : 0 : CHECK_NONFATAL(false);
# 949 : 0 : }
# 950 : :
# 951 : : static std::pair<int64_t, int64_t> ParseRange(const UniValue& value)
# 952 : 87 : {
# 953 [ + + ]: 87 : if (value.isNum()) {
# 954 : 36 : return {0, value.get_int64()};
# 955 : 36 : }
# 956 [ + - ][ + - ]: 51 : if (value.isArray() && value.size() == 2 && value[0].isNum() && value[1].isNum()) {
# [ + - ][ + - ]
# 957 : 51 : int64_t low = value[0].get_int64();
# 958 : 51 : int64_t high = value[1].get_int64();
# 959 [ + + ]: 51 : if (low > high) throw JSONRPCError(RPC_INVALID_PARAMETER, "Range specified as [begin,end] must not have begin after end");
# 960 : 46 : return {low, high};
# 961 : 46 : }
# 962 : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified as end or as [begin,end]");
# 963 : 0 : }
# 964 : :
# 965 : : std::pair<int64_t, int64_t> ParseDescriptorRange(const UniValue& value)
# 966 : 87 : {
# 967 : 87 : int64_t low, high;
# 968 : 87 : std::tie(low, high) = ParseRange(value);
# 969 [ + + ]: 87 : if (low < 0) {
# 970 : 5 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should be greater or equal than 0");
# 971 : 5 : }
# 972 [ + + ]: 82 : if ((high >> 31) != 0) {
# 973 : 8 : throw JSONRPCError(RPC_INVALID_PARAMETER, "End of range is too high");
# 974 : 8 : }
# 975 [ + + ]: 74 : if (high >= low + 1000000) {
# 976 : 5 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Range is too large");
# 977 : 5 : }
# 978 : 69 : return {low, high};
# 979 : 69 : }
# 980 : :
# 981 : : std::vector<CScript> EvalDescriptorStringOrObject(const UniValue& scanobject, FlatSigningProvider& provider)
# 982 : 60 : {
# 983 : 60 : std::string desc_str;
# 984 : 60 : std::pair<int64_t, int64_t> range = {0, 1000};
# 985 [ + + ]: 60 : if (scanobject.isStr()) {
# 986 : 42 : desc_str = scanobject.get_str();
# 987 [ + - ]: 42 : } else if (scanobject.isObject()) {
# 988 : 18 : UniValue desc_uni = find_value(scanobject, "desc");
# 989 [ - + ]: 18 : if (desc_uni.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Descriptor needs to be provided in scan object");
# 990 : 18 : desc_str = desc_uni.get_str();
# 991 : 18 : UniValue range_uni = find_value(scanobject, "range");
# 992 [ + - ]: 18 : if (!range_uni.isNull()) {
# 993 : 18 : range = ParseDescriptorRange(range_uni);
# 994 : 18 : }
# 995 : 18 : } else {
# 996 : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan object needs to be either a string or an object");
# 997 : 0 : }
# 998 : :
# 999 : 60 : std::string error;
# 1000 : 60 : auto desc = Parse(desc_str, provider, error);
# 1001 [ - + ]: 60 : if (!desc) {
# 1002 : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
# 1003 : 0 : }
# 1004 [ + + ]: 60 : if (!desc->IsRange()) {
# 1005 : 42 : range.first = 0;
# 1006 : 42 : range.second = 0;
# 1007 : 42 : }
# 1008 : 60 : std::vector<CScript> ret;
# 1009 [ + + ]: 18109 : for (int i = range.first; i <= range.second; ++i) {
# 1010 : 18049 : std::vector<CScript> scripts;
# 1011 [ - + ]: 18049 : if (!desc->Expand(i, provider, scripts, provider)) {
# 1012 : 0 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Cannot derive script without private keys: '%s'", desc_str));
# 1013 : 0 : }
# 1014 : 18049 : std::move(scripts.begin(), scripts.end(), std::back_inserter(ret));
# 1015 : 18049 : }
# 1016 : 60 : return ret;
# 1017 : 60 : }
# 1018 : :
# 1019 : : UniValue GetServicesNames(ServiceFlags services)
# 1020 : 9717 : {
# 1021 : 9717 : UniValue servicesNames(UniValue::VARR);
# 1022 : :
# 1023 [ + + ]: 26806 : for (const auto& flag : serviceFlagsToStr(services)) {
# 1024 : 26806 : servicesNames.push_back(flag);
# 1025 : 26806 : }
# 1026 : :
# 1027 : 9717 : return servicesNames;
# 1028 : 9717 : }
|