LCOV - code coverage report
Current view: top level - src/rpc - masternode.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 440 537 81.9 %
Date: 2026-06-25 07:23:43 Functions: 27 29 93.1 %

          Line data    Source code
       1             : // Copyright (c) 2014-2025 The Dash Core developers
       2             : // Distributed under the MIT/X11 software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <active/context.h>
       6             : #include <active/masternode.h>
       7             : #include <evo/assetlocktx.h>
       8             : #include <evo/chainhelper.h>
       9             : #include <evo/deterministicmns.h>
      10             : #include <governance/superblock.h>
      11             : #include <masternode/payments.h>
      12             : #include <rpc/evo_util.h>
      13             : 
      14             : #include <chainparams.h>
      15             : #include <core_io.h>
      16             : #include <index/txindex.h>
      17             : #include <net.h>
      18             : #include <netbase.h>
      19             : #include <node/blockstorage.h>
      20             : #include <node/context.h>
      21             : #include <rpc/server.h>
      22             : #include <rpc/server_util.h>
      23             : #include <rpc/util.h>
      24             : #include <util/check.h>
      25             : #include <util/strencodings.h>
      26             : #include <validation.h>
      27             : #include <wallet/coincontrol.h>
      28             : #include <wallet/rpc/util.h>
      29             : 
      30             : #ifdef ENABLE_WALLET
      31             : #include <wallet/spend.h>
      32             : #include <wallet/wallet.h>
      33             : #endif // ENABLE_WALLET
      34             : 
      35             : #include <univalue.h>
      36             : 
      37             : using node::GetTransaction;
      38             : using node::NodeContext;
      39             : using node::ReadBlockFromDisk;
      40             : #ifdef ENABLE_WALLET
      41             : using wallet::CCoinControl;
      42             : using wallet::CoinType;
      43             : using wallet::CWallet;
      44             : using wallet::GetWalletForJSONRPCRequest;
      45             : #endif // ENABLE_WALLET
      46             : 
      47        6138 : static RPCHelpMan masternode_connect()
      48             : {
      49       12276 :     return RPCHelpMan{"masternode connect",
      50        6138 :         "Connect to given masternode\n",
      51       18414 :         {
      52        6138 :             {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the masternode to connect"},
      53        6138 :             {"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol"},
      54             :         },
      55        6138 :         RPCResult{
      56        6138 :             RPCResult::Type::STR, "status", "Returns 'successfully connected' if successful"
      57             :         },
      58        6138 :         RPCExamples{""},
      59        6138 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
      60             : {
      61           0 :     std::string strAddress = request.params[0].get_str();
      62             : 
      63           0 :     std::optional<CService> addr{Lookup(strAddress, 0, false)};
      64           0 :     if (!addr.has_value()) {
      65           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Incorrect masternode address %s", strAddress));
      66             :     }
      67             : 
      68           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
      69           0 :     CConnman& connman = EnsureConnman(node);
      70             : 
      71           0 :     bool node_v2transport = connman.GetLocalServices() & NODE_P2P_V2;
      72           0 :     bool use_v2transport = request.params[1].isNull() ? node_v2transport : ParseBoolV(request.params[1], "v2transport");
      73             : 
      74           0 :     if (use_v2transport && !node_v2transport) {
      75           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Adding v2transport connections requires -v2transport init flag to be set.");
      76             :     }
      77             : 
      78           0 :     connman.OpenMasternodeConnection(CAddress(addr.value(), NODE_NETWORK), use_v2transport);
      79           0 :     if (!connman.IsConnected(CAddress(addr.value(), NODE_NETWORK), CConnman::AllNodes)) {
      80           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to masternode %s", strAddress));
      81             :     }
      82             : 
      83           0 :     return "successfully connected";
      84           0 : },
      85             :     };
      86           0 : }
      87             : 
      88        6148 : static RPCHelpMan masternode_count()
      89             : {
      90       12296 :     return RPCHelpMan{"masternode count",
      91        6148 :         "Get information about number of masternodes.\n",
      92        6148 :         {},
      93        6148 :         RPCResult{
      94        6148 :             RPCResult::Type::OBJ, "", "",
      95       24592 :             {
      96        6148 :                 {RPCResult::Type::NUM, "total", "Total number of Masternodes"},
      97        6148 :                 {RPCResult::Type::NUM, "enabled", "Number of enabled Masternodes"},
      98       12296 :                 {RPCResult::Type::OBJ, "details", "Breakdown of masternodes by type",
      99       12296 :                     {{RPCResult::Type::OBJ, "", "",
     100       18444 :                     {
     101       12296 :                         {RPCResult::Type::OBJ, "regular", "Details for regular masternodes",
     102       18444 :                             {
     103        6148 :                                 {RPCResult::Type::NUM, "total", "Total number of regular Masternodes"},
     104        6148 :                                 {RPCResult::Type::NUM, "enabled", "Number of enabled regular Masternodes"}
     105             :                         }},
     106       12296 :                         {RPCResult::Type::OBJ, "evo", "Details for Evo nodes",
     107       18444 :                             {
     108        6148 :                                 {RPCResult::Type::NUM, "total", "Total number of Evo nodes"},
     109        6148 :                                 {RPCResult::Type::NUM, "enabled", "Number of enabled Evo nodes"}
     110             :                         }},
     111             :                     }},
     112             :                     }}
     113             :             }
     114             :         },
     115        6148 :         RPCExamples{""},
     116        6158 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     117             : {
     118          10 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     119             : 
     120          10 :     const auto counts{node.dmnman->GetListAtChainTip().GetCounts()};
     121             : 
     122          10 :     UniValue obj(UniValue::VOBJ);
     123          10 :     obj.pushKV("total", counts.total());
     124          10 :     obj.pushKV("enabled", counts.enabled());
     125             : 
     126          10 :     UniValue evoObj(UniValue::VOBJ);
     127          10 :     evoObj.pushKV("total", counts.m_total_evo);
     128          10 :     evoObj.pushKV("enabled", counts.m_valid_evo);
     129             : 
     130          10 :     UniValue regularObj(UniValue::VOBJ);
     131          10 :     regularObj.pushKV("total", counts.m_total_mn);
     132          10 :     regularObj.pushKV("enabled", counts.m_valid_mn);
     133             : 
     134          10 :     UniValue detailedObj(UniValue::VOBJ);
     135          10 :     detailedObj.pushKV("regular", regularObj);
     136          10 :     detailedObj.pushKV("evo", evoObj);
     137          10 :     obj.pushKV("detailed", detailedObj);
     138             : 
     139          10 :     return obj;
     140          10 : },
     141             :     };
     142           0 : }
     143             : 
     144             : #ifdef ENABLE_WALLET
     145        2874 : static RPCHelpMan masternode_outputs()
     146             : {
     147        5748 :     return RPCHelpMan{"masternode outputs",
     148        2874 :         "Print masternode compatible outputs\n",
     149        2874 :         {},
     150        2874 :         RPCResult {
     151        2874 :             RPCResult::Type::ARR, "", "A list of outpoints that can be/are used as masternode collaterals",
     152        5748 :             {
     153        2874 :                 {RPCResult::Type::STR, "", "A (potential) masternode collateral"},
     154             :             }},
     155        2874 :         RPCExamples{""},
     156        2876 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     157             : {
     158           2 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     159           2 :     if (!wallet) return UniValue::VNULL;
     160             : 
     161             :     // Find possible candidates
     162           2 :     CCoinControl coin_control(CoinType::ONLY_MASTERNODE_COLLATERAL);
     163             : 
     164           2 :     UniValue outputsArr(UniValue::VARR);
     165          14 :     for (const auto& out : WITH_LOCK(wallet->cs_wallet, return AvailableCoinsListUnspent(*wallet, &coin_control).all())) {
     166          10 :         outputsArr.push_back(out.outpoint.ToStringShort());
     167             :     }
     168             : 
     169           2 :     return outputsArr;
     170           2 : },
     171             :     };
     172           0 : }
     173             : 
     174             : #endif // ENABLE_WALLET
     175             : 
     176        6150 : static RPCHelpMan masternode_status()
     177             : {
     178       12300 :     return RPCHelpMan{"masternode status",
     179        6150 :         "Print masternode status information\n",
     180        6150 :         {},
     181        6150 :         RPCResult{
     182        6150 :             RPCResult::Type::OBJ, "", "",
     183       61500 :             {
     184        6150 :                 GetRpcResult("outpoint"),
     185        6150 :                 GetRpcResult("service", /*optional=*/true),
     186        6150 :                 GetRpcResult("proTxHash", /*optional=*/true),
     187        6150 :                 GetRpcResult("type_str", /*optional=*/true, /*override_name=*/"type"),
     188        6150 :                 GetRpcResult("collateralHash", /*optional=*/true),
     189        6150 :                 GetRpcResult("collateralIndex", /*optional=*/true),
     190        6150 :                 CDeterministicMNState::GetJsonHelp(/*key=*/"dmnState", /*optional=*/true),
     191        6150 :                 {RPCResult::Type::STR, "state", "Masternode state (human-readable string)"},
     192        6150 :                 {RPCResult::Type::STR, "status", "Masternode status (human-readable string, based on current state)"},
     193             :             }
     194             :         },
     195        6150 :         RPCExamples{""},
     196        6162 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     197             : {
     198          12 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     199             : 
     200          12 :     if (!node.active_ctx) {
     201           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "This node does not run an active masternode.");
     202             :     }
     203          12 :     const auto& mn_activeman{*node.active_ctx->nodeman};
     204             : 
     205          12 :     UniValue mnObj(UniValue::VOBJ);
     206             :     // keep compatibility with legacy status for now (TODO: get deprecated/removed later)
     207          12 :     mnObj.pushKV("outpoint", mn_activeman.GetOutPoint().ToStringShort());
     208          12 :     mnObj.pushKV("service", mn_activeman.GetService().ToStringAddrPort());
     209          12 :     auto dmn = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip().GetMN(mn_activeman.GetProTxHash());
     210          12 :     if (dmn) {
     211          12 :         mnObj.pushKV("proTxHash", dmn->proTxHash.ToString());
     212          12 :         mnObj.pushKV("type", std::string(GetMnType(dmn->nType).description));
     213          12 :         mnObj.pushKV("collateralHash", dmn->collateralOutpoint.hash.ToString());
     214          12 :         mnObj.pushKV("collateralIndex", dmn->collateralOutpoint.n);
     215          12 :         mnObj.pushKV("dmnState", dmn->pdmnState->ToJson(dmn->nType));
     216          12 :     }
     217          12 :     mnObj.pushKV("state", mn_activeman.GetStateString());
     218          12 :     mnObj.pushKV("status", mn_activeman.GetStatus());
     219             : 
     220          12 :     return mnObj;
     221          12 : },
     222             :     };
     223           0 : }
     224             : 
     225         122 : static std::string GetRequiredPaymentsString(governance::SuperblockManager& superblocks,
     226             :                                              const CDeterministicMNList& tip_mn_list, int nBlockHeight,
     227             :                                              const CDeterministicMNCPtr& payee)
     228             : {
     229         122 :     std::string strPayments = "Unknown";
     230         122 :     if (payee) {
     231         122 :         CTxDestination dest;
     232         122 :         if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) {
     233           0 :             NONFATAL_UNREACHABLE();
     234             :         }
     235         122 :         strPayments = EncodeDestination(dest);
     236         214 :         if (payee->nOperatorReward != 0 && payee->pdmnState->scriptOperatorPayout != CScript()) {
     237          90 :             if (!ExtractDestination(payee->pdmnState->scriptOperatorPayout, dest)) {
     238           0 :                 NONFATAL_UNREACHABLE();
     239             :             }
     240          90 :             strPayments += ", " + EncodeDestination(dest);
     241          90 :         }
     242         122 :     }
     243         122 :     if (superblocks.IsSuperblockTriggered(tip_mn_list, nBlockHeight)) {
     244           0 :         std::vector<CTxOut> voutSuperblock;
     245           0 :         if (!superblocks.GetSuperblockPayments(tip_mn_list, nBlockHeight, voutSuperblock)) {
     246           0 :             return strPayments + ", error";
     247             :         }
     248           0 :         std::string strSBPayees = "Unknown";
     249           0 :         for (const auto& txout : voutSuperblock) {
     250           0 :             CTxDestination dest;
     251           0 :             ExtractDestination(txout.scriptPubKey, dest);
     252           0 :             if (strSBPayees != "Unknown") {
     253           0 :                 strSBPayees += ", " + EncodeDestination(dest);
     254           0 :             } else {
     255           0 :                 strSBPayees = EncodeDestination(dest);
     256             :             }
     257             :         }
     258           0 :         strPayments += ", " + strSBPayees;
     259           0 :     }
     260         122 :     return strPayments;
     261         122 : }
     262             : 
     263        6147 : static RPCHelpMan masternode_winners()
     264             : {
     265       12294 :     return RPCHelpMan{"masternode winners",
     266        6147 :         "Print list of masternode winners\n",
     267       18441 :         {
     268        6147 :             {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "number of last winners to return"},
     269        6147 :             {"filter", RPCArg::Type::STR, RPCArg::Default{""}, "filter for returned winners"},
     270             :         },
     271        6147 :         RPCResult{
     272        6147 :             RPCResult::Type::OBJ_DYN, "", "Keys are block heights (as strings); values describe the payees for that height",
     273       12294 :             {
     274        6147 :                 {RPCResult::Type::STR, "payee", "Payee for the height"}
     275             :             }
     276             :         },
     277        6147 :         RPCExamples{""},
     278        6156 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     279             : {
     280           9 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     281           9 :     const ChainstateManager& chainman = EnsureChainman(node);
     282           9 :     const CBlockIndex* pindexTip{nullptr};
     283             :     {
     284           9 :         LOCK(::cs_main);
     285           9 :         pindexTip = chainman.ActiveChain().Tip();
     286           9 :         if (!pindexTip) return UniValue::VNULL;
     287           9 :     }
     288             : 
     289           9 :     int nCount = 10;
     290           9 :     std::string strFilter;
     291             : 
     292           9 :     if (!request.params[0].isNull()) {
     293           4 :         nCount = request.params[0].getInt<int>();
     294           4 :     }
     295             : 
     296           9 :     if (!request.params[1].isNull()) {
     297           0 :         strFilter = request.params[1].get_str();
     298           0 :     }
     299             : 
     300           9 :     UniValue obj(UniValue::VOBJ);
     301             : 
     302           9 :     int nChainTipHeight = pindexTip->nHeight;
     303           9 :     int nStartHeight = std::max(nChainTipHeight - nCount, 1);
     304             : 
     305           9 :     const auto tip_mn_list = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip();
     306          68 :     for (int h = nStartHeight; h <= nChainTipHeight; h++) {
     307          59 :         const CBlockIndex* pIndex = pindexTip->GetAncestor(h - 1);
     308          59 :         auto payee = node.dmnman->GetListForBlock(pIndex).GetMNPayee(pIndex);
     309          59 :         if (payee) {
     310          59 :             std::string strPayments = GetRequiredPaymentsString(*CHECK_NONFATAL(node.chain_helper)->superblocks,
     311          59 :                                                                 tip_mn_list, h, payee);
     312          59 :             if (!strFilter.empty() && strPayments.find(strFilter) == std::string::npos) continue;
     313          59 :             obj.pushKV(strprintf("%d", h), strPayments);
     314          59 :         }
     315          59 :     }
     316             : 
     317           9 :     auto projection = node.dmnman->GetListForBlock(pindexTip).GetProjectedMNPayees(pindexTip, /*nCount=*/20);
     318          72 :     for (size_t i = 0; i < projection.size(); i++) {
     319          63 :         int h = nChainTipHeight + 1 + i;
     320          63 :         std::string strPayments = GetRequiredPaymentsString(*node.chain_helper->superblocks, tip_mn_list, h, projection[i]);
     321          63 :         if (!strFilter.empty() && strPayments.find(strFilter) == std::string::npos) continue;
     322          63 :         obj.pushKV(strprintf("%d", h), strPayments);
     323          63 :     }
     324             : 
     325           9 :     return obj;
     326           9 : },
     327             :     };
     328           0 : }
     329             : 
     330        6349 : static RPCHelpMan masternode_payments()
     331             : {
     332       12698 :     return RPCHelpMan{"masternode payments",
     333        6349 :         "\nReturns an array of deterministic masternodes and their payments for the specified block\n",
     334       19047 :         {
     335        6349 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"tip"}, "The hash of the starting block"},
     336        6349 :             {"count", RPCArg::Type::NUM, RPCArg::Default{1}, "The number of blocks to return. Will return <count> previous blocks if <count> is negative. Both 1 and -1 correspond to the chain tip."},
     337             :         },
     338        6349 :         RPCResult {
     339        6349 :             RPCResult::Type::ARR, "", "Blocks",
     340       12698 :             {
     341       12698 :                 {RPCResult::Type::OBJ, "", "",
     342       31745 :                 {
     343        6349 :                     {RPCResult::Type::NUM, "height", "The height of the block"},
     344        6349 :                     {RPCResult::Type::STR_HEX, "blockhash", "The hash of the block"},
     345        6349 :                     {RPCResult::Type::NUM, "amount", "Amount received in this block by all masternodes"},
     346       12698 :                     {RPCResult::Type::ARR, "masternodes", "Masternodes that received payments in this block",
     347       25396 :                     {
     348        6349 :                         {RPCResult::Type::STR_HEX, "proTxHash", "The hash of the corresponding ProRegTx"},
     349        6349 :                         {RPCResult::Type::NUM, "amount", "Amount received by this masternode"},
     350       12698 :                         {RPCResult::Type::ARR, "payees", "Payees who received a share of this payment",
     351       25396 :                         {
     352        6349 :                             {RPCResult::Type::STR, "address", "Payee address"},
     353        6349 :                             {RPCResult::Type::STR_HEX, "script", "Payee scriptPubKey"},
     354        6349 :                             {RPCResult::Type::NUM, "amount", "Amount received by this payee"},
     355             :                         }},
     356             :                     }},
     357             :                 }},
     358             :             },
     359             :         },
     360        6349 :         RPCExamples{""},
     361        6560 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     362             : {
     363         211 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     364         211 :     const ChainstateManager& chainman = EnsureChainman(node);
     365             : 
     366         211 :     const CBlockIndex* pindex{nullptr};
     367             : 
     368         211 :     if (g_txindex) {
     369         211 :         g_txindex->BlockUntilSyncedToCurrentChain();
     370         211 :     }
     371             : 
     372         211 :     if (request.params[0].isNull()) {
     373           8 :         LOCK(::cs_main);
     374           8 :         pindex = chainman.ActiveChain().Tip();
     375           8 :     } else {
     376         203 :         LOCK(::cs_main);
     377         203 :         uint256 blockHash(ParseHashV(request.params[0], "blockhash"));
     378         203 :         pindex = chainman.m_blockman.LookupBlockIndex(blockHash);
     379         203 :         if (pindex == nullptr) {
     380           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     381             :         }
     382         203 :     }
     383             : 
     384         211 :     int64_t nCount = request.params.size() > 1 ? request.params[1].getInt<int64_t>() : 1;
     385             : 
     386             :     // A temporary vector which is used to sort results properly (there is no "reverse" in/for UniValue)
     387         211 :     std::vector<UniValue> vecPayments;
     388             : 
     389         211 :     CHECK_NONFATAL(node.chain_helper);
     390         211 :     CHECK_NONFATAL(node.dmnman);
     391         424 :     while (vecPayments.size() < uint64_t(std::abs(nCount)) && pindex != nullptr) {
     392         213 :         CBlock block;
     393         213 :         if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
     394           0 :             throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
     395             :         }
     396             : 
     397             :         // Note: we have to actually calculate block reward from scratch instead of simply querying coinbase vout
     398             :         // because miners might collect less coins than they potentially could and this would break our calculations.
     399         213 :         CAmount nBlockFees{0};
     400         213 :         const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
     401         712 :         for (const auto& tx : block.vtx) {
     402         499 :             if (tx->IsCoinBase()) {
     403         213 :                 continue;
     404             :             }
     405         286 :             if (tx->IsPlatformTransfer()) {
     406           0 :                 auto payload = GetTxPayload<CAssetUnlockPayload>(*tx);
     407           0 :                 CHECK_NONFATAL(payload);
     408           0 :                 nBlockFees += payload->getFee();
     409             :                 continue;
     410           0 :             }
     411             : 
     412         286 :             CAmount nValueIn{0};
     413         286 :             for (const auto& txin : tx->vin) {
     414           0 :                 uint256 blockHashTmp;
     415           0 :                 CTransactionRef txPrev = GetTransaction(/* block_index */ nullptr, &mempool, txin.prevout.hash, Params().GetConsensus(), blockHashTmp);
     416           0 :                 nValueIn += txPrev->vout[txin.prevout.n].nValue;
     417           0 :             }
     418         286 :             nBlockFees += nValueIn - tx->GetValueOut();
     419             :         }
     420             : 
     421         213 :         std::vector<CTxOut> voutMasternodePayments, voutDummy;
     422         213 :         CMutableTransaction dummyTx;
     423         213 :         CAmount blockSubsidy = GetBlockSubsidy(pindex, Params().GetConsensus());
     424         213 :         node.chain_helper->mn_payments->FillBlockPayments(dummyTx, pindex->pprev, blockSubsidy, nBlockFees, voutMasternodePayments, voutDummy);
     425             : 
     426         213 :         UniValue blockObj(UniValue::VOBJ);
     427         213 :         CAmount payedPerBlock{0};
     428             : 
     429         213 :         UniValue masternodeArr(UniValue::VARR);
     430         213 :         UniValue protxObj(UniValue::VOBJ);
     431         213 :         UniValue payeesArr(UniValue::VARR);
     432         213 :         CAmount payedPerMasternode{0};
     433             : 
     434         724 :         for (const auto& txout : voutMasternodePayments) {
     435         511 :             UniValue obj(UniValue::VOBJ);
     436         511 :             CTxDestination dest;
     437         511 :             ExtractDestination(txout.scriptPubKey, dest);
     438         511 :             obj.pushKV("address", EncodeDestination(dest));
     439         511 :             obj.pushKV("script", HexStr(txout.scriptPubKey));
     440         511 :             obj.pushKV("amount", txout.nValue);
     441         511 :             payedPerMasternode += txout.nValue;
     442         511 :             payeesArr.push_back(obj);
     443         511 :         }
     444             : 
     445             :         // NOTE: we use _previous_ block to find a payee for the current one
     446         213 :         const auto dmnPayee = node.dmnman->GetListForBlock(pindex->pprev).GetMNPayee(pindex->pprev);
     447         213 :         protxObj.pushKV("proTxHash", dmnPayee == nullptr ? "" : dmnPayee->proTxHash.ToString());
     448         213 :         protxObj.pushKV("amount", payedPerMasternode);
     449         213 :         protxObj.pushKV("payees", payeesArr);
     450         213 :         payedPerBlock += payedPerMasternode;
     451         213 :         masternodeArr.push_back(protxObj);
     452             : 
     453         213 :         blockObj.pushKV("height", pindex->nHeight);
     454         213 :         blockObj.pushKV("blockhash", pindex->GetBlockHash().ToString());
     455         213 :         blockObj.pushKV("amount", payedPerBlock);
     456         213 :         blockObj.pushKV("masternodes", masternodeArr);
     457         213 :         vecPayments.push_back(blockObj);
     458             : 
     459         213 :         if (nCount > 0) {
     460         207 :             LOCK(::cs_main);
     461         207 :             pindex = chainman.ActiveChain().Next(pindex);
     462         207 :         } else {
     463           6 :             pindex = pindex->pprev;
     464             :         }
     465         213 :     }
     466             : 
     467         211 :     if (nCount < 0) {
     468           4 :         std::reverse(vecPayments.begin(), vecPayments.end());
     469           4 :     }
     470             : 
     471         211 :     UniValue paymentsArr(UniValue::VARR);
     472         424 :     for (const auto& payment : vecPayments) {
     473         213 :         paymentsArr.push_back(payment);
     474             :     }
     475             : 
     476         211 :     return paymentsArr;
     477         211 : },
     478             :     };
     479           0 : }
     480             : 
     481        6156 : static RPCHelpMan masternode_help()
     482             : {
     483       12312 :     return RPCHelpMan{"masternode",
     484        6156 :         "Set of commands to execute masternode related actions\n"
     485             :         "\nAvailable commands:\n"
     486             :         "  count        - Get information about number of masternodes\n"
     487             :         "  current      - DEPRECATED Print info on current masternode winner to be paid the next block (calculated locally)\n"
     488             : #ifdef ENABLE_WALLET
     489             :         "  outputs      - Print masternode compatible outputs\n"
     490             : #endif // ENABLE_WALLET
     491             :         "  status       - Print masternode status information\n"
     492             :         "  list         - Print list of all known masternodes (see masternodelist for more info)\n"
     493             :         "  payments     - Return information about masternode payments in a mined block\n"
     494             :         "  winner       - DEPRECATED Print info on next masternode winner to vote for\n"
     495             :         "  winners      - Print list of masternode winners\n",
     496       12312 :         {
     497        6156 :             {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
     498             :         },
     499        6156 :         RPCResult{RPCResult::Type::NONE, "", ""},
     500        6156 :         RPCExamples{""},
     501        6156 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     502             : {
     503           0 :     throw JSONRPCError(RPC_INVALID_PARAMETER, "Must be a valid command");
     504           0 : },
     505             :     };
     506           0 : }
     507             : 
     508       13895 : static RPCHelpMan masternodelist_helper(bool is_composite)
     509             : {
     510             :     // We need both composite and non-composite options because we support
     511             :     // both options 'masternodelist' and 'masternode list'
     512       27790 :     return RPCHelpMan{is_composite ? "masternode list" : "masternodelist",
     513       13895 :         "Get a list of masternodes in different modes. This call is identical to 'masternode list' call.\n"
     514             :         "Available modes:\n"
     515             :         "  addr           - Print ip address associated with a masternode (can be additionally filtered, partial match)\n"
     516             :         "  recent         - Print info in JSON format for active and recently banned masternodes (can be additionally filtered, partial match)\n"
     517             :         "  evo            - Print info in JSON format for EvoNodes only\n"
     518             :         "  full           - Print info in format 'status payee lastpaidtime lastpaidblock IP'\n"
     519             :         "                   (can be additionally filtered, partial match)\n"
     520             :         "  info           - Print info in format 'status payee IP'\n"
     521             :         "                   (can be additionally filtered, partial match)\n"
     522             :         "  json           - Print info in JSON format (can be additionally filtered, partial match)\n"
     523             :         "  lastpaidblock  - Print the last block height a node was paid on the network\n"
     524             :         "  lastpaidtime   - Print the last time a node was paid on the network\n"
     525             :         "  owneraddress   - Print the masternode owner Dash address\n"
     526             :         "  payee          - Print the masternode payout Dash address (can be additionally filtered,\n"
     527             :         "                   partial match)\n"
     528             :         "  pubKeyOperator - Print the masternode operator public key\n"
     529             :         "  status         - Print masternode status: ENABLED / POSE_BANNED\n"
     530             :         "                   (can be additionally filtered, partial match)\n"
     531             :         "  votingaddress  - Print the masternode voting Dash address\n",
     532       41685 :         {
     533       13895 :             {"mode", RPCArg::Type::STR, RPCArg::DefaultHint{"json"}, "The mode to run list in"},
     534       13895 :             {"filter", RPCArg::Type::STR, RPCArg::Default{""}, "Filter results. Partial match by outpoint by default in all modes, additional matches in some modes are also available"},
     535             :         },
     536       13895 :         RPCResult{
     537      166740 :             RPCResult::Type::OBJ, "<outpoint>", "", {
     538       13895 :                 RPCResult{"for mode = addr", RPCResult::Type::STR, "<address>", "Flattened list of all addresses registered to masternode"},
     539       13895 :                 RPCResult{"for mode = full", RPCResult::Type::STR, "<info>", "Flattened list of a masternode's status, payee address, last paid block's timestamp, height and service addresses"},
     540       13895 :                 RPCResult{"for mode = info", RPCResult::Type::STR, "<info>", "Flattened list of a masternode's status, payee address and service addresses"},
     541      250110 :                 RPCResult{"for mode = evo, json or recent", RPCResult::Type::OBJ, "", "", {
     542       13895 :                     GetRpcResult("proTxHash"),
     543       13895 :                     GetRpcResult("service", /*optional=*/true, /*override_name=*/"address"),
     544       13895 :                     GetRpcResult("addresses"),
     545       13895 :                     GetRpcResult("payoutAddress", /*optional=*/false, /*override_name=*/"payee"),
     546       13895 :                     {RPCResult::Type::STR, "status", "Masternode status (human-readable string)"},
     547       13895 :                     GetRpcResult("type_str", /*optional=*/false, /*override_name=*/"type"),
     548       13895 :                     GetRpcResult("platformNodeID", /*optional=*/true),
     549       13895 :                     GetRpcResult("platformP2PPort", /*optional=*/true),
     550       13895 :                     GetRpcResult("platformHTTPPort", /*optional=*/true),
     551       13895 :                     GetRpcResult("PoSePenalty", /*optional=*/false, /*override_name=*/"pospenaltyscore"),
     552       13895 :                     GetRpcResult("consecutivePayments"),
     553       13895 :                     {RPCResult::Type::NUM, "lastpaidtime", "Timestamp of block the masternode was last paid"},
     554       13895 :                     GetRpcResult("lastPaidHeight", /*optional=*/false, /*override_name=*/"lastpaidblock"),
     555       13895 :                     GetRpcResult("ownerAddress", /*optional=*/false, /*override_name=*/"owneraddress"),
     556       13895 :                     GetRpcResult("votingAddress", /*optional=*/false, /*override_name=*/"votingaddress"),
     557       13895 :                     GetRpcResult("collateralAddress", /*optional=*/false, /*override_name=*/"collateraladdress"),
     558       13895 :                     GetRpcResult("pubKeyOperator", /*optional=*/false, /*override_name=*/"pubkeyoperator"),
     559             :                 }},
     560       13895 :                 RPCResult{"for mode = lastpaidblock", RPCResult::Type::NUM, "<height>", "Height masternode was last paid"},
     561       13895 :                 RPCResult{"for mode = lastpaidtime", RPCResult::Type::NUM, "<time>", "Timestamp of block the masternode was last paid"},
     562       13895 :                 RPCResult{"for mode = payee", RPCResult::Type::STR, "<addr>", "Dash address used for masternode reward payments"},
     563       13895 :                 RPCResult{"for mode = owneraddress", RPCResult::Type::STR, "<addr>", "Dash address used for payee updates and proposal voting"},
     564       13895 :                 RPCResult{"for mode = pubkeyoperator", RPCResult::Type::STR, "<addr>", "BLS public key used for operator signing"},
     565       13895 :                 RPCResult{"for mode = status", RPCResult::Type::STR, "<status>", "Masternode status (human-readable string)"},
     566       13895 :                 RPCResult{"for mode = votingaddress", RPCResult::Type::STR, "<addr>", "Dash address used for voting"},
     567             :             }
     568             :         },
     569       13895 :         RPCExamples{""},
     570       15496 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     571             : {
     572        1601 :     std::string strMode = "json";
     573        1601 :     std::string strFilter;
     574             : 
     575        1601 :     if (!request.params[0].isNull()) strMode = request.params[0].get_str();
     576        1601 :     if (!request.params[1].isNull()) strFilter = request.params[1].get_str();
     577             : 
     578        1601 :     strMode = ToLower(strMode);
     579             : 
     580             :     if (
     581        1603 :                 strMode != "addr" && strMode != "full" && strMode != "info" && strMode != "json" &&
     582         908 :                 strMode != "owneraddress" && strMode != "votingaddress" &&
     583         908 :                 strMode != "lastpaidtime" && strMode != "lastpaidblock" &&
     584         908 :                 strMode != "payee" && strMode != "pubkeyoperator" &&
     585         908 :                 strMode != "status" && strMode != "recent" && strMode != "evo")
     586             :     {
     587           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("strMode %s not found", strMode));
     588             :     }
     589             : 
     590        1601 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     591        1601 :     const ChainstateManager& chainman = EnsureChainman(node);
     592             : 
     593        1601 :     UniValue obj(UniValue::VOBJ);
     594             : 
     595        1601 :     const auto mnList = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip();
     596       24246 :     const auto dmnToStatus = [&](const auto& dmn) {
     597       24246 :         if (dmn.pdmnState->IsBanned()) {
     598         114 :             return "POSE_BANNED";
     599             :         }
     600       24132 :         return "ENABLED";
     601       24246 :     };
     602       17217 :     const auto dmnToLastPaidTime = [&](const auto& dmn) {
     603       15616 :         if (dmn.pdmnState->nLastPaidHeight == 0) {
     604         136 :             return (int)0;
     605             :         }
     606             : 
     607       15480 :         LOCK(::cs_main);
     608       15480 :         const CBlockIndex* pindex = chainman.ActiveChain()[dmn.pdmnState->nLastPaidHeight];
     609       15480 :         return (int)pindex->nTime;
     610       15616 :     };
     611             : 
     612        1601 :     const bool showRecentMnsOnly = strMode == "recent";
     613        1601 :     const bool showEvoOnly = strMode == "evo";
     614        3202 :     const int tipHeight = WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->nHeight);
     615       18043 :     mnList.ForEachMN(/*onlyValid=*/false, [&](const auto& dmn) {
     616       16442 :         if (showRecentMnsOnly && dmn.pdmnState->IsBanned()) {
     617           0 :             if (tipHeight - dmn.pdmnState->GetBannedHeight() > Params().GetConsensus().nSuperblockCycle) {
     618           0 :                 return;
     619             :             }
     620           0 :         }
     621       16442 :         if (showEvoOnly && dmn.nType != MnType::Evo) {
     622           4 :             return;
     623             :         }
     624             : 
     625       16438 :         std::string strOutpoint = dmn.collateralOutpoint.ToStringShort();
     626       16438 :         Coin coin;
     627       16438 :         std::string collateralAddressStr = "UNKNOWN";
     628       16438 :         if (GetUTXOCoin(chainman.ActiveChainstate(), dmn.collateralOutpoint, coin)) {
     629       16438 :             CTxDestination collateralDest;
     630       16438 :             if (ExtractDestination(coin.out.scriptPubKey, collateralDest)) {
     631       16438 :                 collateralAddressStr = EncodeDestination(collateralDest);
     632       16438 :             }
     633       16438 :         }
     634             : 
     635       16438 :         CScript payeeScript = dmn.pdmnState->scriptPayout;
     636       16438 :         CTxDestination payeeDest;
     637       16438 :         std::string payeeStr = "UNKNOWN";
     638       16438 :         if (ExtractDestination(payeeScript, payeeDest)) {
     639       16438 :             payeeStr = EncodeDestination(payeeDest);
     640       16438 :         }
     641             : 
     642       16438 :         std::string strAddress{};
     643       16438 :         if (strMode == "addr" || strMode == "full" || strMode == "info" || strMode == "json" || strMode == "recent" ||
     644        8638 :             strMode == "evo") {
     645       15607 :             for (const auto& entry : dmn.pdmnState->netInfo->GetEntries()) {
     646        7799 :                 strAddress += entry.ToStringAddrPort() + " ";
     647             :             }
     648        7808 :             if (!strAddress.empty()) strAddress.pop_back(); // Remove trailing space
     649        7808 :         }
     650             : 
     651       16438 :         if (strMode == "addr") {
     652           0 :             if (!strFilter.empty() && strAddress.find(strFilter) == std::string::npos &&
     653           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     654           0 :                 return;
     655           0 :             obj.pushKV(strOutpoint, strAddress);
     656       16438 :         } else if (strMode == "full") {
     657           0 :             std::string strFull = strprintf("%s %d %s %s %s %s",
     658           0 :                                     PadString(dmnToStatus(dmn), 18),
     659           0 :                                     dmn.pdmnState->nPoSePenalty,
     660             :                                     payeeStr,
     661           0 :                                     PadString(ToString(dmnToLastPaidTime(dmn)), 10),
     662           0 :                                     PadString(ToString(dmn.pdmnState->nLastPaidHeight), 6),
     663             :                                     strAddress);
     664           0 :             if (!strFilter.empty() && strFull.find(strFilter) == std::string::npos &&
     665           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     666           0 :                 return;
     667           0 :             obj.pushKV(strOutpoint, strFull);
     668       16438 :         } else if (strMode == "info") {
     669           0 :             std::string strInfo = strprintf("%s %d %s %s",
     670           0 :                                     PadString(dmnToStatus(dmn), 18),
     671           0 :                                     dmn.pdmnState->nPoSePenalty,
     672             :                                     payeeStr,
     673             :                                     strAddress);
     674           0 :             if (!strFilter.empty() && strInfo.find(strFilter) == std::string::npos &&
     675           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     676           0 :                 return;
     677           0 :             obj.pushKV(strOutpoint, strInfo);
     678       16438 :         } else if (strMode == "json" || strMode == "recent" || strMode == "evo") {
     679        7808 :             std::string strInfo = strprintf("%s %s %s %s %d %d %d %s %s %s %s",
     680        7808 :                                     dmn.proTxHash.ToString(),
     681             :                                     strAddress,
     682             :                                     payeeStr,
     683        7808 :                                     dmnToStatus(dmn),
     684        7808 :                                     dmn.pdmnState->nPoSePenalty,
     685        7808 :                                     dmnToLastPaidTime(dmn),
     686        7808 :                                     dmn.pdmnState->nLastPaidHeight,
     687        7808 :                                     EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)),
     688        7808 :                                     EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)),
     689             :                                     collateralAddressStr,
     690        7808 :                                     dmn.pdmnState->pubKeyOperator.ToString());
     691        7808 :             if (!strFilter.empty() && strInfo.find(strFilter) == std::string::npos &&
     692           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     693           0 :                 return;
     694        7808 :             UniValue objMN(UniValue::VOBJ);
     695        7808 :             objMN.pushKV("proTxHash", dmn.proTxHash.ToString());
     696        7808 :             if (IsDeprecatedRPCEnabled("service")) {
     697           8 :                 objMN.pushKV("address", dmn.pdmnState->netInfo->GetPrimary().ToStringAddrPort());
     698           8 :             }
     699        7808 :             objMN.pushKV("addresses", GetNetInfoWithLegacyFields(*dmn.pdmnState, dmn.nType));
     700        7808 :             objMN.pushKV("payee", payeeStr);
     701        7808 :             objMN.pushKV("status", dmnToStatus(dmn));
     702        7808 :             objMN.pushKV("type", std::string(GetMnType(dmn.nType).description));
     703        7808 :             if (dmn.nType == MnType::Evo) {
     704         118 :                 objMN.pushKV("platformNodeID", dmn.pdmnState->platformNodeID.ToString());
     705         118 :                 if (IsDeprecatedRPCEnabled("service")) {
     706           8 :                     objMN.pushKV("platformP2PPort", GetPlatformPort</*is_p2p=*/true>(*dmn.pdmnState));
     707           8 :                     objMN.pushKV("platformHTTPPort", GetPlatformPort</*is_p2p=*/false>(*dmn.pdmnState));
     708           8 :                 }
     709         118 :             }
     710        7808 :             objMN.pushKV("pospenaltyscore", dmn.pdmnState->nPoSePenalty);
     711        7808 :             objMN.pushKV("consecutivePayments", dmn.pdmnState->nConsecutivePayments);
     712        7808 :             objMN.pushKV("lastpaidtime", dmnToLastPaidTime(dmn));
     713        7808 :             objMN.pushKV("lastpaidblock", dmn.pdmnState->nLastPaidHeight);
     714        7808 :             objMN.pushKV("owneraddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)));
     715        7808 :             objMN.pushKV("votingaddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)));
     716        7808 :             objMN.pushKV("collateraladdress", collateralAddressStr);
     717        7808 :             objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.ToString());
     718        7808 :             obj.pushKV(strOutpoint, objMN);
     719       16438 :         } else if (strMode == "lastpaidblock") {
     720           0 :             if (!strFilter.empty() && strOutpoint.find(strFilter) == std::string::npos) return;
     721           0 :             obj.pushKV(strOutpoint, dmn.pdmnState->nLastPaidHeight);
     722        8630 :         } else if (strMode == "lastpaidtime") {
     723           0 :             if (!strFilter.empty() && strOutpoint.find(strFilter) == std::string::npos) return;
     724           0 :             obj.pushKV(strOutpoint, dmnToLastPaidTime(dmn));
     725        8630 :         } else if (strMode == "payee") {
     726           0 :             if (!strFilter.empty() && payeeStr.find(strFilter) == std::string::npos &&
     727           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     728           0 :                 return;
     729           0 :             obj.pushKV(strOutpoint, payeeStr);
     730        8630 :         } else if (strMode == "owneraddress") {
     731           0 :             if (!strFilter.empty() && strOutpoint.find(strFilter) == std::string::npos) return;
     732           0 :             obj.pushKV(strOutpoint, EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)));
     733        8630 :         } else if (strMode == "pubkeyoperator") {
     734           0 :             if (!strFilter.empty() && strOutpoint.find(strFilter) == std::string::npos) return;
     735           0 :             obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.ToString());
     736        8630 :         } else if (strMode == "status") {
     737        8630 :             std::string strStatus = dmnToStatus(dmn);
     738        8630 :             if (!strFilter.empty() && strStatus.find(strFilter) == std::string::npos &&
     739           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     740           0 :                 return;
     741        8630 :             obj.pushKV(strOutpoint, strStatus);
     742        8630 :         } else if (strMode == "votingaddress") {
     743           0 :             if (!strFilter.empty() && strOutpoint.find(strFilter) == std::string::npos) return;
     744           0 :             obj.pushKV(strOutpoint, EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)));
     745           0 :         }
     746       16442 :     });
     747             : 
     748        1601 :     return obj;
     749        1601 : },
     750             :     };
     751           0 : }
     752             : 
     753        6274 : static RPCHelpMan masternodelist()
     754             : {
     755        6274 :     return masternodelist_helper(false);
     756             : }
     757             : 
     758        7621 : static RPCHelpMan masternodelist_composite()
     759             : {
     760        7621 :     return masternodelist_helper(true);
     761             : }
     762             : 
     763             : #ifdef ENABLE_WALLET
     764        1436 : Span<const CRPCCommand> GetWalletMasternodeRPCCommands()
     765             : {
     766        2872 :     static const CRPCCommand commands[]{
     767        1436 :         {"dash", &masternode_outputs},
     768             :     };
     769        1436 :     return commands;
     770           0 : }
     771             : #endif // ENABLE_WALLET
     772             : 
     773        3201 : void RegisterMasternodeRPCCommands(CRPCTable &t)
     774             : {
     775       27753 :     static const CRPCCommand commands[]{
     776        3069 :         {"dash", &masternode_help},
     777        3069 :         {"dash", &masternodelist_composite},
     778        3069 :         {"dash", &masternodelist},
     779        3069 :         {"dash", &masternode_connect},
     780        3069 :         {"dash", &masternode_count},
     781        3069 :         {"dash", &masternode_status},
     782        3069 :         {"dash", &masternode_payments},
     783        3069 :         {"dash", &masternode_winners},
     784             :     };
     785       28809 :     for (const auto& command : commands) {
     786       25608 :         t.appendCommand(command.name, &command);
     787             :     }
     788        3201 : }

Generated by: LCOV version 1.16