LCOV - code coverage report
Current view: top level - src/rpc - node.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 120 248 48.4 %
Date: 2023-09-26 12:08:55 Functions: 15 28 53.6 %

          Line data    Source code
       1             : // Copyright (c) 2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2022 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <chainparams.h>
       7             : #include <httpserver.h>
       8             : #include <index/blockfilterindex.h>
       9             : #include <index/coinstatsindex.h>
      10             : #include <index/txindex.h>
      11             : #include <interfaces/chain.h>
      12             : #include <interfaces/echo.h>
      13             : #include <interfaces/init.h>
      14             : #include <interfaces/ipc.h>
      15             : #include <kernel/cs_main.h>
      16             : #include <logging.h>
      17           2 : #include <node/context.h>
      18           2 : #include <rpc/server.h>
      19             : #include <rpc/server_util.h>
      20             : #include <rpc/util.h>
      21             : #include <scheduler.h>
      22             : #include <univalue.h>
      23             : #include <util/any.h>
      24             : #include <util/check.h>
      25             : 
      26             : #include <stdint.h>
      27           2 : #ifdef HAVE_MALLOC_INFO
      28             : #include <malloc.h>
      29             : #endif
      30             : 
      31             : using node::NodeContext;
      32             : 
      33           2 : static RPCHelpMan setmocktime()
      34             : {
      35           4 :     return RPCHelpMan{"setmocktime",
      36           2 :         "\nSet the local time to given timestamp (-regtest only)\n",
      37           4 :         {
      38           2 :             {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n"
      39             :              "Pass 0 to go back to using the system time."},
      40             :         },
      41           2 :         RPCResult{RPCResult::Type::NONE, "", ""},
      42           2 :         RPCExamples{""},
      43           2 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      44             : {
      45           0 :     if (!Params().IsMockableChain()) {
      46           0 :         throw std::runtime_error("setmocktime is for regression testing (-regtest mode) only");
      47             :     }
      48             : 
      49             :     // For now, don't change mocktime if we're in the middle of validation, as
      50             :     // this could have an effect on mempool time-based eviction, as well as
      51             :     // IsCurrentForFeeEstimation() and IsInitialBlockDownload().
      52             :     // TODO: figure out the right way to synchronize around mocktime, and
      53             :     // ensure all call sites of GetTime() are accessing this safely.
      54           0 :     LOCK(cs_main);
      55             : 
      56           0 :     const int64_t time{request.params[0].getInt<int64_t>()};
      57           0 :     if (time < 0) {
      58           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime cannot be negative: %s.", time));
      59             :     }
      60           0 :     SetMockTime(time);
      61           0 :     const NodeContext& node_context{EnsureAnyNodeContext(request.context)};
      62           0 :     for (const auto& chain_client : node_context.chain_clients) {
      63           0 :         chain_client->setMockTime(time);
      64             :     }
      65             : 
      66           0 :     return UniValue::VNULL;
      67           0 : },
      68             :     };
      69           0 : }
      70             : 
      71           2 : static RPCHelpMan mockscheduler()
      72             : {
      73           4 :     return RPCHelpMan{"mockscheduler",
      74           4 :         "\nBump the scheduler into the future (-regtest only)\n",
      75           4 :         {
      76           2 :             {"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." },
      77             :         },
      78           2 :         RPCResult{RPCResult::Type::NONE, "", ""},
      79           2 :         RPCExamples{""},
      80           2 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      81             : {
      82           0 :     if (!Params().IsMockableChain()) {
      83           2 :         throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only");
      84             :     }
      85             : 
      86           0 :     int64_t delta_seconds = request.params[0].getInt<int64_t>();
      87           0 :     if (delta_seconds <= 0 || delta_seconds > 3600) {
      88           0 :         throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)");
      89             :     }
      90             : 
      91           0 :     const NodeContext& node_context{EnsureAnyNodeContext(request.context)};
      92           0 :     CHECK_NONFATAL(node_context.scheduler)->MockForward(std::chrono::seconds{delta_seconds});
      93           0 :     SyncWithValidationInterfaceQueue();
      94             : 
      95           0 :     return UniValue::VNULL;
      96           0 : },
      97             :     };
      98           0 : }
      99             : 
     100           0 : static UniValue RPCLockedMemoryInfo()
     101             : {
     102           0 :     LockedPool::Stats stats = LockedPoolManager::Instance().stats();
     103           0 :     UniValue obj(UniValue::VOBJ);
     104           0 :     obj.pushKV("used", uint64_t(stats.used));
     105           0 :     obj.pushKV("free", uint64_t(stats.free));
     106           0 :     obj.pushKV("total", uint64_t(stats.total));
     107           0 :     obj.pushKV("locked", uint64_t(stats.locked));
     108           0 :     obj.pushKV("chunks_used", uint64_t(stats.chunks_used));
     109           0 :     obj.pushKV("chunks_free", uint64_t(stats.chunks_free));
     110           0 :     return obj;
     111           0 : }
     112             : 
     113             : #ifdef HAVE_MALLOC_INFO
     114           0 : static std::string RPCMallocInfo()
     115             : {
     116           0 :     char *ptr = nullptr;
     117           0 :     size_t size = 0;
     118           0 :     FILE *f = open_memstream(&ptr, &size);
     119           0 :     if (f) {
     120           0 :         malloc_info(0, f);
     121           0 :         fclose(f);
     122           0 :         if (ptr) {
     123           0 :             std::string rv(ptr, size);
     124           0 :             free(ptr);
     125           0 :             return rv;
     126           0 :         }
     127           0 :     }
     128           0 :     return "";
     129           0 : }
     130             : #endif
     131             : 
     132           2 : static RPCHelpMan getmemoryinfo()
     133             : {
     134             :     /* Please, avoid using the word "pool" here in the RPC interface or help,
     135             :      * as users will undoubtedly confuse it with the other "memory pool"
     136             :      */
     137           4 :     return RPCHelpMan{"getmemoryinfo",
     138           2 :                 "Returns an object containing information about memory usage.\n",
     139           4 :                 {
     140           2 :                     {"mode", RPCArg::Type::STR, RPCArg::Default{"stats"}, "determines what kind of information is returned.\n"
     141             :             "  - \"stats\" returns general statistics about memory usage in the daemon.\n"
     142             :             "  - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc)."},
     143             :                 },
     144           6 :                 {
     145           4 :                     RPCResult{"mode \"stats\"",
     146           2 :                         RPCResult::Type::OBJ, "", "",
     147           4 :                         {
     148           4 :                             {RPCResult::Type::OBJ, "locked", "Information about locked memory manager",
     149          14 :                             {
     150          42 :                                 {RPCResult::Type::NUM, "used", "Number of bytes used"},
     151          42 :                                 {RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"},
     152           2 :                                 {RPCResult::Type::NUM, "total", "Total number of bytes managed"},
     153           2 :                                 {RPCResult::Type::NUM, "locked", "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."},
     154           2 :                                 {RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"},
     155           2 :                                 {RPCResult::Type::NUM, "chunks_free", "Number unused chunks"},
     156             :                             }},
     157             :                         }
     158             :                     },
     159           4 :                     RPCResult{"mode \"mallocinfo\"",
     160           2 :                         RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\""
     161             :                     },
     162             :                 },
     163           2 :                 RPCExamples{
     164           2 :                     HelpExampleCli("getmemoryinfo", "")
     165           2 :             + HelpExampleRpc("getmemoryinfo", "")
     166             :                 },
     167           2 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     168             : {
     169           0 :     std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str();
     170           0 :     if (mode == "stats") {
     171           0 :         UniValue obj(UniValue::VOBJ);
     172           0 :         obj.pushKV("locked", RPCLockedMemoryInfo());
     173           0 :         return obj;
     174           0 :     } else if (mode == "mallocinfo") {
     175             : #ifdef HAVE_MALLOC_INFO
     176           0 :         return RPCMallocInfo();
     177             : #else
     178             :         throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo mode not available");
     179             : #endif
     180             :     } else {
     181           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode);
     182             :     }
     183           0 : },
     184             :     };
     185           0 : }
     186             : 
     187           0 : static void EnableOrDisableLogCategories(UniValue cats, bool enable) {
     188           0 :     cats = cats.get_array();
     189           0 :     for (unsigned int i = 0; i < cats.size(); ++i) {
     190           0 :         std::string cat = cats[i].get_str();
     191             : 
     192             :         bool success;
     193           0 :         if (enable) {
     194           0 :             success = LogInstance().EnableCategory(cat);
     195           0 :         } else {
     196           0 :             success = LogInstance().DisableCategory(cat);
     197             :         }
     198             : 
     199           0 :         if (!success) {
     200           0 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
     201             :         }
     202           0 :     }
     203           0 : }
     204             : 
     205           2 : static RPCHelpMan logging()
     206             : {
     207           4 :     return RPCHelpMan{"logging",
     208             :             "Gets and sets the logging configuration.\n"
     209             :             "When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n"
     210             :             "When called with arguments, adds or removes categories from debug logging and return the lists above.\n"
     211             :             "The arguments are evaluated in order \"include\", \"exclude\".\n"
     212             :             "If an item is both included and excluded, it will thus end up being excluded.\n"
     213           2 :             "The valid logging categories are: " + LogInstance().LogCategoriesString() + "\n"
     214             :             "In addition, the following are available as category names with special meanings:\n"
     215             :             "  - \"all\",  \"1\" : represent all logging categories.\n"
     216             :             "  - \"none\", \"0\" : even if other logging categories are specified, ignore all of them.\n"
     217             :             ,
     218           6 :                 {
     219           4 :                     {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "The categories to add to debug logging",
     220           4 :                         {
     221           2 :                             {"include_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
     222             :                         }},
     223           4 :                     {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "The categories to remove from debug logging",
     224           4 :                         {
     225           2 :                             {"exclude_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"},
     226             :                         }},
     227             :                 },
     228           2 :                 RPCResult{
     229           2 :                     RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status",
     230           4 :                     {
     231           2 :                         {RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"},
     232             :                     }
     233             :                 },
     234           2 :                 RPCExamples{
     235           2 :                     HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
     236           2 :             + HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]")
     237             :                 },
     238           2 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     239             : {
     240           0 :     uint32_t original_log_categories = LogInstance().GetCategoryMask();
     241           0 :     if (request.params[0].isArray()) {
     242           0 :         EnableOrDisableLogCategories(request.params[0], true);
     243           0 :     }
     244           0 :     if (request.params[1].isArray()) {
     245           0 :         EnableOrDisableLogCategories(request.params[1], false);
     246           0 :     }
     247           0 :     uint32_t updated_log_categories = LogInstance().GetCategoryMask();
     248           0 :     uint32_t changed_log_categories = original_log_categories ^ updated_log_categories;
     249             : 
     250             :     // Update libevent logging if BCLog::LIBEVENT has changed.
     251           0 :     if (changed_log_categories & BCLog::LIBEVENT) {
     252           0 :         UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT));
     253           0 :     }
     254             : 
     255           0 :     UniValue result(UniValue::VOBJ);
     256           0 :     for (const auto& logCatActive : LogInstance().LogCategoriesList()) {
     257           0 :         result.pushKV(logCatActive.category, logCatActive.active);
     258             :     }
     259             : 
     260           0 :     return result;
     261           0 : },
     262             :     };
     263           0 : }
     264             : 
     265           4 : static RPCHelpMan echo(const std::string& name)
     266             : {
     267           8 :     return RPCHelpMan{name,
     268           4 :                 "\nSimply echo back the input arguments. This command is for testing.\n"
     269             :                 "\nIt will return an internal bug report when arg9='trigger_internal_bug' is passed.\n"
     270             :                 "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in "
     271             :                 "bitcoin-cli and the GUI. There is no server-side difference.",
     272          44 :         {
     273           4 :             {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     274           4 :             {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     275           4 :             {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     276           4 :             {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     277           4 :             {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     278           4 :             {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     279           4 :             {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     280           4 :             {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     281           4 :             {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     282           4 :             {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}},
     283             :         },
     284           4 :                 RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"},
     285           4 :                 RPCExamples{""},
     286           4 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     287             : {
     288           0 :     if (request.params[9].isStr()) {
     289           0 :         CHECK_NONFATAL(request.params[9].get_str() != "trigger_internal_bug");
     290           0 :     }
     291             : 
     292           0 :     return request.params;
     293             : },
     294             :     };
     295           0 : }
     296             : 
     297           2 : static RPCHelpMan echo() { return echo("echo"); }
     298           2 : static RPCHelpMan echojson() { return echo("echojson"); }
     299             : 
     300           2 : static RPCHelpMan echoipc()
     301             : {
     302           2 :     return RPCHelpMan{
     303           2 :         "echoipc",
     304           2 :         "\nEcho back the input argument, passing it through a spawned process in a multiprocess build.\n"
     305             :         "This command is for testing.\n",
     306           2 :         {{"arg", RPCArg::Type::STR, RPCArg::Optional::NO, "The string to echo",}},
     307           2 :         RPCResult{RPCResult::Type::STR, "echo", "The echoed string."},
     308           4 :         RPCExamples{HelpExampleCli("echo", "\"Hello world\"") +
     309           2 :                     HelpExampleRpc("echo", "\"Hello world\"")},
     310           2 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
     311           0 :             interfaces::Init& local_init = *EnsureAnyNodeContext(request.context).init;
     312           0 :             std::unique_ptr<interfaces::Echo> echo;
     313           0 :             if (interfaces::Ipc* ipc = local_init.ipc()) {
     314             :                 // Spawn a new bitcoin-node process and call makeEcho to get a
     315             :                 // client pointer to a interfaces::Echo instance running in
     316             :                 // that process. This is just for testing. A slightly more
     317             :                 // realistic test spawning a different executable instead of
     318             :                 // the same executable would add a new bitcoin-echo executable,
     319             :                 // and spawn bitcoin-echo below instead of bitcoin-node. But
     320             :                 // using bitcoin-node avoids the need to build and install a
     321             :                 // new executable just for this one test.
     322           0 :                 auto init = ipc->spawnProcess("bitcoin-node");
     323           0 :                 echo = init->makeEcho();
     324           0 :                 ipc->addCleanup(*echo, [init = init.release()] { delete init; });
     325           0 :             } else {
     326             :                 // IPC support is not available because this is a bitcoind
     327             :                 // process not a bitcoind-node process, so just create a local
     328             :                 // interfaces::Echo object and return it so the `echoipc` RPC
     329             :                 // method will work, and the python test calling `echoipc`
     330             :                 // can expect the same result.
     331           0 :                 echo = local_init.makeEcho();
     332             :             }
     333           0 :             return echo->echo(request.params[0].get_str());
     334           0 :         },
     335             :     };
     336           0 : }
     337             : 
     338           0 : static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name)
     339             : {
     340           0 :     UniValue ret_summary(UniValue::VOBJ);
     341           0 :     if (!index_name.empty() && index_name != summary.name) return ret_summary;
     342             : 
     343           0 :     UniValue entry(UniValue::VOBJ);
     344           0 :     entry.pushKV("synced", summary.synced);
     345           0 :     entry.pushKV("best_block_height", summary.best_block_height);
     346           0 :     ret_summary.pushKV(summary.name, entry);
     347           0 :     return ret_summary;
     348           0 : }
     349             : 
     350           2 : static RPCHelpMan getindexinfo()
     351             : {
     352           4 :     return RPCHelpMan{"getindexinfo",
     353           2 :                 "\nReturns the status of one or all available indices currently running in the node.\n",
     354           4 :                 {
     355           2 :                     {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Filter results for an index with a specific name."},
     356             :                 },
     357           2 :                 RPCResult{
     358           4 :                     RPCResult::Type::OBJ_DYN, "", "", {
     359           2 :                         {
     360           2 :                             RPCResult::Type::OBJ, "name", "The name of the index",
     361           6 :                             {
     362           2 :                                 {RPCResult::Type::BOOL, "synced", "Whether the index is synced or not"},
     363           2 :                                 {RPCResult::Type::NUM, "best_block_height", "The block height to which the index is synced"},
     364             :                             }
     365             :                         },
     366             :                     },
     367             :                 },
     368           2 :                 RPCExamples{
     369           2 :                     HelpExampleCli("getindexinfo", "")
     370           2 :                   + HelpExampleRpc("getindexinfo", "")
     371           2 :                   + HelpExampleCli("getindexinfo", "txindex")
     372           2 :                   + HelpExampleRpc("getindexinfo", "txindex")
     373             :                 },
     374           2 :                 [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     375             : {
     376           0 :     UniValue result(UniValue::VOBJ);
     377           0 :     const std::string index_name = request.params[0].isNull() ? "" : request.params[0].get_str();
     378             : 
     379           0 :     if (g_txindex) {
     380           0 :         result.pushKVs(SummaryToJSON(g_txindex->GetSummary(), index_name));
     381           0 :     }
     382             : 
     383           0 :     if (g_coin_stats_index) {
     384           0 :         result.pushKVs(SummaryToJSON(g_coin_stats_index->GetSummary(), index_name));
     385           0 :     }
     386             : 
     387           0 :     ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) {
     388           0 :         result.pushKVs(SummaryToJSON(index.GetSummary(), index_name));
     389           0 :     });
     390             : 
     391           0 :     return result;
     392           0 : },
     393             :     };
     394           0 : }
     395             : 
     396           1 : void RegisterNodeRPCCommands(CRPCTable& t)
     397             : {
     398           9 :     static const CRPCCommand commands[]{
     399           1 :         {"control", &getmemoryinfo},
     400           1 :         {"control", &logging},
     401           1 :         {"util", &getindexinfo},
     402           1 :         {"hidden", &setmocktime},
     403           1 :         {"hidden", &mockscheduler},
     404           1 :         {"hidden", &echo},
     405           1 :         {"hidden", &echojson},
     406           1 :         {"hidden", &echoipc},
     407             :     };
     408           9 :     for (const auto& c : commands) {
     409           8 :         t.appendCommand(c.name, &c);
     410             :     }
     411           1 : }

Generated by: LCOV version 1.14