text: // Copyright (c) 2011-2016 The Cryptonote developers
  // Copyright (c) 2017-2021 The Cycere developers
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "PeerListManager.h"
  
  #include <time.h>
  #include <boost/foreach.hpp>
  #include <System/Ipv4Address.h>
  
  #include "Serialization/SerializationOverloads.h"
  
  using namespace Quid;
  
  namespace Quid {
    template <typename T, typename Indexes>
    bool serialize(boost::multi_index_container<T, Indexes>& value, Common::StringView name, ISerializer& s) {
      if (s.type() == ISerializer::INPUT) {
        readSequence<T>(std::inserter(value, value.end()), name, s);
      } else {
        writeSequence<T>(value.begin(), value.end(), name, s);
      }
  
      return true;
    }
  
    void serialize(NetworkAddress& na, ISerializer& s) {
      s(na.ip, "ip");
      s(na.port, "port");
    }
  
    void serialize(PeerlistEntry& pe, ISerializer& s) {
      s(pe.adr, "adr");
      s(pe.id, "id");
      s(pe.last_seen, "last_seen");
    }
  
  }
  
  PeerlistManager::Peerlist::Peerlist(peers_indexed& peers, size_t maxSize) :
    m_peers(peers), m_maxSize(maxSize) {
  }
  
  void PeerlistManager::serialize(ISerializer& s) {
    const uint8_t currentVersion = 1;
    uint8_t version = currentVersion;
  
    s(version, "version");
  
    if (version != currentVersion) {
      return;
    }
  
    s(m_peers_white, "whitelist");
    s(m_peers_gray, "graylist");
  }
  
  size_t PeerlistManager::Peerlist::count() const {
    return m_peers.size();
  }
  
  bool PeerlistManager::Peerlist::get(PeerlistEntry& entry, size_t i) const {
    if (i >= m_peers.size())
      return false;
  
    peers_indexed::index<by_time>::type& by_time_index = m_peers.get<by_time>();
  
    auto it = by_time_index.rbegin();
    std::advance(it, i);
    entry = *it;
  
    return true;
  }
  
  void PeerlistManager::Peerlist::trim() {
    peers_indexed::index<by_time>::type& sorted_index = m_peers.get<by_time>();
    while (m_peers.size() > m_maxSize) {
      sorted_index.erase(sorted_index.begin());
    }
  }
  
  PeerlistManager::PeerlistManager() : 
    m_whitePeerlist(m_peers_white, Quid::P2P_LOCAL_WHITE_PEERLIST_LIMIT),
    m_grayPeerlist(m_peers_gray, Quid::P2P_LOCAL_GRAY_PEERLIST_LIMIT) {}
  
  //--------------------------------------------------------------------------------------------------
  bool PeerlistManager::init(bool allow_local_ip)
  {
    m_allow_local_ip = allow_local_ip;
    return true;
  }
  
  //--------------------------------------------------------------------------------------------------
  void PeerlistManager::trim_white_peerlist() {
    m_whitePeerlist.trim();
  }
  //--------------------------------------------------------------------------------------------------
  void PeerlistManager::trim_gray_peerlist() {
    m_grayPeerlist.trim();
  }
  
  //--------------------------------------------------------------------------------------------------
  bool PeerlistManager::merge_peerlist(const std::list<PeerlistEntry>& outer_bs)
  { 
    for(const PeerlistEntry& be : outer_bs) {
      append_with_peer_gray(be);
    }
  
    // delete extra elements
    trim_gray_peerlist();
    return true;
  }
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::get_white_peer_by_index(PeerlistEntry& p, size_t i) const {
    return m_whitePeerlist.get(p, i);
  }
  
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::get_gray_peer_by_index(PeerlistEntry& p, size_t i) const {
    return m_grayPeerlist.get(p, i);
  }
  
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::is_ip_allowed(uint32_t ip) const
  {
    System::Ipv4Address addr(networkToHost(ip));
  
    //never allow loopback ip
    if (addr.isLoopback()) {
      return false;
    }
  
    if (!m_allow_local_ip && addr.isPrivate()) {
      return false;
    }
  
    return true;
  }
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::get_peerlist_head(std::list<PeerlistEntry>& bs_head, uint32_t depth) const
  {
    const peers_indexed::index<by_time>::type& by_time_index = m_peers_white.get<by_time>();
    uint32_t cnt = 0;
  
    BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index)
    {
      if (!vl.last_seen)
        continue;
      bs_head.push_back(vl);
      if (cnt++ > depth)
        break;
    }
    return true;
  }
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::get_peerlist_full(std::list<PeerlistEntry>& pl_gray, std::list<PeerlistEntry>& pl_white) const
  {
    const peers_indexed::index<by_time>::type& by_time_index_gr = m_peers_gray.get<by_time>();
    const peers_indexed::index<by_time>::type& by_time_index_wt = m_peers_white.get<by_time>();
  
    std::copy(by_time_index_gr.rbegin(), by_time_index_gr.rend(), std::back_inserter(pl_gray));
    std::copy(by_time_index_wt.rbegin(), by_time_index_wt.rend(), std::back_inserter(pl_white));
  
    return true;
  }
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::set_peer_just_seen(PeerIdType peer, uint32_t ip, uint32_t port)
  {
    NetworkAddress addr;
    addr.ip = ip;
    addr.port = port;
    return set_peer_just_seen(peer, addr);
  }
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::set_peer_just_seen(PeerIdType peer, const NetworkAddress& addr)
  {
    try {
      //find in white list
      PeerlistEntry ple;
      ple.adr = addr;
      ple.id = peer;
      ple.last_seen = time(NULL);
      return append_with_peer_white(ple);
    } catch (std::exception&) {
    }
  
    return false;
  }
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::append_with_peer_white(const PeerlistEntry& ple)
  {
    try {
      if (!is_ip_allowed(ple.adr.ip))
        return true;
  
      //find in white list
      auto by_addr_it_wt = m_peers_white.get<by_addr>().find(ple.adr);
      if (by_addr_it_wt == m_peers_white.get<by_addr>().end()) {
        //put new record into white list
        m_peers_white.insert(ple);
        trim_white_peerlist();
      } else {
        //update record in white list 
        m_peers_white.replace(by_addr_it_wt, ple);
      }
      //remove from gray list, if need
      auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr);
      if (by_addr_it_gr != m_peers_gray.get<by_addr>().end()) {
        m_peers_gray.erase(by_addr_it_gr);
      }
      return true;
    } catch (std::exception&) {
    }
    return false;
  }
  //--------------------------------------------------------------------------------------------------
  
  bool PeerlistManager::append_with_peer_gray(const PeerlistEntry& ple)
  {
    try {
      if (!is_ip_allowed(ple.adr.ip))
        return true;
  
      //find in white list
      auto by_addr_it_wt = m_peers_white.get<by_addr>().find(ple.adr);
      if (by_addr_it_wt != m_peers_white.get<by_addr>().end())
        return true;
  
      //update gray list
      auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr);
      if (by_addr_it_gr == m_peers_gray.get<by_addr>().end())
      {
        //put new record into white list
        m_peers_gray.insert(ple);
        trim_gray_peerlist();
      } else
      {
        //update record in white list 
        m_peers_gray.replace(by_addr_it_gr, ple);
      }
      return true;
    } catch (std::exception&) {
    }
    return false;
  }
  //--------------------------------------------------------------------------------------------------
  
  PeerlistManager::Peerlist& PeerlistManager::getWhite() { 
    return m_whitePeerlist; 
  }
  
  PeerlistManager::Peerlist& PeerlistManager::getGray() { 
    return m_grayPeerlist; 
  }
  