diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 51f301c48..3a23c044a 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -1,599 +1,599 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_MALLOC_INFO #include #endif static UniValue validateaddress(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error(RPCHelpMan{ "validateaddress", "\nReturn information about the given bitcoin address.\n", { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to validate"}, }, RPCResult{ "{\n" " \"isvalid\" : true|false, (boolean) If the address is " "valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin " "address validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex-encoded " "scriptPubKey generated by the address\n" " \"isscript\" : true|false, (boolean) If the key is a " "script\n" "}\n"}, RPCExamples{ HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")}, } .ToString()); } CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); ret.pushKV("isvalid", isValid); if (isValid) { if (ret["address"].isNull()) { std::string currentAddress = EncodeDestination(dest, config); ret.pushKV("address", currentAddress); CScript scriptPubKey = GetScriptForDestination(dest); ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); UniValue detail = DescribeAddress(dest); ret.pushKVs(detail); } } return ret; } static UniValue createmultisig(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 2) { std::string msg = RPCHelpMan{ "createmultisig", "\nCreates a multi-signature address with n signature of m " "keys required.\n" "It returns a json object with the address and redeemScript.\n", { {"nrequired", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "The number of required signatures out of the n keys."}, {"keys", RPCArg::Type::ARR, /* opt */ false, /* default_val */ "", "A json array of hex-encoded public keys.", { {"key", RPCArg::Type::STR_HEX, /* opt */ false, /* default_val */ "", "The hex-encoded public key"}, }}, }, RPCResult{"{\n" " \"address\":\"multisigaddress\", (string) The " "value of the new multisig address.\n" " \"redeemScript\":\"script\" (string) The " "string value of the hex-encoded redemption script.\n" "}\n"}, RPCExamples{ "\nCreate a multisig address from 2 public keys\n" + HelpExampleCli( "createmultisig", "2 " "\"[" "\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3" "42cf11ae157a7ace5fd\\\"," "\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1" "7e107ef3f6aa5a61626\\\"]\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc( "createmultisig", "2, " "\"[" "\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd3" "42cf11ae157a7ace5fd\\\"," "\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e1" "7e107ef3f6aa5a61626\\\"]\"")}, } .ToString(); throw std::runtime_error(msg); } int required = request.params[0].get_int(); // Get the public keys const UniValue &keys = request.params[1].get_array(); std::vector pubkeys; for (size_t i = 0; i < keys.size(); ++i) { if ((keys[i].get_str().length() == 2 * CPubKey::COMPRESSED_PUBLIC_KEY_SIZE || keys[i].get_str().length() == 2 * CPubKey::PUBLIC_KEY_SIZE) && IsHex(keys[i].get_str())) { pubkeys.push_back(HexToPubKey(keys[i].get_str())); } else { throw JSONRPCError( RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n", keys[i].get_str())); } } // Get the output type OutputType output_type = OutputType::LEGACY; // Construct using pay-to-script-hash: const CScript inner = CreateMultisigRedeemscript(required, pubkeys); CBasicKeyStore keystore; const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type); UniValue result(UniValue::VOBJ); result.pushKV("address", EncodeDestination(dest, config)); result.pushKV("redeemScript", HexStr(inner.begin(), inner.end())); return result; } static UniValue verifymessage(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 3) { throw std::runtime_error(RPCHelpMan{ "verifymessage", "\nVerify a signed message\n", { {"address", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The bitcoin address to use for the signature."}, {"signature", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The signature provided by the signer in base 64 encoding " "(see signmessage)."}, {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message that was signed."}, }, RPCResult{"true|false (boolean) If the signature is verified or " "not.\n"}, RPCExamples{ "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" + HelpExampleCli( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\", \"signature\", \"my " "message\"")}, } .ToString()); } LOCK(cs_main); std::string strAddress = request.params[0].get_str(); std::string strSign = request.params[1].get_str(); std::string strMessage = request.params[2].get_str(); CTxDestination destination = DecodeDestination(strAddress, config.GetChainParams()); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } const CKeyID *keyID = boost::get(&destination); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } bool fInvalid = false; std::vector vchSig = DecodeBase64(strSign.c_str(), &fInvalid); if (fInvalid) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; CPubKey pubkey; if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) { return false; } return (pubkey.GetID() == *keyID); } static UniValue signmessagewithprivkey(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 2) { throw std::runtime_error(RPCHelpMan{ "signmessagewithprivkey", "\nSign a message with the private key of an address\n", { {"privkey", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The private key to sign the message with."}, {"message", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "The message to create a signature of."}, }, RPCResult{"\"signature\" (string) The signature of the " "message encoded in base 64\n"}, RPCExamples{"\nCreate the signature\n" + HelpExampleCli("signmessagewithprivkey", "\"privkey\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs a JSON-RPC call\n" + HelpExampleRpc("signmessagewithprivkey", "\"privkey\", \"my message\"")}, } .ToString()); } std::string strPrivkey = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); CKey key = DecodeSecret(strPrivkey); if (!key.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; std::vector vchSig; if (!key.SignCompact(ss.GetHash(), vchSig)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); } return EncodeBase64(vchSig.data(), vchSig.size()); } static UniValue setmocktime(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error(RPCHelpMan{ "setmocktime", "\nSet the local time to given timestamp (-regtest only)\n", { {"timestamp", RPCArg::Type::NUM, /* opt */ false, /* default_val */ "", "Unix seconds-since-epoch timestamp\n" " Pass 0 to go back to using the system time."}, }, RPCResults{}, RPCExamples{""}, } .ToString()); } if (!config.GetChainParams().MineBlocksOnDemand()) { throw std::runtime_error( "setmocktime for regression testing (-regtest mode) only"); } // For now, don't change mocktime if we're in the middle of validation, as // this could have an effect on mempool time-based eviction, as well as // IsInitialBlockDownload(). // TODO: figure out the right way to synchronize around mocktime, and // ensure all call sites of GetTime() are accessing this safely. LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VNUM}); int64_t mockTime = request.params[0].get_int64(); if (mockTime < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Timestamp must be 0 or greater"); } SetMockTime(mockTime); return NullUniValue; } static UniValue RPCLockedMemoryInfo() { LockedPool::Stats stats = LockedPoolManager::Instance().stats(); UniValue obj(UniValue::VOBJ); obj.pushKV("used", uint64_t(stats.used)); obj.pushKV("free", uint64_t(stats.free)); obj.pushKV("total", uint64_t(stats.total)); obj.pushKV("locked", uint64_t(stats.locked)); obj.pushKV("chunks_used", uint64_t(stats.chunks_used)); obj.pushKV("chunks_free", uint64_t(stats.chunks_free)); return obj; } #ifdef HAVE_MALLOC_INFO static std::string RPCMallocInfo() { char *ptr = nullptr; size_t size = 0; FILE *f = open_memstream(&ptr, &size); if (f) { malloc_info(0, f); fclose(f); if (ptr) { std::string rv(ptr, size); free(ptr); return rv; } } return ""; } #endif static UniValue getmemoryinfo(const Config &config, const JSONRPCRequest &request) { /* Please, avoid using the word "pool" here in the RPC interface or help, * as users will undoubtedly confuse it with the other "memory pool" */ if (request.fHelp || request.params.size() > 1) { throw std::runtime_error(RPCHelpMan{ "getmemoryinfo", "Returns an object containing information about memory usage.\n", { {"mode", RPCArg::Type::STR, /* opt */ true, /* default_val */ "", "determines what kind of information is returned. This " "argument is optional, the default mode is \"stats\".\n" " - \"stats\" returns general statistics about memory usage " "in the daemon.\n" " - \"mallocinfo\" returns an XML string describing " "low-level heap state (only available if compiled with glibc " "2.10+)."}, }, { RPCResult{"mode \"stats\"", "{\n" " \"locked\": { (json object) " "Information about locked memory manager\n" " \"used\": xxxxx, (numeric) Number of " "bytes used\n" " \"free\": xxxxx, (numeric) Number of " "bytes available in current arenas\n" " \"total\": xxxxxxx, (numeric) Total " "number of bytes managed\n" " \"locked\": xxxxxx, (numeric) Amount of " "bytes that succeeded locking. If this number is " "smaller than total, locking pages failed at some " "point and key data could be swapped to disk.\n" " \"chunks_used\": xxxxx, (numeric) Number " "allocated chunks\n" " \"chunks_free\": xxxxx, (numeric) Number " "unused chunks\n" " }\n" "}\n"}, RPCResult{"mode \"mallocinfo\"", "\"...\"\n"}, }, RPCExamples{HelpExampleCli("getmemoryinfo", "") + HelpExampleRpc("getmemoryinfo", "")}, } .ToString()); } std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str(); if (mode == "stats") { UniValue obj(UniValue::VOBJ); obj.pushKV("locked", RPCLockedMemoryInfo()); return obj; } else if (mode == "mallocinfo") { #ifdef HAVE_MALLOC_INFO return RPCMallocInfo(); #else throw JSONRPCError( RPC_INVALID_PARAMETER, "mallocinfo is only available when compiled with glibc 2.10+"); #endif } else { throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode); } } static void EnableOrDisableLogCategories(UniValue cats, bool enable) { cats = cats.get_array(); for (size_t i = 0; i < cats.size(); ++i) { std::string cat = cats[i].get_str(); bool success; if (enable) { success = LogInstance().EnableCategory(cat); } else { success = LogInstance().DisableCategory(cat); } if (!success) { throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat); } } } static UniValue logging(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 2) { throw std::runtime_error(RPCHelpMan{ "logging", "Gets and sets the logging configuration.\n" "When called without an argument, returns the list of categories " "with status that are currently being debug logged or not.\n" "When called with arguments, adds or removes categories from debug " "logging and return the lists above.\n" "The arguments are evaluated in order \"include\", \"exclude\".\n" "If an item is both included and excluded, it will thus end up " "being excluded.\n" "The valid logging categories are: " + ListLogCategories() + "\n" "In addition, the following are available as category names " "with special meanings:\n" " - \"all\", \"1\" : represent all logging categories.\n" " - \"none\", \"0\" : even if other logging categories are " "specified, ignore all of them.\n", { {"include", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A json array of categories to add debug logging", { {"include_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"}, }}, {"exclude", RPCArg::Type::ARR, /* opt */ true, /* default_val */ "", "A json array of categories to remove debug logging", { {"exclude_category", RPCArg::Type::STR, /* opt */ false, /* default_val */ "", "the valid logging category"}, }}, }, RPCResult{"{ (json object where keys are the " "logging categories, and values indicates its status\n" " \"category\": 0|1, (numeric) if being debug logged " "or not. 0:inactive, 1:active\n" " ...\n" "}\n"}, RPCExamples{HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")}, } .ToString()); } uint32_t original_log_categories = LogInstance().GetCategoryMask(); if (request.params[0].isArray()) { EnableOrDisableLogCategories(request.params[0], true); } if (request.params[1].isArray()) { EnableOrDisableLogCategories(request.params[1], false); } uint32_t updated_log_categories = LogInstance().GetCategoryMask(); uint32_t changed_log_categories = original_log_categories ^ updated_log_categories; /** * Update libevent logging if BCLog::LIBEVENT has changed. * If the library version doesn't allow it, UpdateHTTPServerLogging() * returns false, in which case we should clear the BCLog::LIBEVENT flag. * Throw an error if the user has explicitly asked to change only the * libevent flag and it failed. */ if (changed_log_categories & BCLog::LIBEVENT) { if (!UpdateHTTPServerLogging( LogInstance().WillLogCategory(BCLog::LIBEVENT))) { LogInstance().DisableCategory(BCLog::LIBEVENT); if (changed_log_categories == BCLog::LIBEVENT) { throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when " "using libevent before v2.1.1."); } } } UniValue result(UniValue::VOBJ); std::vector vLogCatActive = ListActiveLogCategories(); for (const auto &logCatActive : vLogCatActive) { result.pushKV(logCatActive.category, logCatActive.active); } return result; } static UniValue echo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp) { throw std::runtime_error(RPCHelpMan{ "echo|echojson ...", "\nSimply echo back the input arguments. This command is for " "testing.\n" "\nThe difference between echo and echojson is that echojson has " "argument conversion enabled in the client-side table in " "bitcoin-cli and the GUI. There is no server-side difference.", {}, RPCResults{}, RPCExamples{""}, } .ToString()); } CHECK_NONFATAL(request.params.size() != 100); return request.params; } // clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- { "control", "getmemoryinfo", getmemoryinfo, {"mode"} }, - { "control", "logging", logging, {"include", "exclude"} }, + { "control", "logging", logging, {"include", "exclude"} }, { "util", "validateaddress", validateaddress, {"address"} }, { "util", "createmultisig", createmultisig, {"nrequired","keys"} }, { "util", "verifymessage", verifymessage, {"address","signature","message"} }, { "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} }, /* Not shown in help */ { "hidden", "setmocktime", setmocktime, {"timestamp"}}, { "hidden", "echo", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "echojson", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, }; // clang-format on void RegisterMiscRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } }