PIVX Core  5.6.99
P2P Digital Currency
rpcquorums.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2021 The Dash Core developers
2 // Copyright (c) 2022 The PIVX Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "activemasternode.h"
7 #include "chainparams.h"
8 #include "llmq/quorums.h"
11 #include "llmq/quorums_debug.h"
13 #include "llmq/quorums_signing.h"
15 #include "rpc/server.h"
16 #include "validation.h"
17 
18 #include <string>
19 
21 {
22  if (!Params().IsTestChain()) {
23  throw JSONRPCError(RPC_MISC_ERROR, "command available only for RegTest and TestNet network");
24  }
25  if (request.fHelp || (request.params.size() != 3)) {
26  throw std::runtime_error(
27  "signsession llmqType \"id\" \"msgHash\"\n"
28  "\nArguments:\n"
29  "1. llmqType (int, required) LLMQ type.\n"
30  "2. \"id\" (string, required) Request id.\n"
31  "3. \"msgHash\" (string, required) Message hash.\n"
32 
33  "\nResult:\n"
34  "n (bool) True if the sign was successful, false otherwise\n"
35 
36  "\nExample:\n" +
37  HelpExampleRpc("signsession", "100 \"xxx\", \"xxx\"") + HelpExampleCli("signsession", "100 \"xxx\", \"xxx\""));
38  }
39  Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
40  if (!Params().GetConsensus().llmqs.count(llmqType)) {
41  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
42  }
43 
44  uint256 id = ParseHashV(request.params[1], "id");
45  uint256 msgHash = ParseHashV(request.params[2], "msgHash");
46  return llmq::quorumSigningManager->AsyncSignIfMember(llmqType, id, msgHash);
47 }
48 
50 {
51  if (!Params().IsTestChain()) {
52  throw JSONRPCError(RPC_MISC_ERROR, "command available only for RegTest and TestNet network");
53  }
54  if (request.fHelp || (request.params.size() != 3)) {
55  throw std::runtime_error(
56  "hasrecoverysignature llmqType \"id\" \"msgHash\"\n"
57  "\nArguments:\n"
58  "1. llmqType (int, required) LLMQ type.\n"
59  "2. \"id\" (string, required) Request id.\n"
60  "3. \"msgHash\" (string, required) Message hash.\n"
61 
62  "\nResult:\n"
63  "n (bool) True if you have already received a recovery signature for the given signing session\n"
64 
65  "\nExample:\n" +
66  HelpExampleRpc("hasrecoverysignature", "100 \"xxx\", \"xxx\"") + HelpExampleCli("hasrecoverysignature", "100 \"xxx\", \"xxx\""));
67  }
68  Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
69  if (!Params().GetConsensus().llmqs.count(llmqType)) {
70  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
71  }
72 
73  uint256 id = ParseHashV(request.params[1], "id");
74  uint256 msgHash = ParseHashV(request.params[2], "msgHash");
75  return llmq::quorumSigningManager->HasRecoveredSig(llmqType, id, msgHash);
76 }
77 
79 {
80  if (!Params().IsTestChain()) {
81  throw JSONRPCError(RPC_MISC_ERROR, "command available only for RegTest and TestNet network");
82  }
83  if (request.fHelp || (request.params.size() != 3)) {
84  throw std::runtime_error(
85  "issessionconflicting llmqType \"id\" \"msgHash\"\n"
86  "\nArguments:\n"
87  "1. llmqType (int, required) LLMQ type.\n"
88  "2. \"id\" (string, required) Request id.\n"
89  "3. \"msgHash\" (string, required) Message hash.\n"
90 
91  "\nResult:\n"
92  "n (bool) True if you have the recovery signature of an another signing session with same id but different msgHash\n"
93 
94  "\nExample:\n" +
95  HelpExampleRpc("issessionconflicting", "100 \"xxx\", \"xxx\"") + HelpExampleCli("issessionconflicting", "100 \"xxx\", \"xxx\""));
96  }
97  Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
98  if (!Params().GetConsensus().llmqs.count(llmqType)) {
99  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
100  }
101 
102  uint256 id = ParseHashV(request.params[1], "id");
103  uint256 msgHash = ParseHashV(request.params[2], "msgHash");
104  return llmq::quorumSigningManager->IsConflicting(llmqType, id, msgHash);
105 }
106 
108 {
109  if (request.fHelp || request.params.size() > 1) {
110  throw std::runtime_error(
111  "listquorums ( count )\n"
112  "\nArguments:\n"
113  "1. count (numeric, optional, default=10) Number of quorums to list\n"
114 
115  "\nResult:\n"
116  "{\n"
117  " \"llmqType\": [ (array of string) A json array of quorum hashes for a given llmqType\n"
118  " \"quorumhash\", (string) Block hash of the quorum\n"
119  " ...\n"
120  " ],\n"
121  " ...\n"
122  "}\n"
123 
124  "\nExample:\n" +
125  HelpExampleRpc("listquorums", "1") + HelpExampleCli("listquorums", "1"));
126  }
127 
128  LOCK(cs_main);
129  int count = 10;
130  if(request.params.size() == 1) {
131  count = request.params[0].get_int();
132  if(count <= 0) {
133  throw std::runtime_error(
134  "count cannot be 0 or negative!\n");
135  }
136  }
138 
139  for (auto& p : Params().GetConsensus().llmqs) {
141 
142  auto quorums = llmq::quorumManager->ScanQuorums(p.first, chainActive.Tip(), count);
143  for (auto& q : quorums) {
144  v.push_back(q->qc.quorumHash.ToString());
145  }
146 
147  ret.pushKV(p.second.name, v);
148  }
149 
150 
151  return ret;
152 }
153 
155 {
156  if (request.fHelp || request.params.size() > 3 || request.params.size() < 2)
157  throw std::runtime_error(
158  "getquoruminfo llmqType \"quorumHash\" ( includeSkShare )\n"
159  "\nArguments:\n"
160  "1. llmqType (numeric, required) LLMQ type.\n"
161  "2. \"quorumHash\" (string, required) Block hash of quorum.\n"
162  "3. includeSkShare (boolean, optional) Include secret key share in output.\n"
163 
164  "\nResult:\n"
165  "{\n"
166  " \"height\": n, (numeric) The starting block height of the quorum\n"
167  " \"quorumHash\": \"quorumHash\", (string) Block hash of the quorum\n"
168  " \"members\": [ (array of json objects)\n"
169  " {\n"
170  " \"proTxHash\": \"proTxHash\" (string) ProTxHash of the quorum member\n"
171  " \"valid\": true/false (boolean) True/false if the member is valid/invalid\n"
172  " \"pubKeyShare\": pubKeyShare (string) Quorum public key share of the member, will be outputted only if the command is performed by another quorum member or watcher\n"
173  " },\n"
174  " ...\n"
175  " ],\n"
176  " \"quorumPublicKey\": quorumPublicKey, (string) Public key of the quorum\n"
177  " \"secretKeyShare\": secretKeyShare (string) This is outputted only if includeSkShare=true and the command is performed by a valid member of the quorum. It corresponds to the secret key share of that member\n"
178  "}\n"
179 
180  "\nExample:\n" +
181  HelpExampleRpc("getquoruminfo", "2 \"xxx\", true") + HelpExampleCli("getquoruminfo", "2, \"xxx\",true"));
182 
183  LOCK(cs_main);
184 
185  Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
186  if (!Params().GetConsensus().llmqs.count(llmqType)) {
187  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid llmqType");
188  }
189 
190  uint256 blockHash = ParseHashV(request.params[1], "quorumHash");
191  bool includeSkShare = false;
192  if (request.params.size() > 2) {
193  includeSkShare = request.params[2].get_bool();
194  }
195 
196  auto quorum = llmq::quorumManager->GetQuorum(llmqType, blockHash);
197  if (!quorum) {
198  throw JSONRPCError(RPC_INVALID_PARAMETER, "quorum not found");
199  }
200 
202 
203  ret.pushKV("height", quorum->pindexQuorum->nHeight);
204  ret.pushKV("quorumHash", quorum->qc.quorumHash.ToString());
205 
206  UniValue membersArr(UniValue::VARR);
207  for (size_t i = 0; i < quorum->members.size(); i++) {
208  auto& dmn = quorum->members[i];
210  mo.pushKV("proTxHash", dmn->proTxHash.ToString());
211  mo.pushKV("valid", quorum->qc.validMembers[i]);
212  if (quorum->qc.validMembers[i]) {
213  CBLSPublicKey pubKey = quorum->GetPubKeyShare(i);
214  if (pubKey.IsValid()) {
215  mo.pushKV("pubKeyShare", pubKey.ToString());
216  }
217  }
218  membersArr.push_back(mo);
219  }
220 
221  ret.pushKV("members", membersArr);
222  ret.pushKV("quorumPublicKey", quorum->qc.quorumPublicKey.ToString());
223  CBLSSecretKey skShare = quorum->GetSkShare();
224  if (includeSkShare && skShare.IsValid()) {
225  ret.pushKV("secretKeyShare", skShare.ToString());
226  }
227 
228  return ret;
229 }
230 
232 {
233  if (request.fHelp || request.params.size() != 2) {
234  throw std::runtime_error(
235  "getminedcommitment llmq_type quorum_hash\n"
236  "Return information about the commitment for given quorum.\n"
237  "\nArguments:\n"
238  "1. llmq_type (number, required) LLMQ type.\n"
239  "2. quorum_hash (hex string, required) LLMQ hash.\n"
240  "\nExamples:\n"
241  + HelpExampleRpc("getminedcommitment", "2 \"xxx\"")
242  + HelpExampleCli("getminedcommitment", "2, \"xxx\"")
243  );
244  }
245 
246  Consensus::LLMQType llmq_type = static_cast<Consensus::LLMQType>(request.params[0].get_int());
247  if (!Params().GetConsensus().llmqs.count(llmq_type)) {
248  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid llmq_type");
249  }
250  const uint256& quorum_hash = ParseHashV(request.params[1], "quorum_hash");
251  if (WITH_LOCK(cs_main, return LookupBlockIndex(quorum_hash)) == nullptr) {
252  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid quorum_hash");
253  }
254 
256  uint256 block_hash;
257  if (!llmq::quorumBlockProcessor->GetMinedCommitment(llmq_type, quorum_hash, qc, block_hash)) {
258  throw JSONRPCError(RPC_INVALID_PARAMETER, "mined commitment not found");
259  }
260 
262  qc.ToJson(ret);
263  ret.pushKV("block_hash", block_hash.ToString());
264  return ret;
265 }
266 
268 {
269  if (request.fHelp || request.params.size() != 2) {
270  throw std::runtime_error(
271  "getquorummembers llmq_type quorum_hash\n"
272  "Return the list of proTx hashes for given quorum.\n"
273  "\nArguments:\n"
274  "1. llmq_type (number, required) LLMQ type.\n"
275  "2. quorum_hash (hex string, required) LLMQ hash.\n"
276  "\nExamples:\n"
277  + HelpExampleRpc("getquorummembers", "2 \"xxx\"")
278  + HelpExampleCli("getquorummembers", "2, \"xxx\"")
279  );
280  }
281 
282  Consensus::LLMQType llmq_type = static_cast<Consensus::LLMQType>(request.params[0].get_int());
283  if (!Params().GetConsensus().llmqs.count(llmq_type)) {
284  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid llmq_type");
285  }
286 
287  const uint256& quorum_hash = ParseHashV(request.params[1], "quorum_hash");
288  const CBlockIndex* pindexQuorum = WITH_LOCK(cs_main, return LookupBlockIndex(quorum_hash));
289  if (pindexQuorum == nullptr) {
290  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid quorum_hash");
291  }
292 
293  auto mns = deterministicMNManager->GetAllQuorumMembers(llmq_type, pindexQuorum);
295  for (const auto& dmn : mns) {
296  ret.push_back(dmn->proTxHash.ToString());
297  }
298  return ret;
299 }
300 
302 {
303  if (request.fHelp || request.params.size() > 1) {
304  throw std::runtime_error(
305  "quorumdkgstatus ( detail_level )\n"
306  "Return the status of the current DKG process of the active masternode.\n"
307  "\nArguments:\n"
308  "1. detail_level (number, optional, default=0) Detail level of output.\n"
309  " 0=Only show counts. 1=Show member indexes. 2=Show member's ProTxHashes.\n"
310  "\nExamples:\n"
311  + HelpExampleRpc("quorumdkgstatus", "2")
312  + HelpExampleCli("quorumdkgstatus", "")
313  );
314  }
315 
316  int detailLevel = request.params.size() > 0 ? request.params[0].get_int() : 0;
317  if (detailLevel < 0 || detailLevel > 2) {
318  throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid detail_level %d", detailLevel));
319  }
320 
322  throw JSONRPCError(RPC_INVALID_PARAMETER, "This is not a (deterministic) masternode");
323  }
324 
325  llmq::CDKGDebugStatus status;
326  llmq::quorumDKGDebugManager->GetLocalDebugStatus(status);
327 
328  auto ret = status.ToJson(detailLevel);
329 
330  const int tipHeight = WITH_LOCK(cs_main, return chainActive.Height(); );
331 
332  UniValue minableCommitments(UniValue::VOBJ);
333  for (const auto& p : Params().GetConsensus().llmqs) {
334  auto& params = p.second;
336  if (llmq::quorumBlockProcessor->GetMinableCommitment(params.type, tipHeight, fqc)) {
338  fqc.ToJson(obj);
339  minableCommitments.pushKV(params.name, obj);
340  }
341  }
342  ret.pushKV("minableCommitments", minableCommitments);
343 
344  return ret;
345 }
346 
348 {
349  if (request.fHelp || request.params.size() != 2) {
350  throw std::runtime_error(
351  "quorum selectquorum llmqType \"id\"\n"
352  "Returns the quorum that would/should sign a request\n"
353  "\nArguments:\n"
354  "1. llmqType (int, required) LLMQ type.\n"
355  "2. \"id\" (string, required) Request id.\n");
356  }
357 
358  Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
359  if (!Params().GetConsensus().llmqs.count(llmqType)) {
360  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
361  }
362 
363  uint256 id = ParseHashV(request.params[1], "id");
364 
366 
367  auto quorum = llmq::quorumSigningManager->SelectQuorumForSigning(llmqType, id);
368  if (!quorum) {
369  throw JSONRPCError(RPC_MISC_ERROR, "no quorums active");
370  }
371  ret.pushKV("quorumHash", quorum->qc.quorumHash.ToString());
372 
373  UniValue recoveryMembers(UniValue::VARR);
374  for (int i = 0; i < quorum->params.recoveryMembers; i++) {
375  auto dmn = llmq::quorumSigSharesManager->SelectMemberForRecovery(quorum, id, i);
376  recoveryMembers.push_back(dmn->proTxHash.ToString());
377  }
378  ret.pushKV("recoveryMembers", recoveryMembers);
379 
380  return ret;
381 }
382 
384 {
385  if (request.fHelp || request.params.size() != 2) {
386  throw std::runtime_error(
387  "quorumdkgsimerror \"error_type\" rate\n"
388  "This enables simulation of errors and malicious behaviour in the DKG.\n"
389  "Only available on testnet/regtest for LLMQ_TEST llmq type.\n"
390  "\nArguments:\n"
391  "1. \"error_type\" (string, required) Error type.\n"
392  "2. rate (number, required) Rate at which to simulate this error type.\n"
393  "\nExamples:\n"
394  + HelpExampleRpc("quorumdkgsimerror", "\"justify-lie\", 0.1")
395  + HelpExampleCli("quorumdkgsimerror", "\"justify-lie\" 0.1")
396  );
397  }
398 
399  if (!Params().IsTestChain()) {
400  throw JSONRPCError(RPC_MISC_ERROR, "This command cannot be used on main net.");
401  }
402 
403  std::string error_type = request.params[0].get_str();
404  double rate = ParseDoubleV(request.params[1], "rate");
405  if (rate < 0 || rate > 1) {
406  throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid rate. Must be between 0 and 1");
407  }
408 
409  if (!llmq::SetSimulatedDKGErrorRate(error_type, rate)) {
410  throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid error_type: %s", error_type));
411  }
412 
413  return NullUniValue;
414 }
415 
416 // clang-format off
417 static const CRPCCommand commands[] =
418 { // category name actor (function) okSafe argNames
419  // -------------- ------------------------- --------------------- ------ --------
420  { "evo", "getminedcommitment", &getminedcommitment, true, {"llmq_type", "quorum_hash"} },
421  { "evo", "getquorummembers", &getquorummembers, true, {"llmq_type", "quorum_hash"} },
422  { "evo", "quorumselectquorum", &quorumselectquorum, true, {"llmq_type", "id"} },
423  { "evo", "quorumdkgsimerror", &quorumdkgsimerror, true, {"error_type", "rate"} },
424  { "evo", "quorumdkgstatus", &quorumdkgstatus, true, {"detail_level"} },
425  { "evo", "listquorums", &listquorums, true, {"count"} },
426  { "evo", "getquoruminfo", &getquoruminfo, true, {"llmqType", "quorumHash", "includeSkShare"} },
427 
429  { "hidden", "signsession", &signsession, true, {"llmqType", "id", "msgHash"} },
430  { "hidden", "hasrecoverysignature", &hasrecoverysignature,true, {"llmqType", "id", "msgHash"} },
431  { "hidden", "issessionconflicting", &issessionconflicting,true, {"llmqType", "id", "msgHash"} },
432  };
433 // clang-format on
434 
436 {
437  for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
438  tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
439 }
CActiveDeterministicMasternodeManager * activeMasternodeManager
const CChainParams & Params()
Return the currently selected parameters.
bool IsValid() const
Definition: bls_wrapper.h:87
std::string ToString() const
Definition: bls_wrapper.h:164
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:139
CBlockIndex * Tip(bool fProofOfStake=false) const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:405
int Height() const
Return the maximal height in the chain.
Definition: chain.h:450
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:72
std::string name
Definition: server.h:137
PIVX RPC command dispatcher.
Definition: server.h:147
bool appendCommand(const std::string &name, const CRPCCommand *pcmd)
Appends a CRPCCommand to the dispatch table.
Definition: server.cpp:314
UniValue params
Definition: server.h:47
bool fHelp
Definition: server.h:48
const std::string & get_str() const
@ VOBJ
Definition: univalue.h:21
@ VARR
Definition: univalue.h:21
size_t size() const
Definition: univalue.h:68
bool push_back(const UniValue &val)
Definition: univalue.cpp:108
bool pushKV(const std::string &key, const UniValue &val)
Definition: univalue.cpp:133
bool get_bool() const
int get_int() const
std::string ToString() const
Definition: uint256.cpp:65
UniValue ToJson(int detailLevel) const
void ToJson(UniValue &obj) const
256-bit opaque blob.
Definition: uint256.h:138
std::unique_ptr< CDeterministicMNManager > deterministicMNManager
@ LOCK
Definition: lockunlock.h:16
LLMQType
Definition: params.h:90
std::unique_ptr< CQuorumBlockProcessor > quorumBlockProcessor
std::unique_ptr< CQuorumManager > quorumManager
Definition: quorums.cpp:31
std::unique_ptr< CDKGDebugManager > quorumDKGDebugManager
bool SetSimulatedDKGErrorRate(const std::string &error_type, double rate)
std::unique_ptr< CSigningManager > quorumSigningManager
std::unique_ptr< CSigSharesManager > quorumSigSharesManager
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:80
UniValue JSONRPCError(int code, const std::string &message)
Definition: protocol.cpp:53
@ RPC_MISC_ERROR
General application defined errors.
Definition: protocol.h:41
@ RPC_INVALID_PARAMETER
Ran out of memory during operation.
Definition: protocol.h:46
UniValue quorumdkgsimerror(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:383
UniValue getquorummembers(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:267
UniValue issessionconflicting(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:78
UniValue getminedcommitment(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:231
UniValue quorumselectquorum(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:347
UniValue quorumdkgstatus(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:301
UniValue getquoruminfo(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:154
void RegisterQuorumsRPCCommands(CRPCTable &tableRPC)
Register Quorums RPC commands.
Definition: rpcquorums.cpp:435
UniValue hasrecoverysignature(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:49
UniValue signsession(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:20
UniValue listquorums(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:107
std::string HelpExampleCli(std::string methodname, std::string args)
Definition: server.cpp:527
double ParseDoubleV(const UniValue &v, const std::string &strName)
Definition: server.cpp:174
uint256 ParseHashV(const UniValue &v, std::string strName)
Utilities: convert hex-encoded Values (throws error if not hex).
Definition: server.cpp:138
std::string HelpExampleRpc(std::string methodname, std::string args)
Definition: server.cpp:532
CRPCTable tableRPC
Definition: server.cpp:565
std::map< LLMQType, LLMQParams > llmqs
Definition: params.h:279
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:247
std::atomic< bool > fMasterNode
Definition: system.cpp:87
#define strprintf
Definition: tinyformat.h:1056
const UniValue NullUniValue
Definition: univalue.cpp:13
#define ARRAYLEN(array)
CChain chainActive
The currently-connected chain of blocks (protected by cs_main).
Definition: validation.cpp:84
CBlockIndex * LookupBlockIndex(const uint256 &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: validation.h:345