neobytes/src/rpc/masternode.cpp

872 lines
36 KiB
C++
Raw Normal View History

2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 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 "activemasternode.h"
#include "base58.h"
#include "init.h"
#include "netbase.h"
#include "validation.h"
#include "masternode-payments.h"
#include "masternode-sync.h"
#include "masternodeconfig.h"
#include "masternodeman.h"
#ifdef ENABLE_WALLET
#include "privatesend-client.h"
#endif // ENABLE_WALLET
#include "privatesend-server.h"
#include "rpc/server.h"
#include "util.h"
2015-04-03 00:51:08 +02:00
#include "utilmoneystr.h"
#include <fstream>
#include <iomanip>
#include <univalue.h>
UniValue masternodelist(const UniValue& params, bool fHelp);
#ifdef ENABLE_WALLET
void EnsureWalletIsUnlocked();
UniValue privatesend(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw std::runtime_error(
"privatesend \"command\"\n"
"\nArguments:\n"
"1. \"command\" (string or set of strings, required) The command to execute\n"
"\nAvailable commands:\n"
" start - Start mixing\n"
" stop - Stop mixing\n"
" reset - Reset mixing\n"
);
if(params[0].get_str() == "start") {
{
LOCK(pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
}
if(fMasterNode)
return "Mixing is not supported from masternodes";
privateSendClient.fEnablePrivateSend = true;
Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537) * net: move CBanDB and CAddrDB out of net.h/cpp This will eventually solve a circular dependency * net: Create CConnman to encapsulate p2p connections * net: Move socket binding into CConnman * net: move OpenNetworkConnection into CConnman * net: move ban and addrman functions into CConnman * net: Add oneshot functions to CConnman * net: move added node functions to CConnman * net: Add most functions needed for vNodes to CConnman * net: handle nodesignals in CConnman * net: Pass CConnection to wallet rather than using the global * net: Add rpc error for missing/disabled p2p functionality * net: Pass CConnman around as needed * gui: add NodeID to the peer table * net: create generic functor accessors and move vNodes to CConnman * net: move whitelist functions into CConnman * net: move nLastNodeId to CConnman * net: move nLocalHostNonce to CConnman This behavior seems to have been quite racy and broken. Move nLocalHostNonce into CNode, and check received nonces against all non-fully-connected nodes. If there's a match, assume we've connected to ourself. * net: move messageHandlerCondition to CConnman * net: move send/recv statistics to CConnman * net: move SendBufferSize/ReceiveFloodSize to CConnman * net: move nLocalServices/nRelevantServices to CConnman These are in-turn passed to CNode at connection time. This allows us to offer different services to different peers (or test the effects of doing so). * net: move semOutbound and semMasternodeOutbound to CConnman * net: SocketSendData returns written size * net: move max/max-outbound to CConnman * net: Pass best block known height into CConnman CConnman then passes the current best height into CNode at creation time. This way CConnman/CNode have no dependency on main for height, and the signals only move in one direction. This also helps to prevent identity leakage a tiny bit. Before this change, an attacker could theoretically make 2 connections on different interfaces. They would connect fully on one, and only establish the initial connection on the other. Once they receive a new block, they would relay it to your first connection, and immediately commence the version handshake on the second. Since the new block height is reflected immediately, they could attempt to learn whether the two connections were correlated. This is, of course, incredibly unlikely to work due to the small timings involved and receipt from other senders. But it doesn't hurt to lock-in nBestHeight at the time of connection, rather than letting the remote choose the time. * net: pass CClientUIInterface into CConnman * net: Drop StartNode/StopNode and use CConnman directly * net: Introduce CConnection::Options to avoid passing so many params * net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options * net: move vNodesDisconnected into CConnman * Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting * Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead * net: move MAX_FEELER_CONNECTIONS into connman
2017-07-21 11:35:19 +02:00
bool result = privateSendClient.DoAutomaticDenominating(*g_connman);
return "Mixing " + (result ? "started successfully" : ("start failed: " + privateSendClient.GetStatus() + ", will retry"));
}
if(params[0].get_str() == "stop") {
privateSendClient.fEnablePrivateSend = false;
return "Mixing was stopped";
}
if(params[0].get_str() == "reset") {
privateSendClient.ResetPool();
return "Mixing was reset";
}
return "Unknown command, please see \"help privatesend\"";
}
#endif // ENABLE_WALLET
UniValue getpoolinfo(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
throw std::runtime_error(
"getpoolinfo\n"
"Returns an object containing mixing pool related information.\n");
#ifdef ENABLE_WALLET
CPrivateSendBase* pprivateSendBase = fMasterNode ? (CPrivateSendBase*)&privateSendServer : (CPrivateSendBase*)&privateSendClient;
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("state", pprivateSendBase->GetStateString()));
obj.push_back(Pair("mixing_mode", (!fMasterNode && privateSendClient.fPrivateSendMultiSession) ? "multi-session" : "normal"));
obj.push_back(Pair("queue", pprivateSendBase->GetQueueSize()));
obj.push_back(Pair("entries", pprivateSendBase->GetEntriesCount()));
obj.push_back(Pair("status", privateSendClient.GetStatus()));
masternode_info_t mnInfo;
if (privateSendClient.GetMixingMasternodeInfo(mnInfo)) {
obj.push_back(Pair("outpoint", mnInfo.vin.prevout.ToStringShort()));
obj.push_back(Pair("addr", mnInfo.addr.ToString()));
}
if (pwalletMain) {
obj.push_back(Pair("keys_left", pwalletMain->nKeysLeftSinceAutoBackup));
obj.push_back(Pair("warnings", pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING
? "WARNING: keypool is almost depleted!" : ""));
}
#else // ENABLE_WALLET
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("state", privateSendServer.GetStateString()));
obj.push_back(Pair("queue", privateSendServer.GetQueueSize()));
obj.push_back(Pair("entries", privateSendServer.GetEntriesCount()));
#endif // ENABLE_WALLET
return obj;
}
UniValue masternode(const UniValue& params, bool fHelp)
{
std::string strCommand;
if (params.size() >= 1) {
strCommand = params[0].get_str();
}
#ifdef ENABLE_WALLET
if (strCommand == "start-many")
throw JSONRPCError(RPC_INVALID_PARAMETER, "DEPRECATED, please use start-all instead");
#endif // ENABLE_WALLET
if (fHelp ||
(
#ifdef ENABLE_WALLET
strCommand != "start-alias" && strCommand != "start-all" && strCommand != "start-missing" &&
strCommand != "start-disabled" && strCommand != "outputs" &&
#endif // ENABLE_WALLET
strCommand != "list" && strCommand != "list-conf" && strCommand != "count" &&
strCommand != "debug" && strCommand != "current" && strCommand != "winner" && strCommand != "winners" && strCommand != "genkey" &&
strCommand != "connect" && strCommand != "status"))
throw std::runtime_error(
"masternode \"command\"...\n"
"Set of commands to execute masternode related actions\n"
"\nArguments:\n"
"1. \"command\" (string or set of strings, required) The command to execute\n"
"\nAvailable commands:\n"
" count - Print number of all known masternodes (optional: 'ps', 'enabled', 'all', 'qualify')\n"
" current - Print info on current masternode winner to be paid the next block (calculated locally)\n"
" genkey - Generate new masternodeprivkey\n"
#ifdef ENABLE_WALLET
" outputs - Print masternode compatible outputs\n"
" start-alias - Start single remote masternode by assigned alias configured in masternode.conf\n"
" start-<mode> - Start remote masternodes configured in masternode.conf (<mode>: 'all', 'missing', 'disabled')\n"
#endif // ENABLE_WALLET
2015-07-18 21:17:17 +02:00
" status - Print masternode status information\n"
" list - Print list of all known masternodes (see masternodelist for more info)\n"
" list-conf - Print masternode.conf in JSON format\n"
" winner - Print info on next masternode winner to vote for\n"
" winners - Print list of masternode winners\n"
);
if (strCommand == "list")
{
UniValue newParams(UniValue::VARR);
// forward params but skip "list"
for (unsigned int i = 1; i < params.size(); i++) {
newParams.push_back(params[i]);
}
return masternodelist(newParams, fHelp);
}
2015-07-18 21:17:17 +02:00
if(strCommand == "connect")
{
if (params.size() < 2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Masternode address required");
std::string strAddress = params[1].get_str();
2015-07-18 21:17:17 +02:00
CService addr;
if (!Lookup(strAddress.c_str(), addr, 0, false))
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Incorrect masternode address %s", strAddress));
2015-07-18 21:17:17 +02:00
Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537) * net: move CBanDB and CAddrDB out of net.h/cpp This will eventually solve a circular dependency * net: Create CConnman to encapsulate p2p connections * net: Move socket binding into CConnman * net: move OpenNetworkConnection into CConnman * net: move ban and addrman functions into CConnman * net: Add oneshot functions to CConnman * net: move added node functions to CConnman * net: Add most functions needed for vNodes to CConnman * net: handle nodesignals in CConnman * net: Pass CConnection to wallet rather than using the global * net: Add rpc error for missing/disabled p2p functionality * net: Pass CConnman around as needed * gui: add NodeID to the peer table * net: create generic functor accessors and move vNodes to CConnman * net: move whitelist functions into CConnman * net: move nLastNodeId to CConnman * net: move nLocalHostNonce to CConnman This behavior seems to have been quite racy and broken. Move nLocalHostNonce into CNode, and check received nonces against all non-fully-connected nodes. If there's a match, assume we've connected to ourself. * net: move messageHandlerCondition to CConnman * net: move send/recv statistics to CConnman * net: move SendBufferSize/ReceiveFloodSize to CConnman * net: move nLocalServices/nRelevantServices to CConnman These are in-turn passed to CNode at connection time. This allows us to offer different services to different peers (or test the effects of doing so). * net: move semOutbound and semMasternodeOutbound to CConnman * net: SocketSendData returns written size * net: move max/max-outbound to CConnman * net: Pass best block known height into CConnman CConnman then passes the current best height into CNode at creation time. This way CConnman/CNode have no dependency on main for height, and the signals only move in one direction. This also helps to prevent identity leakage a tiny bit. Before this change, an attacker could theoretically make 2 connections on different interfaces. They would connect fully on one, and only establish the initial connection on the other. Once they receive a new block, they would relay it to your first connection, and immediately commence the version handshake on the second. Since the new block height is reflected immediately, they could attempt to learn whether the two connections were correlated. This is, of course, incredibly unlikely to work due to the small timings involved and receipt from other senders. But it doesn't hurt to lock-in nBestHeight at the time of connection, rather than letting the remote choose the time. * net: pass CClientUIInterface into CConnman * net: Drop StartNode/StopNode and use CConnman directly * net: Introduce CConnection::Options to avoid passing so many params * net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options * net: move vNodesDisconnected into CConnman * Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting * Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead * net: move MAX_FEELER_CONNECTIONS into connman
2017-07-21 11:35:19 +02:00
// TODO: Pass CConnman instance somehow and don't use global variable.
CNode *pnode = g_connman->ConnectNode(CAddress(addr, NODE_NETWORK), NULL);
if(!pnode)
throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to masternode %s", strAddress));
return "successfully connected";
2015-07-18 21:17:17 +02:00
}
if (strCommand == "count")
{
if (params.size() > 2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Too many parameters");
if (params.size() == 1)
return mnodeman.size();
std::string strMode = params[1].get_str();
if (strMode == "ps")
return mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION);
if (strMode == "enabled")
return mnodeman.CountEnabled();
int nCount;
masternode_info_t mnInfo;
mnodeman.GetNextMasternodeInQueueForPayment(true, nCount, mnInfo);
if (strMode == "qualify")
return nCount;
if (strMode == "all")
return strprintf("Total: %d (PS Compatible: %d / Enabled: %d / Qualify: %d)",
mnodeman.size(), mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION),
mnodeman.CountEnabled(), nCount);
}
if (strCommand == "current" || strCommand == "winner")
2015-07-18 21:17:17 +02:00
{
int nCount;
int nHeight;
masternode_info_t mnInfo;
CBlockIndex* pindex = NULL;
{
LOCK(cs_main);
pindex = chainActive.Tip();
2015-07-18 21:17:17 +02:00
}
nHeight = pindex->nHeight + (strCommand == "current" ? 1 : 10);
mnodeman.UpdateLastPaid(pindex);
if(!mnodeman.GetNextMasternodeInQueueForPayment(nHeight, true, nCount, mnInfo))
return "unknown";
2015-07-18 21:17:17 +02:00
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("height", nHeight));
obj.push_back(Pair("IP:port", mnInfo.addr.ToString()));
obj.push_back(Pair("protocol", (int64_t)mnInfo.nProtocolVersion));
obj.push_back(Pair("outpoint", mnInfo.vin.prevout.ToStringShort()));
obj.push_back(Pair("payee", CBitcoinAddress(mnInfo.pubKeyCollateralAddress.GetID()).ToString()));
obj.push_back(Pair("lastseen", mnInfo.nTimeLastPing));
obj.push_back(Pair("activeseconds", mnInfo.nTimeLastPing - mnInfo.sigTime));
return obj;
2015-07-18 21:17:17 +02:00
}
#ifdef ENABLE_WALLET
if (strCommand == "start-alias")
{
if (params.size() < 2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Please specify an alias");
2015-07-18 03:23:52 +02:00
{
LOCK(pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
}
2015-07-18 03:23:52 +02:00
std::string strAlias = params[1].get_str();
bool fFound = false;
UniValue statusObj(UniValue::VOBJ);
statusObj.push_back(Pair("alias", strAlias));
2015-03-21 20:49:21 +01:00
2015-07-18 03:23:52 +02:00
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
if(mne.getAlias() == strAlias) {
fFound = true;
std::string strError;
CMasternodeBroadcast mnb;
bool fResult = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb);
statusObj.push_back(Pair("result", fResult ? "successful" : "failed"));
if(fResult) {
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.UpdateMasternodeList(mnb, *g_connman);
mnb.Relay(*g_connman);
} else {
statusObj.push_back(Pair("errorMessage", strError));
2015-07-18 03:23:52 +02:00
}
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.NotifyMasternodeUpdates(*g_connman);
2015-07-18 03:23:52 +02:00
break;
}
}
if(!fFound) {
2015-07-18 03:23:52 +02:00
statusObj.push_back(Pair("result", "failed"));
statusObj.push_back(Pair("errorMessage", "Could not find alias in config. Verify with list-conf."));
2015-07-18 03:23:52 +02:00
}
return statusObj;
}
if (strCommand == "start-all" || strCommand == "start-missing" || strCommand == "start-disabled")
{
{
LOCK(pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
}
if((strCommand == "start-missing" || strCommand == "start-disabled") && !masternodeSync.IsMasternodeListSynced()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "You can't use this command until masternode list is synced");
}
int nSuccessful = 0;
int nFailed = 0;
UniValue resultsObj(UniValue::VOBJ);
2015-07-18 03:23:52 +02:00
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
std::string strError;
2015-03-21 20:49:21 +01:00
COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str())));
CMasternode mn;
bool fFound = mnodeman.Get(outpoint, mn);
CMasternodeBroadcast mnb;
2015-08-26 22:15:37 +02:00
if(strCommand == "start-missing" && fFound) continue;
if(strCommand == "start-disabled" && fFound && mn.IsEnabled()) continue;
2015-08-26 22:15:37 +02:00
bool fResult = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb);
UniValue statusObj(UniValue::VOBJ);
2015-07-18 03:23:52 +02:00
statusObj.push_back(Pair("alias", mne.getAlias()));
statusObj.push_back(Pair("result", fResult ? "successful" : "failed"));
if (fResult) {
nSuccessful++;
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.UpdateMasternodeList(mnb, *g_connman);
mnb.Relay(*g_connman);
2015-07-18 03:23:52 +02:00
} else {
nFailed++;
statusObj.push_back(Pair("errorMessage", strError));
2015-07-18 03:23:52 +02:00
}
2015-07-18 03:23:52 +02:00
resultsObj.push_back(Pair("status", statusObj));
}
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.NotifyMasternodeUpdates(*g_connman);
UniValue returnObj(UniValue::VOBJ);
returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", nSuccessful, nFailed, nSuccessful + nFailed)));
2015-07-18 03:23:52 +02:00
returnObj.push_back(Pair("detail", resultsObj));
2015-07-18 03:23:52 +02:00
return returnObj;
}
#endif // ENABLE_WALLET
if (strCommand == "genkey")
{
CKey secret;
secret.MakeNewKey(false);
return CBitcoinSecret(secret).ToString();
}
if (strCommand == "list-conf")
{
UniValue resultObj(UniValue::VOBJ);
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), uint32_t(atoi(mne.getOutputIndex().c_str())));
CMasternode mn;
bool fFound = mnodeman.Get(outpoint, mn);
std::string strStatus = fFound ? mn.GetStatus() : "MISSING";
UniValue mnObj(UniValue::VOBJ);
mnObj.push_back(Pair("alias", mne.getAlias()));
mnObj.push_back(Pair("address", mne.getIp()));
mnObj.push_back(Pair("privateKey", mne.getPrivKey()));
mnObj.push_back(Pair("txHash", mne.getTxHash()));
mnObj.push_back(Pair("outputIndex", mne.getOutputIndex()));
mnObj.push_back(Pair("status", strStatus));
resultObj.push_back(Pair("masternode", mnObj));
}
return resultObj;
}
#ifdef ENABLE_WALLET
if (strCommand == "outputs") {
// Find possible candidates
std::vector<COutput> vPossibleCoins;
pwalletMain->AvailableCoins(vPossibleCoins, true, NULL, false, ONLY_1000);
UniValue obj(UniValue::VOBJ);
BOOST_FOREACH(COutput& out, vPossibleCoins) {
obj.push_back(Pair(out.tx->GetHash().ToString(), strprintf("%d", out.i)));
}
return obj;
}
#endif // ENABLE_WALLET
if (strCommand == "status")
2015-06-24 18:41:03 +02:00
{
if (!fMasterNode)
throw JSONRPCError(RPC_INTERNAL_ERROR, "This is not a masternode");
2015-06-24 18:41:03 +02:00
UniValue mnObj(UniValue::VOBJ);
mnObj.push_back(Pair("outpoint", activeMasternode.outpoint.ToStringShort()));
mnObj.push_back(Pair("service", activeMasternode.service.ToString()));
2016-09-18 23:13:23 +02:00
CMasternode mn;
if(mnodeman.Get(activeMasternode.outpoint, mn)) {
2016-09-18 23:13:23 +02:00
mnObj.push_back(Pair("payee", CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString()));
}
2015-08-15 16:09:13 +02:00
mnObj.push_back(Pair("status", activeMasternode.GetStatus()));
2015-06-24 18:41:03 +02:00
return mnObj;
}
2015-07-18 21:17:17 +02:00
if (strCommand == "winners")
{
int nHeight;
{
LOCK(cs_main);
CBlockIndex* pindex = chainActive.Tip();
if(!pindex) return NullUniValue;
nHeight = pindex->nHeight;
}
int nLast = 10;
std::string strFilter = "";
if (params.size() >= 2) {
2015-07-22 00:14:54 +02:00
nLast = atoi(params[1].get_str());
}
if (params.size() == 3) {
strFilter = params[2].get_str();
}
if (params.size() > 3)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'masternode winners ( \"count\" \"filter\" )'");
UniValue obj(UniValue::VOBJ);
2015-07-18 21:17:17 +02:00
for(int i = nHeight - nLast; i < nHeight + 20; i++) {
std::string strPayment = GetRequiredPaymentsString(i);
if (strFilter !="" && strPayment.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strprintf("%d", i), strPayment));
2015-07-18 21:17:17 +02:00
}
return obj;
}
return NullUniValue;
}
UniValue masternodelist(const UniValue& params, bool fHelp)
{
std::string strMode = "status";
std::string strFilter = "";
if (params.size() >= 1) strMode = params[0].get_str();
if (params.size() == 2) strFilter = params[1].get_str();
if (fHelp || (
strMode != "activeseconds" && strMode != "addr" && strMode != "full" && strMode != "info" &&
strMode != "lastseen" && strMode != "lastpaidtime" && strMode != "lastpaidblock" &&
strMode != "protocol" && strMode != "payee" && strMode != "pubkey" &&
strMode != "rank" && strMode != "status"))
{
throw std::runtime_error(
"masternodelist ( \"mode\" \"filter\" )\n"
"Get a list of masternodes in different modes\n"
"\nArguments:\n"
"1. \"mode\" (string, optional/required to use filter, defaults = status) The mode to run list in\n"
"2. \"filter\" (string, optional) Filter results. Partial match by outpoint by default in all modes,\n"
2015-04-05 23:59:25 +02:00
" additional matches in some modes are also available\n"
"\nAvailable modes:\n"
" activeseconds - Print number of seconds masternode recognized by the network as enabled\n"
" (since latest issued \"masternode start/start-many/start-alias\")\n"
" addr - Print ip address associated with a masternode (can be additionally filtered, partial match)\n"
" full - Print info in format 'status protocol payee lastseen activeseconds lastpaidtime lastpaidblock IP'\n"
2015-04-05 23:59:25 +02:00
" (can be additionally filtered, partial match)\n"
" info - Print info in format 'status protocol payee lastseen activeseconds sentinelversion sentinelstate IP'\n"
" (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"
" lastseen - Print timestamp of when a masternode was last seen on the network\n"
" payee - Print Dash address associated with a masternode (can be additionally filtered,\n"
2015-04-05 23:59:25 +02:00
" partial match)\n"
" protocol - Print protocol of a masternode (can be additionally filtered, exact match)\n"
" pubkey - Print the masternode (not collateral) public key\n"
" rank - Print rank of a masternode based on current block\n"
" status - Print masternode status: PRE_ENABLED / ENABLED / EXPIRED / WATCHDOG_EXPIRED / NEW_START_REQUIRED /\n"
" UPDATE_REQUIRED / POSE_BAN / OUTPOINT_SPENT (can be additionally filtered, partial match)\n"
);
}
if (strMode == "full" || strMode == "lastpaidtime" || strMode == "lastpaidblock") {
CBlockIndex* pindex = NULL;
{
LOCK(cs_main);
pindex = chainActive.Tip();
}
mnodeman.UpdateLastPaid(pindex);
}
UniValue obj(UniValue::VOBJ);
if (strMode == "rank") {
CMasternodeMan::rank_pair_vec_t vMasternodeRanks;
mnodeman.GetMasternodeRanks(vMasternodeRanks);
BOOST_FOREACH(PAIRTYPE(int, CMasternode)& s, vMasternodeRanks) {
std::string strOutpoint = s.second.vin.prevout.ToStringShort();
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, s.first));
}
} else {
std::map<COutPoint, CMasternode> mapMasternodes = mnodeman.GetFullMasternodeMap();
for (auto& mnpair : mapMasternodes) {
CMasternode mn = mnpair.second;
std::string strOutpoint = mnpair.first.ToStringShort();
if (strMode == "activeseconds") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, (int64_t)(mn.lastPing.sigTime - mn.sigTime)));
} else if (strMode == "addr") {
std::string strAddress = mn.addr.ToString();
if (strFilter !="" && strAddress.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, strAddress));
} else if (strMode == "full") {
std::ostringstream streamFull;
streamFull << std::setw(18) <<
mn.GetStatus() << " " <<
mn.nProtocolVersion << " " <<
CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString() << " " <<
(int64_t)mn.lastPing.sigTime << " " << std::setw(8) <<
(int64_t)(mn.lastPing.sigTime - mn.sigTime) << " " << std::setw(10) <<
mn.GetLastPaidTime() << " " << std::setw(6) <<
mn.GetLastPaidBlock() << " " <<
mn.addr.ToString();
std::string strFull = streamFull.str();
if (strFilter !="" && strFull.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, strFull));
} else if (strMode == "info") {
std::ostringstream streamInfo;
streamInfo << std::setw(18) <<
mn.GetStatus() << " " <<
mn.nProtocolVersion << " " <<
CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString() << " " <<
(int64_t)mn.lastPing.sigTime << " " << std::setw(8) <<
(int64_t)(mn.lastPing.sigTime - mn.sigTime) << " " <<
SafeIntVersionToString(mn.lastPing.nSentinelVersion) << " " <<
(mn.lastPing.fSentinelIsCurrent ? "current" : "expired") << " " <<
mn.addr.ToString();
std::string strInfo = streamInfo.str();
if (strFilter !="" && strInfo.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, strInfo));
} else if (strMode == "lastpaidblock") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, mn.GetLastPaidBlock()));
} else if (strMode == "lastpaidtime") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, mn.GetLastPaidTime()));
} else if (strMode == "lastseen") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, (int64_t)mn.lastPing.sigTime));
} else if (strMode == "payee") {
CBitcoinAddress address(mn.pubKeyCollateralAddress.GetID());
std::string strPayee = address.ToString();
if (strFilter !="" && strPayee.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, strPayee));
} else if (strMode == "protocol") {
if (strFilter !="" && strFilter != strprintf("%d", mn.nProtocolVersion) &&
strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, (int64_t)mn.nProtocolVersion));
} else if (strMode == "pubkey") {
if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, HexStr(mn.pubKeyMasternode)));
} else if (strMode == "status") {
std::string strStatus = mn.GetStatus();
if (strFilter !="" && strStatus.find(strFilter) == std::string::npos &&
strOutpoint.find(strFilter) == std::string::npos) continue;
obj.push_back(Pair(strOutpoint, strStatus));
}
}
}
return obj;
}
bool DecodeHexVecMnb(std::vector<CMasternodeBroadcast>& vecMnb, std::string strHexMnb) {
if (!IsHex(strHexMnb))
return false;
std::vector<unsigned char> mnbData(ParseHex(strHexMnb));
CDataStream ssData(mnbData, SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> vecMnb;
}
catch (const std::exception&) {
return false;
}
return true;
}
UniValue masternodebroadcast(const UniValue& params, bool fHelp)
{
std::string strCommand;
if (params.size() >= 1)
strCommand = params[0].get_str();
if (fHelp ||
(
#ifdef ENABLE_WALLET
strCommand != "create-alias" && strCommand != "create-all" &&
#endif // ENABLE_WALLET
strCommand != "decode" && strCommand != "relay"))
throw std::runtime_error(
"masternodebroadcast \"command\"...\n"
"Set of commands to create and relay masternode broadcast messages\n"
"\nArguments:\n"
"1. \"command\" (string or set of strings, required) The command to execute\n"
"\nAvailable commands:\n"
#ifdef ENABLE_WALLET
" create-alias - Create single remote masternode broadcast message by assigned alias configured in masternode.conf\n"
" create-all - Create remote masternode broadcast messages for all masternodes configured in masternode.conf\n"
#endif // ENABLE_WALLET
" decode - Decode masternode broadcast message\n"
" relay - Relay masternode broadcast message to the network\n"
);
#ifdef ENABLE_WALLET
if (strCommand == "create-alias")
{
// wait for reindex and/or import to finish
if (fImporting || fReindex)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wait for reindex and/or import to finish");
if (params.size() < 2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Please specify an alias");
{
LOCK(pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
}
bool fFound = false;
std::string strAlias = params[1].get_str();
UniValue statusObj(UniValue::VOBJ);
std::vector<CMasternodeBroadcast> vecMnb;
statusObj.push_back(Pair("alias", strAlias));
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
if(mne.getAlias() == strAlias) {
fFound = true;
std::string strError;
CMasternodeBroadcast mnb;
bool fResult = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb, true);
statusObj.push_back(Pair("result", fResult ? "successful" : "failed"));
if(fResult) {
vecMnb.push_back(mnb);
CDataStream ssVecMnb(SER_NETWORK, PROTOCOL_VERSION);
ssVecMnb << vecMnb;
statusObj.push_back(Pair("hex", HexStr(ssVecMnb.begin(), ssVecMnb.end())));
} else {
statusObj.push_back(Pair("errorMessage", strError));
}
break;
}
}
if(!fFound) {
statusObj.push_back(Pair("result", "not found"));
statusObj.push_back(Pair("errorMessage", "Could not find alias in config. Verify with list-conf."));
}
return statusObj;
}
if (strCommand == "create-all")
{
// wait for reindex and/or import to finish
if (fImporting || fReindex)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wait for reindex and/or import to finish");
{
LOCK(pwalletMain->cs_wallet);
EnsureWalletIsUnlocked();
}
std::vector<CMasternodeConfig::CMasternodeEntry> mnEntries;
mnEntries = masternodeConfig.getEntries();
int nSuccessful = 0;
int nFailed = 0;
UniValue resultsObj(UniValue::VOBJ);
std::vector<CMasternodeBroadcast> vecMnb;
BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
std::string strError;
CMasternodeBroadcast mnb;
bool fResult = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb, true);
UniValue statusObj(UniValue::VOBJ);
statusObj.push_back(Pair("alias", mne.getAlias()));
statusObj.push_back(Pair("result", fResult ? "successful" : "failed"));
if(fResult) {
nSuccessful++;
vecMnb.push_back(mnb);
} else {
nFailed++;
statusObj.push_back(Pair("errorMessage", strError));
}
resultsObj.push_back(Pair("status", statusObj));
}
CDataStream ssVecMnb(SER_NETWORK, PROTOCOL_VERSION);
ssVecMnb << vecMnb;
UniValue returnObj(UniValue::VOBJ);
returnObj.push_back(Pair("overall", strprintf("Successfully created broadcast messages for %d masternodes, failed to create %d, total %d", nSuccessful, nFailed, nSuccessful + nFailed)));
returnObj.push_back(Pair("detail", resultsObj));
returnObj.push_back(Pair("hex", HexStr(ssVecMnb.begin(), ssVecMnb.end())));
return returnObj;
}
#endif // ENABLE_WALLET
if (strCommand == "decode")
{
if (params.size() != 2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'masternodebroadcast decode \"hexstring\"'");
std::vector<CMasternodeBroadcast> vecMnb;
if (!DecodeHexVecMnb(vecMnb, params[1].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Masternode broadcast message decode failed");
int nSuccessful = 0;
int nFailed = 0;
int nDos = 0;
UniValue returnObj(UniValue::VOBJ);
BOOST_FOREACH(CMasternodeBroadcast& mnb, vecMnb) {
UniValue resultObj(UniValue::VOBJ);
if(mnb.CheckSignature(nDos)) {
nSuccessful++;
resultObj.push_back(Pair("outpoint", mnb.vin.prevout.ToStringShort()));
resultObj.push_back(Pair("addr", mnb.addr.ToString()));
resultObj.push_back(Pair("pubKeyCollateralAddress", CBitcoinAddress(mnb.pubKeyCollateralAddress.GetID()).ToString()));
resultObj.push_back(Pair("pubKeyMasternode", CBitcoinAddress(mnb.pubKeyMasternode.GetID()).ToString()));
resultObj.push_back(Pair("vchSig", EncodeBase64(&mnb.vchSig[0], mnb.vchSig.size())));
resultObj.push_back(Pair("sigTime", mnb.sigTime));
resultObj.push_back(Pair("protocolVersion", mnb.nProtocolVersion));
resultObj.push_back(Pair("nLastDsq", mnb.nLastDsq));
UniValue lastPingObj(UniValue::VOBJ);
lastPingObj.push_back(Pair("outpoint", mnb.lastPing.vin.prevout.ToStringShort()));
lastPingObj.push_back(Pair("blockHash", mnb.lastPing.blockHash.ToString()));
lastPingObj.push_back(Pair("sigTime", mnb.lastPing.sigTime));
lastPingObj.push_back(Pair("vchSig", EncodeBase64(&mnb.lastPing.vchSig[0], mnb.lastPing.vchSig.size())));
resultObj.push_back(Pair("lastPing", lastPingObj));
} else {
nFailed++;
resultObj.push_back(Pair("errorMessage", "Masternode broadcast signature verification failed"));
}
returnObj.push_back(Pair(mnb.GetHash().ToString(), resultObj));
}
returnObj.push_back(Pair("overall", strprintf("Successfully decoded broadcast messages for %d masternodes, failed to decode %d, total %d", nSuccessful, nFailed, nSuccessful + nFailed)));
return returnObj;
}
if (strCommand == "relay")
{
if (params.size() < 2 || params.size() > 3)
throw JSONRPCError(RPC_INVALID_PARAMETER, "masternodebroadcast relay \"hexstring\" ( fast )\n"
"\nArguments:\n"
"1. \"hex\" (string, required) Broadcast messages hex string\n"
"2. fast (string, optional) If none, using safe method\n");
std::vector<CMasternodeBroadcast> vecMnb;
if (!DecodeHexVecMnb(vecMnb, params[1].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Masternode broadcast message decode failed");
int nSuccessful = 0;
int nFailed = 0;
bool fSafe = params.size() == 2;
UniValue returnObj(UniValue::VOBJ);
// verify all signatures first, bailout if any of them broken
BOOST_FOREACH(CMasternodeBroadcast& mnb, vecMnb) {
UniValue resultObj(UniValue::VOBJ);
resultObj.push_back(Pair("outpoint", mnb.vin.prevout.ToStringShort()));
resultObj.push_back(Pair("addr", mnb.addr.ToString()));
int nDos = 0;
bool fResult;
if (mnb.CheckSignature(nDos)) {
if (fSafe) {
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
fResult = mnodeman.CheckMnbAndUpdateMasternodeList(NULL, mnb, nDos, *g_connman);
} else {
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.UpdateMasternodeList(mnb, *g_connman);
mnb.Relay(*g_connman);
fResult = true;
}
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.NotifyMasternodeUpdates(*g_connman);
} else fResult = false;
if(fResult) {
nSuccessful++;
resultObj.push_back(Pair(mnb.GetHash().ToString(), "successful"));
} else {
nFailed++;
resultObj.push_back(Pair("errorMessage", "Masternode broadcast signature verification failed"));
}
returnObj.push_back(Pair(mnb.GetHash().ToString(), resultObj));
}
returnObj.push_back(Pair("overall", strprintf("Successfully relayed broadcast messages for %d masternodes, failed to relay %d, total %d", nSuccessful, nFailed, nSuccessful + nFailed)));
return returnObj;
}
return NullUniValue;
}
UniValue sentinelping(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 1) {
throw std::runtime_error(
"sentinelping version\n"
"\nSentinel ping.\n"
"\nArguments:\n"
"1. version (string, required) Sentinel version in the form \"x.x.x\"\n"
"\nResult:\n"
"state (boolean) Ping result\n"
"\nExamples:\n"
+ HelpExampleCli("sentinelping", "1.0.2")
+ HelpExampleRpc("sentinelping", "1.0.2")
);
}
activeMasternode.UpdateSentinelPing(StringVersionToInt(params[0].get_str()));
return true;
}
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
/* Dash features */
{ "dash", "masternode", &masternode, true },
{ "dash", "masternodelist", &masternodelist, true },
{ "dash", "masternodebroadcast", &masternodebroadcast, true },
{ "dash", "getpoolinfo", &getpoolinfo, true },
{ "dash", "sentinelping", &sentinelping, true },
#ifdef ENABLE_WALLET
{ "dash", "privatesend", &privatesend, false },
#endif // ENABLE_WALLET
};
void RegisterMasternodeRPCCommands(CRPCTable &tableRPC)
{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]);
}