diff --git a/TODO b/TODO index 25745068e..39877c0de 100644 --- a/TODO +++ b/TODO @@ -23,11 +23,8 @@ FOR ALPHA: Network: * Manage GetBlocks properly; should work for when > 256 blocks away. - configurable (lower) mine times, configurable (reduced) max blocks to be sent then check. -* Kill peers when too many - - Respect idealPeerCount. - - Peer network traversal for peer discovery. - - Check peer utility (useful transactions & blocks passed) for when dismissing. - - Respect peer count & dismiss/collect/stop listening for peers as necessary. +* Respect and handle capabilities. +* Use GetTransactions on connect & respect it. UI: * State panel shouldn't show pending (i.e. post-mined) transactions. diff --git a/libethereum/PeerNetwork.cpp b/libethereum/PeerNetwork.cpp index cc82cf842..1dd6351b2 100644 --- a/libethereum/PeerNetwork.cpp +++ b/libethereum/PeerNetwork.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "Common.h" #include "BlockChain.h" #include "TransactionQueue.h" @@ -35,11 +36,13 @@ PeerSession::PeerSession(PeerServer* _s, bi::tcp::socket _socket, uint _rNId): m_reqNetworkId(_rNId), m_rating(0) { + m_disconnect = std::chrono::steady_clock::time_point::max(); + m_connect = std::chrono::steady_clock::now(); } PeerSession::~PeerSession() { - disconnect(); + m_socket.close(); } bi::tcp::endpoint PeerSession::endpoint() const @@ -139,6 +142,8 @@ bool PeerSession::interpret(RLP const& _r) } break; case Transactions: + if (m_server->m_mode == NodeMode::PeerServer) + break; cout << std::setw(2) << m_socket.native_handle() << " | Transactions (" << dec << (_r.itemCount() - 1) << " entries)" << endl; m_rating += _r.itemCount() - 1; for (unsigned i = 1; i < _r.itemCount(); ++i) @@ -148,6 +153,8 @@ bool PeerSession::interpret(RLP const& _r) } break; case Blocks: + if (m_server->m_mode == NodeMode::PeerServer) + break; cout << std::setw(2) << m_socket.native_handle() << " | Blocks (" << dec << (_r.itemCount() - 1) << " entries)" << endl; m_rating += _r.itemCount() - 1; for (unsigned i = 1; i < _r.itemCount(); ++i) @@ -167,6 +174,8 @@ bool PeerSession::interpret(RLP const& _r) break; case GetChain: { + if (m_server->m_mode == NodeMode::PeerServer) + break; // ******************************************************************** // NEEDS FULL REWRITE! h256s parents; @@ -239,6 +248,8 @@ bool PeerSession::interpret(RLP const& _r) } case NotInChain: { + if (m_server->m_mode == NodeMode::PeerServer) + break; h256 noGood = _r[1].toHash(); cout << std::setw(2) << m_socket.native_handle() << " | NotInChain (" << noGood << ")" << endl; if (noGood != m_server->m_chain->genesisHash()) @@ -337,12 +348,19 @@ void PeerSession::dropped() void PeerSession::disconnect() { - RLPStream s; - prep(s); - s.appendList(1) << (uint)Disconnect; - sealAndSend(s); - sleep(1); - m_socket.close(); + if (m_socket.is_open()) + { + if (m_disconnect == chrono::steady_clock::time_point::max()) + { + RLPStream s; + prep(s); + s.appendList(1) << (uint)Disconnect; + sealAndSend(s); + m_disconnect = chrono::steady_clock::now(); + } + else + m_socket.close(); + } } void PeerSession::start() @@ -538,19 +556,17 @@ bool PeerServer::process(BlockChain& _bc) bool ret = false; m_ioService.poll(); for (auto i = m_peers.begin(); i != m_peers.end();) - if (auto j = i->lock()) - if (j->m_socket.is_open()) - ++i; - else - { - i = m_peers.erase(i); - ret = true; - } + { + auto p = i->lock(); + if (p && p->m_socket.is_open() && + (p->m_disconnect == chrono::steady_clock::time_point::max() || chrono::steady_clock::now() - p->m_disconnect < chrono::seconds(1))) // kill old peers that should be disconnected. + ++i; else { i = m_peers.erase(i); ret = true; } + } return ret; } @@ -658,7 +674,6 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) // guarantees that everyone else respect the rules of the system. (i.e. obeys laws). // Connect to additional peers - // TODO: Need to avoid connecting to self & existing peers. Existing peers is easy, but need portable method of listing all addresses we can listen to avoid self. while (m_peers.size() < m_idealPeerCount) { if (m_incomingPeers.empty()) @@ -679,6 +694,25 @@ bool PeerServer::process(BlockChain& _bc, TransactionQueue& _tq, Overlay& _o) connect(m_incomingPeers.back()); m_incomingPeers.pop_back(); } + + while (m_peers.size() > m_idealPeerCount) + { + // look for worst peer to kick off + // first work out how many are old enough to kick off. + shared_ptr worst; + unsigned agedPeers = 0; + for (auto i: m_peers) + if (auto p = i.lock()) + if (chrono::steady_clock::now() - p->m_connect > chrono::seconds(10)) + { + ++agedPeers; + if ((!worst || p->m_rating < worst->m_rating || (p->m_rating == worst->m_rating && p->m_connect > worst->m_connect))) // keep younger ones. + worst = p; + } + if (!worst || agedPeers <= m_idealPeerCount) + break; + worst->dropped(); // should really disconnect, but that's no good. + } return ret; } diff --git a/libethereum/PeerNetwork.h b/libethereum/PeerNetwork.h index 7abc9c096..2ab134794 100644 --- a/libethereum/PeerNetwork.h +++ b/libethereum/PeerNetwork.h @@ -100,6 +100,8 @@ private: short m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. std::chrono::steady_clock::time_point m_ping; + std::chrono::steady_clock::time_point m_connect; + std::chrono::steady_clock::time_point m_disconnect; unsigned m_rating; @@ -107,6 +109,12 @@ private: std::set m_knownTransactions; }; +enum class NodeMode +{ + Full, + PeerServer +}; + class PeerServer { friend class PeerSession; @@ -129,7 +137,9 @@ public: bool process(BlockChain& _bc); /// Set ideal number of peers. - void setIdealPeerCount(uint _n) { m_idealPeerCount = _n; } + void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } + + void setMode(NodeMode _m) { m_mode = _m; } /// Get peer information. std::vector peers() const; @@ -149,6 +159,7 @@ private: std::vector potentialPeers(); std::string m_clientVersion; + NodeMode m_mode = NodeMode::Full; BlockChain const* m_chain = nullptr; ba::io_service m_ioService;