feat(rpc): Added quorum listextended RPC (#5076)

* Added quorum listextended

* Indentation fix

* Added release notes

* Added quorum listextended func test

* Refactored reply into map

* fix: change type from ARR to OBJ_DYN to properly print out the placeholder

Co-authored-by: pasta <pasta@dashboost.org>
This commit is contained in:
Odysseas Gabrielides 2022-11-21 20:17:28 +02:00 committed by GitHub
parent dbe3f71eab
commit 06dfe1bd1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 0 deletions

View File

@ -0,0 +1,9 @@
New RPCs
--------
- `quorum listextended` is the cousin of `quorum list` with more enriched reply. Like `quorum list` "count" parameter can be used. Will list active quorums if "count" is not specified.
This RPC returns the following data per quorum grouped per llmqTypes:
- For each `quorumHash`:
- `creationHeight`: Block height where its DKG started
- `quorumIndex`: Returned only for rotated llmqTypes
- `minedBlockHash`: Hash of the block containing the mined final commitment

View File

@ -82,6 +82,75 @@ static UniValue quorum_list(const JSONRPCRequest& request)
return ret;
}
static void quorum_list_extended_help(const JSONRPCRequest& request)
{
RPCHelpMan{"quorum listextended",
"Extended list of on-chain quorums\n",
{
{"count", RPCArg::Type::NUM, /* default */ "", "Number of quorums to list. Will list active quorums if \"count\" is not specified."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::OBJ_DYN, "quorumName", "List of quorum details per some quorum type",
{
{RPCResult::Type::OBJ, "xxxx", "Quorum hash. Note: most recent quorums come first.",
{
{RPCResult::Type::NUM, "creationHeight", "Block height where the DKG started."},
{RPCResult::Type::NUM, "quorumIndex", "Quorum index (applicable only to rotated quorums)."},
{RPCResult::Type::STR_HEX, "minedBlockHash", "Blockhash where the commitment was mined."}
}},
}}
}},
RPCExamples{
HelpExampleCli("quorum", "listextended")
+ HelpExampleCli("quorum", "listextended 10")
+ HelpExampleRpc("quorum", "listextended, 10")
},
}.Check(request);
}
static UniValue quorum_list_extended(const JSONRPCRequest& request)
{
quorum_list_extended_help(request);
int count = -1;
if (!request.params[0].isNull()) {
count = ParseInt32V(request.params[0], "count");
if (count < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "count can't be negative");
}
}
UniValue ret(UniValue::VOBJ);
LLMQContext& llmq_ctx = EnsureLLMQContext(request.context);
CBlockIndex* pindexTip = WITH_LOCK(cs_main, return ::ChainActive().Tip());
for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pindexTip)) {
const auto& llmq_params = llmq::GetLLMQParams(type);
UniValue v(UniValue::VARR);
auto quorums = llmq_ctx.qman->ScanQuorums(type, pindexTip, count > -1 ? count : llmq_params.signingActiveQuorumCount);
for (const auto& q : quorums) {
UniValue obj(UniValue::VOBJ);
{
UniValue j(UniValue::VOBJ);
if (llmq_params.useRotation) {
j.pushKV("quorumIndex", q->qc->quorumIndex);
}
j.pushKV("creationHeight", q->m_quorum_base_block_index->nHeight);
j.pushKV("minedBlockHash", q->minedBlockHash.ToString());
obj.pushKV(q->qc->quorumHash.ToString(),j);
}
v.push_back(obj);
}
ret.pushKV(std::string(llmq_params.name), v);
}
return ret;
}
static void quorum_info_help(const JSONRPCRequest& request)
{
RPCHelpMan{"quorum info",
@ -727,6 +796,8 @@ static UniValue _quorum(const JSONRPCRequest& request)
if (command == "quorumlist") {
return quorum_list(new_request);
} else if (command == "quorumlistextended") {
return quorum_list_extended(new_request);
} else if (command == "quoruminfo") {
return quorum_info(new_request);
} else if (command == "quorumdkgstatus") {

View File

@ -88,6 +88,8 @@ class LLMQQuorumRotationTest(DashTestFramework):
b_0 = self.nodes[0].getbestblockhash()
(quorum_info_0_0, quorum_info_0_1) = self.mine_cycle_quorum(llmq_type_name=llmq_type_name, llmq_type=llmq_type)
assert(self.test_quorum_listextended(quorum_info_0_0, llmq_type_name))
assert(self.test_quorum_listextended(quorum_info_0_1, llmq_type_name))
quorum_members_0_0 = extract_quorum_members(quorum_info_0_0)
quorum_members_0_1 = extract_quorum_members(quorum_info_0_1)
assert_equal(len(intersection(quorum_members_0_0, quorum_members_0_1)), 0)
@ -106,6 +108,8 @@ class LLMQQuorumRotationTest(DashTestFramework):
quorumList = self.test_getmnlistdiff_quorums(b_0, b_1, {}, expectedDeleted, expectedNew)
(quorum_info_1_0, quorum_info_1_1) = self.mine_cycle_quorum(llmq_type_name=llmq_type_name, llmq_type=llmq_type)
assert(self.test_quorum_listextended(quorum_info_1_0, llmq_type_name))
assert(self.test_quorum_listextended(quorum_info_1_1, llmq_type_name))
quorum_members_1_0 = extract_quorum_members(quorum_info_1_0)
quorum_members_1_1 = extract_quorum_members(quorum_info_1_1)
assert_equal(len(intersection(quorum_members_1_0, quorum_members_1_1)), 0)
@ -213,5 +217,21 @@ class LLMQQuorumRotationTest(DashTestFramework):
return d
def test_quorum_listextended(self, quorum_info, llmq_type_name):
extended_quorum_list = self.nodes[0].quorum("listextended")[llmq_type_name]
quorum_dict = {}
for dictionary in extended_quorum_list:
quorum_dict.update(dictionary)
if quorum_info["quorumHash"] in quorum_dict:
q = quorum_dict[quorum_info["quorumHash"]]
if q["minedBlockHash"] != quorum_info["minedBlock"]:
return False
if q["creationHeight"] != quorum_info["height"]:
return False
if q["quorumIndex"] != quorum_info["quorumIndex"]:
return False
return True
return False
if __name__ == '__main__':
LLMQQuorumRotationTest().main()