text: // Copyright (c) 2009-2012 The Bitcoin developers
  // Copyright (c) 2015-2017 The PIVX 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 "db.h"
  #include "init.h"
  #include "main.h"
  #include "masternode-budget.h"
  #include "masternode-payments.h"
  #include "masternodeconfig.h"
  #include "masternodeman.h"
  #include "rpcserver.h"
  #include "utilmoneystr.h"
  
  #include <univalue.h>
  
  #include <boost/tokenizer.hpp>
  #include <fstream>
  
  
  void SendMoney(const CTxDestination& address, CAmount nValue, CWalletTx& wtxNew, AvailableCoinsType coin_type = ALL_COINS)
  {
      // Check amount
      if (nValue <= 0)
          throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
  
      if (nValue > pwalletMain->GetBalance())
          throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
  
      string strError;
      if (pwalletMain->IsLocked()) {
          strError = "Error: Wallet locked, unable to create transaction!";
          LogPrintf("SendMoney() : %s", strError);
          throw JSONRPCError(RPC_WALLET_ERROR, strError);
      }
  
      // Parse DACH address
      CScript scriptPubKey = GetScriptForDestination(address);
  
      // Create and send the transaction
      CReserveKey reservekey(pwalletMain);
      CAmount nFeeRequired;
      if (!pwalletMain->CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired, strError, NULL, coin_type)) {
          if (nValue + nFeeRequired > pwalletMain->GetBalance())
              strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
          LogPrintf("SendMoney() : %s\n", strError);
          throw JSONRPCError(RPC_WALLET_ERROR, strError);
      }
      if (!pwalletMain->CommitTransaction(wtxNew, reservekey))
          throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
  }
  
  // This command is retained for backwards compatibility, but is deprecated.
  // Future removal of this command is planned to keep things clean.
  UniValue masternode(const UniValue& params, bool fHelp)
  {
      string strCommand;
      if (params.size() >= 1)
          strCommand = params[0].get_str();
  
      if (fHelp ||
          (strCommand != "start" && strCommand != "start-alias" && strCommand != "start-many" && strCommand != "start-all" && strCommand != "start-missing" &&
              strCommand != "start-disabled" && strCommand != "list" && strCommand != "list-conf" && strCommand != "count" && strCommand != "enforce" &&
              strCommand != "debug" && strCommand != "current" && strCommand != "winners" && strCommand != "genkey" && strCommand != "connect" &&
              strCommand != "outputs" && strCommand != "status" && strCommand != "calcscore"))
          throw runtime_error(
              "masternode \"command\"...\n"
              "\nSet of commands to execute masternode related actions\n"
              "This command is deprecated, please see individual command documentation for future reference\n\n"
  
              "\nArguments:\n"
              "1. \"command\"        (string or set of strings, required) The command to execute\n"
  
              "\nAvailable commands:\n"
              "  count        - Print count information of all known masternodes\n"
              "  current      - Print info on current masternode winner\n"
              "  debug        - Print masternode status\n"
              "  genkey       - Generate new masternodeprivkey\n"
              "  outputs      - Print masternode compatible outputs\n"
              "  start        - Start masternode configured in dachcoin.conf\n"
              "  start-alias  - Start single masternode by assigned alias configured in masternode.conf\n"
              "  start-<mode> - Start masternodes configured in masternode.conf (<mode>: 'all', 'missing', 'disabled')\n"
              "  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"
              "  winners      - Print list of masternode winners\n");
  
      if (strCommand == "list") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return listmasternodes(newParams, fHelp);
      }
  
      if (strCommand == "connect") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return masternodeconnect(newParams, fHelp);
      }
  
      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 == "enabled")
              return mnodeman.CountEnabled();
  
          int nCount;
  
          if (strMode == "qualify")
              return nCount;
  
          if (strMode == "all")
              return strprintf("Total: %d (Enabled: %d / Qualify: %d)",
                  mnodeman.size(),
                  mnodeman.CountEnabled(), nCount);
  
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return getmasternodecount(newParams, fHelp);
      }
  
      if (strCommand == "current") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return masternodecurrent(newParams, fHelp);
      }
  
      if (strCommand == "debug") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return masternodedebug(newParams, fHelp);
      }
  
      if (strCommand == "start" || strCommand == "start-alias" || strCommand == "start-many" || strCommand == "start-all" || strCommand == "start-missing" || strCommand == "start-disabled") {
          return startmasternode(params, fHelp);
      }
  
      if (strCommand == "genkey") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return createmasternodekey(newParams, fHelp);
      }
  
      if (strCommand == "list-conf") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return listmasternodeconf(newParams, fHelp);
      }
  
      if (strCommand == "outputs") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return getmasternodeoutputs(newParams, fHelp);
      }
  
      if (strCommand == "status") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return getmasternodestatus(newParams, fHelp);
      }
  
      if (strCommand == "winners") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return getmasternodewinners(newParams, fHelp);
      }
  
      if (strCommand == "calcscore") {
          UniValue newParams(UniValue::VARR);
          // forward params but skip command
          for (unsigned int i = 1; i < params.size(); i++) {
              newParams.push_back(params[i]);
          }
          return getmasternodescores(newParams, fHelp);
      }
  
      return NullUniValue;
  }
  
  UniValue listmasternodes(const UniValue& params, bool fHelp)
  {
      std::string strFilter = "";
  
      if (params.size() == 1) strFilter = params[0].get_str();
  
      if (fHelp || (params.size() > 1))
          throw runtime_error(
              "listmasternodes ( \"filter\" )\n"
              "\nGet a ranked list of masternodes\n"
  
              "\nArguments:\n"
              "1. \"filter\"    (string, optional) Filter search text. Partial match by txhash, status, or addr.\n"
  
              "\nResult:\n"
              "[\n"
              "  {\n"
              "    \"rank\": n,           (numeric) Masternode Rank (or 0 if not enabled)\n"
              "    \"txhash\": \"hash\",    (string) Collateral transaction hash\n"
              "    \"outidx\": n,         (numeric) Collateral transaction output index\n"
              "    \"status\": s,         (string) Status (ENABLED/EXPIRED/REMOVE/etc)\n"
              "    \"addr\": \"addr\",      (string) Masternode DACH address\n"
              "    \"version\": v,        (numeric) Masternode protocol version\n"
              "    \"lastseen\": ttt,     (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last seen\n"
              "    \"activetime\": ttt,   (numeric) The time in seconds since epoch (Jan 1 1970 GMT) masternode has been active\n"
              "    \"lastpaid\": ttt,     (numeric) The time in seconds since epoch (Jan 1 1970 GMT) masternode was last paid\n"
              "  }\n"
              "  ,...\n"
              "]\n"
              "\nExamples:\n" +
              HelpExampleCli("masternodelist", "") + HelpExampleRpc("masternodelist", ""));
  
      UniValue ret(UniValue::VARR);
      int nHeight;
      {
          LOCK(cs_main);
          CBlockIndex* pindex = chainActive.Tip();
          if(!pindex) return 0;
          nHeight = pindex->nHeight;
      }
      std::vector<pair<int, CMasternode> > vMasternodeRanks = mnodeman.GetMasternodeRanks(nHeight);
      BOOST_FOREACH (PAIRTYPE(int, CMasternode) & s, vMasternodeRanks) {
          UniValue obj(UniValue::VOBJ);
          std::string strVin = s.second.vin.prevout.ToStringShort();
          std::string strTxHash = s.second.vin.prevout.hash.ToString();
          uint32_t oIdx = s.second.vin.prevout.n;
  
          CMasternode* mn = mnodeman.Find(s.second.vin);
  
          if (mn != NULL) {
              if (strFilter != "" && strTxHash.find(strFilter) == string::npos &&
                  mn->Status().find(strFilter) == string::npos &&
                  CBitcoinAddress(mn->pubKeyCollateralAddress.GetID()).ToString().find(strFilter) == string::npos) continue;
  
              std::string strStatus = mn->Status();
              std::string strHost;
              int port;
              SplitHostPort(mn->addr.ToString(), port, strHost);
              CNetAddr node = CNetAddr(strHost, false);
              std::string strNetwork = GetNetworkName(node.GetNetwork());
  
              obj.push_back(Pair("rank", (strStatus == "ENABLED" ? s.first : 0)));
              obj.push_back(Pair("network", strNetwork));
              obj.push_back(Pair("txhash", strTxHash));
              obj.push_back(Pair("outidx", (uint64_t)oIdx));
              obj.push_back(Pair("status", strStatus));
              obj.push_back(Pair("addr", CBitcoinAddress(mn->pubKeyCollateralAddress.GetID()).ToString()));
              obj.push_back(Pair("version", mn->protocolVersion));
              obj.push_back(Pair("lastseen", (int64_t)mn->lastPing.sigTime));
              obj.push_back(Pair("activetime", (int64_t)(mn->lastPing.sigTime - mn->sigTime)));
              obj.push_back(Pair("lastpaid", (int64_t)mn->GetLastPaid()));
  
              ret.push_back(obj);
          }
      }
  
      return ret;
  }
  
  UniValue masternodeconnect(const UniValue& params, bool fHelp)
  {
      if (fHelp || (params.size() != 1))
          throw runtime_error(
              "masternodeconnect \"address\"\n"
              "\nAttempts to connect to specified masternode address\n"
  
              "\nArguments:\n"
              "1. \"address\"     (string, required) IP or net address to connect to\n"
  
              "\nExamples:\n" +
              HelpExampleCli("masternodeconnect", "\"192.168.0.6:31701\"") + HelpExampleRpc("masternodeconnect", "\"192.168.0.6:31701\""));
  
      std::string strAddress = params[0].get_str();
  
      CService addr = CService(strAddress);
  
      CNode* pnode = ConnectNode((CAddress)addr, NULL);
      if (pnode) {
          pnode->Release();
          return NullUniValue;
      } else {
          throw runtime_error("error connecting\n");
      }
  }
  
  UniValue getmasternodecount (const UniValue& params, bool fHelp)
  {
      if (fHelp || (params.size() > 0))
          throw runtime_error(
              "getmasternodecount\n"
              "\nGet masternode count values\n"
  
              "\nResult:\n"
              "{\n"
              "  \"total\": n,        (numeric) Total masternodes\n"
              "  \"stable\": n,       (numeric) Stable count\n"
              "  \"enabled\": n,      (numeric) Enabled masternodes\n"
              "  \"inqueue\": n       (numeric) Masternodes in queue\n"
              "}\n"
              "\nExamples:\n" +
              HelpExampleCli("getmasternodecount", "") + HelpExampleRpc("getmasternodecount", ""));
  
      UniValue obj(UniValue::VOBJ);
      int nCount = 0;
      int ipv4 = 0, ipv6 = 0, onion = 0;
  
      if (chainActive.Tip())
          mnodeman.GetNextMasternodeInQueueForPayment(chainActive.Tip()->nHeight, true, nCount);
  
      mnodeman.CountNetworks(ActiveProtocol(), ipv4, ipv6, onion);
  
      obj.push_back(Pair("total", mnodeman.size()));
      obj.push_back(Pair("stable", mnodeman.stable_size()));
      obj.push_back(Pair("enabled", mnodeman.CountEnabled()));
      obj.push_back(Pair("inqueue", nCount));
      obj.push_back(Pair("ipv4", ipv4));
      obj.push_back(Pair("ipv6", ipv6));
      obj.push_back(Pair("onion", onion));
  
      return obj;
  }
  
  UniValue masternodecurrent (const UniValue& params, bool fHelp)
  {
      if (fHelp || (params.size() != 0))
          throw runtime_error(
              "masternodecurrent\n"
              "\nGet current masternode winner\n"
  
              "\nResult:\n"
              "{\n"
              "  \"protocol\": xxxx,        (numeric) Protocol version\n"
              "  \"txhash\": \"xxxx\",      (string) Collateral transaction hash\n"
              "  \"pubkey\": \"xxxx\",      (string) MN Public key\n"
              "  \"lastseen\": xxx,       (numeric) Time since epoch of last seen\n"
              "  \"activeseconds\": xxx,  (numeric) Seconds MN has been active\n"
              "}\n"
              "\nExamples:\n" +
              HelpExampleCli("masternodecurrent", "") + HelpExampleRpc("masternodecurrent", ""));
  
      CMasternode* winner = mnodeman.GetCurrentMasterNode(1);
      if (winner) {
          UniValue obj(UniValue::VOBJ);
  
          obj.push_back(Pair("protocol", (int64_t)winner->protocolVersion));
          obj.push_back(Pair("txhash", winner->vin.prevout.hash.ToString()));
          obj.push_back(Pair("pubkey", CBitcoinAddress(winner->pubKeyCollateralAddress.GetID()).ToString()));
          obj.push_back(Pair("lastseen", (winner->lastPing == CMasternodePing()) ? winner->sigTime : (int64_t)winner->lastPing.sigTime));
          obj.push_back(Pair("activeseconds", (winner->lastPing == CMasternodePing()) ? 0 : (int64_t)(winner->lastPing.sigTime - winner->sigTime)));
          return obj;
      }
  
      throw runtime_error("unknown");
  }
  
  UniValue masternodedebug (const UniValue& params, bool fHelp)
  {
      if (fHelp || (params.size() != 0))
          throw runtime_error(
              "masternodedebug\n"
              "\nPrint masternode status\n"
  
              "\nResult:\n"
              "\"status\"     (string) Masternode status message\n"
              "\nExamples:\n" +
              HelpExampleCli("masternodedebug", "") + HelpExampleRpc("masternodedebug", ""));
  
      if (activeMasternode.status != ACTIVE_MASTERNODE_INITIAL || !masternodeSync.IsSynced())
          return activeMasternode.GetStatus();
  
      CTxIn vin = CTxIn();
      CPubKey pubkey = CScript();
      CKey key;
      if (!activeMasternode.GetMasterNodeVin(vin, pubkey, key))
          throw runtime_error("Missing masternode input, please look at the documentation for instructions on masternode creation\n");
      else
          return activeMasternode.GetStatus();
  }
  
  UniValue startmasternode (const UniValue& params, bool fHelp)
  {
      std::string strCommand;
      if (params.size() >= 1) {
          strCommand = params[0].get_str();
  
          // Backwards compatibility with legacy 'masternode' super-command forwarder
          if (strCommand == "start") strCommand = "local";
          if (strCommand == "start-alias") strCommand = "alias";
          if (strCommand == "start-all") strCommand = "all";
          if (strCommand == "start-many") strCommand = "many";
          if (strCommand == "start-missing") strCommand = "missing";
          if (strCommand == "start-disabled") strCommand = "disabled";
      }
  
      if (fHelp || params.size() < 2 || params.size() > 3 ||
          (params.size() == 2 && (strCommand != "local" && strCommand != "all" && strCommand != "many" && strCommand != "missing" && strCommand != "disabled")) ||
          (params.size() == 3 && strCommand != "alias"))
          throw runtime_error(
              "startmasternode \"local|all|many|missing|disabled|alias\" lockwallet ( \"alias\" )\n"
              "\nAttempts to start one or more masternode(s)\n"
  
              "\nArguments:\n"
              "1. set         (string, required) Specify which set of masternode(s) to start.\n"
              "2. lockwallet  (boolean, required) Lock wallet after completion.\n"
              "3. alias       (string) Masternode alias. Required if using 'alias' as the set.\n"
  
              "\nResult: (for 'local' set):\n"
              "\"status\"     (string) Masternode status message\n"
  
              "\nResult: (for other sets):\n"
              "{\n"
              "  \"overall\": \"xxxx\",     (string) Overall status message\n"
              "  \"detail\": [\n"
              "    {\n"
              "      \"node\": \"xxxx\",    (string) Node name or alias\n"
              "      \"result\": \"xxxx\",  (string) 'success' or 'failed'\n"
              "      \"error\": \"xxxx\"    (string) Error message, if failed\n"
              "    }\n"
              "    ,...\n"
              "  ]\n"
              "}\n"
              "\nExamples:\n" +
              HelpExampleCli("startmasternode", "\"alias\" \"0\" \"my_mn\"") + HelpExampleRpc("startmasternode", "\"alias\" \"0\" \"my_mn\""));
  
      bool fLock = (params[1].get_str() == "true" ? true : false);
  
      if (strCommand == "local") {
          if (!fMasterNode) throw runtime_error("you must set masternode=1 in the configuration\n");
  
          if (pwalletMain->IsLocked())
              throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
  
          if (activeMasternode.status != ACTIVE_MASTERNODE_STARTED) {
              activeMasternode.status = ACTIVE_MASTERNODE_INITIAL; // TODO: consider better way
              activeMasternode.ManageStatus();
              if (fLock)
                  pwalletMain->Lock();
          }
  
          return activeMasternode.GetStatus();
      }
  
      if (strCommand == "all" || strCommand == "many" || strCommand == "missing" || strCommand == "disabled") {
          if (pwalletMain->IsLocked())
              throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
  
          if ((strCommand == "missing" || strCommand == "disabled") &&
              (masternodeSync.RequestedMasternodeAssets <= MASTERNODE_SYNC_LIST ||
                  masternodeSync.RequestedMasternodeAssets == MASTERNODE_SYNC_FAILED)) {
              throw runtime_error("You can't use this command until masternode list is synced\n");
          }
  
          std::vector<CMasternodeConfig::CMasternodeEntry> mnEntries;
          mnEntries = masternodeConfig.getEntries();
  
          int successful = 0;
          int failed = 0;
  
          UniValue resultsObj(UniValue::VARR);
  
          BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
              std::string errorMessage;
              int nIndex;
              if(!mne.castOutputIndex(nIndex))
                  continue;
              CTxIn vin = CTxIn(uint256(mne.getTxHash()), uint32_t(nIndex));
              CMasternode* pmn = mnodeman.Find(vin);
  
              if (pmn != NULL) {
                  if (strCommand == "missing") continue;
                  if (strCommand == "disabled" && pmn->IsEnabled()) continue;
              }
  
              bool result = activeMasternode.Register(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), errorMessage);
  
              UniValue statusObj(UniValue::VOBJ);
              statusObj.push_back(Pair("alias", mne.getAlias()));
              statusObj.push_back(Pair("result", result ? "success" : "failed"));
  
              if (result) {
                  successful++;
                  statusObj.push_back(Pair("error", ""));
              } else {
                  failed++;
                  statusObj.push_back(Pair("error", errorMessage));
              }
  
              resultsObj.push_back(statusObj);
          }
          if (fLock)
              pwalletMain->Lock();
  
          UniValue returnObj(UniValue::VOBJ);
          returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", successful, failed, successful + failed)));
          returnObj.push_back(Pair("detail", resultsObj));
  
          return returnObj;
      }
  
      if (strCommand == "alias") {
          std::string alias = params[2].get_str();
  
          if (pwalletMain->IsLocked())
              throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first.");
  
          bool found = false;
          int successful = 0;
          int failed = 0;
  
          UniValue resultsObj(UniValue::VARR);
          UniValue statusObj(UniValue::VOBJ);
          statusObj.push_back(Pair("alias", alias));
  
          BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
              if (mne.getAlias() == alias) {
                  found = true;
                  std::string errorMessage;
  
                  bool result = activeMasternode.Register(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), errorMessage);
  
                  statusObj.push_back(Pair("result", result ? "successful" : "failed"));
  
                  if (result) {
                      successful++;
                      statusObj.push_back(Pair("error", ""));
                  } else {
                      failed++;
                      statusObj.push_back(Pair("error", errorMessage));
                  }
                  break;
              }
          }
  
          if (!found) {
              failed++;
              statusObj.push_back(Pair("result", "failed"));
              statusObj.push_back(Pair("error", "could not find alias in config. Verify with list-conf."));
          }
  
          resultsObj.push_back(statusObj);
  
          if (fLock)
              pwalletMain->Lock();
  
          UniValue returnObj(UniValue::VOBJ);
          returnObj.push_back(Pair("overall", strprintf("Successfully started %d masternodes, failed to start %d, total %d", successful, failed, successful + failed)));
          returnObj.push_back(Pair("detail", resultsObj));
  
          return returnObj;
      }
      return NullUniValue;
  }
  
  UniValue createmasternodekey (const UniValue& params, bool fHelp)
  {
      if (fHelp || (params.size() != 0))
          throw runtime_error(
              "createmasternodekey\n"
              "\nCreate a new masternode private key\n"
  
              "\nResult:\n"
              "\"key\"    (string) Masternode private key\n"
              "\nExamples:\n" +
              HelpExampleCli("createmasternodekey", "") + HelpExampleRpc("createmasternodekey", ""));
  
      CKey secret;
      secret.MakeNewKey(false);
  
      return CBitcoinSecret(secret).ToString();
  }
  
  UniValue getmasternodeoutputs (const UniValue& params, bool fHelp)
  {
      if (fHelp || (params.size() != 0))
          throw runtime_error(
              "getmasternodeoutputs\n"
              "\nPrint all masternode transaction outputs\n"
  
              "\nResult:\n"
              "[\n"
              "  {\n"
              "    \"txhash\": \"xxxx\",    (string) output transaction hash\n"
              "    \"outputidx\": n       (numeric) output index number\n"
              "  }\n"
              "  ,...\n"
              "]\n"
  
              "\nExamples:\n" +
              HelpExampleCli("getmasternodeoutputs", "") + HelpExampleRpc("getmasternodeoutputs", ""));
  
      // Find possible candidates
      vector<COutput> possibleCoins = activeMasternode.SelectCoinsMasternode();
  
      UniValue ret(UniValue::VARR);
      BOOST_FOREACH (COutput& out, possibleCoins) {
          UniValue obj(UniValue::VOBJ);
          obj.push_back(Pair("txhash", out.tx->GetHash().ToString()));
          obj.push_back(Pair("outputidx", out.i));
          ret.push_back(obj);
      }
  
      return ret;
  }
  
  UniValue listmasternodeconf (const UniValue& params, bool fHelp)
  {
      std::string strFilter = "";
  
      if (params.size() == 1) strFilter = params[0].get_str();
  
      if (fHelp || (params.size() > 1))
          throw runtime_error(
              "listmasternodeconf ( \"filter\" )\n"
              "\nPrint masternode.conf in JSON format\n"
  
              "\nArguments:\n"
              "1. \"filter\"    (string, optional) Filter search text. Partial match on alias, address, txHash, or status.\n"
  
              "\nResult:\n"
              "[\n"
              "  {\n"
              "    \"alias\": \"xxxx\",        (string) masternode alias\n"
              "    \"address\": \"xxxx\",      (string) masternode IP address\n"
              "    \"privateKey\": \"xxxx\",   (string) masternode private key\n"
              "    \"txHash\": \"xxxx\",       (string) transaction hash\n"
              "    \"outputIndex\": n,       (numeric) transaction output index\n"
              "    \"status\": \"xxxx\"        (string) masternode status\n"
              "  }\n"
              "  ,...\n"
              "]\n"
  
              "\nExamples:\n" +
              HelpExampleCli("listmasternodeconf", "") + HelpExampleRpc("listmasternodeconf", ""));
  
      std::vector<CMasternodeConfig::CMasternodeEntry> mnEntries;
      mnEntries = masternodeConfig.getEntries();
  
      UniValue ret(UniValue::VARR);
  
      BOOST_FOREACH (CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
          int nIndex;
          if(!mne.castOutputIndex(nIndex))
              continue;
          CTxIn vin = CTxIn(uint256(mne.getTxHash()), uint32_t(nIndex));
          CMasternode* pmn = mnodeman.Find(vin);
  
          std::string strStatus = pmn ? pmn->Status() : "MISSING";
  
          if (strFilter != "" && mne.getAlias().find(strFilter) == string::npos &&
              mne.getIp().find(strFilter) == string::npos &&
              mne.getTxHash().find(strFilter) == string::npos &&
              strStatus.find(strFilter) == string::npos) continue;
  
          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));
          ret.push_back(mnObj);
      }
  
      return ret;
  }
  
  UniValue getmasternodestatus (const UniValue& params, bool fHelp)
  {
      if (fHelp || (params.size() != 0))
          throw runtime_error(
              "getmasternodestatus\n"
              "\nPrint masternode status\n"
  
              "\nResult:\n"
              "{\n"
              "  \"txhash\": \"xxxx\",      (string) Collateral transaction hash\n"
              "  \"outputidx\": n,        (numeric) Collateral transaction output index number\n"
              "  \"netaddr\": \"xxxx\",     (string) Masternode network address\n"
              "  \"addr\": \"xxxx\",        (string) DACH address for masternode payments\n"
              "  \"status\": \"xxxx\",      (string) Masternode status\n"
              "  \"message\": \"xxxx\"      (string) Masternode status message\n"
              "}\n"
  
              "\nExamples:\n" +
              HelpExampleCli("getmasternodestatus", "") + HelpExampleRpc("getmasternodestatus", ""));
  
      if (!fMasterNode) throw runtime_error("This is not a masternode");
  
      CMasternode* pmn = mnodeman.Find(activeMasternode.vin);
  
      if (pmn) {
          UniValue mnObj(UniValue::VOBJ);
          mnObj.push_back(Pair("txhash", activeMasternode.vin.prevout.hash.ToString()));
          mnObj.push_back(Pair("outputidx", (uint64_t)activeMasternode.vin.prevout.n));
          mnObj.push_back(Pair("netaddr", activeMasternode.service.ToString()));
          mnObj.push_back(Pair("addr", CBitcoinAddress(pmn->pubKeyCollateralAddress.GetID()).ToString()));
          mnObj.push_back(Pair("status", activeMasternode.status));
          mnObj.push_back(Pair("message", activeMasternode.GetStatus()));
          return mnObj;
      }
      throw runtime_error("Masternode not found in the list of available masternodes. Current status: "
                          + activeMasternode.GetStatus());
  }
  
  UniValue getmasternodewinners (const UniValue& params, bool fHelp)
  {
      if (fHelp || params.size() > 3)
          throw runtime_error(
              "getmasternodewinners ( blocks \"filter\" )\n"
              "\nPrint the masternode winners for the last n blocks\n"
  
              "\nArguments:\n"
              "1. blocks      (numeric, optional) Number of previous blocks to show (default: 10)\n"
              "2. filter      (string, optional) Search filter matching MN address\n"
  
              "\nResult (single winner):\n"
              "[\n"
              "  {\n"
              "    \"nHeight\": n,           (numeric) block height\n"
              "    \"winner\": {\n"
              "      \"address\": \"xxxx\",    (string) DACH MN Address\n"
              "      \"nVotes\": n,          (numeric) Number of votes for winner\n"
              "    }\n"
              "  }\n"
              "  ,...\n"
              "]\n"
  
              "\nResult (multiple winners):\n"
              "[\n"
              "  {\n"
              "    \"nHeight\": n,           (numeric) block height\n"
              "    \"winner\": [\n"
              "      {\n"
              "        \"address\": \"xxxx\",  (string) DACH MN Address\n"
              "        \"nVotes\": n,        (numeric) Number of votes for winner\n"
              "      }\n"
              "      ,...\n"
              "    ]\n"
              "  }\n"
              "  ,...\n"
              "]\n"
              "\nExamples:\n" +
              HelpExampleCli("getmasternodewinners", "") + HelpExampleRpc("getmasternodewinners", ""));
  
      int nHeight;
      {
          LOCK(cs_main);
          CBlockIndex* pindex = chainActive.Tip();
          if(!pindex) return 0;
          nHeight = pindex->nHeight;
      }
  
      int nLast = 10;
      std::string strFilter = "";
  
      if (params.size() >= 1)
          nLast = atoi(params[0].get_str());
  
      if (params.size() == 2)
          strFilter = params[1].get_str();
  
      UniValue ret(UniValue::VARR);
  
      for (int i = nHeight - nLast; i < nHeight + 20; i++) {
          UniValue obj(UniValue::VOBJ);
          obj.push_back(Pair("nHeight", i));
  
          std::string strPayment = GetRequiredPaymentsString(i);
          if (strFilter != "" && strPayment.find(strFilter) == std::string::npos) continue;
  
          if (strPayment.find(',') != std::string::npos) {
              UniValue winner(UniValue::VARR);
              boost::char_separator<char> sep(",");
              boost::tokenizer< boost::char_separator<char> > tokens(strPayment, sep);
              BOOST_FOREACH (const string& t, tokens) {
                  UniValue addr(UniValue::VOBJ);
                  std::size_t pos = t.find(":");
                  std::string strAddress = t.substr(0,pos);
                  uint64_t nVotes = atoi(t.substr(pos+1));
                  addr.push_back(Pair("address", strAddress));
                  addr.push_back(Pair("nVotes", nVotes));
                  winner.push_back(addr);
              }
              obj.push_back(Pair("winner", winner));
          } else if (strPayment.find("Unknown") == std::string::npos) {
              UniValue winner(UniValue::VOBJ);
              std::size_t pos = strPayment.find(":");
              std::string strAddress = strPayment.substr(0,pos);
              uint64_t nVotes = atoi(strPayment.substr(pos+1));
              winner.push_back(Pair("address", strAddress));
              winner.push_back(Pair("nVotes", nVotes));
              obj.push_back(Pair("winner", winner));
          } else {
              UniValue winner(UniValue::VOBJ);
              winner.push_back(Pair("address", strPayment));
              winner.push_back(Pair("nVotes", 0));
              obj.push_back(Pair("winner", winner));
          }
  
              ret.push_back(obj);
      }
  
      return ret;
  }
  
  UniValue getmasternodescores (const UniValue& params, bool fHelp)
  {
      if (fHelp || params.size() > 1)
          throw runtime_error(
              "getmasternodescores ( blocks )\n"
              "\nPrint list of winning masternode by score\n"
  
              "\nArguments:\n"
              "1. blocks      (numeric, optional) Show the last n blocks (default 10)\n"
  
              "\nResult:\n"
              "{\n"
              "  xxxx: \"xxxx\"   (numeric : string) Block height : Masternode hash\n"
              "  ,...\n"
              "}\n"
              "\nExamples:\n" +
              HelpExampleCli("getmasternodescores", "") + HelpExampleRpc("getmasternodescores", ""));
  
      int nLast = 10;
  
      if (params.size() == 1) {
          try {
              nLast = std::stoi(params[0].get_str());
          } catch (const boost::bad_lexical_cast &) {
              throw runtime_error("Exception on param 2");
          }
      }
      UniValue obj(UniValue::VOBJ);
  
      std::vector<CMasternode> vMasternodes = mnodeman.GetFullMasternodeVector();
      for (int nHeight = chainActive.Tip()->nHeight - nLast; nHeight < chainActive.Tip()->nHeight + 20; nHeight++) {
          uint256 nHigh = 0;
          CMasternode* pBestMasternode = NULL;
          BOOST_FOREACH (CMasternode& mn, vMasternodes) {
              uint256 n = mn.CalculateScore(1, nHeight - 100);
              if (n > nHigh) {
                  nHigh = n;
                  pBestMasternode = &mn;
              }
          }
          if (pBestMasternode)
              obj.push_back(Pair(strprintf("%d", nHeight), pBestMasternode->vin.prevout.hash.ToString().c_str()));
      }
  
      return obj;
  }
  