Merge #6106: feat: create new composite quorum-command platformsign

2db69d7b81 chore: add release notes for "quorum platformsign" (Konstantin Akimov)
283c5f89a2 feat: create new composite command "quorum platformsign" (Konstantin Akimov)

Pull request description:

  ## Issue being fixed or feature implemented
  It splits from #6100
  With just whitelist it is impossible to limit the RPC `quorum sign` to use only one specific quorum type, this PR aim to provide ability for quorum signing for platform quorum only.

  ## What was done?
  Implemented a new composite command "quorum platformsign"

  This composite command let to limit quorum type for signing for case of whitelist.
  After that old way to limit platform commands can be deprecated - #6105

  ## How Has This Been Tested?
  Updated a functional tests to use platform signing for Asset Unlocks feature.

  ## 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
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone

ACKs for top commit:
  UdjinM6:
    utACK 2db69d7b81
  PastaPastaPasta:
    utACK 2db69d7b81

Tree-SHA512: b0dff9934137c4faa85664058e1e77f85067cc8d931e6d76ee5b9e610164ac8b0609736d5f09475256cb78d65bf92466624d784f0b13d20136df7e75613662cb
This commit is contained in:
pasta 2024-07-15 11:47:39 -05:00
parent a45e6df58b
commit db828177bf
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
4 changed files with 67 additions and 25 deletions

View File

@ -0,0 +1,6 @@
## Remote Procedure Call (RPC) Changes
### The new RPCs are:
- `quorum signplatform` This RPC is added for Platform needs. This composite command let to limit quorum type for signing by platform. It is equivalent of `quorum sign <platform type>`.

View File

@ -427,42 +427,27 @@ static RPCHelpMan quorum_memberof()
}; };
} }
static RPCHelpMan quorum_sign() static UniValue quorum_sign_helper(const JSONRPCRequest& request, Consensus::LLMQType llmqType)
{
return RPCHelpMan{"quorum sign",
"Threshold-sign a message\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."},
{"quorumHash", RPCArg::Type::STR_HEX, /* default */ "", "The quorum identifier."},
{"submit", RPCArg::Type::BOOL, /* default */ "true", "Submits the signature share to the network if this is true. "
"Returns an object containing the signature share if this is false."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{ {
const NodeContext& node = EnsureAnyNodeContext(request.context); const NodeContext& node = EnsureAnyNodeContext(request.context);
const ChainstateManager& chainman = EnsureChainman(node); const ChainstateManager& chainman = EnsureChainman(node);
const LLMQContext& llmq_ctx = EnsureLLMQContext(node); 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); 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");
} }
const uint256 id(ParseHashV(request.params[1], "id")); const uint256 id(ParseHashV(request.params[0], "id"));
const uint256 msgHash(ParseHashV(request.params[2], "msgHash")); const uint256 msgHash(ParseHashV(request.params[1], "msgHash"));
uint256 quorumHash; uint256 quorumHash;
if (!request.params[3].isNull() && !request.params[3].get_str().empty()) { if (!request.params[2].isNull() && !request.params[2].get_str().empty()) {
quorumHash = ParseHashV(request.params[3], "quorumHash"); quorumHash = ParseHashV(request.params[2], "quorumHash");
} }
bool fSubmit{true}; bool fSubmit{true};
if (!request.params[4].isNull()) { if (!request.params[3].isNull()) {
fSubmit = ParseBoolV(request.params[4], "submit"); fSubmit = ParseBoolV(request.params[3], "submit");
} }
if (fSubmit) { if (fSubmit) {
return llmq_ctx.sigman->AsyncSignIfMember(llmqType, *llmq_ctx.shareman, id, msgHash, quorumHash); return llmq_ctx.sigman->AsyncSignIfMember(llmqType, *llmq_ctx.shareman, id, msgHash, quorumHash);
@ -496,6 +481,53 @@ static RPCHelpMan quorum_sign()
return obj; return obj;
} }
}
static RPCHelpMan quorum_sign()
{
return RPCHelpMan{"quorum sign",
"Threshold-sign a message\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."},
{"quorumHash", RPCArg::Type::STR_HEX, /* default */ "", "The quorum identifier."},
{"submit", RPCArg::Type::BOOL, /* default */ "true", "Submits the signature share to the network if this is true. "
"Returns an object containing the signature share if this is false."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const Consensus::LLMQType llmqType{static_cast<Consensus::LLMQType>(ParseInt32V(request.params[0], "llmqType"))};
JSONRPCRequest new_request{request};
new_request.params.setArray();
for (unsigned int i = 1; i < request.params.size(); ++i) {
new_request.params.push_back(request.params[i]);
}
return quorum_sign_helper(new_request, llmqType);
},
};
}
static RPCHelpMan quorum_platformsign()
{
return RPCHelpMan{"quorum platformsign",
"Threshold-sign a message. It signs messages only for platform quorums\n",
{
{"id", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Request id."},
{"msgHash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Message hash."},
{"quorumHash", RPCArg::Type::STR_HEX, /* default */ "", "The quorum identifier."},
{"submit", RPCArg::Type::BOOL, /* default */ "true", "Submits the signature share to the network if this is true. "
"Returns an object containing the signature share if this is false."},
},
RPCResults{},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const Consensus::LLMQType llmqType{Params().GetConsensus().llmqTypePlatform};
return quorum_sign_helper(request, llmqType);
}, },
}; };
} }
@ -1110,6 +1142,7 @@ static const CRPCCommand commands[] =
{ "evo", "quorum", "dkgstatus", &quorum_dkgstatus, {"detail_level"} }, { "evo", "quorum", "dkgstatus", &quorum_dkgstatus, {"detail_level"} },
{ "evo", "quorum", "memberof", &quorum_memberof, {"proTxHash", "scanQuorumsCount"} }, { "evo", "quorum", "memberof", &quorum_memberof, {"proTxHash", "scanQuorumsCount"} },
{ "evo", "quorum", "sign", &quorum_sign, {"llmqType", "id", "msgHash", "quorumHash", "submit"} }, { "evo", "quorum", "sign", &quorum_sign, {"llmqType", "id", "msgHash", "quorumHash", "submit"} },
{ "evo", "quorum", "platformsign", &quorum_platformsign, {"id", "msgHash", "quorumHash", "submit"} },
{ "evo", "quorum", "verify", &quorum_verify, {"llmqType", "id", "msgHash", "signature", "quorumHash", "signHeight"} }, { "evo", "quorum", "verify", &quorum_verify, {"llmqType", "id", "msgHash", "signature", "quorumHash", "signHeight"} },
{ "evo", "quorum", "hasrecsig", &quorum_hasrecsig, {"llmqType", "id", "msgHash"} }, { "evo", "quorum", "hasrecsig", &quorum_hasrecsig, {"llmqType", "id", "msgHash"} },
{ "evo", "quorum", "getrecsig", &quorum_getrecsig, {"llmqType", "id", "msgHash"} }, { "evo", "quorum", "getrecsig", &quorum_getrecsig, {"llmqType", "id", "msgHash"} },

View File

@ -119,7 +119,7 @@ class AssetLocksTest(DashTestFramework):
unlock_tx.calc_sha256() unlock_tx.calc_sha256()
msgHash = format(unlock_tx.sha256, '064x') msgHash = format(unlock_tx.sha256, '064x')
recsig = self.get_recovered_sig(request_id, msgHash, llmq_type=llmq_type_test) recsig = self.get_recovered_sig(request_id, msgHash, llmq_type=llmq_type_test, use_platformsign=True)
unlockTx_payload.quorumSig = bytearray.fromhex(recsig["sig"]) unlockTx_payload.quorumSig = bytearray.fromhex(recsig["sig"])
unlock_tx.vExtraPayload = unlockTx_payload.serialize() unlock_tx.vExtraPayload = unlockTx_payload.serialize()

View File

@ -2033,12 +2033,15 @@ class DashTestFramework(BitcoinTestFramework):
return True return True
wait_until_helper(check_recovered_sig, timeout=timeout, sleep=1) wait_until_helper(check_recovered_sig, timeout=timeout, sleep=1)
def get_recovered_sig(self, rec_sig_id, rec_sig_msg_hash, llmq_type=100): def get_recovered_sig(self, rec_sig_id, rec_sig_msg_hash, llmq_type=100, use_platformsign=False):
# Note: recsigs aren't relayed to regular nodes by default, # Note: recsigs aren't relayed to regular nodes by default,
# make sure to pick a mn as a node to query for recsigs. # make sure to pick a mn as a node to query for recsigs.
try: try:
quorumHash = self.mninfo[0].node.quorum("selectquorum", llmq_type, rec_sig_id)["quorumHash"] quorumHash = self.mninfo[0].node.quorum("selectquorum", llmq_type, rec_sig_id)["quorumHash"]
for mn in self.mninfo: for mn in self.mninfo:
if use_platformsign:
mn.node.quorum("platformsign", rec_sig_id, rec_sig_msg_hash, quorumHash)
else:
mn.node.quorum("sign", llmq_type, rec_sig_id, rec_sig_msg_hash, quorumHash) mn.node.quorum("sign", llmq_type, rec_sig_id, rec_sig_msg_hash, quorumHash)
self.wait_for_recovered_sig(rec_sig_id, rec_sig_msg_hash, llmq_type, 10) self.wait_for_recovered_sig(rec_sig_id, rec_sig_msg_hash, llmq_type, 10)
return self.mninfo[0].node.quorum("getrecsig", llmq_type, rec_sig_id, rec_sig_msg_hash) return self.mninfo[0].node.quorum("getrecsig", llmq_type, rec_sig_id, rec_sig_msg_hash)