diff --git a/doc/release-notes-5853.md b/doc/release-notes-5853.md new file mode 100644 index 0000000000..f2a52635a5 --- /dev/null +++ b/doc/release-notes-5853.md @@ -0,0 +1,6 @@ +Added RPC +-------- + +- `quorum dkginfo` RPC returns information about DKGs: + - `active_dkgs`: Total number of active DKG sessions this node is participating in right now. + - `next_dkg`: The number of blocks until the next potential DKG session. diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index 37059788f1..82ca73c9e2 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -789,6 +789,49 @@ static UniValue quorum_rotationinfo(const JSONRPCRequest& request, const LLMQCon return quorumRotationInfoRet.ToJson(); } +static void quorum_dkginfo_help(const JSONRPCRequest& request) +{ + RPCHelpMan{ + "quorum dkginfo", + "Return information regarding DKGs.\n", + { + {}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::NUM, "active_dkgs", "Total number of active DKG sessions this node is participating in right now"}, + {RPCResult::Type::NUM, "next_dkg", "The number of blocks until the next potential DKG session"}, + } + }, + RPCExamples{""}, + }.Check(request); +} + +static UniValue quorum_dkginfo(const JSONRPCRequest& request, const LLMQContext& llmq_ctx, const ChainstateManager& chainman) +{ + quorum_dkginfo_help(request); + + llmq::CDKGDebugStatus status; + llmq_ctx.dkg_debugman->GetLocalDebugStatus(status); + UniValue ret(UniValue::VOBJ); + ret.pushKV("active_dkgs", int(status.sessions.size())); + + const int nTipHeight{WITH_LOCK(cs_main, return chainman.ActiveChain().Height())}; + auto minNextDKG = [](const Consensus::Params& consensusParams, int nTipHeight) { + int minDkgWindow{std::numeric_limits::max()}; + for (const auto& params: consensusParams.llmqs) { + if (params.useRotation && (nTipHeight % params.dkgInterval <= params.signingActiveQuorumCount)) { + return 1; + } + minDkgWindow = std::min(minDkgWindow, params.dkgInterval - (nTipHeight % params.dkgInterval)); + } + return minDkgWindow; + }; + ret.pushKV("next_dkg", minNextDKG(Params().GetConsensus(), nTipHeight)); + + return ret; +} [[ noreturn ]] static void quorum_help() { @@ -801,6 +844,7 @@ static UniValue quorum_rotationinfo(const JSONRPCRequest& request, const LLMQCon " list - List of on-chain quorums\n" " listextended - Extended list of on-chain quorums\n" " info - Return information about a quorum\n" + " dkginfo - Return information about DKGs\n" " dkgsimerror - Simulates DKG errors and malicious behavior\n" " dkgstatus - Return the status of the current DKG process\n" " memberof - Checks which quorums the given masternode is a member of\n" @@ -836,6 +880,8 @@ static UniValue _quorum(const JSONRPCRequest& request) return quorum_list_extended(new_request, chainman, llmq_ctx); } else if (command == "quoruminfo") { return quorum_info(new_request, llmq_ctx); + } else if (command == "quorumdkginfo") { + return quorum_dkginfo(new_request, llmq_ctx, chainman); } else if (command == "quorumdkgstatus") { return quorum_dkgstatus(new_request, chainman, llmq_ctx); } else if (command == "quorummemberof") { diff --git a/test/functional/feature_llmq_rotation.py b/test/functional/feature_llmq_rotation.py index 117f36666b..568358a4ca 100755 --- a/test/functional/feature_llmq_rotation.py +++ b/test/functional/feature_llmq_rotation.py @@ -76,6 +76,13 @@ class LLMQQuorumRotationTest(DashTestFramework): b_h_0 = self.nodes[0].getbestblockhash() + tip = self.nodes[0].getblockcount() + next_dkg = 24 - (tip % 24) + for node in self.nodes: + dkg_info = node.quorum("dkginfo") + assert_equal(dkg_info['active_dkgs'], 0) + assert_equal(dkg_info['next_dkg'], next_dkg) + #Mine 2 quorums so that Chainlocks can be available: Need them to include CL in CbTx as soon as v20 activates self.log.info("Mining 2 quorums") h_0 = self.mine_quorum() @@ -93,6 +100,18 @@ class LLMQQuorumRotationTest(DashTestFramework): b_h_1 = self.nodes[0].getbestblockhash() + tip = self.nodes[0].getblockcount() + next_dkg = 24 - (tip % 24) + assert next_dkg < 24 + nonzero_dkgs = 0 + for i in range(len(self.nodes)): + dkg_info = self.nodes[i].quorum("dkginfo") + if i == 0: + assert_equal(dkg_info['active_dkgs'], 0) + nonzero_dkgs += dkg_info['active_dkgs'] + assert_equal(dkg_info['next_dkg'], next_dkg) + assert_equal(nonzero_dkgs, 11) # 2 quorums 4 nodes each and 1 quorum of 3 nodes + expectedDeleted = [] expectedNew = [h_100_0, h_106_0, h_104_0, h_100_1, h_106_1, h_104_1] quorumList = self.test_getmnlistdiff_quorums(b_h_0, b_h_1, {}, expectedDeleted, expectedNew, testQuorumsCLSigs=False)