text: // Copyright (c) 2014-2015 The Dash developers
  // Copyright (c) 2015-2020 The Passion developers
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #ifndef MASTERNODE_PAYMENTS_H
  #define MASTERNODE_PAYMENTS_H
  
  #include "key.h"
  #include "masternode.h"
  
  
  extern RecursiveMutex cs_vecPayments;
  extern RecursiveMutex cs_mapMasternodeBlocks;
  extern RecursiveMutex cs_mapMasternodePayeeVotes;
  
  class CMasternodePayments;
  class CMasternodePaymentWinner;
  class CMasternodeBlockPayees;
  
  extern CMasternodePayments masternodePayments;
  
  #define MNPAYMENTS_SIGNATURES_REQUIRED 6
  #define MNPAYMENTS_SIGNATURES_TOTAL 10
  
  void ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
  bool IsBlockPayeeValid(const CBlock& block, int nBlockHeight);
  std::string GetRequiredPaymentsString(int nBlockHeight);
  bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted);
  void FillBlockPayee(CMutableTransaction& txNew, const int nHeight, bool fProofOfStake);
  
  void DumpMasternodePayments();
  
  /** Save Masternode Payment Data (mnpayments.dat)
   */
  class CMasternodePaymentDB
  {
  private:
      fs::path pathDB;
      std::string strMagicMessage;
  
  public:
      enum ReadResult {
          Ok,
          FileError,
          HashReadError,
          IncorrectHash,
          IncorrectMagicMessage,
          IncorrectMagicNumber,
          IncorrectFormat
      };
  
      CMasternodePaymentDB();
      bool Write(const CMasternodePayments& objToSave);
      ReadResult Read(CMasternodePayments& objToLoad);
  };
  
  class CMasternodePayee
  {
  public:
      CScript scriptPubKey;
      int mnlevel;
      int nVotes;
  
      CMasternodePayee()
      {
          scriptPubKey = CScript();
          mnlevel = CMasternode::LevelValue::UNSPECIFIED;
          nVotes = 0;
      }
  
      CMasternodePayee(int mnlevel, CScript payee, int nVotesIn)
      {
          scriptPubKey = payee;
          mnlevel = mnlevel;
          nVotes = nVotesIn;
      }
  
      ADD_SERIALIZE_METHODS;
  
      template <typename Stream, typename Operation>
      inline void SerializationOp(Stream& s, Operation ser_action)
      {
          READWRITE(*(CScriptBase*)(&scriptPubKey));
          READWRITE(mnlevel);
          READWRITE(nVotes);
      }
  };
  
  // Keep track of votes for payees from masternodes
  class CMasternodeBlockPayees
  {
  public:
      int nBlockHeight;
      int mnlevel;
      std::vector<CMasternodePayee> vecPayments;
  
      CMasternodeBlockPayees()
      {
          nBlockHeight = 0;
          vecPayments.clear();
      }
      CMasternodeBlockPayees(int nBlockHeightIn)
      {
          nBlockHeight = nBlockHeightIn;
          vecPayments.clear();
      }
  
      void AddPayee(const CScript& payeeIn, int mnlevel, int nIncrement)
      {
          LOCK(cs_vecPayments);
  
          //MulitMN
          auto payee = std::find_if(vecPayments.begin(), vecPayments.end(), [&payeeIn](const CMasternodePayee& p){
              return p.scriptPubKey == payeeIn;
          });
  
          if(payee == vecPayments.end())
              vecPayments.emplace_back(mnlevel, payeeIn, nIncrement);
          else
              payee->nVotes += nIncrement;
  
          /*
          for (CMasternodePayee& payee : vecPayments) {
              if (payee.scriptPubKey == payeeIn) {
                  payee.nVotes += nIncrement;
                  return;
              }
          }
  
          CMasternodePayee c(payeeIn, nIncrement);
          vecPayments.push_back(c);*/
      }
  
      bool GetPayee(int mnlevel, CScript& payee)
      {
          LOCK(cs_vecPayments);
          //MulitMN
          auto payment = vecPayments.cend();
          for(auto p = vecPayments.cbegin(), e = vecPayments.cend(); p != e; ++p) {
              if(p->mnlevel != mnlevel)
                  continue;
              if(payment == vecPayments.cend() || p->nVotes > payment->nVotes)
                  payment = p;
          };
          if(payment == vecPayments.cend())
              return false;
          payee = payment->scriptPubKey;
  
          return true;
          /*
          LOCK(cs_vecPayments);
  
          int nVotes = -1;
          for (CMasternodePayee& p : vecPayments) {
              if (p.nVotes > nVotes) {
                  payee = p.scriptPubKey;
                  nVotes = p.nVotes;
              }
          }
  
          return (nVotes > -1);*/
      }
  
      bool HasPayeeWithVotes(const CScript& payee, int nVotesReq)
      {
          LOCK(cs_vecPayments);
  
          for (CMasternodePayee& p : vecPayments) {
              if (p.nVotes >= nVotesReq && p.scriptPubKey == payee) return true;
          }
  
          return false;
      }
  
      bool IsTransactionValid(const CTransaction& txNew);
      std::string GetRequiredPaymentsString();
  
      ADD_SERIALIZE_METHODS;
  
      template <typename Stream, typename Operation>
      inline void SerializationOp(Stream& s, Operation ser_action)
      {
          READWRITE(nBlockHeight);
          READWRITE(vecPayments);
      }
  };
  
  // for storing the winning payments
  class CMasternodePaymentWinner : public CSignedMessage
  {
  public:
      CTxIn vinMasternode;
      int nBlockHeight;
      CScript payee;
      int payeeLevel;
  
      CMasternodePaymentWinner() :
          CSignedMessage(),
          vinMasternode(),
          nBlockHeight(0),
          payee()
      {
          payeeLevel = CMasternode::LevelValue::UNSPECIFIED;
      }
  
      CMasternodePaymentWinner(CTxIn vinIn) :
          CSignedMessage(),
          vinMasternode(vinIn),
          nBlockHeight(0),
          payee()
      {
          payeeLevel = CMasternode::LevelValue::UNSPECIFIED;
      }
  
      uint256 GetHash() const;
  
      // override CSignedMessage functions
      uint256 GetSignatureHash() const override { return GetHash(); }
      std::string GetStrMessage() const override;
      CTxIn GetVin() const { return vinMasternode; };
  
      bool IsValid(CNode* pnode, std::string& strError);
      void Relay();
  
      void AddPayee(const CScript& payeeIn, int payeeLevelIn)
      {
          payee = payeeIn;
          payeeLevel = payeeLevelIn;
      }
  
      ADD_SERIALIZE_METHODS;
  
      template <typename Stream, typename Operation>
      inline void SerializationOp(Stream& s, Operation ser_action)
      {
          READWRITE(vinMasternode);
          READWRITE(nBlockHeight);
          READWRITE(*(CScriptBase*)(&payee));
          READWRITE(vchSig);
          try
          {
              READWRITE(nMessVersion);
          } catch (...) {
              nMessVersion = MessageVersion::MESS_VER_STRMESS;
          }
  
          //READWRITE(payeeLevel);
      }
  
      std::string ToString()
      {
          std::string ret = "";
          ret += vinMasternode.ToString();
          ret += ", " + std::to_string(nBlockHeight);
          //ret += ", " + HexStr(payee);
          ret += ", " + std::to_string(payeeLevel) + ":" + HexStr(payee);
          ret += ", " + std::to_string((int)vchSig.size());
          return ret;
      }
  };
  
  //
  // Masternode Payments Class
  // Keeps track of who should get paid for which blocks
  //
  
  class CMasternodePayments
  {
  private:
      int nLastBlockHeight;
  
  public:
      std::map<uint256, CMasternodePaymentWinner> mapMasternodePayeeVotes;
      std::map<int, CMasternodeBlockPayees> mapMasternodeBlocks;
      std::map<COutPoint, int> mapMasternodesLastVote; //prevout, nBlockHeight
  
      CMasternodePayments()
      {
          nLastBlockHeight = 0;
      }
  
      void Clear()
      {
          LOCK2(cs_mapMasternodeBlocks, cs_mapMasternodePayeeVotes);
          mapMasternodeBlocks.clear();
          mapMasternodePayeeVotes.clear();
      }
  
      bool AddWinningMasternode(CMasternodePaymentWinner& winner);
      bool ProcessBlock(int nBlockHeight);
  
      void Sync(CNode* node, int nCountNeeded);
      void CleanPaymentList(int mnCount, int nHeight);
  
      bool GetBlockPayee(int nBlockHeight,int mnlevel, CScript& payee);
      bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight);
      bool IsScheduled(const CMasternode& mn, int nSameLevelMNCount, int mnlevel, int nNotBlockHeight);
  
      bool CanVote(const COutPoint& outMasternode, int nBlockHeight)
      {
          LOCK(cs_mapMasternodePayeeVotes);
  
          if (mapMasternodesLastVote.count(outMasternode)) {
              if (mapMasternodesLastVote[outMasternode] == nBlockHeight) {
                  return false;
              }
          }
  
          //record this masternode voted
          mapMasternodesLastVote[outMasternode] = nBlockHeight;
          return true;
      }
  
      void ProcessMessageMasternodePayments(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);
      std::string GetRequiredPaymentsString(int nBlockHeight);
      void FillBlockPayee(CMutableTransaction& txNew, const int nHeight, bool fProofOfStake);
      std::string ToString() const;
  
      ADD_SERIALIZE_METHODS;
  
      template <typename Stream, typename Operation>
      inline void SerializationOp(Stream& s, Operation ser_action)
      {
          READWRITE(mapMasternodePayeeVotes);
          READWRITE(mapMasternodeBlocks);
      }
  };
  
  
  #endif
  