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