Merge #6055: refactor: use new type of composite commands for quorum NNN

b478406f9f refactor: clean-up, missing const in rpc/quorums (Konstantin Akimov)
50c99e84f8 fix: adopt platform restriction for new composite commands (Konstantin Akimov)
e3c4d66ef3 refactor: quorum sign methods (Konstantin Akimov)
2af9d86654 refactor: use new approach for rpc quorums NNN (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  See #6051

  ## What was done?
  Commands starting from 'quorum ...' uses new a new way to make composite commands.

  ## How Has This Been Tested?
  Run unit/functional tests.
  Please notice, there's required extra changes for platform restrictions, the subcommand is no more first argument, but part of command, space separated.

  ## Breaking Changes
  N/A

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [x] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  UdjinM6:
    utACK b478406f9f
  PastaPastaPasta:
    utACK b478406f9f

Tree-SHA512: c53856a3f45fee44a8130876ed4545f6f2c0f59f74e4d4745e7354097fee3a74ed526cc8160de532dbb4aeb81b82638ee912a0961b910692e78889baa2c976c3
This commit is contained in:
pasta 2024-06-13 11:06:59 -05:00
commit 692a076aa3
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
2 changed files with 308 additions and 303 deletions

View File

@ -34,9 +34,9 @@ namespace llmq {
extern const std::string CLSIG_REQUESTID_PREFIX; extern const std::string CLSIG_REQUESTID_PREFIX;
} }
static void quorum_list_help(const JSONRPCRequest& request) static RPCHelpMan quorum_list()
{ {
RPCHelpMan{"quorum list", return RPCHelpMan{"quorum list",
"List of on-chain quorums\n", "List of on-chain quorums\n",
{ {
{"count", RPCArg::Type::NUM, /* default */ "", {"count", RPCArg::Type::NUM, /* default */ "",
@ -57,12 +57,11 @@ static void quorum_list_help(const JSONRPCRequest& request)
+ HelpExampleCli("quorum", "list 10") + HelpExampleCli("quorum", "list 10")
+ HelpExampleRpc("quorum", "list, 10") + HelpExampleRpc("quorum", "list, 10")
}, },
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_list(const JSONRPCRequest& request, const ChainstateManager& chainman, const LLMQContext& llmq_ctx)
{ {
quorum_list_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
int count = -1; int count = -1;
if (!request.params[0].isNull()) { if (!request.params[0].isNull()) {
@ -77,7 +76,7 @@ static UniValue quorum_list(const JSONRPCRequest& request, const ChainstateManag
CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip());
for (const auto& type : llmq::GetEnabledQuorumTypes(pindexTip)) { for (const auto& type : llmq::GetEnabledQuorumTypes(pindexTip)) {
const auto& llmq_params_opt = Params().GetLLMQ(type); const auto llmq_params_opt = Params().GetLLMQ(type);
CHECK_NONFATAL(llmq_params_opt.has_value()); CHECK_NONFATAL(llmq_params_opt.has_value());
UniValue v(UniValue::VARR); UniValue v(UniValue::VARR);
@ -90,11 +89,13 @@ static UniValue quorum_list(const JSONRPCRequest& request, const ChainstateManag
} }
return ret; return ret;
},
};
} }
static void quorum_list_extended_help(const JSONRPCRequest& request) static RPCHelpMan quorum_list_extended()
{ {
RPCHelpMan{"quorum listextended", return RPCHelpMan{"quorum listextended",
"Extended list of on-chain quorums\n", "Extended list of on-chain quorums\n",
{ {
{"height", RPCArg::Type::NUM, /* default */ "", "The height index. Will list active quorums at tip if \"height\" is not specified."}, {"height", RPCArg::Type::NUM, /* default */ "", "The height index. Will list active quorums at tip if \"height\" is not specified."},
@ -122,12 +123,11 @@ static void quorum_list_extended_help(const JSONRPCRequest& request)
+ HelpExampleCli("quorum", "listextended 2500") + HelpExampleCli("quorum", "listextended 2500")
+ HelpExampleRpc("quorum", "listextended, 2500") + HelpExampleRpc("quorum", "listextended, 2500")
}, },
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_list_extended(const JSONRPCRequest& request, const ChainstateManager& chainman, const LLMQContext& llmq_ctx)
{ {
quorum_list_extended_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
int nHeight = -1; int nHeight = -1;
if (!request.params[0].isNull()) { if (!request.params[0].isNull()) {
@ -142,7 +142,7 @@ static UniValue quorum_list_extended(const JSONRPCRequest& request, const Chains
CBlockIndex* pblockindex = nHeight != -1 ? WITH_LOCK(cs_main, return chainman.ActiveChain()[nHeight]) : WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); CBlockIndex* pblockindex = nHeight != -1 ? WITH_LOCK(cs_main, return chainman.ActiveChain()[nHeight]) : WITH_LOCK(cs_main, return chainman.ActiveChain().Tip());
for (const auto& type : llmq::GetEnabledQuorumTypes(pblockindex)) { for (const auto& type : llmq::GetEnabledQuorumTypes(pblockindex)) {
const auto& llmq_params_opt = Params().GetLLMQ(type); const auto llmq_params_opt = Params().GetLLMQ(type);
CHECK_NONFATAL(llmq_params_opt.has_value()); CHECK_NONFATAL(llmq_params_opt.has_value());
const auto& llmq_params = llmq_params_opt.value(); const auto& llmq_params = llmq_params_opt.value();
UniValue v(UniValue::VARR); UniValue v(UniValue::VARR);
@ -172,20 +172,8 @@ static UniValue quorum_list_extended(const JSONRPCRequest& request, const Chains
} }
return ret; return ret;
} },
};
static void quorum_info_help(const JSONRPCRequest& request)
{
RPCHelpMan{"quorum info",
"Return information about a quorum\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"quorumHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash of quorum."},
{"includeSkShare", RPCArg::Type::BOOL, /* default */ "", "Include secret key share in output."},
},
RPCResults{},
RPCExamples{""},
}.Check(request);
} }
static UniValue BuildQuorumInfo(const llmq::CQuorumBlockProcessor& quorum_block_processor, const llmq::CQuorumCPtr& quorum, bool includeMembers, bool includeSkShare) static UniValue BuildQuorumInfo(const llmq::CQuorumBlockProcessor& quorum_block_processor, const llmq::CQuorumCPtr& quorum, bool includeMembers, bool includeSkShare)
@ -237,16 +225,28 @@ static UniValue BuildQuorumInfo(const llmq::CQuorumBlockProcessor& quorum_block_
return ret; return ret;
} }
static UniValue quorum_info(const JSONRPCRequest& request, const LLMQContext& llmq_ctx) static RPCHelpMan quorum_info()
{ {
quorum_info_help(request); return RPCHelpMan{"quorum info",
"Return information about a quorum\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"quorumHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Block hash of quorum."},
{"includeSkShare", RPCArg::Type::BOOL, /* default */ "", "Include secret key share in output."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node = EnsureAnyNodeContext(request.context);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[0], "llmqType"); const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
if (!Params().GetLLMQ(llmqType).has_value()) { if (!Params().GetLLMQ(llmqType).has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
} }
uint256 quorumHash(ParseHashV(request.params[1], "quorumHash")); const uint256 quorumHash(ParseHashV(request.params[1], "quorumHash"));
bool includeSkShare = false; bool includeSkShare = false;
if (!request.params[2].isNull()) { if (!request.params[2].isNull()) {
includeSkShare = ParseBoolV(request.params[2], "includeSkShare"); includeSkShare = ParseBoolV(request.params[2], "includeSkShare");
@ -258,11 +258,13 @@ static UniValue quorum_info(const JSONRPCRequest& request, const LLMQContext& ll
} }
return BuildQuorumInfo(*llmq_ctx.quorum_block_processor, quorum, true, includeSkShare); return BuildQuorumInfo(*llmq_ctx.quorum_block_processor, quorum, true, includeSkShare);
},
};
} }
static void quorum_dkgstatus_help(const JSONRPCRequest& request) static RPCHelpMan quorum_dkgstatus()
{ {
RPCHelpMan{"quorum dkgstatus", return RPCHelpMan{"quorum dkgstatus",
"Return the status of the current DKG process.\n" "Return the status of the current DKG process.\n"
"Works only when SPORK_17_QUORUM_DKG_ENABLED spork is ON.\n", "Works only when SPORK_17_QUORUM_DKG_ENABLED spork is ON.\n",
{ {
@ -272,13 +274,11 @@ static void quorum_dkgstatus_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_dkgstatus(const JSONRPCRequest& request, CDeterministicMNManager& dmnman, const CActiveMasternodeManager* const mn_activeman,
const ChainstateManager& chainman, const CSporkManager& sporkman, const LLMQContext& llmq_ctx)
{ {
quorum_dkgstatus_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
int detailLevel = 0; int detailLevel = 0;
if (!request.params[0].isNull()) { if (!request.params[0].isNull()) {
@ -291,17 +291,16 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request, CDeterministicMN
llmq::CDKGDebugStatus status; llmq::CDKGDebugStatus status;
llmq_ctx.dkg_debugman->GetLocalDebugStatus(status); llmq_ctx.dkg_debugman->GetLocalDebugStatus(status);
auto ret = status.ToJson(dmnman, chainman, detailLevel); auto ret = status.ToJson(*node.dmnman, chainman, detailLevel);
CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); CBlockIndex* pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip());
int tipHeight = pindexTip->nHeight; int tipHeight = pindexTip->nHeight;
const uint256 proTxHash = node.mn_activeman ? node.mn_activeman->GetProTxHash() : uint256();
const uint256 proTxHash = mn_activeman ? mn_activeman->GetProTxHash() : uint256();
UniValue minableCommitments(UniValue::VARR); UniValue minableCommitments(UniValue::VARR);
UniValue quorumArrConnections(UniValue::VARR); UniValue quorumArrConnections(UniValue::VARR);
for (const auto& type : llmq::GetEnabledQuorumTypes(pindexTip)) { for (const auto& type : llmq::GetEnabledQuorumTypes(pindexTip)) {
const auto& llmq_params_opt = Params().GetLLMQ(type); const auto llmq_params_opt = Params().GetLLMQ(type);
CHECK_NONFATAL(llmq_params_opt.has_value()); CHECK_NONFATAL(llmq_params_opt.has_value());
const auto& llmq_params = llmq_params_opt.value(); const auto& llmq_params = llmq_params_opt.value();
bool rotation_enabled = llmq::IsQuorumRotationEnabled(llmq_params, pindexTip); bool rotation_enabled = llmq::IsQuorumRotationEnabled(llmq_params, pindexTip);
@ -312,7 +311,7 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request, CDeterministicMN
obj.pushKV("llmqType", std::string(llmq_params.name)); obj.pushKV("llmqType", std::string(llmq_params.name));
obj.pushKV("quorumIndex", quorumIndex); obj.pushKV("quorumIndex", quorumIndex);
if (mn_activeman) { if (node.mn_activeman) {
int quorumHeight = tipHeight - (tipHeight % llmq_params.dkgInterval) + quorumIndex; int quorumHeight = tipHeight - (tipHeight % llmq_params.dkgInterval) + quorumIndex;
if (quorumHeight <= tipHeight) { if (quorumHeight <= tipHeight) {
const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return chainman.ActiveChain()[quorumHeight]); const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return chainman.ActiveChain()[quorumHeight]);
@ -320,10 +319,9 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request, CDeterministicMN
obj.pushKV("quorumHash", pQuorumBaseBlockIndex->GetBlockHash().ToString()); obj.pushKV("quorumHash", pQuorumBaseBlockIndex->GetBlockHash().ToString());
obj.pushKV("pindexTip", pindexTip->nHeight); obj.pushKV("pindexTip", pindexTip->nHeight);
auto allConnections = llmq::utils::GetQuorumConnections(llmq_params, dmnman, sporkman, pQuorumBaseBlockIndex, proTxHash, false); auto allConnections = llmq::utils::GetQuorumConnections(llmq_params, *node.dmnman, *node.sporkman, pQuorumBaseBlockIndex, proTxHash, false);
auto outboundConnections = llmq::utils::GetQuorumConnections(llmq_params, dmnman, sporkman, pQuorumBaseBlockIndex, proTxHash, true); auto outboundConnections = llmq::utils::GetQuorumConnections(llmq_params, *node.dmnman, *node.sporkman, pQuorumBaseBlockIndex, proTxHash, true);
std::map<uint256, CAddress> foundConnections; std::map<uint256, CAddress> foundConnections;
const NodeContext& node = EnsureAnyNodeContext(request.context);
node.connman->ForEachNode([&](const CNode* pnode) { node.connman->ForEachNode([&](const CNode* pnode) {
auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash(); auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash();
if (!verifiedProRegTxHash.IsNull() && allConnections.count(verifiedProRegTxHash)) { if (!verifiedProRegTxHash.IsNull() && allConnections.count(verifiedProRegTxHash)) {
@ -360,11 +358,13 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request, CDeterministicMN
ret.pushKV("quorumConnections", quorumArrConnections); ret.pushKV("quorumConnections", quorumArrConnections);
ret.pushKV("minableCommitments", minableCommitments); ret.pushKV("minableCommitments", minableCommitments);
return ret; return ret;
},
};
} }
static void quorum_memberof_help(const JSONRPCRequest& request) static RPCHelpMan quorum_memberof()
{ {
RPCHelpMan{"quorum memberof", return RPCHelpMan{"quorum memberof",
"Checks which quorums the given masternode is a member of.\n", "Checks which quorums the given masternode is a member of.\n",
{ {
{"proTxHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "ProTxHash of the masternode."}, {"proTxHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "ProTxHash of the masternode."},
@ -376,12 +376,11 @@ static void quorum_memberof_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_memberof(const JSONRPCRequest& request, const ChainstateManager& chainman, const NodeContext& node, const LLMQContext& llmq_ctx)
{ {
quorum_memberof_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
uint256 protxHash(ParseHashV(request.params[0], "proTxHash")); uint256 protxHash(ParseHashV(request.params[0], "proTxHash"));
int scanQuorumsCount = -1; int scanQuorumsCount = -1;
@ -401,7 +400,7 @@ static UniValue quorum_memberof(const JSONRPCRequest& request, const ChainstateM
UniValue result(UniValue::VARR); UniValue result(UniValue::VARR);
for (const auto& type : llmq::GetEnabledQuorumTypes(pindexTip)) { for (const auto& type : llmq::GetEnabledQuorumTypes(pindexTip)) {
const auto& llmq_params_opt = Params().GetLLMQ(type); const auto llmq_params_opt = Params().GetLLMQ(type);
CHECK_NONFATAL(llmq_params_opt.has_value()); CHECK_NONFATAL(llmq_params_opt.has_value());
size_t count = llmq_params_opt->signingActiveQuorumCount; size_t count = llmq_params_opt->signingActiveQuorumCount;
if (scanQuorumsCount != -1) { if (scanQuorumsCount != -1) {
@ -419,11 +418,13 @@ static UniValue quorum_memberof(const JSONRPCRequest& request, const ChainstateM
} }
return result; return result;
},
};
} }
static void quorum_sign_help(const JSONRPCRequest& request) static RPCHelpMan quorum_sign()
{ {
RPCHelpMan{"quorum sign", return RPCHelpMan{"quorum sign",
"Threshold-sign a message\n", "Threshold-sign a message\n",
{ {
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."}, {"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
@ -435,103 +436,20 @@ static void quorum_sign_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static void quorum_verify_help(const JSONRPCRequest& request)
{ {
RPCHelpMan{"quorum verify", const NodeContext& node = EnsureAnyNodeContext(request.context);
"Test if a quorum signature is valid for a request id and a message hash\n", const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
{"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "Quorum signature to verify."},
{"quorumHash", RPCArg::Type::STR_HEX, /* default */ "",
"The quorum identifier.\n"
"Set to \"\" if you want to specify signHeight instead."},
{"signHeight", RPCArg::Type::NUM, /* default */ "",
"The height at which the message was signed.\n"
"Only works when quorumHash is \"\"."},
},
RPCResults{},
RPCExamples{""},
}.Check(request);
}
static void quorum_hasrecsig_help(const JSONRPCRequest& request) const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
{ const auto llmq_params_opt = Params().GetLLMQ(llmqType);
RPCHelpMan{"quorum hasrecsig",
"Test if a valid recovered signature is present\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
},
RPCResults{},
RPCExamples{""},
}.Check(request);
}
static void quorum_getrecsig_help(const JSONRPCRequest& request)
{
RPCHelpMan{"quorum getrecsig",
"Get a recovered signature\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
},
RPCResults{},
RPCExamples{""},
}.Check(request);
}
static void quorum_isconflicting_help(const JSONRPCRequest& request)
{
RPCHelpMan{"quorum isconflicting",
"Test if a conflict exists\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
},
RPCResults{},
RPCExamples{""},
}.Check(request);
}
static UniValue quorum_sigs_cmd(const JSONRPCRequest& request, const LLMQContext& llmq_ctx)
{
auto cmd = request.strMethod;
if (request.fHelp || (request.params.size() != 3)) {
if (cmd == "quorumsign") {
quorum_sign_help(request);
} else if (cmd == "quorumverify") {
quorum_verify_help(request);
} else if (cmd == "quorumhasrecsig") {
quorum_hasrecsig_help(request);
} else if (cmd == "quorumgetrecsig") {
quorum_getrecsig_help(request);
} else if (cmd == "quorumisconflicting") {
quorum_isconflicting_help(request);
} else {
// shouldn't happen as it's already handled by the caller
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid cmd");
}
}
Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[0], "llmqType");
const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
if (!llmq_params_opt.has_value()) { if (!llmq_params_opt.has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
} }
uint256 id(ParseHashV(request.params[1], "id")); const uint256 id(ParseHashV(request.params[1], "id"));
uint256 msgHash(ParseHashV(request.params[2], "msgHash")); const uint256 msgHash(ParseHashV(request.params[2], "msgHash"));
if (cmd == "quorumsign") {
uint256 quorumHash; uint256 quorumHash;
if (!request.params[3].isNull() && !request.params[3].get_str().empty()) { if (!request.params[3].isNull() && !request.params[3].get_str().empty()) {
quorumHash = ParseHashV(request.params[3], "quorumHash"); quorumHash = ParseHashV(request.params[3], "quorumHash");
@ -572,7 +490,43 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request, const LLMQContext
return obj; return obj;
} }
} else if (cmd == "quorumverify") { },
};
}
static RPCHelpMan quorum_verify()
{
return RPCHelpMan{"quorum verify",
"Test if a quorum signature is valid for a request id and a message hash\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
{"signature", RPCArg::Type::STR, RPCArg::Optional::NO, "Quorum signature to verify."},
{"quorumHash", RPCArg::Type::STR_HEX, /* default */ "",
"The quorum identifier.\n"
"Set to \"\" if you want to specify signHeight instead."},
{"signHeight", RPCArg::Type::NUM, /* default */ "",
"The height at which the message was signed.\n"
"Only works when quorumHash is \"\"."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node = EnsureAnyNodeContext(request.context);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
const auto llmq_params_opt = Params().GetLLMQ(llmqType);
if (!llmq_params_opt.has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
}
const uint256 id(ParseHashV(request.params[1], "id"));
const uint256 msgHash(ParseHashV(request.params[2], "msgHash"));
const bool use_bls_legacy = bls::bls_legacy_scheme.load(); const bool use_bls_legacy = bls::bls_legacy_scheme.load();
CBLSSignature sig; CBLSSignature sig;
if (!sig.SetHexStr(request.params[3].get_str(), use_bls_legacy)) { if (!sig.SetHexStr(request.params[3].get_str(), use_bls_legacy)) {
@ -588,7 +542,8 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request, const LLMQContext
int signOffset{llmq_params_opt->dkgInterval}; int signOffset{llmq_params_opt->dkgInterval};
return llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, 0) || return llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, 0) ||
llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, signOffset); llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, signOffset);
} else { }
uint256 quorumHash(ParseHashV(request.params[4], "quorumHash")); uint256 quorumHash(ParseHashV(request.params[4], "quorumHash"));
llmq::CQuorumCPtr quorum = llmq_ctx.qman->GetQuorum(llmqType, quorumHash); llmq::CQuorumCPtr quorum = llmq_ctx.qman->GetQuorum(llmqType, quorumHash);
@ -598,10 +553,63 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request, const LLMQContext
uint256 signHash = llmq::BuildSignHash(llmqType, quorum->qc->quorumHash, id, msgHash); uint256 signHash = llmq::BuildSignHash(llmqType, quorum->qc->quorumHash, id, msgHash);
return sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash); return sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash);
},
};
}
static RPCHelpMan quorum_hasrecsig()
{
return RPCHelpMan{"quorum hasrecsig",
"Test if a valid recovered signature is present\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node = EnsureAnyNodeContext(request.context);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
if (!Params().GetLLMQ(llmqType).has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
} }
} else if (cmd == "quorumhasrecsig") {
const uint256 id(ParseHashV(request.params[1], "id"));
const uint256 msgHash(ParseHashV(request.params[2], "msgHash"));
return llmq_ctx.sigman->HasRecoveredSig(llmqType, id, msgHash); return llmq_ctx.sigman->HasRecoveredSig(llmqType, id, msgHash);
} else if (cmd == "quorumgetrecsig") { },
};
}
static RPCHelpMan quorum_getrecsig()
{
return RPCHelpMan{"quorum getrecsig",
"Get a recovered signature\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node = EnsureAnyNodeContext(request.context);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
if (!Params().GetLLMQ(llmqType).has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
}
const uint256 id(ParseHashV(request.params[1], "id"));
const uint256 msgHash(ParseHashV(request.params[2], "msgHash"));
llmq::CRecoveredSig recSig; llmq::CRecoveredSig recSig;
if (!llmq_ctx.sigman->GetRecoveredSigForId(llmqType, id, recSig)) { if (!llmq_ctx.sigman->GetRecoveredSigForId(llmqType, id, recSig)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "recovered signature not found"); throw JSONRPCError(RPC_INVALID_PARAMETER, "recovered signature not found");
@ -610,17 +618,42 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request, const LLMQContext
throw JSONRPCError(RPC_INVALID_PARAMETER, "recovered signature not found"); throw JSONRPCError(RPC_INVALID_PARAMETER, "recovered signature not found");
} }
return recSig.ToJson(); return recSig.ToJson();
} else if (cmd == "quorumisconflicting") { },
return llmq_ctx.sigman->IsConflicting(llmqType, id, msgHash); };
} else {
// shouldn't happen as it's already handled by the caller
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid cmd");
}
} }
static void quorum_selectquorum_help(const JSONRPCRequest& request) static RPCHelpMan quorum_isconflicting()
{ {
RPCHelpMan{"quorum selectquorum", return RPCHelpMan{"quorum isconflicting",
"Test if a conflict exists\n",
{
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node = EnsureAnyNodeContext(request.context);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
if (!Params().GetLLMQ(llmqType).has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
}
const uint256 id(ParseHashV(request.params[1], "id"));
const uint256 msgHash(ParseHashV(request.params[2], "msgHash"));
return llmq_ctx.sigman->IsConflicting(llmqType, id, msgHash);
},
};
}
static RPCHelpMan quorum_selectquorum()
{
return RPCHelpMan{"quorum selectquorum",
"Returns the quorum that would/should sign a request\n", "Returns the quorum that would/should sign a request\n",
{ {
{"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."}, {"llmqType", RPCArg::Type::NUM, RPCArg::Optional::NO, "LLMQ type."},
@ -628,24 +661,22 @@ static void quorum_selectquorum_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_selectquorum(const JSONRPCRequest& request, const LLMQContext& llmq_ctx)
{ {
quorum_selectquorum_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[0], "llmqType"); const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
const auto& llmq_params_opt = Params().GetLLMQ(llmqType); const auto llmq_params_opt = Params().GetLLMQ(llmqType);
if (!llmq_params_opt.has_value()) { if (!llmq_params_opt.has_value()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
} }
uint256 id(ParseHashV(request.params[1], "id")); const uint256 id(ParseHashV(request.params[1], "id"));
UniValue ret(UniValue::VOBJ); UniValue ret(UniValue::VOBJ);
auto quorum = llmq::SelectQuorumForSigning(llmq_params_opt.value(), *llmq_ctx.qman, id); const auto quorum = llmq::SelectQuorumForSigning(llmq_params_opt.value(), *llmq_ctx.qman, id);
if (!quorum) { if (!quorum) {
throw JSONRPCError(RPC_MISC_ERROR, "no quorums active"); throw JSONRPCError(RPC_MISC_ERROR, "no quorums active");
} }
@ -659,11 +690,13 @@ static UniValue quorum_selectquorum(const JSONRPCRequest& request, const LLMQCon
ret.pushKV("recoveryMembers", recoveryMembers); ret.pushKV("recoveryMembers", recoveryMembers);
return ret; return ret;
},
};
} }
static void quorum_dkgsimerror_help(const JSONRPCRequest& request) static RPCHelpMan quorum_dkgsimerror()
{ {
RPCHelpMan{"quorum dkgsimerror", return RPCHelpMan{"quorum dkgsimerror",
"This enables simulation of errors and malicious behaviour in the DKG. Do NOT use this on mainnet\n" "This enables simulation of errors and malicious behaviour in the DKG. Do NOT use this on mainnet\n"
"as you will get yourself very likely PoSe banned for this.\n", "as you will get yourself very likely PoSe banned for this.\n",
{ {
@ -672,13 +705,8 @@ static void quorum_dkgsimerror_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_dkgsimerror(const JSONRPCRequest& request)
{ {
quorum_dkgsimerror_help(request);
std::string type_str = request.params[0].get_str(); std::string type_str = request.params[0].get_str();
double rate = ParseDoubleV(request.params[1], "rate"); double rate = ParseDoubleV(request.params[1], "rate");
@ -693,11 +721,13 @@ static UniValue quorum_dkgsimerror(const JSONRPCRequest& request)
llmq::SetSimulatedDKGErrorRate(type, rate); llmq::SetSimulatedDKGErrorRate(type, rate);
return UniValue(); return UniValue();
} }
},
};
} }
static void quorum_getdata_help(const JSONRPCRequest& request) static RPCHelpMan quorum_getdata()
{ {
RPCHelpMan{"quorum getdata", return RPCHelpMan{"quorum getdata",
"Send a QGETDATA message to the specified peer.\n", "Send a QGETDATA message to the specified peer.\n",
{ {
{"nodeId", RPCArg::Type::NUM, RPCArg::Optional::NO, "The internal nodeId of the peer to request quorum data from."}, {"nodeId", RPCArg::Type::NUM, RPCArg::Optional::NO, "The internal nodeId of the peer to request quorum data from."},
@ -712,12 +742,11 @@ static void quorum_getdata_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_getdata(const JSONRPCRequest& request, const LLMQContext& llmq_ctx, const ChainstateManager& chainman)
{ {
quorum_getdata_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
NodeId nodeId = ParseInt64V(request.params[0], "nodeId"); NodeId nodeId = ParseInt64V(request.params[0], "nodeId");
Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(ParseInt32V(request.params[1], "llmqType")); Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(ParseInt32V(request.params[1], "llmqType"));
@ -739,15 +768,16 @@ static UniValue quorum_getdata(const JSONRPCRequest& request, const LLMQContext&
const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(quorumHash)); const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return chainman.m_blockman.LookupBlockIndex(quorumHash));
const NodeContext& node = EnsureAnyNodeContext(request.context);
return node.connman->ForNode(nodeId, [&](CNode* pNode) { return node.connman->ForNode(nodeId, [&](CNode* pNode) {
return llmq_ctx.qman->RequestQuorumData(pNode, llmqType, pQuorumBaseBlockIndex, nDataMask, proTxHash); return llmq_ctx.qman->RequestQuorumData(pNode, llmqType, pQuorumBaseBlockIndex, nDataMask, proTxHash);
}); });
},
};
} }
static void quorum_rotationinfo_help(const JSONRPCRequest& request) static RPCHelpMan quorum_rotationinfo()
{ {
RPCHelpMan{ return RPCHelpMan{
"quorum rotationinfo", "quorum rotationinfo",
"Get quorum rotation information\n", "Get quorum rotation information\n",
{ {
@ -757,12 +787,10 @@ static void quorum_rotationinfo_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_rotationinfo(const JSONRPCRequest& request, CDeterministicMNManager& dmnman, const LLMQContext& llmq_ctx)
{ {
quorum_rotationinfo_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
llmq::CGetQuorumRotationInfo cmd; llmq::CGetQuorumRotationInfo cmd;
llmq::CQuorumRotationInfo quorumRotationInfoRet; llmq::CQuorumRotationInfo quorumRotationInfoRet;
@ -779,16 +807,18 @@ static UniValue quorum_rotationinfo(const JSONRPCRequest& request, CDeterministi
LOCK(cs_main); LOCK(cs_main);
if (!BuildQuorumRotationInfo(cmd, quorumRotationInfoRet, dmnman, *llmq_ctx.qman, *llmq_ctx.quorum_block_processor, strError)) { if (!BuildQuorumRotationInfo(cmd, quorumRotationInfoRet, *node.dmnman, *llmq_ctx.qman, *llmq_ctx.quorum_block_processor, strError)) {
throw JSONRPCError(RPC_INVALID_REQUEST, strError); throw JSONRPCError(RPC_INVALID_REQUEST, strError);
} }
return quorumRotationInfoRet.ToJson(); return quorumRotationInfoRet.ToJson();
},
};
} }
static void quorum_dkginfo_help(const JSONRPCRequest& request) static RPCHelpMan quorum_dkginfo()
{ {
RPCHelpMan{ return RPCHelpMan{
"quorum dkginfo", "quorum dkginfo",
"Return information regarding DKGs.\n", "Return information regarding DKGs.\n",
{ {
@ -802,12 +832,11 @@ static void quorum_dkginfo_help(const JSONRPCRequest& request)
} }
}, },
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue quorum_dkginfo(const JSONRPCRequest& request, const LLMQContext& llmq_ctx, const ChainstateManager& chainman)
{ {
quorum_dkginfo_help(request); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
llmq::CDKGDebugStatus status; llmq::CDKGDebugStatus status;
llmq_ctx.dkg_debugman->GetLocalDebugStatus(status); llmq_ctx.dkg_debugman->GetLocalDebugStatus(status);
@ -828,12 +857,13 @@ static UniValue quorum_dkginfo(const JSONRPCRequest& request, const LLMQContext&
ret.pushKV("next_dkg", minNextDKG(Params().GetConsensus(), nTipHeight)); ret.pushKV("next_dkg", minNextDKG(Params().GetConsensus(), nTipHeight));
return ret; return ret;
},
};
} }
[[ noreturn ]] static void quorum_help() static RPCHelpMan quorum_help()
{ {
throw std::runtime_error( return RPCHelpMan{
RPCHelpMan{
"quorum", "quorum",
"Set of commands for quorums/LLMQs.\n" "Set of commands for quorums/LLMQs.\n"
"To get help on individual commands, use \"help quorum command\".\n" "To get help on individual commands, use \"help quorum command\".\n"
@ -858,49 +888,16 @@ static UniValue quorum_dkginfo(const JSONRPCRequest& request, const LLMQContext&
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
} [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
.ToString()); {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Must be a valid command");
},
};
} }
static UniValue _quorum(const JSONRPCRequest& request) static RPCHelpMan verifychainlock()
{ {
const JSONRPCRequest new_request{request.strMethod == "quorum" ? request.squashed() : request}; return RPCHelpMan{"verifychainlock",
const std::string command{new_request.strMethod};
const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
if (command == "quorumlist") {
return quorum_list(new_request, chainman, llmq_ctx);
} else if (command == "quorumlistextended") {
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, *node.dmnman, node.mn_activeman.get(), chainman, *node.sporkman, llmq_ctx);
} else if (command == "quorummemberof") {
return quorum_memberof(new_request, chainman, node, llmq_ctx);
} else if (command == "quorumsign" || command == "quorumverify" || command == "quorumhasrecsig" || command == "quorumgetrecsig" || command == "quorumisconflicting") {
return quorum_sigs_cmd(new_request, llmq_ctx);
} else if (command == "quorumselectquorum") {
return quorum_selectquorum(new_request, llmq_ctx);
} else if (command == "quorumdkgsimerror") {
return quorum_dkgsimerror(new_request);
} else if (command == "quorumgetdata") {
return quorum_getdata(new_request, llmq_ctx, chainman);
} else if (command == "quorumrotationinfo") {
return quorum_rotationinfo(new_request, *node.dmnman, llmq_ctx);
} else {
quorum_help();
}
}
static void verifychainlock_help(const JSONRPCRequest& request)
{
RPCHelpMan{"verifychainlock",
"Test if a quorum signature is valid for a ChainLock.\n", "Test if a quorum signature is valid for a ChainLock.\n",
{ {
{"blockHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash of the ChainLock."}, {"blockHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash of the ChainLock."},
@ -909,13 +906,8 @@ static void verifychainlock_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue verifychainlock(const JSONRPCRequest& request)
{ {
verifychainlock_help(request);
const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash")); const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash"));
const NodeContext& node = EnsureAnyNodeContext(request.context); const NodeContext& node = EnsureAnyNodeContext(request.context);
@ -956,11 +948,13 @@ static UniValue verifychainlock(const JSONRPCRequest& request)
const LLMQContext& llmq_ctx = EnsureLLMQContext(node); const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
return llmq_ctx.clhandler->VerifyChainLock(llmq::CChainLockSig(nBlockHeight, nBlockHash, sig)); return llmq_ctx.clhandler->VerifyChainLock(llmq::CChainLockSig(nBlockHeight, nBlockHash, sig));
},
};
} }
static void verifyislock_help(const JSONRPCRequest& request) static RPCHelpMan verifyislock()
{ {
RPCHelpMan{"verifyislock", return RPCHelpMan{"verifyislock",
"Test if a quorum signature is valid for an InstantSend Lock\n", "Test if a quorum signature is valid for an InstantSend Lock\n",
{ {
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."}, {"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
@ -970,15 +964,10 @@ static void verifyislock_help(const JSONRPCRequest& request)
}, },
RPCResults{}, RPCResults{},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue verifyislock(const JSONRPCRequest& request)
{ {
verifyislock_help(request); const uint256 id(ParseHashV(request.params[0], "id"));
const uint256 txid(ParseHashV(request.params[1], "txid"));
uint256 id(ParseHashV(request.params[0], "id"));
uint256 txid(ParseHashV(request.params[1], "txid"));
const NodeContext& node = EnsureAnyNodeContext(request.context); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node); const ChainstateManager& chainman = EnsureChainman(node);
@ -987,7 +976,7 @@ static UniValue verifyislock(const JSONRPCRequest& request)
g_txindex->BlockUntilSyncedToCurrentChain(); g_txindex->BlockUntilSyncedToCurrentChain();
} }
CBlockIndex* pindexMined{nullptr}; const CBlockIndex* pindexMined{nullptr};
{ {
LOCK(cs_main); LOCK(cs_main);
uint256 hash_block; uint256 hash_block;
@ -1031,16 +1020,18 @@ static UniValue verifyislock(const JSONRPCRequest& request)
auto llmqType = Params().GetConsensus().llmqTypeDIP0024InstantSend; auto llmqType = Params().GetConsensus().llmqTypeDIP0024InstantSend;
// First check against the current active set, if it fails check against the last active set // First check against the current active set, if it fails check against the last active set
const auto& llmq_params_opt = Params().GetLLMQ(llmqType); const auto llmq_params_opt = Params().GetLLMQ(llmqType);
CHECK_NONFATAL(llmq_params_opt.has_value()); CHECK_NONFATAL(llmq_params_opt.has_value());
int signOffset{llmq_params_opt->dkgInterval}; const int signOffset{llmq_params_opt->dkgInterval};
return llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, 0) || return llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, 0) ||
llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, signOffset); llmq::VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, signOffset);
},
};
} }
static void submitchainlock_help(const JSONRPCRequest& request) static RPCHelpMan submitchainlock()
{ {
RPCHelpMan{"submitchainlock", return RPCHelpMan{"submitchainlock",
"Submit a ChainLock signature if needed\n", "Submit a ChainLock signature if needed\n",
{ {
{"blockHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash of the ChainLock."}, {"blockHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash of the ChainLock."},
@ -1050,13 +1041,8 @@ static void submitchainlock_help(const JSONRPCRequest& request)
RPCResult{ RPCResult{
RPCResult::Type::NUM, "", "The height of the current best ChainLock"}, RPCResult::Type::NUM, "", "The height of the current best ChainLock"},
RPCExamples{""}, RPCExamples{""},
}.Check(request); [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
}
static UniValue submitchainlock(const JSONRPCRequest& request)
{ {
submitchainlock_help(request);
const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash")); const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash"));
const int nBlockHeight = ParseInt32V(request.params[2], "blockHeight"); const int nBlockHeight = ParseInt32V(request.params[2], "blockHeight");
@ -1080,6 +1066,8 @@ static UniValue submitchainlock(const JSONRPCRequest& request)
llmq_ctx.clhandler->ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig)); llmq_ctx.clhandler->ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
return llmq_ctx.clhandler->GetBestChainLock().getHeight(); return llmq_ctx.clhandler->GetBestChainLock().getHeight();
},
};
} }
@ -1089,13 +1077,28 @@ void RegisterQuorumsRPCCommands(CRPCTable &tableRPC)
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) { // category name actor (function)
// --------------------- ------------------------ ----------------------- // --------------------- ------------------------ -----------------------
{ "evo", "quorum", &_quorum, {} }, { "evo", "quorum", &quorum_help, {"command"} },
{ "evo", "quorum", "list", &quorum_list, {"count"} },
{ "evo", "quorum", "listextended", &quorum_list_extended, {"height"} },
{ "evo", "quorum", "info", &quorum_info, {"llmqType", "quorumHash", "includeSkShare"} },
{ "evo", "quorum", "dkginfo", &quorum_dkginfo, {} },
{ "evo", "quorum", "dkgstatus", &quorum_dkgstatus, {"detail_level"} },
{ "evo", "quorum", "memberof", &quorum_memberof, {"proTxHash", "scanQuorumsCount"} },
{ "evo", "quorum", "sign", &quorum_sign, {"llmqType", "id", "msgHash", "quorumHash", "submit"} },
{ "evo", "quorum", "verify", &quorum_verify, {"llmqType", "id", "msgHash", "signature", "quorumHash", "signHeight"} },
{ "evo", "quorum", "hasrecsig", &quorum_hasrecsig, {"llmqType", "id", "msgHash"} },
{ "evo", "quorum", "getrecsig", &quorum_getrecsig, {"llmqType", "id", "msgHash"} },
{ "evo", "quorum", "isconflicting",&quorum_isconflicting, {"llmqType", "id", "msgHash"} },
{ "evo", "quorum", "selectquorum", &quorum_selectquorum, {"llmqType", "id"} },
{ "evo", "quorum", "dkgsimerror", &quorum_dkgsimerror, {"type", "rate"} },
{ "evo", "quorum", "getdata", &quorum_getdata, {"nodeId", "llmqType", "quorumHash", "dataMask", "proTxHash"} },
{ "evo", "quorum", "rotationinfo", &quorum_rotationinfo, {"blockRequestHash", "extraShare", "baseBlockHash..."} },
{ "evo", "submitchainlock", &submitchainlock, {"blockHash", "signature", "blockHeight"} }, { "evo", "submitchainlock", &submitchainlock, {"blockHash", "signature", "blockHeight"} },
{ "evo", "verifychainlock", &verifychainlock, {"blockHash", "signature", "blockHeight"} }, { "evo", "verifychainlock", &verifychainlock, {"blockHash", "signature", "blockHeight"} },
{ "evo", "verifyislock", &verifyislock, {"id", "txid", "signature", "maxHeight"} }, { "evo", "verifyislock", &verifyislock, {"id", "txid", "signature", "maxHeight"} },
}; };
// clang-format on // clang-format on
for (const auto& command : commands) { for (const auto& command : commands) {
tableRPC.appendCommand(command.name, &command); tableRPC.appendCommand(command.name, command.subname, &command);
} }
} }

View File

@ -153,8 +153,8 @@ void CRPCTable::InitPlatformRestrictions()
{"getblockhash", {}}, {"getblockhash", {}},
{"getblockcount", {}}, {"getblockcount", {}},
{"getbestchainlock", {}}, {"getbestchainlock", {}},
{"quorum", {"sign", static_cast<uint8_t>(Params().GetConsensus().llmqTypePlatform)}}, {"quorum sign", {static_cast<uint8_t>(Params().GetConsensus().llmqTypePlatform)}},
{"quorum", {"verify"}}, {"quorum verify", {}},
{"submitchainlock", {}}, {"submitchainlock", {}},
{"verifyislock", {}}, {"verifyislock", {}},
}; };
@ -538,7 +538,9 @@ static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& req
// Before executing the RPC Command, filter commands from platform rpc user // Before executing the RPC Command, filter commands from platform rpc user
if (node.mn_activeman && request.authUser == gArgs.GetArg("-platform-user", defaultPlatformUser)) { if (node.mn_activeman && request.authUser == gArgs.GetArg("-platform-user", defaultPlatformUser)) {
// replace this with structured binding in c++20 // replace this with structured binding in c++20
const auto& it = mapPlatformRestrictions.equal_range(request.strMethod); std::string command_name = command.name;
if (!command.subname.empty()) command_name += " " + command.subname;
const auto& it = mapPlatformRestrictions.equal_range(command_name);
const auto& allowed_begin = it.first; const auto& allowed_begin = it.first;
const auto& allowed_end = it.second; const auto& allowed_end = it.second;
/** /**
@ -548,8 +550,8 @@ static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& req
* *
* if request.strMethod == "quorum": * if request.strMethod == "quorum":
* [ * [
* "quorum", ["sign", platformLlmqType], * "quorum sign", [platformLlmqType],
* "quorum", ["verify"] * "quorum verify", []
* ] * ]
* if request.strMethod == "verifyislock" * if request.strMethod == "verifyislock"
* [ * [