dash/src/rpc/masternode.cpp

703 lines
28 KiB
C++

// Copyright (c) 2014-2021 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
#include <evo/deterministicmns.h>
#include <governance/classes.h>
#include <index/txindex.h>
#include <node/context.h>
#include <masternode/node.h>
#include <masternode/payments.h>
#include <net.h>
#include <netbase.h>
#include <rpc/blockchain.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <univalue.h>
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/rpcwallet.h>
#ifdef ENABLE_WALLET
#include <wallet/wallet.h>
#endif // ENABLE_WALLET
#include <fstream>
#include <iomanip>
static UniValue masternodelist(const JSONRPCRequest& request);
static void masternode_list_help(const JSONRPCRequest& request)
{
RPCHelpMan{"masternodelist",
"Get a list of masternodes in different modes. This call is identical to 'masternode list' call.\n"
"Available modes:\n"
" addr - Print ip address associated with a masternode (can be additionally filtered, partial match)\n"
" full - Print info in format 'status payee lastpaidtime lastpaidblock IP'\n"
" (can be additionally filtered, partial match)\n"
" info - Print info in format 'status payee IP'\n"
" (can be additionally filtered, partial match)\n"
" json - Print info in JSON format (can be additionally filtered, partial match)\n"
" lastpaidblock - Print the last block height a node was paid on the network\n"
" lastpaidtime - Print the last time a node was paid on the network\n"
" owneraddress - Print the masternode owner Dash address\n"
" payee - Print the masternode payout Dash address (can be additionally filtered,\n"
" partial match)\n"
" pubKeyOperator - Print the masternode operator public key\n"
" status - Print masternode status: ENABLED / POSE_BANNED\n"
" (can be additionally filtered, partial match)\n"
" votingaddress - Print the masternode voting Dash address\n",
{
{"mode", RPCArg::Type::STR, /* default */ "json", "The mode to run list in"},
{"filter", RPCArg::Type::STR, /* default */ "", "Filter results. Partial match by outpoint by default in all modes, additional matches in some modes are also available"},
},
RPCResults{},
RPCExamples{""},
}.Check(request);
}
static void masternode_connect_help(const JSONRPCRequest& request)
{
RPCHelpMan{"masternode connect",
"Connect to given masternode\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the masternode to connect"},
},
RPCResults{},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_connect(const JSONRPCRequest& request)
{
masternode_connect_help(request);
std::string strAddress = request.params[0].get_str();
CService addr;
if (!Lookup(strAddress.c_str(), addr, 0, false))
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Incorrect masternode address %s", strAddress));
// TODO: Pass CConnman instance somehow and don't use global variable.
NodeContext& node = EnsureNodeContext(request.context);
node.connman->OpenMasternodeConnection(CAddress(addr, NODE_NETWORK));
if (!node.connman->IsConnected(CAddress(addr, NODE_NETWORK), CConnman::AllNodes))
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to masternode %s", strAddress));
return "successfully connected";
}
static void masternode_count_help(const JSONRPCRequest& request)
{
RPCHelpMan{"masternode count",
"Get information about number of masternodes.\n",
{},
RPCResults{},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_count(const JSONRPCRequest& request)
{
masternode_count_help(request);
auto mnList = deterministicMNManager->GetListAtChainTip();
int total = mnList.GetAllMNsCount();
int enabled = mnList.GetValidMNsCount();
UniValue obj(UniValue::VOBJ);
obj.pushKV("total", total);
obj.pushKV("enabled", enabled);
return obj;
}
static UniValue GetNextMasternodeForPayment(int heightShift)
{
auto mnList = deterministicMNManager->GetListAtChainTip();
auto payees = mnList.GetProjectedMNPayees(heightShift);
if (payees.empty())
return "unknown";
auto payee = payees.back();
CScript payeeScript = payee->pdmnState->scriptPayout;
CTxDestination payeeDest;
ExtractDestination(payeeScript, payeeDest);
UniValue obj(UniValue::VOBJ);
obj.pushKV("height", mnList.GetHeight() + heightShift);
obj.pushKV("IP:port", payee->pdmnState->addr.ToString());
obj.pushKV("proTxHash", payee->proTxHash.ToString());
obj.pushKV("outpoint", payee->collateralOutpoint.ToStringShort());
obj.pushKV("payee", IsValidDestination(payeeDest) ? EncodeDestination(payeeDest) : "UNKNOWN");
return obj;
}
static void masternode_winner_help(const JSONRPCRequest& request)
{
if (!IsDeprecatedRPCEnabled("masternode_winner")) {
throw std::runtime_error("DEPRECATED: set -deprecatedrpc=masternode_winner to enable it");
}
RPCHelpMan{"masternode winner",
"Print info on next masternode winner to vote for\n",
{},
RPCResults{},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_winner(const JSONRPCRequest& request)
{
masternode_winner_help(request);
return GetNextMasternodeForPayment(10);
}
static void masternode_current_help(const JSONRPCRequest& request)
{
if (!IsDeprecatedRPCEnabled("masternode_current")) {
throw std::runtime_error("DEPRECATED: set -deprecatedrpc=masternode_current to enable it");
}
RPCHelpMan{"masternode current",
"Print info on current masternode winner to be paid the next block (calculated locally)\n",
{},
RPCResults{},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_current(const JSONRPCRequest& request)
{
masternode_current_help(request);
return GetNextMasternodeForPayment(1);
}
#ifdef ENABLE_WALLET
static void masternode_outputs_help(const JSONRPCRequest& request)
{
RPCHelpMan{"masternode outputs",
"Print masternode compatible outputs\n",
{},
RPCResults{},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_outputs(const JSONRPCRequest& request)
{
masternode_outputs_help(request);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
// Find possible candidates
std::vector<COutput> vPossibleCoins;
CCoinControl coin_control;
coin_control.nCoinType = CoinType::ONLY_MASTERNODE_COLLATERAL;
{
LOCK(pwallet->cs_wallet);
pwallet->AvailableCoins(vPossibleCoins, true, &coin_control);
}
UniValue obj(UniValue::VOBJ);
for (const auto& out : vPossibleCoins) {
obj.pushKV(out.tx->GetHash().ToString(), strprintf("%d", out.i));
}
return obj;
}
#endif // ENABLE_WALLET
static void masternode_status_help(const JSONRPCRequest& request)
{
RPCHelpMan{"masternode status",
"Print masternode status information\n",
{},
RPCResults{},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_status(const JSONRPCRequest& request)
{
masternode_status_help(request);
if (!fMasternodeMode)
throw JSONRPCError(RPC_INTERNAL_ERROR, "This is not a masternode");
UniValue mnObj(UniValue::VOBJ);
CDeterministicMNCPtr dmn;
{
LOCK(activeMasternodeInfoCs);
// keep compatibility with legacy status for now (might get deprecated/removed later)
mnObj.pushKV("outpoint", activeMasternodeInfo.outpoint.ToStringShort());
mnObj.pushKV("service", activeMasternodeInfo.service.ToString());
dmn = deterministicMNManager->GetListAtChainTip().GetMN(activeMasternodeInfo.proTxHash);
}
if (dmn) {
mnObj.pushKV("proTxHash", dmn->proTxHash.ToString());
mnObj.pushKV("collateralHash", dmn->collateralOutpoint.hash.ToString());
mnObj.pushKV("collateralIndex", (int)dmn->collateralOutpoint.n);
UniValue stateObj;
dmn->pdmnState->ToJson(stateObj);
mnObj.pushKV("dmnState", stateObj);
}
mnObj.pushKV("state", activeMasternodeManager->GetStateString());
mnObj.pushKV("status", activeMasternodeManager->GetStatus());
return mnObj;
}
static std::string GetRequiredPaymentsString(int nBlockHeight, const CDeterministicMNCPtr &payee)
{
std::string strPayments = "Unknown";
if (payee) {
CTxDestination dest;
if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) {
CHECK_NONFATAL(false);
}
strPayments = EncodeDestination(dest);
if (payee->nOperatorReward != 0 && payee->pdmnState->scriptOperatorPayout != CScript()) {
if (!ExtractDestination(payee->pdmnState->scriptOperatorPayout, dest)) {
CHECK_NONFATAL(false);
}
strPayments += ", " + EncodeDestination(dest);
}
}
if (CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) {
std::vector<CTxOut> voutSuperblock;
if (!CSuperblockManager::GetSuperblockPayments(nBlockHeight, voutSuperblock)) {
return strPayments + ", error";
}
std::string strSBPayees = "Unknown";
for (const auto& txout : voutSuperblock) {
CTxDestination dest;
ExtractDestination(txout.scriptPubKey, dest);
if (strSBPayees != "Unknown") {
strSBPayees += ", " + EncodeDestination(dest);
} else {
strSBPayees = EncodeDestination(dest);
}
}
strPayments += ", " + strSBPayees;
}
return strPayments;
}
static void masternode_winners_help(const JSONRPCRequest& request)
{
RPCHelpMan{"masternode winners",
"Print list of masternode winners\n",
{
{"count", RPCArg::Type::NUM, /* default */ "", "number of last winners to return"},
{"filter", RPCArg::Type::STR, /* default */ "", "filter for returned winners"},
},
RPCResults{},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_winners(const JSONRPCRequest& request)
{
masternode_winners_help(request);
const CBlockIndex* pindexTip{nullptr};
{
LOCK(cs_main);
pindexTip = ::ChainActive().Tip();
if (!pindexTip) return NullUniValue;
}
int nCount = 10;
std::string strFilter = "";
if (!request.params[0].isNull()) {
nCount = atoi(request.params[0].get_str());
}
if (!request.params[1].isNull()) {
strFilter = request.params[1].get_str();
}
UniValue obj(UniValue::VOBJ);
int nChainTipHeight = pindexTip->nHeight;
int nStartHeight = std::max(nChainTipHeight - nCount, 1);
for (int h = nStartHeight; h <= nChainTipHeight; h++) {
auto payee = deterministicMNManager->GetListForBlock(pindexTip->GetAncestor(h - 1)).GetMNPayee();
std::string strPayments = GetRequiredPaymentsString(h, payee);
if (strFilter != "" && strPayments.find(strFilter) == std::string::npos) continue;
obj.pushKV(strprintf("%d", h), strPayments);
}
auto projection = deterministicMNManager->GetListForBlock(pindexTip).GetProjectedMNPayees(20);
for (size_t i = 0; i < projection.size(); i++) {
int h = nChainTipHeight + 1 + i;
std::string strPayments = GetRequiredPaymentsString(h, projection[i]);
if (strFilter != "" && strPayments.find(strFilter) == std::string::npos) continue;
obj.pushKV(strprintf("%d", h), strPayments);
}
return obj;
}
static void masternode_payments_help(const JSONRPCRequest& request)
{
RPCHelpMan{"masternode payments",
"\nReturns an array of deterministic masternodes and their payments for the specified block\n",
{
{"blockhash", RPCArg::Type::STR_HEX, /* default */ "tip", "The hash of the starting block"},
{"count", RPCArg::Type::NUM, /* 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."},
},
RPCResult {
RPCResult::Type::ARR, "", "Blocks",
{
{RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::NUM, "height", "The height of the block"},
{RPCResult::Type::STR_HEX, "blockhash", "The hash of the block"},
{RPCResult::Type::NUM, "amount", "Amount received in this block by all masternodes"},
{RPCResult::Type::ARR, "masternodes", "Masternodes that received payments in this block",
{
{RPCResult::Type::STR_HEX, "proTxHash", "The hash of the corresponding ProRegTx"},
{RPCResult::Type::NUM, "amount", "Amount received by this masternode"},
{RPCResult::Type::ARR, "payees", "Payees who received a share of this payment",
{
{RPCResult::Type::STR, "address", "Payee address"},
{RPCResult::Type::STR_HEX, "script", "Payee scriptPubKey"},
{RPCResult::Type::NUM, "amount", "Amount received by this payee"},
}},
}},
}},
},
},
RPCExamples{""}
}.Check(request);
}
static UniValue masternode_payments(const JSONRPCRequest& request)
{
masternode_payments_help(request);
CBlockIndex* pindex{nullptr};
if (g_txindex) {
g_txindex->BlockUntilSyncedToCurrentChain();
}
if (request.params[0].isNull()) {
LOCK(cs_main);
pindex = ::ChainActive().Tip();
} else {
LOCK(cs_main);
uint256 blockHash = ParseHashV(request.params[0], "blockhash");
pindex = LookupBlockIndex(blockHash);
if (pindex == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
int64_t nCount = request.params.size() > 1 ? ParseInt64V(request.params[1], "count") : 1;
// A temporary vector which is used to sort results properly (there is no "reverse" in/for UniValue)
std::vector<UniValue> vecPayments;
while (vecPayments.size() < uint64_t(std::abs(nCount)) && pindex != nullptr) {
CBlock block;
if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
}
// Note: we have to actually calculate block reward from scratch instead of simply querying coinbase vout
// because miners might collect less coins than they potentially could and this would break our calculations.
CAmount nBlockFees{0};
NodeContext& node = EnsureNodeContext(request.context);
for (const auto& tx : block.vtx) {
if (tx->IsCoinBase()) {
continue;
}
CAmount nValueIn{0};
for (const auto& txin : tx->vin) {
uint256 blockHashTmp;
CTransactionRef txPrev = GetTransaction(/* block_index */ nullptr, node.mempool, txin.prevout.hash, Params().GetConsensus(), blockHashTmp);
nValueIn += txPrev->vout[txin.prevout.n].nValue;
}
nBlockFees += nValueIn - tx->GetValueOut();
}
std::vector<CTxOut> voutMasternodePayments, voutDummy;
CMutableTransaction dummyTx;
CAmount blockReward = nBlockFees + GetBlockSubsidy(pindex->pprev->nBits, pindex->pprev->nHeight, Params().GetConsensus());
FillBlockPayments(dummyTx, pindex->nHeight, blockReward, voutMasternodePayments, voutDummy);
UniValue blockObj(UniValue::VOBJ);
CAmount payedPerBlock{0};
UniValue masternodeArr(UniValue::VARR);
UniValue protxObj(UniValue::VOBJ);
UniValue payeesArr(UniValue::VARR);
CAmount payedPerMasternode{0};
for (const auto& txout : voutMasternodePayments) {
UniValue obj(UniValue::VOBJ);
CTxDestination dest;
ExtractDestination(txout.scriptPubKey, dest);
obj.pushKV("address", EncodeDestination(dest));
obj.pushKV("script", HexStr(txout.scriptPubKey));
obj.pushKV("amount", txout.nValue);
payedPerMasternode += txout.nValue;
payeesArr.push_back(obj);
}
const auto dmnPayee = deterministicMNManager->GetListForBlock(pindex).GetMNPayee();
protxObj.pushKV("proTxHash", dmnPayee == nullptr ? "" : dmnPayee->proTxHash.ToString());
protxObj.pushKV("amount", payedPerMasternode);
protxObj.pushKV("payees", payeesArr);
payedPerBlock += payedPerMasternode;
masternodeArr.push_back(protxObj);
blockObj.pushKV("height", pindex->nHeight);
blockObj.pushKV("blockhash", pindex->GetBlockHash().ToString());
blockObj.pushKV("amount", payedPerBlock);
blockObj.pushKV("masternodes", masternodeArr);
vecPayments.push_back(blockObj);
if (nCount > 0) {
LOCK(cs_main);
pindex = ::ChainActive().Next(pindex);
} else {
pindex = pindex->pprev;
}
}
if (nCount < 0) {
std::reverse(vecPayments.begin(), vecPayments.end());
}
UniValue paymentsArr(UniValue::VARR);
for (const auto& payment : vecPayments) {
paymentsArr.push_back(payment);
}
return paymentsArr;
}
[[ noreturn ]] static void masternode_help()
{
RPCHelpMan{"masternode",
"Set of commands to execute masternode related actions\n"
"\nAvailable commands:\n"
" count - Get information about number of masternodes\n"
" current - DEPRECATED Print info on current masternode winner to be paid the next block (calculated locally)\n"
#ifdef ENABLE_WALLET
" outputs - Print masternode compatible outputs\n"
#endif // ENABLE_WALLET
" status - Print masternode status information\n"
" list - Print list of all known masternodes (see masternodelist for more info)\n"
" payments - Return information about masternode payments in a mined block\n"
" winner - DEPRECATED Print info on next masternode winner to vote for\n"
" winners - Print list of masternode winners\n",
{
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "The command to execute"},
},
RPCResults{},
RPCExamples{""},
}.Throw();
}
static UniValue masternode(const JSONRPCRequest& request)
{
const JSONRPCRequest new_request{request.strMethod == "masternode" ? request.squashed() : request};
const std::string command{new_request.strMethod};
if (command == "masternodeconnect") {
return masternode_connect(new_request);
} else if (command == "masternodecount") {
return masternode_count(new_request);
} else if (command == "masternodecurrent") {
return masternode_current(new_request);
} else if (command == "masternodewinner") {
return masternode_winner(new_request);
#ifdef ENABLE_WALLET
} else if (command == "masternodeoutputs") {
return masternode_outputs(new_request);
#endif // ENABLE_WALLET
} else if (command == "masternodestatus") {
return masternode_status(new_request);
} else if (command == "masternodepayments") {
return masternode_payments(new_request);
} else if (command == "masternodewinners") {
return masternode_winners(new_request);
} else if (command == "masternodelist") {
return masternodelist(new_request);
} else {
masternode_help();
}
}
static UniValue masternodelist(const JSONRPCRequest& request)
{
std::string strMode = "json";
std::string strFilter = "";
if (!request.params[0].isNull()) strMode = request.params[0].get_str();
if (!request.params[1].isNull()) strFilter = request.params[1].get_str();
strMode = ToLower(strMode);
if (request.fHelp || (
strMode != "addr" && strMode != "full" && strMode != "info" && strMode != "json" &&
strMode != "owneraddress" && strMode != "votingaddress" &&
strMode != "lastpaidtime" && strMode != "lastpaidblock" &&
strMode != "payee" && strMode != "pubkeyoperator" &&
strMode != "status"))
{
masternode_list_help(request);
}
UniValue obj(UniValue::VOBJ);
auto mnList = deterministicMNManager->GetListAtChainTip();
auto dmnToStatus = [&](auto& dmn) {
if (mnList.IsMNValid(dmn)) {
return "ENABLED";
}
if (mnList.IsMNPoSeBanned(dmn)) {
return "POSE_BANNED";
}
return "UNKNOWN";
};
auto dmnToLastPaidTime = [&](auto& dmn) {
if (dmn.pdmnState->nLastPaidHeight == 0) {
return (int)0;
}
LOCK(cs_main);
const CBlockIndex* pindex = ::ChainActive()[dmn.pdmnState->nLastPaidHeight];
return (int)pindex->nTime;
};
mnList.ForEachMN(false, [&](auto& dmn) {
std::string strOutpoint = dmn.collateralOutpoint.ToStringShort();
Coin coin;
std::string collateralAddressStr = "UNKNOWN";
if (GetUTXOCoin(dmn.collateralOutpoint, coin)) {
CTxDestination collateralDest;
if (ExtractDestination(coin.out.scriptPubKey, collateralDest)) {
collateralAddressStr = EncodeDestination(collateralDest);
}
}
CScript payeeScript = dmn.pdmnState->scriptPayout;
CTxDestination payeeDest;
std::string payeeStr = "UNKNOWN";
if (ExtractDestination(payeeScript, payeeDest)) {
payeeStr = EncodeDestination(payeeDest);
}
if (strMode == "addr") {
std::string strAddress = dmn.pdmnState->addr.ToString(false);
if (strFilter !="" && strAddress.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, strAddress);
} else if (strMode == "full") {
std::ostringstream streamFull;
streamFull << std::setw(18) <<
dmnToStatus(dmn) << " " <<
dmn.pdmnState->nPoSePenalty << " " <<
payeeStr << " " << std::setw(10) <<
dmnToLastPaidTime(dmn) << " " << std::setw(6) <<
dmn.pdmnState->nLastPaidHeight << " " <<
dmn.pdmnState->addr.ToString();
std::string strFull = streamFull.str();
if (strFilter !="" && strFull.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, strFull);
} else if (strMode == "info") {
std::ostringstream streamInfo;
streamInfo << std::setw(18) <<
dmnToStatus(dmn) << " " <<
dmn.pdmnState->nPoSePenalty << " " <<
payeeStr << " " <<
dmn.pdmnState->addr.ToString();
std::string strInfo = streamInfo.str();
if (strFilter !="" && strInfo.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, strInfo);
} else if (strMode == "json") {
std::ostringstream streamInfo;
streamInfo << dmn.proTxHash.ToString() << " " <<
dmn.pdmnState->addr.ToString() << " " <<
payeeStr << " " <<
dmnToStatus(dmn) << " " <<
dmn.pdmnState->nPoSePenalty << " " <<
dmnToLastPaidTime(dmn) << " " <<
dmn.pdmnState->nLastPaidHeight << " " <<
EncodeDestination(dmn.pdmnState->keyIDOwner) << " " <<
EncodeDestination(dmn.pdmnState->keyIDVoting) << " " <<
collateralAddressStr << " " <<
dmn.pdmnState->pubKeyOperator.Get().ToString();
std::string strInfo = streamInfo.str();
if (strFilter !="" && strInfo.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
UniValue objMN(UniValue::VOBJ);
objMN.pushKV("proTxHash", dmn.proTxHash.ToString());
objMN.pushKV("address", dmn.pdmnState->addr.ToString());
objMN.pushKV("payee", payeeStr);
objMN.pushKV("status", dmnToStatus(dmn));
objMN.pushKV("pospenaltyscore", dmn.pdmnState->nPoSePenalty);
objMN.pushKV("lastpaidtime", dmnToLastPaidTime(dmn));
objMN.pushKV("lastpaidblock", dmn.pdmnState->nLastPaidHeight);
objMN.pushKV("owneraddress", EncodeDestination(dmn.pdmnState->keyIDOwner));
objMN.pushKV("votingaddress", EncodeDestination(dmn.pdmnState->keyIDVoting));
objMN.pushKV("collateraladdress", collateralAddressStr);
objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.Get().ToString());
obj.pushKV(strOutpoint, objMN);
} else if (strMode == "lastpaidblock") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, dmn.pdmnState->nLastPaidHeight);
} else if (strMode == "lastpaidtime") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, dmnToLastPaidTime(dmn));
} else if (strMode == "payee") {
if (strFilter !="" && payeeStr.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, payeeStr);
} else if (strMode == "owneraddress") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, EncodeDestination(dmn.pdmnState->keyIDOwner));
} else if (strMode == "pubkeyoperator") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.Get().ToString());
} else if (strMode == "status") {
std::string strStatus = dmnToStatus(dmn);
if (strFilter !="" && strStatus.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, strStatus);
} else if (strMode == "votingaddress") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return;
obj.pushKV(strOutpoint, EncodeDestination(dmn.pdmnState->keyIDVoting));
}
});
return obj;
}
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "dash", "masternode", &masternode, {} },
{ "dash", "masternodelist", &masternode, {} },
};
// clang-format on
void RegisterMasternodeRPCCommands(CRPCTable &t)
{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}