You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

176 lines
5.4 KiB

/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file EthereumHost.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <mutex>
#include <map>
#include <vector>
#include <set>
#include <memory>
#include <utility>
#include <thread>
#include <libdevcore/Guards.h>
#include <libdevcore/Worker.h>
#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 <class T> std::shared_ptr<T> registerCapability(T* _t) { _t->m_host = this; auto ret = std::shared_ptr<T>(_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 <class T> std::shared_ptr<T> cap() const { try { return std::static_pointer_cast<T>(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<PeerInfo> 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<Session> _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<h512, bi::tcp::endpoint> 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<h512, std::weak_ptr<Session>> m_peers; // mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method.
std::map<h512, std::pair<bi::tcp::endpoint, unsigned>> m_incomingPeers; // TODO: does this need a lock?
std::vector<h512> m_freePeers;
std::chrono::steady_clock::time_point m_lastPeersRequest;
unsigned m_idealPeerCount = 5;
std::vector<bi::address_v4> m_addresses;
std::vector<bi::address_v4> m_peerAddresses;
std::map<CapDesc, std::shared_ptr<HostCapabilityFace>> m_capabilities;
bool m_accepting = false;
};
}
}