/* 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 PeerNetwork.h * @author Gav Wood * @date 2014 */ #pragma once #include #include #include #include #include #include #include "RLP.h" #include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; namespace eth { bool isPrivateAddress(bi::address _addressToCheck); class BlockChain; class TransactionQueue; struct NetWarn: public LogChannel { static const char constexpr* name = "!N!"; static const int verbosity = 0; }; struct NetNote: public LogChannel { static const char constexpr* name = "*N*"; static const int verbosity = 1; }; struct NetMessageSummary: public LogChannel { static const char constexpr* name = "-N-"; static const int verbosity = 2; }; struct NetMessageDetail: public LogChannel { static const char constexpr* name = "=N="; static const int verbosity = 3; }; struct NetAllDetail: public LogChannel { static const char constexpr* name = "=N="; static const int verbosity = 6; }; struct NetRight: public LogChannel { static const char constexpr* name = ">N>"; static const int verbosity = 8; }; struct NetLeft: public LogChannel { static const char constexpr* name = " { friend class PeerServer; public: PeerSession(PeerServer* _server, bi::tcp::socket _socket, uint _rNId, bi::address _peerAddress, ushort _peerPort = 0); ~PeerSession(); void start(); void disconnect(int _reason); void ping(); bool isOpen() const { return m_socket.is_open(); } bi::tcp::endpoint endpoint() const; ///< for other peers to connect to. private: void dropped(); void doRead(); void doWrite(std::size_t length); bool interpret(RLP const& _r); /// @returns true iff the _msg forms a valid message for sending or receiving on the network. static bool checkPacket(bytesConstRef _msg); static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); void sendDestroy(bytes& _msg); void send(bytesConstRef _msg); PeerServer* m_server; bi::tcp::socket m_socket; std::array m_data; PeerInfo m_info; Public m_id; bytes m_incoming; uint m_protocolVersion; uint m_networkId; uint m_reqNetworkId; ushort m_listenPort; ///< Port that the remote client is listening on for connections. Useful for giving to peers. uint m_caps; 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; bool m_requireTransactions; std::set m_knownBlocks; std::set m_knownTransactions; }; enum class NodeMode { Full, PeerServer }; class UPnP; class PeerServer { friend class PeerSession; public: /// Start server, listening for connections on the given port. PeerServer(std::string const& _clientVersion, BlockChain const& _ch, uint _networkId, ushort _port, NodeMode _m = NodeMode::Full, std::string const& _publicAddress = std::string(), bool _upnp = true); /// Start server, but don't listen. PeerServer(std::string const& _clientVersion, uint _networkId, NodeMode _m = NodeMode::Full); ~PeerServer(); /// Connect to a peer explicitly. void connect(std::string const& _addr, ushort _port = 30303) noexcept; void connect(bi::tcp::endpoint const& _ep); /// Sync with the BlockChain. It might contain one of our mined blocks, we might have new candidates from the network. bool sync(BlockChain& _bc, TransactionQueue&, Overlay& _o); bool sync(); /// 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. void process() { if (isInitialised()) m_ioService.poll(); } /// Set ideal number of peers. void setIdealPeerCount(unsigned _n) { m_idealPeerCount = _n; } void setMode(NodeMode _m) { m_mode = _m; } /// Get peer information. std::vector peers() const; /// Get number of peers connected; equivalent to, but faster than, peers().size(). unsigned peerCount() const { return m_peers.size(); } /// Ping the peers, to update the latency information. void pingAll(); /// Get the port we're listening on currently. ushort listenPort() const { return m_public.port(); } bytes savePeers() const; void restorePeers(bytesConstRef _b); private: void seal(bytes& _b); void populateAddresses(); void determinePublic(std::string const& _publicAddress, bool _upnp); void ensureAccepting(); /// Check to see if the network peer-state initialisation has happened. bool isInitialised() const { return m_latestBlockSent; } /// Initialises the network peer-state, doing the stuff that needs to be once-only. @returns true if it really was first. bool ensureInitialised(BlockChain& _bc, TransactionQueue& _tq); std::map potentialPeers(); std::string m_clientVersion; NodeMode m_mode = NodeMode::Full; ushort m_listenPort; BlockChain const* m_chain = nullptr; ba::io_service m_ioService; bi::tcp::acceptor m_acceptor; bi::tcp::socket m_socket; UPnP* m_upnp = nullptr; bi::tcp::endpoint m_public; KeyPair m_key; uint m_requiredNetworkId; std::map> m_peers; std::vector m_incomingTransactions; std::vector m_incomingBlocks; std::vector m_unknownParentBlocks; std::vector m_freePeers; std::map> m_incomingPeers; h256 m_latestBlockSent; std::set m_transactionsSent; std::chrono::steady_clock::time_point m_lastPeersRequest; unsigned m_idealPeerCount = 5; std::vector m_addresses; std::vector m_peerAddresses; bool m_accepting = false; }; }