// 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 "clientversion.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" #include "utilmoneystr.h" #include "txmempool.h" #include "evo/specialtx.h" #include "evo/deterministicmns.h" #include "evo/deterministicmns.h" #include #include #include UniValue masternodelist(const JSONRPCRequest& request); bool EnsureWalletIsAvailable(bool avoidException); #ifdef ENABLE_WALLET void EnsureWalletIsUnlocked(); UniValue privatesend(const JSONRPCRequest& request) { if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; if (request.fHelp || request.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 (fMasternodeMode) throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes"); if (request.params[0].get_str() == "start") { { LOCK(pwalletMain->cs_wallet); if (pwalletMain->IsLocked(true)) throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please unlock wallet for mixing with walletpassphrase first."); } privateSendClient.fEnablePrivateSend = true; bool result = privateSendClient.DoAutomaticDenominating(*g_connman); return "Mixing " + (result ? "started successfully" : ("start failed: " + privateSendClient.GetStatuses() + ", will retry")); } if (request.params[0].get_str() == "stop") { privateSendClient.fEnablePrivateSend = false; return "Mixing was stopped"; } if (request.params[0].get_str() == "reset") { privateSendClient.ResetPool(); return "Mixing was reset"; } return "Unknown command, please see \"help privatesend\""; } #endif // ENABLE_WALLET UniValue getpoolinfo(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) throw std::runtime_error( "getpoolinfo\n" "Returns an object containing mixing pool related information.\n"); #ifdef ENABLE_WALLET CPrivateSendBaseManager* pprivateSendBaseManager = fMasternodeMode ? (CPrivateSendBaseManager*)&privateSendServer : (CPrivateSendBaseManager*)&privateSendClient; UniValue obj(UniValue::VOBJ); // TODO: // obj.push_back(Pair("state", pprivateSendBase->GetStateString())); obj.push_back(Pair("queue", pprivateSendBaseManager->GetQueueSize())); // obj.push_back(Pair("entries", pprivateSendBase->GetEntriesCount())); obj.push_back(Pair("status", privateSendClient.GetStatuses())); std::vector vecMnInfo; if (privateSendClient.GetMixingMasternodesInfo(vecMnInfo)) { UniValue pools(UniValue::VARR); for (const auto& mnInfo : vecMnInfo) { UniValue pool(UniValue::VOBJ); pool.push_back(Pair("outpoint", mnInfo.outpoint.ToStringShort())); pool.push_back(Pair("addr", mnInfo.addr.ToString())); pools.push_back(pool); } obj.push_back(Pair("pools", pools)); } 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; } void masternode_list_help() { throw std::runtime_error( "masternode list ( \"mode\" \"filter\" )\n" "Get a list of masternodes in different modes. This call is identical to masternodelist call.\n" "\nArguments:\n" "1. \"mode\" (string, optional/required to use filter, defaults = json) The mode to run list in\n" "2. \"filter\" (string, optional) Filter results. Partial match by outpoint by default in all modes,\n" " 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" " daemon - Print daemon version of a masternode (can be additionally filtered, exact match)\n" " full - Print info in format 'status protocol payee lastseen activeseconds lastpaidtime lastpaidblock IP'\n" " (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" " 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" " 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" " partial match)\n" " protocol - Print protocol of a masternode (can be additionally filtered, exact match)\n" " keyid - Print the masternode (not collateral) key id\n" " rank - Print rank of a masternode based on current block\n" " sentinel - Print sentinel version of a masternode (can be additionally filtered, exact match)\n" " status - Print masternode status: PRE_ENABLED / ENABLED / EXPIRED / SENTINEL_PING_EXPIRED / NEW_START_REQUIRED /\n" " UPDATE_REQUIRED / POSE_BAN / OUTPOINT_SPENT (can be additionally filtered, partial match)\n" ); } UniValue masternode_list(const JSONRPCRequest& request) { if (request.fHelp) masternode_list_help(); JSONRPCRequest newRequest = request; newRequest.params.setArray(); // forward params but skip "list" for (unsigned int i = 1; i < request.params.size(); i++) { newRequest.params.push_back(request.params[i]); } return masternodelist(newRequest); } void masternode_connect_help() { throw std::runtime_error( "masternode connect \"address\"\n" "Connect to given masternode\n" "\nArguments:\n" "1. \"address\" (string, required) The address of the masternode to connect\n" ); } UniValue masternode_connect(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2) masternode_connect_help(); std::string strAddress = request.params[1].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. g_connman->OpenMasternodeConnection(CAddress(addr, NODE_NETWORK)); if (!g_connman->IsConnected(CAddress(addr, NODE_NETWORK), CConnman::AllNodes)) throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to masternode %s", strAddress)); return "successfully connected"; } void masternode_count_help() { throw std::runtime_error( "masternode count (\"mode\")\n" " Get information about number of masternodes. Mode\n" " usage is depricated, call without mode params returns\n" " all values in JSON format.\n" "\nArguments:\n" "1. \"mode\" (string, optional, DEPRICATED) Option to get number of masternodes in different states\n" "\nAvailable modes:\n" " total - total number of masternodes" " ps - number of PrivateSend compatible masternodes" " enabled - number of enabled masternodes" " qualify - number of qualified masternodes" " all - all above in one string" ); } UniValue masternode_count(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() > 2) masternode_count_help(); int nCount; int total; if (deterministicMNManager->IsDeterministicMNsSporkActive()) { nCount = total = mnodeman.CountEnabled(); } else { masternode_info_t mnInfo; mnodeman.GetNextMasternodeInQueueForPayment(true, nCount, mnInfo); total = mnodeman.size(); } int ps = mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION); int enabled = mnodeman.CountEnabled(); if (request.params.size() == 1) { UniValue obj(UniValue::VOBJ); obj.push_back(Pair("total", total)); obj.push_back(Pair("ps_compatible", ps)); obj.push_back(Pair("enabled", enabled)); obj.push_back(Pair("qualify", nCount)); return obj; } std::string strMode = request.params[1].get_str(); if (strMode == "total") return total; if (strMode == "ps") return ps; if (strMode == "enabled") return enabled; if (strMode == "qualify") return nCount; if (strMode == "all") return strprintf("Total: %d (PS Compatible: %d / Enabled: %d / Qualify: %d)", total, ps, enabled, nCount); throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown mode value"); } UniValue GetNextMasternodeForPayment(int heightShift) { int nCount; int nHeight; masternode_info_t mnInfo; CBlockIndex* pindex = NULL; { LOCK(cs_main); pindex = chainActive.Tip(); } nHeight = pindex->nHeight + heightShift; mnodeman.UpdateLastPaid(pindex); if (deterministicMNManager->IsDeterministicMNsSporkActive()) { auto payee = deterministicMNManager->GetListAtChainTip().GetMNPayee(); if (!payee || !mnodeman.GetMasternodeInfo(payee->proTxHash, mnInfo)) return "unknown"; } else { if (!mnodeman.GetNextMasternodeInQueueForPayment(nHeight, true, nCount, mnInfo)) return "unknown"; } UniValue obj(UniValue::VOBJ); obj.push_back(Pair("height", nHeight)); obj.push_back(Pair("IP:port", mnInfo.addr.ToString())); obj.push_back(Pair("protocol", mnInfo.nProtocolVersion)); obj.push_back(Pair("outpoint", mnInfo.outpoint.ToStringShort())); obj.push_back(Pair("payee", CBitcoinAddress(mnInfo.keyIDCollateralAddress).ToString())); obj.push_back(Pair("lastseen", mnInfo.nTimeLastPing)); obj.push_back(Pair("activeseconds", mnInfo.nTimeLastPing - mnInfo.sigTime)); return obj; } void masternode_winner_help() { throw std::runtime_error( "masternode winner\n" "Print info on next masternode winner to vote for\n" ); } UniValue masternode_winner(const JSONRPCRequest& request) { if (request.fHelp) masternode_winner_help(); return GetNextMasternodeForPayment(10); } void masternode_current_help() { throw std::runtime_error( "masternode current\n" "Print info on current masternode winner to be paid the next block (calculated locally)\n" ); } UniValue masternode_current(const JSONRPCRequest& request) { if (request.fHelp) masternode_current_help(); return GetNextMasternodeForPayment(1); } #ifdef ENABLE_WALLET void masternode_start_alias_help() { throw std::runtime_error( "masternode start-alias \"alias\"\n" "Start single remote masternode by assigned alias\n" "\nArguments:\n" "1. \"alias\" (string, required) The alias of the remote masternode configured in masternode.conf\n" ); } UniValue masternode_start_alias(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2) masternode_start_alias_help(); if (deterministicMNManager->IsDeterministicMNsSporkActive()) throw JSONRPCError(RPC_MISC_ERROR, "start-alias is not supported when deterministic masternode list is active (DIP3)"); if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; { LOCK(pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); } std::string strAlias = request.params[1].get_str(); bool fFound = false; UniValue statusObj(UniValue::VOBJ); statusObj.push_back(Pair("alias", strAlias)); for (const auto& 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); int nDoS; if (fResult && !mnodeman.CheckMnbAndUpdateMasternodeList(NULL, mnb, nDoS, *g_connman)) { strError = "Failed to verify MNB"; fResult = false; } statusObj.push_back(Pair("result", fResult ? "successful" : "failed")); if (!fResult) { statusObj.push_back(Pair("errorMessage", strError)); } mnodeman.NotifyMasternodeUpdates(*g_connman); break; } } if (!fFound) { statusObj.push_back(Pair("result", "failed")); statusObj.push_back(Pair("errorMessage", "Could not find alias in config. Verify with list-conf.")); } return statusObj; } void masternode_start_all_help() { throw std::runtime_error( "masternode start-all\n" "Start remote masternodes configured in masternode.conf\n" ); } UniValue StartMasternodeList(const std::vector& entries) { int nSuccessful = 0; int nFailed = 0; UniValue resultsObj(UniValue::VOBJ); for (const auto& mne : entries) { std::string strError; CMasternodeBroadcast mnb; bool fResult = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb); int nDoS; if (fResult && !mnodeman.CheckMnbAndUpdateMasternodeList(NULL, mnb, nDoS, *g_connman)) { strError = "Failed to verify MNB"; fResult = false; } UniValue statusObj(UniValue::VOBJ); statusObj.push_back(Pair("alias", mne.getAlias())); statusObj.push_back(Pair("result", fResult ? "successful" : "failed")); if (fResult) { nSuccessful++; } else { nFailed++; statusObj.push_back(Pair("errorMessage", strError)); } resultsObj.push_back(Pair("status", statusObj)); } 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))); returnObj.push_back(Pair("detail", resultsObj)); return returnObj; } UniValue masternode_start_all(const JSONRPCRequest& request) { if (request.fHelp) masternode_start_all_help(); if (deterministicMNManager->IsDeterministicMNsSporkActive()) throw JSONRPCError(RPC_MISC_ERROR, strprintf("start-all is not supported when deterministic masternode list is active (DIP3)")); if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; { LOCK(pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); } return StartMasternodeList(masternodeConfig.getEntries()); } void masternode_start_missing_help() { throw std::runtime_error( "masternode start-missing\n" "Start not started remote masternodes configured in masternode.conf\n" ); } UniValue masternode_start_missing(const JSONRPCRequest& request) { if (request.fHelp) masternode_start_missing_help(); if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; { LOCK(pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); } if (!masternodeSync.IsMasternodeListSynced()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "You can't use this command until masternode list is synced"); } std::vector entries; for (const auto& mne : masternodeConfig.getEntries()) { COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), (uint32_t)atoi(mne.getOutputIndex())); CMasternode mn; bool fFound = mnodeman.Get(outpoint, mn); if (fFound) entries.push_back(mne); } return StartMasternodeList(entries); } void masternode_start_disabled_help() { throw std::runtime_error( "masternode start-disabled\n" "Start not started and disabled remote masternodes configured in masternode.conf\n" ); } UniValue masternode_start_disabled(const JSONRPCRequest& request) { if (request.fHelp) masternode_start_disabled_help(); if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; { LOCK(pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); } if (!masternodeSync.IsMasternodeListSynced()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "You can't use this command until masternode list is synced"); } std::vector entries; for (const auto& mne : masternodeConfig.getEntries()) { COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), (uint32_t)atoi(mne.getOutputIndex())); CMasternode mn; bool fFound = mnodeman.Get(outpoint, mn); if (fFound && mn.IsEnabled()) continue; entries.push_back(mne); } return StartMasternodeList(entries); } void masternode_outputs_help() { throw std::runtime_error( "masternode outputs\n" "Print masternode compatible outputs\n" ); } UniValue masternode_outputs(const JSONRPCRequest& request) { if (request.fHelp) masternode_outputs_help(); if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; // Find possible candidates std::vector vPossibleCoins; pwalletMain->AvailableCoins(vPossibleCoins, true, NULL, false, ONLY_1000); UniValue obj(UniValue::VOBJ); for (const auto& out : vPossibleCoins) { obj.push_back(Pair(out.tx->GetHash().ToString(), strprintf("%d", out.i))); } return obj; } #endif // ENABLE_WALLET void masternode_genkey_help() { throw std::runtime_error( "masternode genkey (compressed)\n" "Generate new masternodeprivkey\n" "\nArguments:\n" "1. compressed (boolean, optional, default=false) generate compressed privkey\n" ); } UniValue masternode_genkey(const JSONRPCRequest& request) { if (request.fHelp) masternode_genkey_help(); bool fCompressed = false; if (request.params.size() > 1) { fCompressed = ParseBoolV(request.params[1], "compressed"); } CKey secret; secret.MakeNewKey(fCompressed); return CBitcoinSecret(secret).ToString(); } void masternode_list_conf_help() { throw std::runtime_error( "masternode list-conf\n" "Print masternode.conf in JSON format\n" ); } UniValue masternode_list_conf(const JSONRPCRequest& request) { if (request.fHelp) masternode_list_conf_help(); UniValue resultObj(UniValue::VOBJ); for (const auto& mne : masternodeConfig.getEntries()) { COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), (uint32_t)atoi(mne.getOutputIndex())); 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; } void masternode_status_help() { throw std::runtime_error( "masternode status\n" "Print masternode status information\n" ); } UniValue masternode_status(const JSONRPCRequest& request) { if (request.fHelp) masternode_status_help(); if (!fMasternodeMode) throw JSONRPCError(RPC_INTERNAL_ERROR, "This is not a masternode"); UniValue mnObj(UniValue::VOBJ); // keep compatibility with legacy status for now (might get deprecated/removed later) mnObj.push_back(Pair("outpoint", activeMasternodeInfo.outpoint.ToStringShort())); mnObj.push_back(Pair("service", activeMasternodeInfo.service.ToString())); if (deterministicMNManager->IsDeterministicMNsSporkActive()) { auto dmn = activeMasternodeManager->GetDMN(); if (dmn) { mnObj.push_back(Pair("proTxHash", dmn->proTxHash.ToString())); mnObj.push_back(Pair("collateralHash", dmn->collateralOutpoint.hash.ToString())); mnObj.push_back(Pair("collateralIndex", (int)dmn->collateralOutpoint.n)); UniValue stateObj; dmn->pdmnState->ToJson(stateObj); mnObj.push_back(Pair("dmnState", stateObj)); } mnObj.push_back(Pair("state", activeMasternodeManager->GetStateString())); mnObj.push_back(Pair("status", activeMasternodeManager->GetStatus())); } else { CMasternode mn; if (mnodeman.Get(activeMasternodeInfo.outpoint, mn)) { mnObj.push_back(Pair("payee", CBitcoinAddress(mn.keyIDCollateralAddress).ToString())); } mnObj.push_back(Pair("status", legacyActiveMasternodeManager.GetStatus())); } return mnObj; } void masternode_winners_help() { throw std::runtime_error( "masternode winners ( count \"filter\" )\n" "Print list of masternode winners\n" "\nArguments:\n" "1. count (numeric, optional) number of last winners to return\n" "2. filter (string, optional) filter for returned winners\n" ); } UniValue masternode_winners(const JSONRPCRequest& request) { if (request.fHelp) masternode_winners_help(); int nHeight; { LOCK(cs_main); CBlockIndex* pindex = chainActive.Tip(); if (!pindex) return NullUniValue; nHeight = pindex->nHeight; } int nLast = 10; std::string strFilter = ""; if (request.params.size() >= 2) { nLast = atoi(request.params[1].get_str()); } if (request.params.size() == 3) { strFilter = request.params[2].get_str(); } if (request.params.size() > 3) throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'masternode winners ( \"count\" \"filter\" )'"); UniValue obj(UniValue::VOBJ); auto mapPayments = GetRequiredPaymentsStrings(nHeight - nLast, nHeight + 20); for (const auto &p : mapPayments) { obj.push_back(Pair(strprintf("%d", p.first), p.second)); } return obj; } void masternode_check_help() { throw std::runtime_error( "masternode check\n" "Force check all masternodes and remove invalid ones\n" ); } UniValue masternode_check(const JSONRPCRequest& request) { if (request.fHelp) masternode_check_help(); int countBeforeCheck = mnodeman.CountMasternodes(); int countEnabledBeforeCheck = mnodeman.CountEnabled(); mnodeman.CheckAndRemove(*g_connman); int countAfterCheck = mnodeman.CountMasternodes(); int countEnabledAfterCheck = mnodeman.CountEnabled(); int removedCount = std::max(0, countBeforeCheck - countAfterCheck); int removedEnabledCount = std::max(0, countEnabledBeforeCheck - countEnabledAfterCheck); UniValue obj(UniValue::VOBJ); obj.push_back(Pair("removedTotalCount", removedCount)); obj.push_back(Pair("removedEnabledCount", removedEnabledCount)); obj.push_back(Pair("totalCount", countAfterCheck)); obj.push_back(Pair("enabledCount", countEnabledAfterCheck)); return obj; } [[ noreturn ]] void masternode_help() { 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" " check - Force check all masternodes and remove invalid ones\n" " count - Get information about number of masternodes (DEPRECATED options: 'total', 'ps', 'enabled', 'qualify', 'all')\n" " current - Print info on current masternode winner to be paid the next block (calculated locally)\n" " genkey - Generate new masternodeprivkey, optional param: 'compressed' (boolean, optional, default=false) generate compressed privkey\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- - Start remote masternodes configured in masternode.conf (: 'all', 'missing', 'disabled')\n" #endif // ENABLE_WALLET " 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" ); } UniValue masternode(const JSONRPCRequest& request) { std::string strCommand; if (request.params.size() >= 1) { strCommand = request.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 (request.fHelp && strCommand.empty()) { masternode_help(); } if (strCommand == "list") { return masternode_list(request); } else if (strCommand == "connect") { return masternode_connect(request); } else if (strCommand == "count") { return masternode_count(request); } else if (strCommand == "current") { return masternode_current(request); } else if (strCommand == "winner") { return masternode_winner(request); #ifdef ENABLE_WALLET } else if (strCommand == "start-alias") { return masternode_start_alias(request); } else if (strCommand == "start-all") { return masternode_start_all(request); } else if (strCommand == "start-missing") { return masternode_start_missing(request); } else if (strCommand == "start-disabled") { return masternode_start_disabled(request); #endif // ENABLE_WALLET } else if (strCommand == "genkey") { return masternode_genkey(request); } else if (strCommand == "list-conf") { return masternode_list_conf(request); #ifdef ENABLE_WALLET } else if (strCommand == "outputs") { return masternode_outputs(request); #endif // ENABLE_WALLET } else if (strCommand == "status") { return masternode_status(request); } else if (strCommand == "winners") { return masternode_winners(request); } else if (strCommand == "check") { return masternode_check(request); } else { masternode_help(); } } UniValue masternodelist(const JSONRPCRequest& request) { std::string strMode = "json"; std::string strFilter = ""; if (request.params.size() >= 1) strMode = request.params[0].get_str(); if (request.params.size() == 2) strFilter = request.params[1].get_str(); if (request.fHelp || ( strMode != "activeseconds" && strMode != "addr" && strMode != "daemon" && strMode != "full" && strMode != "info" && strMode != "json" && strMode != "lastseen" && strMode != "lastpaidtime" && strMode != "lastpaidblock" && strMode != "protocol" && strMode != "payee" && strMode != "pubkey" && strMode != "rank" && strMode != "sentinel" && strMode != "status")) { masternode_list_help(); } if (strMode == "full" || strMode == "json" || 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); for (const auto& rankpair : vMasternodeRanks) { std::string strOutpoint = rankpair.second.outpoint.ToStringShort(); if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue; obj.push_back(Pair(strOutpoint, rankpair.first)); } } else { std::map mapMasternodes = mnodeman.GetFullMasternodeMap(); for (const 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 == "daemon") { std::string strDaemon = mn.lastPing.GetDaemonString(); if (strFilter !="" && strDaemon.find(strFilter) == std::string::npos && strOutpoint.find(strFilter) == std::string::npos) continue; obj.push_back(Pair(strOutpoint, strDaemon)); } else if (strMode == "sentinel") { std::string strSentinel = mn.lastPing.GetSentinelString(); if (strFilter !="" && strSentinel.find(strFilter) == std::string::npos && strOutpoint.find(strFilter) == std::string::npos) continue; obj.push_back(Pair(strOutpoint, strSentinel)); } else if (strMode == "full") { std::ostringstream streamFull; streamFull << std::setw(18) << mn.GetStatus() << " " << mn.nProtocolVersion << " " << CBitcoinAddress(mn.keyIDCollateralAddress).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.keyIDCollateralAddress).ToString() << " " << (int64_t)mn.lastPing.sigTime << " " << std::setw(8) << (int64_t)(mn.lastPing.sigTime - mn.sigTime) << " " << mn.lastPing.GetSentinelString() << " " << (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 == "json") { std::ostringstream streamInfo; streamInfo << mn.addr.ToString() << " " << CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString() << " " << mn.GetStatus() << " " << mn.nProtocolVersion << " " << mn.lastPing.nDaemonVersion << " " << mn.lastPing.GetSentinelString() << " " << (mn.lastPing.fSentinelIsCurrent ? "current" : "expired") << " " << (int64_t)mn.lastPing.sigTime << " " << (int64_t)(mn.lastPing.sigTime - mn.sigTime) << " " << mn.GetLastPaidTime() << " " << mn.GetLastPaidBlock(); std::string strInfo = streamInfo.str(); if (strFilter !="" && strInfo.find(strFilter) == std::string::npos && strOutpoint.find(strFilter) == std::string::npos) continue; UniValue objMN(UniValue::VOBJ); objMN.push_back(Pair("address", mn.addr.ToString())); objMN.push_back(Pair("payee", CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString())); objMN.push_back(Pair("status", mn.GetStatus())); objMN.push_back(Pair("protocol", mn.nProtocolVersion)); objMN.push_back(Pair("daemonversion", mn.lastPing.GetDaemonString())); objMN.push_back(Pair("sentinelversion", mn.lastPing.GetSentinelString())); objMN.push_back(Pair("sentinelstate", (mn.lastPing.fSentinelIsCurrent ? "current" : "expired"))); objMN.push_back(Pair("lastseen", (int64_t)mn.lastPing.sigTime)); objMN.push_back(Pair("activeseconds", (int64_t)(mn.lastPing.sigTime - mn.sigTime))); objMN.push_back(Pair("lastpaidtime", mn.GetLastPaidTime())); objMN.push_back(Pair("lastpaidblock", mn.GetLastPaidBlock())); obj.push_back(Pair(strOutpoint, objMN)); } 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.keyIDCollateralAddress); 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, mn.nProtocolVersion)); } else if (strMode == "keyIDOwner") { if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue; obj.push_back(Pair(strOutpoint, HexStr(mn.keyIDOwner))); } else if (strMode == "keyIDOperator") { if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue; obj.push_back(Pair(strOutpoint, HexStr(mn.legacyKeyIDOperator))); } else if (strMode == "keyIDVoting") { if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) continue; obj.push_back(Pair(strOutpoint, HexStr(mn.keyIDVoting))); } 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& vecMnb, std::string strHexMnb) { if (!IsHex(strHexMnb)) return false; std::vector mnbData(ParseHex(strHexMnb)); CDataStream ssData(mnbData, SER_NETWORK, PROTOCOL_VERSION); try { ssData >> vecMnb; } catch (const std::exception&) { return false; } return true; } UniValue masternodebroadcast(const JSONRPCRequest& request) { std::string strCommand; if (request.params.size() >= 1) strCommand = request.params[0].get_str(); if (request.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") { if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; // 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 (request.params.size() < 2) throw JSONRPCError(RPC_INVALID_PARAMETER, "Please specify an alias"); { LOCK(pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); } bool fFound = false; std::string strAlias = request.params[1].get_str(); UniValue statusObj(UniValue::VOBJ); std::vector vecMnb; statusObj.push_back(Pair("alias", strAlias)); for (const auto& 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))); } 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") { if (!EnsureWalletIsAvailable(request.fHelp)) return NullUniValue; // 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(); } int nSuccessful = 0; int nFailed = 0; UniValue resultsObj(UniValue::VOBJ); std::vector vecMnb; for (const auto& 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 (request.params.size() != 2) throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'masternodebroadcast decode \"hexstring\"'"); std::vector vecMnb; if (!DecodeHexVecMnb(vecMnb, request.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); for (const auto& mnb : vecMnb) { UniValue resultObj(UniValue::VOBJ); if (mnb.CheckSignature(nDos)) { nSuccessful++; resultObj.push_back(Pair("outpoint", mnb.outpoint.ToStringShort())); resultObj.push_back(Pair("addr", mnb.addr.ToString())); resultObj.push_back(Pair("keyIDCollateralAddress", CBitcoinAddress(mnb.keyIDCollateralAddress).ToString())); resultObj.push_back(Pair("keyIDMasternode", CBitcoinAddress(mnb.legacyKeyIDOperator).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.masternodeOutpoint.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 (request.params.size() < 2 || request.params.size() > 3) throw JSONRPCError(RPC_INVALID_PARAMETER, "masternodebroadcast relay \"hexstring\"\n" "\nArguments:\n" "1. \"hex\" (string, required) Broadcast messages hex string\n"); std::vector vecMnb; if (!DecodeHexVecMnb(vecMnb, request.params[1].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Masternode broadcast message decode failed"); int nSuccessful = 0; int nFailed = 0; UniValue returnObj(UniValue::VOBJ); // verify all signatures first, bailout if any of them broken for (const auto& mnb : vecMnb) { UniValue resultObj(UniValue::VOBJ); resultObj.push_back(Pair("outpoint", mnb.outpoint.ToStringShort())); resultObj.push_back(Pair("addr", mnb.addr.ToString())); int nDos = 0; bool fResult; if (mnb.CheckSignature(nDos)) { fResult = mnodeman.CheckMnbAndUpdateMasternodeList(NULL, mnb, nDos, *g_connman); 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 JSONRPCRequest& request) { if (request.fHelp || request.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") ); } legacyActiveMasternodeManager.UpdateSentinelPing(StringVersionToInt(request.params[0].get_str())); return true; } static const CRPCCommand commands[] = { // category name actor (function) okSafe argNames // --------------------- ------------------------ ----------------------- ------ ---------- { "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 &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) t.appendCommand(commands[vcidx].name, &commands[vcidx]); }