LCOV - code coverage report
Current view: top level - src/rpc - rpcquorums.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 75 196 38.3 %
Date: 2025-02-23 09:33:43 Functions: 10 14 71.4 %

          Line data    Source code
       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"
       9             : #include "llmq/quorums_blockprocessor.h"
      10             : #include "llmq/quorums_commitment.h"
      11             : #include "llmq/quorums_debug.h"
      12             : #include "llmq/quorums_dkgsession.h"
      13             : #include "llmq/quorums_signing.h"
      14             : #include "llmq/quorums_signing_shares.h"
      15             : #include "rpc/server.h"
      16             : #include "validation.h"
      17             : 
      18             : #include <string>
      19             : 
      20          16 : UniValue signsession(const JSONRPCRequest& request)
      21             : {
      22          16 :     if (!Params().IsTestChain()) {
      23           0 :         throw JSONRPCError(RPC_MISC_ERROR, "command available only for RegTest and TestNet network");
      24             :     }
      25          16 :     if (request.fHelp || (request.params.size() != 3)) {
      26           0 :         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           0 :             "\nExample:\n" +
      37           0 :             HelpExampleRpc("signsession", "100 \"xxx\", \"xxx\"") + HelpExampleCli("signsession", "100 \"xxx\", \"xxx\""));
      38             :     }
      39          16 :     Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
      40          16 :     if (!Params().GetConsensus().llmqs.count(llmqType)) {
      41           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
      42             :     }
      43             : 
      44          16 :     uint256 id = ParseHashV(request.params[1], "id");
      45          16 :     uint256 msgHash = ParseHashV(request.params[2], "msgHash");
      46          16 :     return llmq::quorumSigningManager->AsyncSignIfMember(llmqType, id, msgHash);
      47             : }
      48             : 
      49          12 : UniValue hasrecoverysignature(const JSONRPCRequest& request)
      50             : {
      51          12 :     if (!Params().IsTestChain()) {
      52           0 :         throw JSONRPCError(RPC_MISC_ERROR, "command available only for RegTest and TestNet network");
      53             :     }
      54          12 :     if (request.fHelp || (request.params.size() != 3)) {
      55           0 :         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           0 :             "\nExample:\n" +
      66           0 :             HelpExampleRpc("hasrecoverysignature", "100 \"xxx\", \"xxx\"") + HelpExampleCli("hasrecoverysignature", "100 \"xxx\", \"xxx\""));
      67             :     }
      68          12 :     Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
      69          12 :     if (!Params().GetConsensus().llmqs.count(llmqType)) {
      70           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
      71             :     }
      72             : 
      73          12 :     uint256 id = ParseHashV(request.params[1], "id");
      74          12 :     uint256 msgHash = ParseHashV(request.params[2], "msgHash");
      75          12 :     return llmq::quorumSigningManager->HasRecoveredSig(llmqType, id, msgHash);
      76             : }
      77             : 
      78           0 : UniValue issessionconflicting(const JSONRPCRequest& request)
      79             : {
      80           0 :     if (!Params().IsTestChain()) {
      81           0 :         throw JSONRPCError(RPC_MISC_ERROR, "command available only for RegTest and TestNet network");
      82             :     }
      83           0 :     if (request.fHelp || (request.params.size() != 3)) {
      84           0 :         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           0 :             "\nExample:\n" +
      95           0 :             HelpExampleRpc("issessionconflicting", "100 \"xxx\", \"xxx\"") + HelpExampleCli("issessionconflicting", "100 \"xxx\", \"xxx\""));
      96             :     }
      97           0 :     Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
      98           0 :     if (!Params().GetConsensus().llmqs.count(llmqType)) {
      99           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
     100             :     }
     101             : 
     102           0 :     uint256 id = ParseHashV(request.params[1], "id");
     103           0 :     uint256 msgHash = ParseHashV(request.params[2], "msgHash");
     104           0 :     return llmq::quorumSigningManager->IsConflicting(llmqType, id, msgHash);
     105             : }
     106             : 
     107           0 : UniValue listquorums(const JSONRPCRequest& request)
     108             : {
     109           0 :     if (request.fHelp || request.params.size() > 1) {
     110           0 :         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           0 :             "\nExample:\n" +
     125           0 :             HelpExampleRpc("listquorums", "1") + HelpExampleCli("listquorums", "1"));
     126             :     }
     127             : 
     128           0 :     LOCK(cs_main);
     129           0 :     int count = 10;
     130           0 :     if(request.params.size() == 1) {
     131           0 :         count = request.params[0].get_int();
     132           0 :         if(count <= 0) {
     133           0 :             throw std::runtime_error(
     134           0 :             "count cannot be 0 or negative!\n");
     135             :         }
     136             :     }
     137           0 :     UniValue ret(UniValue::VOBJ);
     138             : 
     139           0 :     for (auto& p : Params().GetConsensus().llmqs) {
     140           0 :         UniValue v(UniValue::VARR);
     141             : 
     142           0 :         auto quorums = llmq::quorumManager->ScanQuorums(p.first, chainActive.Tip(), count);
     143           0 :         for (auto& q : quorums) {
     144           0 :             v.push_back(q->qc.quorumHash.ToString());
     145             :         }
     146             : 
     147           0 :         ret.pushKV(p.second.name, v);
     148             :     }
     149             : 
     150             : 
     151           0 :     return ret;
     152             : }
     153             : 
     154           0 : UniValue getquoruminfo(const JSONRPCRequest& request)
     155             : {
     156           0 :     if (request.fHelp || request.params.size() > 3 || request.params.size() < 2)
     157           0 :         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           0 :             "\nExample:\n" +
     181           0 :             HelpExampleRpc("getquoruminfo", "2 \"xxx\", true") + HelpExampleCli("getquoruminfo", "2, \"xxx\",true"));
     182             : 
     183           0 :     LOCK(cs_main);
     184             : 
     185           0 :     Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
     186           0 :     if (!Params().GetConsensus().llmqs.count(llmqType)) {
     187           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid llmqType");
     188             :     }
     189             : 
     190           0 :     uint256 blockHash = ParseHashV(request.params[1], "quorumHash");
     191           0 :     bool includeSkShare = false;
     192           0 :     if (request.params.size() > 2) {
     193           0 :         includeSkShare = request.params[2].get_bool();
     194             :     }
     195             : 
     196           0 :     auto quorum = llmq::quorumManager->GetQuorum(llmqType, blockHash);
     197           0 :     if (!quorum) {
     198           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "quorum not found");
     199             :     }
     200             : 
     201           0 :     UniValue ret(UniValue::VOBJ);
     202             : 
     203           0 :     ret.pushKV("height", quorum->pindexQuorum->nHeight);
     204           0 :     ret.pushKV("quorumHash", quorum->qc.quorumHash.ToString());
     205             : 
     206           0 :     UniValue membersArr(UniValue::VARR);
     207           0 :     for (size_t i = 0; i < quorum->members.size(); i++) {
     208           0 :         auto& dmn = quorum->members[i];
     209           0 :         UniValue mo(UniValue::VOBJ);
     210           0 :         mo.pushKV("proTxHash", dmn->proTxHash.ToString());
     211           0 :         mo.pushKV("valid", quorum->qc.validMembers[i]);
     212           0 :         if (quorum->qc.validMembers[i]) {
     213           0 :             CBLSPublicKey pubKey = quorum->GetPubKeyShare(i);
     214           0 :             if (pubKey.IsValid()) {
     215           0 :                 mo.pushKV("pubKeyShare", pubKey.ToString());
     216             :             }
     217             :         }
     218           0 :         membersArr.push_back(mo);
     219             :     }
     220             : 
     221           0 :     ret.pushKV("members", membersArr);
     222           0 :     ret.pushKV("quorumPublicKey", quorum->qc.quorumPublicKey.ToString());
     223           0 :     CBLSSecretKey skShare = quorum->GetSkShare();
     224           0 :     if (includeSkShare && skShare.IsValid()) {
     225           0 :         ret.pushKV("secretKeyShare", skShare.ToString());
     226             :     }
     227             : 
     228           0 :     return ret;
     229             : }
     230             : 
     231          18 : UniValue getminedcommitment(const JSONRPCRequest& request)
     232             : {
     233          18 :     if (request.fHelp || request.params.size() != 2) {
     234           0 :         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           0 :                 + HelpExampleRpc("getminedcommitment", "2 \"xxx\"")
     242           0 :                 + HelpExampleCli("getminedcommitment", "2, \"xxx\"")
     243           0 :         );
     244             :     }
     245             : 
     246          18 :     Consensus::LLMQType llmq_type = static_cast<Consensus::LLMQType>(request.params[0].get_int());
     247          18 :     if (!Params().GetConsensus().llmqs.count(llmq_type)) {
     248           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid llmq_type");
     249             :     }
     250          18 :     const uint256& quorum_hash = ParseHashV(request.params[1], "quorum_hash");
     251          36 :     if (WITH_LOCK(cs_main, return LookupBlockIndex(quorum_hash)) == nullptr) {
     252           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid quorum_hash");
     253             :     }
     254             : 
     255          18 :     llmq::CFinalCommitment qc;
     256          18 :     uint256 block_hash;
     257          18 :     if (!llmq::quorumBlockProcessor->GetMinedCommitment(llmq_type, quorum_hash, qc, block_hash)) {
     258           2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "mined commitment not found");
     259             :     }
     260             : 
     261          17 :     UniValue ret(UniValue::VOBJ);
     262          17 :     qc.ToJson(ret);
     263          34 :     ret.pushKV("block_hash", block_hash.ToString());
     264          34 :     return ret;
     265             : }
     266             : 
     267          19 : UniValue getquorummembers(const JSONRPCRequest& request)
     268             : {
     269          19 :     if (request.fHelp || request.params.size() != 2) {
     270           0 :         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           0 :                 + HelpExampleRpc("getquorummembers", "2 \"xxx\"")
     278           0 :                 + HelpExampleCli("getquorummembers", "2, \"xxx\"")
     279           0 :         );
     280             :     }
     281             : 
     282          19 :     Consensus::LLMQType llmq_type = static_cast<Consensus::LLMQType>(request.params[0].get_int());
     283          19 :     if (!Params().GetConsensus().llmqs.count(llmq_type)) {
     284           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid llmq_type");
     285             :     }
     286             : 
     287          19 :     const uint256& quorum_hash = ParseHashV(request.params[1], "quorum_hash");
     288          38 :     const CBlockIndex* pindexQuorum = WITH_LOCK(cs_main, return LookupBlockIndex(quorum_hash));
     289          19 :     if (pindexQuorum == nullptr) {
     290           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid quorum_hash");
     291             :     }
     292             : 
     293          19 :     auto mns = deterministicMNManager->GetAllQuorumMembers(llmq_type, pindexQuorum);
     294          19 :     UniValue ret(UniValue::VARR);
     295          76 :     for (const auto& dmn : mns) {
     296         114 :         ret.push_back(dmn->proTxHash.ToString());
     297             :     }
     298          19 :     return ret;
     299             : }
     300             : 
     301         306 : UniValue quorumdkgstatus(const JSONRPCRequest& request)
     302             : {
     303         306 :     if (request.fHelp || request.params.size() > 1) {
     304           0 :         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           0 :                 + HelpExampleRpc("quorumdkgstatus", "2")
     312           0 :                 + HelpExampleCli("quorumdkgstatus", "")
     313           0 :         );
     314             :     }
     315             : 
     316         306 :     int detailLevel = request.params.size() > 0 ? request.params[0].get_int() : 0;
     317         306 :     if (detailLevel < 0 || detailLevel > 2) {
     318           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid detail_level %d", detailLevel));
     319             :     }
     320             : 
     321         306 :     if (!fMasterNode || !activeMasternodeManager) {
     322           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "This is not a (deterministic) masternode");
     323             :     }
     324             : 
     325         306 :     llmq::CDKGDebugStatus status;
     326         306 :     llmq::quorumDKGDebugManager->GetLocalDebugStatus(status);
     327             : 
     328         306 :     auto ret = status.ToJson(detailLevel);
     329             : 
     330         612 :     const int tipHeight = WITH_LOCK(cs_main, return chainActive.Height(); );
     331             : 
     332         612 :     UniValue minableCommitments(UniValue::VOBJ);
     333         612 :     for (const auto& p : Params().GetConsensus().llmqs) {
     334         306 :         auto& params = p.second;
     335         612 :         llmq::CFinalCommitment fqc;
     336         306 :         if (llmq::quorumBlockProcessor->GetMinableCommitment(params.type, tipHeight, fqc)) {
     337         102 :             UniValue obj(UniValue::VOBJ);
     338          51 :             fqc.ToJson(obj);
     339          51 :             minableCommitments.pushKV(params.name, obj);
     340             :         }
     341             :     }
     342         306 :     ret.pushKV("minableCommitments", minableCommitments);
     343             : 
     344         612 :     return ret;
     345             : }
     346             : 
     347           0 : UniValue quorumselectquorum(const JSONRPCRequest& request)
     348             : {
     349           0 :     if (request.fHelp || request.params.size() != 2) {
     350           0 :         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           0 :             "2. \"id\"                  (string, required) Request id.\n");
     356             :     }
     357             : 
     358           0 :     Consensus::LLMQType llmqType = static_cast<Consensus::LLMQType>(request.params[0].get_int());
     359           0 :     if (!Params().GetConsensus().llmqs.count(llmqType)) {
     360           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
     361             :     }
     362             : 
     363           0 :     uint256 id = ParseHashV(request.params[1], "id");
     364             : 
     365           0 :     UniValue ret(UniValue::VOBJ);
     366             : 
     367           0 :     auto quorum = llmq::quorumSigningManager->SelectQuorumForSigning(llmqType, id);
     368           0 :     if (!quorum) {
     369           0 :         throw JSONRPCError(RPC_MISC_ERROR, "no quorums active");
     370             :     }
     371           0 :     ret.pushKV("quorumHash", quorum->qc.quorumHash.ToString());
     372             : 
     373           0 :     UniValue recoveryMembers(UniValue::VARR);
     374           0 :     for (int i = 0; i < quorum->params.recoveryMembers; i++) {
     375           0 :         auto dmn = llmq::quorumSigSharesManager->SelectMemberForRecovery(quorum, id, i);
     376           0 :         recoveryMembers.push_back(dmn->proTxHash.ToString());
     377             :     }
     378           0 :     ret.pushKV("recoveryMembers", recoveryMembers);
     379             : 
     380           0 :     return ret;
     381             : }
     382             : 
     383          61 : UniValue quorumdkgsimerror(const JSONRPCRequest& request)
     384             : {
     385          61 :     if (request.fHelp || request.params.size() != 2) {
     386           0 :         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           0 :                 + HelpExampleRpc("quorumdkgsimerror", "\"justify-lie\", 0.1")
     395           0 :                 + HelpExampleCli("quorumdkgsimerror", "\"justify-lie\" 0.1")
     396           0 :         );
     397             :     }
     398             : 
     399          61 :     if (!Params().IsTestChain()) {
     400           0 :         throw JSONRPCError(RPC_MISC_ERROR, "This command cannot be used on main net.");
     401             :     }
     402             : 
     403          61 :     std::string error_type = request.params[0].get_str();
     404          61 :     double rate = ParseDoubleV(request.params[1], "rate");
     405          61 :     if (rate < 0 || rate > 1) {
     406           4 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid rate. Must be between 0 and 1");
     407             :     }
     408             : 
     409          59 :     if (!llmq::SetSimulatedDKGErrorRate(error_type, rate)) {
     410           2 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("invalid error_type: %s", error_type));
     411             :     }
     412             : 
     413         116 :     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             : 
     428             :     /** Not shown in help */
     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             : 
     435         494 : void RegisterQuorumsRPCCommands(CRPCTable& tableRPC)
     436             : {
     437        5434 :     for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
     438        4940 :         tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
     439         494 : }

Generated by: LCOV version 1.14