diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -7,3 +7,6 @@ This release includes the following features and fixes: - The message signature prefix has been rebranded to `eCash Signed Message:` (previously was `Bitcoin Signed Message:`). + - A new `getindexinfo` RPC returns the actively running indices of the node, + including their current sync status and height. It also accepts an `index_name` + to specify returning only the status of that index. \ No newline at end of file diff --git a/src/index/base.h b/src/index/base.h --- a/src/index/base.h +++ b/src/index/base.h @@ -13,6 +13,12 @@ class CBlockIndex; +struct IndexSummary { + std::string name; + bool synced{false}; + int best_block_height{0}; +}; + /** * Base class for indices of blockchain data. This implements * CValidationInterface and ensures blocks are indexed sequentially according @@ -110,6 +116,9 @@ /// Stops the instance from staying in sync with blockchain updates. void Stop(); + + /// Get a summary of the index and its state. + IndexSummary GetSummary() const; }; #endif // BITCOIN_INDEX_BASE_H diff --git a/src/index/base.cpp b/src/index/base.cpp --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -325,3 +325,11 @@ m_thread_sync.join(); } } + +IndexSummary BaseIndex::GetSummary() const { + IndexSummary summary{}; + summary.name = GetName(); + summary.synced = m_synced; + summary.best_block_height = m_best_block_index.load()->nHeight; + return summary; +} diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -822,6 +824,71 @@ }; } +static UniValue SummaryToJSON(const IndexSummary &&summary, + std::string index_name) { + UniValue ret_summary(UniValue::VOBJ); + if (!index_name.empty() && index_name != summary.name) { + return ret_summary; + } + + UniValue entry(UniValue::VOBJ); + entry.pushKV("synced", summary.synced); + entry.pushKV("best_block_height", summary.best_block_height); + ret_summary.pushKV(summary.name, entry); + return ret_summary; +} + +static RPCHelpMan getindexinfo() { + return RPCHelpMan{ + "getindexinfo", + "\nReturns the status of one or all available indices currently " + "running in the node.\n", + { + {"index_name", RPCArg::Type::STR, + RPCArg::Optional::OMITTED_NAMED_ARG, + "Filter results for an index with a specific name."}, + }, + RPCResult{ + RPCResult::Type::OBJ, + "", + "", + { + {RPCResult::Type::OBJ, + "name", + "The name of the index", + { + {RPCResult::Type::BOOL, "synced", + "Whether the index is synced or not"}, + {RPCResult::Type::NUM, "best_block_height", + "The block height to which the index is synced"}, + }}, + }, + }, + RPCExamples{HelpExampleCli("getindexinfo", "") + + HelpExampleRpc("getindexinfo", "") + + HelpExampleCli("getindexinfo", "txindex") + + HelpExampleRpc("getindexinfo", "txindex")}, + [&](const RPCHelpMan &self, Config &config, + const JSONRPCRequest &request) -> UniValue { + UniValue result(UniValue::VOBJ); + const std::string index_name = + request.params[0].isNull() ? "" : request.params[0].get_str(); + + if (g_txindex) { + result.pushKVs( + SummaryToJSON(g_txindex->GetSummary(), index_name)); + } + + ForEachBlockFilterIndex([&result, &index_name]( + const BlockFilterIndex &index) { + result.pushKVs(SummaryToJSON(index.GetSummary(), index_name)); + }); + + return result; + }, + }; +} + void RegisterMiscRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = { @@ -836,6 +903,7 @@ { "util", "verifymessage", verifymessage, {"address","signature","message"} }, { "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} }, { "util", "getcurrencyinfo", getcurrencyinfo, {} }, + { "util", "getindexinfo", getindexinfo, {"index_name"} }, /* Not shown in help */ { "hidden", "setmocktime", setmocktime, {"timestamp"}}, diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -64,6 +64,35 @@ node.logging(include=['qt']) assert_equal(node.logging()['qt'], True) + self.log.info("test getindexinfo") + # Without any indices running the RPC returns an empty object + assert_equal(node.getindexinfo(), {}) + + # Restart the node with indices and wait for them to sync + self.restart_node(0, ["-txindex", "-blockfilterindex"]) + self.wait_until( + lambda: all(i["synced"] for i in node.getindexinfo().values())) + + # Returns a list of all running indices by default + assert_equal( + node.getindexinfo(), + { + "txindex": {"synced": True, "best_block_height": 200}, + "basic block filter index": {"synced": True, "best_block_height": 200} + } + ) + + # Specifying an index by name returns only the status of that index + assert_equal( + node.getindexinfo("txindex"), + { + "txindex": {"synced": True, "best_block_height": 200}, + } + ) + + # Specifying an unknown index name returns an empty result + assert_equal(node.getindexinfo("foo"), {}) + if __name__ == '__main__': RpcMiscTest().main()