LCOV - code coverage report
Current view: top level - src/rpc - masternode.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 150 537 27.9 %
Date: 2026-06-25 07:23:51 Functions: 13 29 44.8 %

          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          92 : static RPCHelpMan masternode_connect()
      48             : {
      49         184 :     return RPCHelpMan{"masternode connect",
      50          92 :         "Connect to given masternode\n",
      51         276 :         {
      52          92 :             {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the masternode to connect"},
      53          92 :             {"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol"},
      54             :         },
      55          92 :         RPCResult{
      56          92 :             RPCResult::Type::STR, "status", "Returns 'successfully connected' if successful"
      57             :         },
      58          92 :         RPCExamples{""},
      59          92 :         [&](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          92 : static RPCHelpMan masternode_count()
      89             : {
      90         184 :     return RPCHelpMan{"masternode count",
      91          92 :         "Get information about number of masternodes.\n",
      92          92 :         {},
      93          92 :         RPCResult{
      94          92 :             RPCResult::Type::OBJ, "", "",
      95         368 :             {
      96          92 :                 {RPCResult::Type::NUM, "total", "Total number of Masternodes"},
      97          92 :                 {RPCResult::Type::NUM, "enabled", "Number of enabled Masternodes"},
      98         184 :                 {RPCResult::Type::OBJ, "details", "Breakdown of masternodes by type",
      99         184 :                     {{RPCResult::Type::OBJ, "", "",
     100         276 :                     {
     101         184 :                         {RPCResult::Type::OBJ, "regular", "Details for regular masternodes",
     102         276 :                             {
     103          92 :                                 {RPCResult::Type::NUM, "total", "Total number of regular Masternodes"},
     104          92 :                                 {RPCResult::Type::NUM, "enabled", "Number of enabled regular Masternodes"}
     105             :                         }},
     106         184 :                         {RPCResult::Type::OBJ, "evo", "Details for Evo nodes",
     107         276 :                             {
     108          92 :                                 {RPCResult::Type::NUM, "total", "Total number of Evo nodes"},
     109          92 :                                 {RPCResult::Type::NUM, "enabled", "Number of enabled Evo nodes"}
     110             :                         }},
     111             :                     }},
     112             :                     }}
     113             :             }
     114             :         },
     115          92 :         RPCExamples{""},
     116          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     117             : {
     118           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     119             : 
     120           0 :     const auto counts{node.dmnman->GetListAtChainTip().GetCounts()};
     121             : 
     122           0 :     UniValue obj(UniValue::VOBJ);
     123           0 :     obj.pushKV("total", counts.total());
     124           0 :     obj.pushKV("enabled", counts.enabled());
     125             : 
     126           0 :     UniValue evoObj(UniValue::VOBJ);
     127           0 :     evoObj.pushKV("total", counts.m_total_evo);
     128           0 :     evoObj.pushKV("enabled", counts.m_valid_evo);
     129             : 
     130           0 :     UniValue regularObj(UniValue::VOBJ);
     131           0 :     regularObj.pushKV("total", counts.m_total_mn);
     132           0 :     regularObj.pushKV("enabled", counts.m_valid_mn);
     133             : 
     134           0 :     UniValue detailedObj(UniValue::VOBJ);
     135           0 :     detailedObj.pushKV("regular", regularObj);
     136           0 :     detailedObj.pushKV("evo", evoObj);
     137           0 :     obj.pushKV("detailed", detailedObj);
     138             : 
     139           0 :     return obj;
     140           0 : },
     141             :     };
     142           0 : }
     143             : 
     144             : #ifdef ENABLE_WALLET
     145           0 : static RPCHelpMan masternode_outputs()
     146             : {
     147           0 :     return RPCHelpMan{"masternode outputs",
     148           0 :         "Print masternode compatible outputs\n",
     149           0 :         {},
     150           0 :         RPCResult {
     151           0 :             RPCResult::Type::ARR, "", "A list of outpoints that can be/are used as masternode collaterals",
     152           0 :             {
     153           0 :                 {RPCResult::Type::STR, "", "A (potential) masternode collateral"},
     154             :             }},
     155           0 :         RPCExamples{""},
     156           0 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     157             : {
     158           0 :     const std::shared_ptr<const CWallet> wallet = GetWalletForJSONRPCRequest(request);
     159           0 :     if (!wallet) return UniValue::VNULL;
     160             : 
     161             :     // Find possible candidates
     162           0 :     CCoinControl coin_control(CoinType::ONLY_MASTERNODE_COLLATERAL);
     163             : 
     164           0 :     UniValue outputsArr(UniValue::VARR);
     165           0 :     for (const auto& out : WITH_LOCK(wallet->cs_wallet, return AvailableCoinsListUnspent(*wallet, &coin_control).all())) {
     166           0 :         outputsArr.push_back(out.outpoint.ToStringShort());
     167             :     }
     168             : 
     169           0 :     return outputsArr;
     170           0 : },
     171             :     };
     172           0 : }
     173             : 
     174             : #endif // ENABLE_WALLET
     175             : 
     176          92 : static RPCHelpMan masternode_status()
     177             : {
     178         184 :     return RPCHelpMan{"masternode status",
     179          92 :         "Print masternode status information\n",
     180          92 :         {},
     181          92 :         RPCResult{
     182          92 :             RPCResult::Type::OBJ, "", "",
     183         920 :             {
     184          92 :                 GetRpcResult("outpoint"),
     185          92 :                 GetRpcResult("service", /*optional=*/true),
     186          92 :                 GetRpcResult("proTxHash", /*optional=*/true),
     187          92 :                 GetRpcResult("type_str", /*optional=*/true, /*override_name=*/"type"),
     188          92 :                 GetRpcResult("collateralHash", /*optional=*/true),
     189          92 :                 GetRpcResult("collateralIndex", /*optional=*/true),
     190          92 :                 CDeterministicMNState::GetJsonHelp(/*key=*/"dmnState", /*optional=*/true),
     191          92 :                 {RPCResult::Type::STR, "state", "Masternode state (human-readable string)"},
     192          92 :                 {RPCResult::Type::STR, "status", "Masternode status (human-readable string, based on current state)"},
     193             :             }
     194             :         },
     195          92 :         RPCExamples{""},
     196          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     197             : {
     198           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     199             : 
     200           0 :     if (!node.active_ctx) {
     201           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "This node does not run an active masternode.");
     202             :     }
     203           0 :     const auto& mn_activeman{*node.active_ctx->nodeman};
     204             : 
     205           0 :     UniValue mnObj(UniValue::VOBJ);
     206             :     // keep compatibility with legacy status for now (TODO: get deprecated/removed later)
     207           0 :     mnObj.pushKV("outpoint", mn_activeman.GetOutPoint().ToStringShort());
     208           0 :     mnObj.pushKV("service", mn_activeman.GetService().ToStringAddrPort());
     209           0 :     auto dmn = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip().GetMN(mn_activeman.GetProTxHash());
     210           0 :     if (dmn) {
     211           0 :         mnObj.pushKV("proTxHash", dmn->proTxHash.ToString());
     212           0 :         mnObj.pushKV("type", std::string(GetMnType(dmn->nType).description));
     213           0 :         mnObj.pushKV("collateralHash", dmn->collateralOutpoint.hash.ToString());
     214           0 :         mnObj.pushKV("collateralIndex", dmn->collateralOutpoint.n);
     215           0 :         mnObj.pushKV("dmnState", dmn->pdmnState->ToJson(dmn->nType));
     216           0 :     }
     217           0 :     mnObj.pushKV("state", mn_activeman.GetStateString());
     218           0 :     mnObj.pushKV("status", mn_activeman.GetStatus());
     219             : 
     220           0 :     return mnObj;
     221           0 : },
     222             :     };
     223           0 : }
     224             : 
     225           0 : static std::string GetRequiredPaymentsString(governance::SuperblockManager& superblocks,
     226             :                                              const CDeterministicMNList& tip_mn_list, int nBlockHeight,
     227             :                                              const CDeterministicMNCPtr& payee)
     228             : {
     229           0 :     std::string strPayments = "Unknown";
     230           0 :     if (payee) {
     231           0 :         CTxDestination dest;
     232           0 :         if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) {
     233           0 :             NONFATAL_UNREACHABLE();
     234             :         }
     235           0 :         strPayments = EncodeDestination(dest);
     236           0 :         if (payee->nOperatorReward != 0 && payee->pdmnState->scriptOperatorPayout != CScript()) {
     237           0 :             if (!ExtractDestination(payee->pdmnState->scriptOperatorPayout, dest)) {
     238           0 :                 NONFATAL_UNREACHABLE();
     239             :             }
     240           0 :             strPayments += ", " + EncodeDestination(dest);
     241           0 :         }
     242           0 :     }
     243           0 :     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           0 :     return strPayments;
     261           0 : }
     262             : 
     263          92 : static RPCHelpMan masternode_winners()
     264             : {
     265         184 :     return RPCHelpMan{"masternode winners",
     266          92 :         "Print list of masternode winners\n",
     267         276 :         {
     268          92 :             {"count", RPCArg::Type::NUM, RPCArg::Default{10}, "number of last winners to return"},
     269          92 :             {"filter", RPCArg::Type::STR, RPCArg::Default{""}, "filter for returned winners"},
     270             :         },
     271          92 :         RPCResult{
     272          92 :             RPCResult::Type::OBJ_DYN, "", "Keys are block heights (as strings); values describe the payees for that height",
     273         184 :             {
     274          92 :                 {RPCResult::Type::STR, "payee", "Payee for the height"}
     275             :             }
     276             :         },
     277          92 :         RPCExamples{""},
     278          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     279             : {
     280           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     281           0 :     const ChainstateManager& chainman = EnsureChainman(node);
     282           0 :     const CBlockIndex* pindexTip{nullptr};
     283             :     {
     284           0 :         LOCK(::cs_main);
     285           0 :         pindexTip = chainman.ActiveChain().Tip();
     286           0 :         if (!pindexTip) return UniValue::VNULL;
     287           0 :     }
     288             : 
     289           0 :     int nCount = 10;
     290           0 :     std::string strFilter;
     291             : 
     292           0 :     if (!request.params[0].isNull()) {
     293           0 :         nCount = request.params[0].getInt<int>();
     294           0 :     }
     295             : 
     296           0 :     if (!request.params[1].isNull()) {
     297           0 :         strFilter = request.params[1].get_str();
     298           0 :     }
     299             : 
     300           0 :     UniValue obj(UniValue::VOBJ);
     301             : 
     302           0 :     int nChainTipHeight = pindexTip->nHeight;
     303           0 :     int nStartHeight = std::max(nChainTipHeight - nCount, 1);
     304             : 
     305           0 :     const auto tip_mn_list = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip();
     306           0 :     for (int h = nStartHeight; h <= nChainTipHeight; h++) {
     307           0 :         const CBlockIndex* pIndex = pindexTip->GetAncestor(h - 1);
     308           0 :         auto payee = node.dmnman->GetListForBlock(pIndex).GetMNPayee(pIndex);
     309           0 :         if (payee) {
     310           0 :             std::string strPayments = GetRequiredPaymentsString(*CHECK_NONFATAL(node.chain_helper)->superblocks,
     311           0 :                                                                 tip_mn_list, h, payee);
     312           0 :             if (!strFilter.empty() && strPayments.find(strFilter) == std::string::npos) continue;
     313           0 :             obj.pushKV(strprintf("%d", h), strPayments);
     314           0 :         }
     315           0 :     }
     316             : 
     317           0 :     auto projection = node.dmnman->GetListForBlock(pindexTip).GetProjectedMNPayees(pindexTip, /*nCount=*/20);
     318           0 :     for (size_t i = 0; i < projection.size(); i++) {
     319           0 :         int h = nChainTipHeight + 1 + i;
     320           0 :         std::string strPayments = GetRequiredPaymentsString(*node.chain_helper->superblocks, tip_mn_list, h, projection[i]);
     321           0 :         if (!strFilter.empty() && strPayments.find(strFilter) == std::string::npos) continue;
     322           0 :         obj.pushKV(strprintf("%d", h), strPayments);
     323           0 :     }
     324             : 
     325           0 :     return obj;
     326           0 : },
     327             :     };
     328           0 : }
     329             : 
     330          92 : static RPCHelpMan masternode_payments()
     331             : {
     332         184 :     return RPCHelpMan{"masternode payments",
     333          92 :         "\nReturns an array of deterministic masternodes and their payments for the specified block\n",
     334         276 :         {
     335          92 :             {"blockhash", RPCArg::Type::STR_HEX, RPCArg::DefaultHint{"tip"}, "The hash of the starting block"},
     336          92 :             {"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          92 :         RPCResult {
     339          92 :             RPCResult::Type::ARR, "", "Blocks",
     340         184 :             {
     341         184 :                 {RPCResult::Type::OBJ, "", "",
     342         460 :                 {
     343          92 :                     {RPCResult::Type::NUM, "height", "The height of the block"},
     344          92 :                     {RPCResult::Type::STR_HEX, "blockhash", "The hash of the block"},
     345          92 :                     {RPCResult::Type::NUM, "amount", "Amount received in this block by all masternodes"},
     346         184 :                     {RPCResult::Type::ARR, "masternodes", "Masternodes that received payments in this block",
     347         368 :                     {
     348          92 :                         {RPCResult::Type::STR_HEX, "proTxHash", "The hash of the corresponding ProRegTx"},
     349          92 :                         {RPCResult::Type::NUM, "amount", "Amount received by this masternode"},
     350         184 :                         {RPCResult::Type::ARR, "payees", "Payees who received a share of this payment",
     351         368 :                         {
     352          92 :                             {RPCResult::Type::STR, "address", "Payee address"},
     353          92 :                             {RPCResult::Type::STR_HEX, "script", "Payee scriptPubKey"},
     354          92 :                             {RPCResult::Type::NUM, "amount", "Amount received by this payee"},
     355             :                         }},
     356             :                     }},
     357             :                 }},
     358             :             },
     359             :         },
     360          92 :         RPCExamples{""},
     361          92 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     362             : {
     363           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     364           0 :     const ChainstateManager& chainman = EnsureChainman(node);
     365             : 
     366           0 :     const CBlockIndex* pindex{nullptr};
     367             : 
     368           0 :     if (g_txindex) {
     369           0 :         g_txindex->BlockUntilSyncedToCurrentChain();
     370           0 :     }
     371             : 
     372           0 :     if (request.params[0].isNull()) {
     373           0 :         LOCK(::cs_main);
     374           0 :         pindex = chainman.ActiveChain().Tip();
     375           0 :     } else {
     376           0 :         LOCK(::cs_main);
     377           0 :         uint256 blockHash(ParseHashV(request.params[0], "blockhash"));
     378           0 :         pindex = chainman.m_blockman.LookupBlockIndex(blockHash);
     379           0 :         if (pindex == nullptr) {
     380           0 :             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
     381             :         }
     382           0 :     }
     383             : 
     384           0 :     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           0 :     std::vector<UniValue> vecPayments;
     388             : 
     389           0 :     CHECK_NONFATAL(node.chain_helper);
     390           0 :     CHECK_NONFATAL(node.dmnman);
     391           0 :     while (vecPayments.size() < uint64_t(std::abs(nCount)) && pindex != nullptr) {
     392           0 :         CBlock block;
     393           0 :         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           0 :         CAmount nBlockFees{0};
     400           0 :         const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
     401           0 :         for (const auto& tx : block.vtx) {
     402           0 :             if (tx->IsCoinBase()) {
     403           0 :                 continue;
     404             :             }
     405           0 :             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           0 :             CAmount nValueIn{0};
     413           0 :             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           0 :             nBlockFees += nValueIn - tx->GetValueOut();
     419             :         }
     420             : 
     421           0 :         std::vector<CTxOut> voutMasternodePayments, voutDummy;
     422           0 :         CMutableTransaction dummyTx;
     423           0 :         CAmount blockSubsidy = GetBlockSubsidy(pindex, Params().GetConsensus());
     424           0 :         node.chain_helper->mn_payments->FillBlockPayments(dummyTx, pindex->pprev, blockSubsidy, nBlockFees, voutMasternodePayments, voutDummy);
     425             : 
     426           0 :         UniValue blockObj(UniValue::VOBJ);
     427           0 :         CAmount payedPerBlock{0};
     428             : 
     429           0 :         UniValue masternodeArr(UniValue::VARR);
     430           0 :         UniValue protxObj(UniValue::VOBJ);
     431           0 :         UniValue payeesArr(UniValue::VARR);
     432           0 :         CAmount payedPerMasternode{0};
     433             : 
     434           0 :         for (const auto& txout : voutMasternodePayments) {
     435           0 :             UniValue obj(UniValue::VOBJ);
     436           0 :             CTxDestination dest;
     437           0 :             ExtractDestination(txout.scriptPubKey, dest);
     438           0 :             obj.pushKV("address", EncodeDestination(dest));
     439           0 :             obj.pushKV("script", HexStr(txout.scriptPubKey));
     440           0 :             obj.pushKV("amount", txout.nValue);
     441           0 :             payedPerMasternode += txout.nValue;
     442           0 :             payeesArr.push_back(obj);
     443           0 :         }
     444             : 
     445             :         // NOTE: we use _previous_ block to find a payee for the current one
     446           0 :         const auto dmnPayee = node.dmnman->GetListForBlock(pindex->pprev).GetMNPayee(pindex->pprev);
     447           0 :         protxObj.pushKV("proTxHash", dmnPayee == nullptr ? "" : dmnPayee->proTxHash.ToString());
     448           0 :         protxObj.pushKV("amount", payedPerMasternode);
     449           0 :         protxObj.pushKV("payees", payeesArr);
     450           0 :         payedPerBlock += payedPerMasternode;
     451           0 :         masternodeArr.push_back(protxObj);
     452             : 
     453           0 :         blockObj.pushKV("height", pindex->nHeight);
     454           0 :         blockObj.pushKV("blockhash", pindex->GetBlockHash().ToString());
     455           0 :         blockObj.pushKV("amount", payedPerBlock);
     456           0 :         blockObj.pushKV("masternodes", masternodeArr);
     457           0 :         vecPayments.push_back(blockObj);
     458             : 
     459           0 :         if (nCount > 0) {
     460           0 :             LOCK(::cs_main);
     461           0 :             pindex = chainman.ActiveChain().Next(pindex);
     462           0 :         } else {
     463           0 :             pindex = pindex->pprev;
     464             :         }
     465           0 :     }
     466             : 
     467           0 :     if (nCount < 0) {
     468           0 :         std::reverse(vecPayments.begin(), vecPayments.end());
     469           0 :     }
     470             : 
     471           0 :     UniValue paymentsArr(UniValue::VARR);
     472           0 :     for (const auto& payment : vecPayments) {
     473           0 :         paymentsArr.push_back(payment);
     474             :     }
     475             : 
     476           0 :     return paymentsArr;
     477           0 : },
     478             :     };
     479           0 : }
     480             : 
     481          92 : static RPCHelpMan masternode_help()
     482             : {
     483         184 :     return RPCHelpMan{"masternode",
     484          92 :         "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         184 :         {
     497          92 :             {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
     498             :         },
     499          92 :         RPCResult{RPCResult::Type::NONE, "", ""},
     500          92 :         RPCExamples{""},
     501          92 :         [&](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         184 : 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         368 :     return RPCHelpMan{is_composite ? "masternode list" : "masternodelist",
     513         184 :         "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         552 :         {
     533         184 :             {"mode", RPCArg::Type::STR, RPCArg::DefaultHint{"json"}, "The mode to run list in"},
     534         184 :             {"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         184 :         RPCResult{
     537        2208 :             RPCResult::Type::OBJ, "<outpoint>", "", {
     538         184 :                 RPCResult{"for mode = addr", RPCResult::Type::STR, "<address>", "Flattened list of all addresses registered to masternode"},
     539         184 :                 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         184 :                 RPCResult{"for mode = info", RPCResult::Type::STR, "<info>", "Flattened list of a masternode's status, payee address and service addresses"},
     541        3312 :                 RPCResult{"for mode = evo, json or recent", RPCResult::Type::OBJ, "", "", {
     542         184 :                     GetRpcResult("proTxHash"),
     543         184 :                     GetRpcResult("service", /*optional=*/true, /*override_name=*/"address"),
     544         184 :                     GetRpcResult("addresses"),
     545         184 :                     GetRpcResult("payoutAddress", /*optional=*/false, /*override_name=*/"payee"),
     546         184 :                     {RPCResult::Type::STR, "status", "Masternode status (human-readable string)"},
     547         184 :                     GetRpcResult("type_str", /*optional=*/false, /*override_name=*/"type"),
     548         184 :                     GetRpcResult("platformNodeID", /*optional=*/true),
     549         184 :                     GetRpcResult("platformP2PPort", /*optional=*/true),
     550         184 :                     GetRpcResult("platformHTTPPort", /*optional=*/true),
     551         184 :                     GetRpcResult("PoSePenalty", /*optional=*/false, /*override_name=*/"pospenaltyscore"),
     552         184 :                     GetRpcResult("consecutivePayments"),
     553         184 :                     {RPCResult::Type::NUM, "lastpaidtime", "Timestamp of block the masternode was last paid"},
     554         184 :                     GetRpcResult("lastPaidHeight", /*optional=*/false, /*override_name=*/"lastpaidblock"),
     555         184 :                     GetRpcResult("ownerAddress", /*optional=*/false, /*override_name=*/"owneraddress"),
     556         184 :                     GetRpcResult("votingAddress", /*optional=*/false, /*override_name=*/"votingaddress"),
     557         184 :                     GetRpcResult("collateralAddress", /*optional=*/false, /*override_name=*/"collateraladdress"),
     558         184 :                     GetRpcResult("pubKeyOperator", /*optional=*/false, /*override_name=*/"pubkeyoperator"),
     559             :                 }},
     560         184 :                 RPCResult{"for mode = lastpaidblock", RPCResult::Type::NUM, "<height>", "Height masternode was last paid"},
     561         184 :                 RPCResult{"for mode = lastpaidtime", RPCResult::Type::NUM, "<time>", "Timestamp of block the masternode was last paid"},
     562         184 :                 RPCResult{"for mode = payee", RPCResult::Type::STR, "<addr>", "Dash address used for masternode reward payments"},
     563         184 :                 RPCResult{"for mode = owneraddress", RPCResult::Type::STR, "<addr>", "Dash address used for payee updates and proposal voting"},
     564         184 :                 RPCResult{"for mode = pubkeyoperator", RPCResult::Type::STR, "<addr>", "BLS public key used for operator signing"},
     565         184 :                 RPCResult{"for mode = status", RPCResult::Type::STR, "<status>", "Masternode status (human-readable string)"},
     566         184 :                 RPCResult{"for mode = votingaddress", RPCResult::Type::STR, "<addr>", "Dash address used for voting"},
     567             :             }
     568             :         },
     569         184 :         RPCExamples{""},
     570         184 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     571             : {
     572           0 :     std::string strMode = "json";
     573           0 :     std::string strFilter;
     574             : 
     575           0 :     if (!request.params[0].isNull()) strMode = request.params[0].get_str();
     576           0 :     if (!request.params[1].isNull()) strFilter = request.params[1].get_str();
     577             : 
     578           0 :     strMode = ToLower(strMode);
     579             : 
     580             :     if (
     581           0 :                 strMode != "addr" && strMode != "full" && strMode != "info" && strMode != "json" &&
     582           0 :                 strMode != "owneraddress" && strMode != "votingaddress" &&
     583           0 :                 strMode != "lastpaidtime" && strMode != "lastpaidblock" &&
     584           0 :                 strMode != "payee" && strMode != "pubkeyoperator" &&
     585           0 :                 strMode != "status" && strMode != "recent" && strMode != "evo")
     586             :     {
     587           0 :         throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("strMode %s not found", strMode));
     588             :     }
     589             : 
     590           0 :     const NodeContext& node = EnsureAnyNodeContext(request.context);
     591           0 :     const ChainstateManager& chainman = EnsureChainman(node);
     592             : 
     593           0 :     UniValue obj(UniValue::VOBJ);
     594             : 
     595           0 :     const auto mnList = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip();
     596           0 :     const auto dmnToStatus = [&](const auto& dmn) {
     597           0 :         if (dmn.pdmnState->IsBanned()) {
     598           0 :             return "POSE_BANNED";
     599             :         }
     600           0 :         return "ENABLED";
     601           0 :     };
     602           0 :     const auto dmnToLastPaidTime = [&](const auto& dmn) {
     603           0 :         if (dmn.pdmnState->nLastPaidHeight == 0) {
     604           0 :             return (int)0;
     605             :         }
     606             : 
     607           0 :         LOCK(::cs_main);
     608           0 :         const CBlockIndex* pindex = chainman.ActiveChain()[dmn.pdmnState->nLastPaidHeight];
     609           0 :         return (int)pindex->nTime;
     610           0 :     };
     611             : 
     612           0 :     const bool showRecentMnsOnly = strMode == "recent";
     613           0 :     const bool showEvoOnly = strMode == "evo";
     614           0 :     const int tipHeight = WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip()->nHeight);
     615           0 :     mnList.ForEachMN(/*onlyValid=*/false, [&](const auto& dmn) {
     616           0 :         if (showRecentMnsOnly && dmn.pdmnState->IsBanned()) {
     617           0 :             if (tipHeight - dmn.pdmnState->GetBannedHeight() > Params().GetConsensus().nSuperblockCycle) {
     618           0 :                 return;
     619             :             }
     620           0 :         }
     621           0 :         if (showEvoOnly && dmn.nType != MnType::Evo) {
     622           0 :             return;
     623             :         }
     624             : 
     625           0 :         std::string strOutpoint = dmn.collateralOutpoint.ToStringShort();
     626           0 :         Coin coin;
     627           0 :         std::string collateralAddressStr = "UNKNOWN";
     628           0 :         if (GetUTXOCoin(chainman.ActiveChainstate(), dmn.collateralOutpoint, coin)) {
     629           0 :             CTxDestination collateralDest;
     630           0 :             if (ExtractDestination(coin.out.scriptPubKey, collateralDest)) {
     631           0 :                 collateralAddressStr = EncodeDestination(collateralDest);
     632           0 :             }
     633           0 :         }
     634             : 
     635           0 :         CScript payeeScript = dmn.pdmnState->scriptPayout;
     636           0 :         CTxDestination payeeDest;
     637           0 :         std::string payeeStr = "UNKNOWN";
     638           0 :         if (ExtractDestination(payeeScript, payeeDest)) {
     639           0 :             payeeStr = EncodeDestination(payeeDest);
     640           0 :         }
     641             : 
     642           0 :         std::string strAddress{};
     643           0 :         if (strMode == "addr" || strMode == "full" || strMode == "info" || strMode == "json" || strMode == "recent" ||
     644           0 :             strMode == "evo") {
     645           0 :             for (const auto& entry : dmn.pdmnState->netInfo->GetEntries()) {
     646           0 :                 strAddress += entry.ToStringAddrPort() + " ";
     647             :             }
     648           0 :             if (!strAddress.empty()) strAddress.pop_back(); // Remove trailing space
     649           0 :         }
     650             : 
     651           0 :         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           0 :         } 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           0 :         } 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           0 :         } else if (strMode == "json" || strMode == "recent" || strMode == "evo") {
     679           0 :             std::string strInfo = strprintf("%s %s %s %s %d %d %d %s %s %s %s",
     680           0 :                                     dmn.proTxHash.ToString(),
     681             :                                     strAddress,
     682             :                                     payeeStr,
     683           0 :                                     dmnToStatus(dmn),
     684           0 :                                     dmn.pdmnState->nPoSePenalty,
     685           0 :                                     dmnToLastPaidTime(dmn),
     686           0 :                                     dmn.pdmnState->nLastPaidHeight,
     687           0 :                                     EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)),
     688           0 :                                     EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)),
     689             :                                     collateralAddressStr,
     690           0 :                                     dmn.pdmnState->pubKeyOperator.ToString());
     691           0 :             if (!strFilter.empty() && strInfo.find(strFilter) == std::string::npos &&
     692           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     693           0 :                 return;
     694           0 :             UniValue objMN(UniValue::VOBJ);
     695           0 :             objMN.pushKV("proTxHash", dmn.proTxHash.ToString());
     696           0 :             if (IsDeprecatedRPCEnabled("service")) {
     697           0 :                 objMN.pushKV("address", dmn.pdmnState->netInfo->GetPrimary().ToStringAddrPort());
     698           0 :             }
     699           0 :             objMN.pushKV("addresses", GetNetInfoWithLegacyFields(*dmn.pdmnState, dmn.nType));
     700           0 :             objMN.pushKV("payee", payeeStr);
     701           0 :             objMN.pushKV("status", dmnToStatus(dmn));
     702           0 :             objMN.pushKV("type", std::string(GetMnType(dmn.nType).description));
     703           0 :             if (dmn.nType == MnType::Evo) {
     704           0 :                 objMN.pushKV("platformNodeID", dmn.pdmnState->platformNodeID.ToString());
     705           0 :                 if (IsDeprecatedRPCEnabled("service")) {
     706           0 :                     objMN.pushKV("platformP2PPort", GetPlatformPort</*is_p2p=*/true>(*dmn.pdmnState));
     707           0 :                     objMN.pushKV("platformHTTPPort", GetPlatformPort</*is_p2p=*/false>(*dmn.pdmnState));
     708           0 :                 }
     709           0 :             }
     710           0 :             objMN.pushKV("pospenaltyscore", dmn.pdmnState->nPoSePenalty);
     711           0 :             objMN.pushKV("consecutivePayments", dmn.pdmnState->nConsecutivePayments);
     712           0 :             objMN.pushKV("lastpaidtime", dmnToLastPaidTime(dmn));
     713           0 :             objMN.pushKV("lastpaidblock", dmn.pdmnState->nLastPaidHeight);
     714           0 :             objMN.pushKV("owneraddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)));
     715           0 :             objMN.pushKV("votingaddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)));
     716           0 :             objMN.pushKV("collateraladdress", collateralAddressStr);
     717           0 :             objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.ToString());
     718           0 :             obj.pushKV(strOutpoint, objMN);
     719           0 :         } 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           0 :         } else if (strMode == "lastpaidtime") {
     723           0 :             if (!strFilter.empty() && strOutpoint.find(strFilter) == std::string::npos) return;
     724           0 :             obj.pushKV(strOutpoint, dmnToLastPaidTime(dmn));
     725           0 :         } 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           0 :         } 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           0 :         } 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           0 :         } else if (strMode == "status") {
     737           0 :             std::string strStatus = dmnToStatus(dmn);
     738           0 :             if (!strFilter.empty() && strStatus.find(strFilter) == std::string::npos &&
     739           0 :                 strOutpoint.find(strFilter) == std::string::npos)
     740           0 :                 return;
     741           0 :             obj.pushKV(strOutpoint, strStatus);
     742           0 :         } 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           0 :     });
     747             : 
     748           0 :     return obj;
     749           0 : },
     750             :     };
     751           0 : }
     752             : 
     753          92 : static RPCHelpMan masternodelist()
     754             : {
     755          92 :     return masternodelist_helper(false);
     756             : }
     757             : 
     758          92 : static RPCHelpMan masternodelist_composite()
     759             : {
     760          92 :     return masternodelist_helper(true);
     761             : }
     762             : 
     763             : #ifdef ENABLE_WALLET
     764           0 : Span<const CRPCCommand> GetWalletMasternodeRPCCommands()
     765             : {
     766           0 :     static const CRPCCommand commands[]{
     767           0 :         {"dash", &masternode_outputs},
     768             :     };
     769           0 :     return commands;
     770           0 : }
     771             : #endif // ENABLE_WALLET
     772             : 
     773         178 : void RegisterMasternodeRPCCommands(CRPCTable &t)
     774             : {
     775         546 :     static const CRPCCommand commands[]{
     776          46 :         {"dash", &masternode_help},
     777          46 :         {"dash", &masternodelist_composite},
     778          46 :         {"dash", &masternodelist},
     779          46 :         {"dash", &masternode_connect},
     780          46 :         {"dash", &masternode_count},
     781          46 :         {"dash", &masternode_status},
     782          46 :         {"dash", &masternode_payments},
     783          46 :         {"dash", &masternode_winners},
     784             :     };
     785        1602 :     for (const auto& command : commands) {
     786        1424 :         t.appendCommand(command.name, &command);
     787             :     }
     788         178 : }

Generated by: LCOV version 1.16