diff --git a/doc/release-notes.md b/doc/release-notes.md index 1f94949887..f9fa8ed7e9 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -40,7 +40,9 @@ this change in requirements will have minimal to zero impact on our users, most ## New RPC methods -TODO +- The `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. ## User interface changes diff --git a/src/index/base.cpp b/src/index/base.cpp index c90c2d1b84..2789971f16 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -299,3 +299,18 @@ void BaseIndex::Stop() { m_thread_sync.join(); } } + +IndexSummary BaseIndex::GetSummary() const { + IndexSummary summary{}; + summary.name = GetName(); + summary.synced = m_synced.load(); + const CBlockIndex *pindex = m_best_block_index.load(); + if (!pindex) { + WITH_LOCK(cs_main, pindex = ::ChainActive().Genesis()); // grab genesis (height 0) as default + } + if (pindex) { // this should always be non-nullptr normally, but guard against it being nullptr for safety. + summary.best_block_height = pindex->nHeight; + summary.best_block_hash = pindex->GetBlockHash(); + } + return summary; +} diff --git a/src/index/base.h b/src/index/base.h index d9d35a3352..e9597f98c3 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -1,5 +1,5 @@ // Copyright (c) 2017-2018 The Bitcoin Core developers -// Copyright (c) 2019-2021 The Bitcoin developers +// Copyright (c) 2019-2024 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -14,6 +14,14 @@ class CBlockIndex; +/** Result type returned by BaseIndex::GetSummary() */ +struct IndexSummary { + std::string name; + bool synced{false}; + int best_block_height{0}; + uint256 best_block_hash; +}; + /** * Base class for indices of blockchain data. This implements * CValidationInterface and ensures blocks are indexed sequentially according @@ -108,4 +116,7 @@ class BaseIndex : public CValidationInterface { /// Stops the instance from staying in sync with blockchain updates. void Stop(); + + /// Get a summary of the index and its state. + IndexSummary GetSummary() const; }; diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 970c20d129..a46f969a15 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #ifdef HAVE_MALLOC_INFO #include #endif @@ -534,6 +536,61 @@ static UniValue getinfo_deprecated(const Config &config, "format these in the old format."); } +static UniValue getindexinfo(const Config &, const JSONRPCRequest &request) { + if (request.fHelp || request.params.size() > 1) { + throw std::runtime_error( + RPCHelpMan{ + "getindexinfo", + "\nReturns the status of one or all available indices currently running in the node.\n", + { + {"index_name", RPCArg::Type::STR, /* optional */ true, /* default */ "", "Filter results for an index with a specific name."}, + }, + RPCResult{ + "{ (json object)\n" + " \"name\" : { (json object) The name of the index\n" + " \"synced\" : true|false, (boolean) Whether the index is synced or not\n" + " \"best_block_height\" : n (numeric) The block height to which the index is synced\n" + " },\n" + " ...\n" + "}\n" + }, + RPCExamples{ + HelpExampleCli("getindexinfo", "") + + HelpExampleRpc("getindexinfo", "") + + HelpExampleCli("getindexinfo", "txindex") + + HelpExampleRpc("getindexinfo", "\"txindex\"") + } + }.ToStringWithResultsAndExamples() + ); + } + + UniValue::Object result; + const std::string index_name = request.params[0].isNull() ? "" : request.params[0].get_str(); + + auto SummaryToJSON = [&index_name](const IndexSummary &summary) { + UniValue::Object ret_summary; + if (index_name.empty() || index_name == summary.name) { + UniValue::Object entry; + entry.reserve(2); + entry.emplace_back("synced", summary.synced); + entry.emplace_back("best_block_height", summary.best_block_height); + ret_summary.emplace_back(summary.name, std::move(entry)); + } + return ret_summary; + }; + + auto ExtendResult = [&result](UniValue::Object &&obj) { + for (auto &&kvpair : obj) { + result.emplace_back(std::move(kvpair)); + } + }; + + if (g_txindex) { + ExtendResult(SummaryToJSON(g_txindex->GetSummary())); + } + + return result; +} // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames @@ -544,6 +601,7 @@ static const ContextFreeRPCCommand commands[] = { { "util", "createmultisig", createmultisig, {"nrequired","keys"} }, { "util", "verifymessage", verifymessage, {"address","signature","message"} }, { "util", "signmessagewithprivkey", signmessagewithprivkey, {"privkey","message"} }, + { "util", "getindexinfo", getindexinfo, {"index_name"} }, /* Not shown in help */ { "hidden", "setmocktime", setmocktime, {"timestamp"}}, { "hidden", "echo", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index 8bbc2820af..12afeafb14 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) 2019 The Bitcoin Core developers +# Copyright (c) 2024 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test RPC misc output.""" @@ -11,6 +12,7 @@ assert_equal, assert_greater_than, assert_greater_than_or_equal, + wait_until, ) from test_framework.authproxy import JSONRPCException @@ -56,6 +58,25 @@ def run_test(self): 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"]) + 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}}) + + # 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()