/* This file is part of cpp-ethereum. cpp-ethereum is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. cpp-ethereum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ /** @file EthereumHost.h * @author Gav Wood * @date 2014 */ #pragma once #include #include #include #include #include #include #include #include #include #include "HostCapability.h" #include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; namespace dev { class RLPStream; namespace p2p { struct NetworkPreferences { NetworkPreferences(unsigned short p = 30303, std::string i = std::string(), bool u = true, bool l = false): listenPort(p), publicIP(i), upnp(u), localNetworking(l) {} unsigned short listenPort = 30303; std::string publicIP; bool upnp = true; bool localNetworking = false; }; /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. */ class Host: public Worker { friend class Session; friend class HostCapabilityFace; public: /// Start server, listening for connections on the given port. Host(std::string const& _clientVersion, NetworkPreferences const& _n = NetworkPreferences(), bool _start = false); /// Will block on network process events. virtual ~Host(); /// Closes all peers. void disconnectPeers(); /// Basic peer network protocol version. unsigned protocolVersion() const; /// Register a peer-capability; all new peer connections will have this capability. template std::shared_ptr registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr(_t); m_capabilities[std::make_pair(T::staticName(), T::staticVersion())] = ret; return ret; } bool haveCapability(CapDesc const& _name) const { return m_capabilities.count(_name) != 0; } CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } } /// Connect to a peer explicitly. static std::string pocHost(); void connect(std::string const& _addr, unsigned short _port = 30303) noexcept; void connect(bi::tcp::endpoint const& _ep); /// @returns true iff we have the a peer of the given id. bool havePeer(h512 _id) const; /// Set ideal number of peers. void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } /// Get peer information. std::vector peers(bool _updatePing = false) const; /// Get number of peers connected; equivalent to, but faster than, peers().size(). size_t peerCount() const { Guard l(x_peers); return m_peers.size(); } /// Ping the peers, to update the latency information. void pingAll(); /// Get the port we're listening on currently. unsigned short listenPort() const { return m_public.port(); } /// Serialise the set of known peers. bytes savePeers() const; /// Deserialise the data and populate the set of known peers. void restorePeers(bytesConstRef _b); void setNetworkPreferences(NetworkPreferences const& _p) { stop(); m_netPrefs = _p; start(); } void start(); void stop(); bool isStarted() const { return isWorking(); } h512 id() const { return m_id; } void registerPeer(std::shared_ptr _s, CapDescs const& _caps); private: /// Called when the session has provided us with a new peer we can connect to. void noteNewPeers() {} void seal(bytes& _b); void populateAddresses(); void determinePublic(std::string const& _publicAddress, bool _upnp); void ensureAccepting(); void growPeers(); void prunePeers(); /// Conduct I/O, polling, syncing, whatever. /// Ideally all time-consuming I/O is done in a background thread or otherwise asynchronously, but you get this call every 100ms or so anyway. /// This won't touch alter the blockchain. virtual void doWork(); std::map potentialPeers(); std::string m_clientVersion; NetworkPreferences m_netPrefs; static const int NetworkStopped = -1; int m_listenPort = NetworkStopped; ba::io_service m_ioService; bi::tcp::acceptor m_acceptor; bi::tcp::socket m_socket; UPnP* m_upnp = nullptr; bi::tcp::endpoint m_public; h512 m_id; mutable std::mutex x_peers; mutable std::map> m_peers; // mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. std::map> m_incomingPeers; // TODO: does this need a lock? std::vector m_freePeers; std::chrono::steady_clock::time_point m_lastPeersRequest; unsigned m_idealPeerCount = 5; std::vector m_addresses; std::vector m_peerAddresses; std::map> m_capabilities; bool m_accepting = false; }; } }