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