|
|
@ -45,7 +45,7 @@ void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType con |
|
|
|
m_host.onNodeTableEvent(_n, _e); |
|
|
|
} |
|
|
|
|
|
|
|
Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork): |
|
|
|
Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bytesConstRef _restoreNetwork, bool _structuredLogging): |
|
|
|
Worker("p2p", 0), |
|
|
|
m_restoreNetwork(_restoreNetwork.toBytes()), |
|
|
|
m_clientVersion(_clientVersion), |
|
|
@ -54,12 +54,13 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, byte |
|
|
|
m_ioService(2), |
|
|
|
m_tcp4Acceptor(m_ioService), |
|
|
|
m_alias(networkAlias(_restoreNetwork)), |
|
|
|
m_lastPing(chrono::steady_clock::time_point::min()) |
|
|
|
m_lastPing(chrono::steady_clock::time_point::min()), |
|
|
|
m_structuredLogging(_structuredLogging) |
|
|
|
{ |
|
|
|
for (auto address: m_ifAddresses) |
|
|
|
if (address.is_v4()) |
|
|
|
clog(NetNote) << "IP Address: " << address << " = " << (isPrivateAddress(address) ? "[LOCAL]" : "[PEER]"); |
|
|
|
|
|
|
|
|
|
|
|
clog(NetNote) << "Id:" << id(); |
|
|
|
} |
|
|
|
|
|
|
@ -88,11 +89,11 @@ void Host::stop() |
|
|
|
return; |
|
|
|
m_run = false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// wait for m_timer to reset (indicating network scheduler has stopped)
|
|
|
|
while (!!m_timer) |
|
|
|
this_thread::sleep_for(chrono::milliseconds(50)); |
|
|
|
|
|
|
|
|
|
|
|
// stop worker thread
|
|
|
|
stopWorking(); |
|
|
|
} |
|
|
@ -101,12 +102,12 @@ void Host::doneWorking() |
|
|
|
{ |
|
|
|
// reset ioservice (allows manually polling network, below)
|
|
|
|
m_ioService.reset(); |
|
|
|
|
|
|
|
|
|
|
|
// shutdown acceptor
|
|
|
|
m_tcp4Acceptor.cancel(); |
|
|
|
if (m_tcp4Acceptor.is_open()) |
|
|
|
m_tcp4Acceptor.close(); |
|
|
|
|
|
|
|
|
|
|
|
// There maybe an incoming connection which started but hasn't finished.
|
|
|
|
// Wait for acceptor to end itself instead of assuming it's complete.
|
|
|
|
// This helps ensure a peer isn't stopped at the same time it's starting
|
|
|
@ -133,17 +134,17 @@ void Host::doneWorking() |
|
|
|
} |
|
|
|
if (!n) |
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
// poll so that peers send out disconnect packets
|
|
|
|
m_ioService.poll(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// stop network (again; helpful to call before subsequent reset())
|
|
|
|
m_ioService.stop(); |
|
|
|
|
|
|
|
|
|
|
|
// reset network (allows reusing ioservice in future)
|
|
|
|
m_ioService.reset(); |
|
|
|
|
|
|
|
|
|
|
|
// finally, clear out peers (in case they're lingering)
|
|
|
|
RecursiveGuard l(x_sessions); |
|
|
|
m_sessions.clear(); |
|
|
@ -165,7 +166,7 @@ void Host::registerPeer(std::shared_ptr<Session> _s, CapDescs const& _caps) |
|
|
|
m_peers[_s->m_peer->id] = _s->m_peer; |
|
|
|
m_sessions[_s->m_peer->id] = _s; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
unsigned o = (unsigned)UserPacket; |
|
|
|
for (auto const& i: _caps) |
|
|
|
if (haveCapability(i)) |
|
|
@ -181,7 +182,7 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) |
|
|
|
if (_e == NodeEntryAdded) |
|
|
|
{ |
|
|
|
clog(NetNote) << "p2p.host.nodeTable.events.nodeEntryAdded " << _n; |
|
|
|
|
|
|
|
|
|
|
|
auto n = m_nodeTable->node(_n); |
|
|
|
if (n) |
|
|
|
{ |
|
|
@ -198,12 +199,12 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) |
|
|
|
p->endpoint = NodeIPEndpoint(n.endpoint.udp, n.endpoint.tcp); |
|
|
|
p->required = n.required; |
|
|
|
m_peers[_n] = p; |
|
|
|
|
|
|
|
|
|
|
|
clog(NetNote) << "p2p.host.peers.events.peersAdded " << _n << p->endpoint.tcp.address() << p->endpoint.udp.address(); |
|
|
|
} |
|
|
|
p->endpoint.tcp = n.endpoint.tcp; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// TODO: Implement similar to discover. Attempt connecting to nodes
|
|
|
|
// until ideal peer count is reached; if all nodes are tried,
|
|
|
|
// repeat. Notably, this is an integrated process such that
|
|
|
@ -219,7 +220,7 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) |
|
|
|
else if (_e == NodeEntryRemoved) |
|
|
|
{ |
|
|
|
clog(NetNote) << "p2p.host.nodeTable.events.nodeEntryRemoved " << _n; |
|
|
|
|
|
|
|
|
|
|
|
RecursiveGuard l(x_sessions); |
|
|
|
m_peers.erase(_n); |
|
|
|
} |
|
|
@ -241,7 +242,7 @@ void Host::seal(bytes& _b) |
|
|
|
void Host::determinePublic(string const& _publicAddress, bool _upnp) |
|
|
|
{ |
|
|
|
m_peerAddresses.clear(); |
|
|
|
|
|
|
|
|
|
|
|
// no point continuing if there are no interface addresses or valid listen port
|
|
|
|
if (!m_ifAddresses.size() || m_listenPort < 1) |
|
|
|
return; |
|
|
@ -250,7 +251,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) |
|
|
|
for (auto addr: m_ifAddresses) |
|
|
|
if ((m_netPrefs.localNetworking || !isPrivateAddress(addr)) && !isLocalHostAddress(addr)) |
|
|
|
m_peerAddresses.insert(addr); |
|
|
|
|
|
|
|
|
|
|
|
// if user supplied address is a public address then we use it
|
|
|
|
// if user supplied address is private, and localnetworking is enabled, we use it
|
|
|
|
bi::address reqPublicAddr(bi::address(_publicAddress.empty() ? bi::address() : bi::address::from_string(_publicAddress))); |
|
|
@ -264,7 +265,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) |
|
|
|
m_tcpPublic = reqPublic; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// if address wasn't provided, then use first public ipv4 address found
|
|
|
|
for (auto addr: m_peerAddresses) |
|
|
|
if (addr.is_v4() && !isPrivateAddress(addr)) |
|
|
@ -272,7 +273,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) |
|
|
|
m_tcpPublic = bi::tcp::endpoint(*m_peerAddresses.begin(), m_listenPort); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// or find address via upnp
|
|
|
|
if (_upnp) |
|
|
|
{ |
|
|
@ -296,7 +297,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) |
|
|
|
m_tcpPublic = bi::tcp::endpoint(addr, m_listenPort); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// otherwise address is unspecified
|
|
|
|
m_tcpPublic = bi::tcp::endpoint(bi::address(), m_listenPort); |
|
|
|
} |
|
|
@ -304,7 +305,7 @@ void Host::determinePublic(string const& _publicAddress, bool _upnp) |
|
|
|
void Host::runAcceptor() |
|
|
|
{ |
|
|
|
assert(m_listenPort > 0); |
|
|
|
|
|
|
|
|
|
|
|
if (m_run && !m_accepting) |
|
|
|
{ |
|
|
|
clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_tcpPublic << ")"; |
|
|
@ -325,7 +326,7 @@ void Host::runAcceptor() |
|
|
|
// It's possible for an accepted connection to return an error in which
|
|
|
|
// case the socket may be open and must be closed to prevent asio from
|
|
|
|
// processing socket events after socket is deallocated.
|
|
|
|
|
|
|
|
|
|
|
|
bi::tcp::socket *s = new bi::tcp::socket(m_ioService); |
|
|
|
m_tcp4Acceptor.async_accept(*s, [=](boost::system::error_code ec) |
|
|
|
{ |
|
|
@ -349,7 +350,7 @@ void Host::runAcceptor() |
|
|
|
clog(NetWarn) << "ERROR: " << _e.what(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// asio doesn't close socket on error
|
|
|
|
if (!success && s->is_open()) |
|
|
|
{ |
|
|
@ -360,7 +361,7 @@ void Host::runAcceptor() |
|
|
|
|
|
|
|
m_accepting = false; |
|
|
|
delete s; |
|
|
|
|
|
|
|
|
|
|
|
if (ec.value() < 1) |
|
|
|
runAcceptor(); |
|
|
|
}); |
|
|
@ -376,7 +377,7 @@ void Host::doHandshake(bi::tcp::socket* _socket, NodeId _nodeId) |
|
|
|
shared_ptr<Peer> p; |
|
|
|
if (_nodeId) |
|
|
|
p = m_peers[_nodeId]; |
|
|
|
|
|
|
|
|
|
|
|
if (!p) |
|
|
|
p.reset(new Peer()); |
|
|
|
p->endpoint.tcp.address(_socket->remote_endpoint().address()); |
|
|
@ -399,16 +400,16 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short |
|
|
|
this_thread::sleep_for(chrono::milliseconds(50)); |
|
|
|
if (!m_run) |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
if (_tcpPeerPort < 30300 || _tcpPeerPort > 30305) |
|
|
|
cwarn << "Non-standard port being recorded: " << _tcpPeerPort; |
|
|
|
|
|
|
|
|
|
|
|
if (_tcpPeerPort >= /*49152*/32768) |
|
|
|
{ |
|
|
|
cwarn << "Private port being recorded - setting to 0"; |
|
|
|
_tcpPeerPort = 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
boost::system::error_code ec; |
|
|
|
bi::address addr = bi::address::from_string(_addr, ec); |
|
|
|
if (ec) |
|
|
@ -435,20 +436,20 @@ void Host::connect(std::shared_ptr<Peer> const& _p) |
|
|
|
this_thread::sleep_for(chrono::milliseconds(50)); |
|
|
|
if (!m_run) |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
if (havePeerSession(_p->id)) |
|
|
|
{ |
|
|
|
clog(NetWarn) << "Aborted connect. Node already connected."; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!m_nodeTable->haveNode(_p->id)) |
|
|
|
{ |
|
|
|
clog(NetWarn) << "Aborted connect. Node not in node table."; |
|
|
|
m_nodeTable->addNode(*_p.get()); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// prevent concurrently connecting to a node
|
|
|
|
Peer *nptr = _p.get(); |
|
|
|
{ |
|
|
@ -457,7 +458,7 @@ void Host::connect(std::shared_ptr<Peer> const& _p) |
|
|
|
return; |
|
|
|
m_pendingPeerConns.insert(nptr); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
clog(NetConnect) << "Attempting connection to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "from" << id().abridged(); |
|
|
|
bi::tcp::socket* s = new bi::tcp::socket(m_ioService); |
|
|
|
s->async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec) |
|
|
@ -474,9 +475,10 @@ void Host::connect(std::shared_ptr<Peer> const& _p) |
|
|
|
_p->m_lastDisconnect = NoDisconnect; |
|
|
|
_p->m_lastConnected = std::chrono::system_clock::now(); |
|
|
|
_p->m_failedAttempts = 0; |
|
|
|
|
|
|
|
auto ps = make_shared<Session>(this, std::move(*s), _p); |
|
|
|
ps->start(); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
delete s; |
|
|
|
Guard l(x_pendingNodeConns); |
|
|
@ -515,22 +517,22 @@ void Host::run(boost::system::error_code const&) |
|
|
|
{ |
|
|
|
// reset NodeTable
|
|
|
|
m_nodeTable.reset(); |
|
|
|
|
|
|
|
|
|
|
|
// stopping io service allows running manual network operations for shutdown
|
|
|
|
// and also stops blocking worker thread, allowing worker thread to exit
|
|
|
|
m_ioService.stop(); |
|
|
|
|
|
|
|
|
|
|
|
// resetting timer signals network that nothing else can be scheduled to run
|
|
|
|
m_timer.reset(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
m_nodeTable->processEvents(); |
|
|
|
|
|
|
|
|
|
|
|
for (auto p: m_sessions) |
|
|
|
if (auto pp = p.second.lock()) |
|
|
|
pp->serviceNodesRequest(); |
|
|
|
|
|
|
|
|
|
|
|
keepAlivePeers(); |
|
|
|
disconnectLatePeers(); |
|
|
|
|
|
|
@ -544,15 +546,15 @@ void Host::run(boost::system::error_code const&) |
|
|
|
connect(p.second); |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (c < m_idealPeerCount) |
|
|
|
m_nodeTable->discover(); |
|
|
|
|
|
|
|
|
|
|
|
auto runcb = [this](boost::system::error_code const& error) { run(error); }; |
|
|
|
m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); |
|
|
|
m_timer->async_wait(runcb); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Host::startedWorking() |
|
|
|
{ |
|
|
|
asserts(!m_timer); |
|
|
@ -566,33 +568,33 @@ void Host::startedWorking() |
|
|
|
m_timer.reset(new boost::asio::deadline_timer(m_ioService)); |
|
|
|
m_run = true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// try to open acceptor (todo: ipv6)
|
|
|
|
m_listenPort = Network::tcp4Listen(m_tcp4Acceptor, m_netPrefs.listenPort); |
|
|
|
|
|
|
|
|
|
|
|
// start capability threads
|
|
|
|
for (auto const& h: m_capabilities) |
|
|
|
h.second->onStarting(); |
|
|
|
|
|
|
|
|
|
|
|
// determine public IP, but only if we're able to listen for connections
|
|
|
|
// todo: GUI when listen is unavailable in UI
|
|
|
|
if (m_listenPort) |
|
|
|
{ |
|
|
|
determinePublic(m_netPrefs.publicIP, m_netPrefs.upnp); |
|
|
|
|
|
|
|
|
|
|
|
if (m_listenPort > 0) |
|
|
|
runAcceptor(); |
|
|
|
} |
|
|
|
else |
|
|
|
clog(NetNote) << "p2p.start.notice id:" << id().abridged() << "Listen port is invalid or unavailable. Node Table using default port (30303)."; |
|
|
|
|
|
|
|
|
|
|
|
// TODO: add m_tcpPublic endpoint; sort out endpoint stuff for nodetable
|
|
|
|
m_nodeTable.reset(new NodeTable(m_ioService, m_alias, m_listenPort > 0 ? m_listenPort : 30303)); |
|
|
|
m_nodeTable->setEventHandler(new HostNodeTableHandler(*this)); |
|
|
|
restoreNetwork(&m_restoreNetwork); |
|
|
|
|
|
|
|
|
|
|
|
clog(NetNote) << "p2p.started id:" << id().abridged(); |
|
|
|
|
|
|
|
|
|
|
|
run(boost::system::error_code()); |
|
|
|
} |
|
|
|
|
|
|
@ -606,7 +608,7 @@ void Host::keepAlivePeers() |
|
|
|
{ |
|
|
|
if (chrono::steady_clock::now() - c_keepAliveInterval < m_lastPing) |
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
RecursiveGuard l(x_sessions); |
|
|
|
for (auto p: m_sessions) |
|
|
|
if (auto pp = p.second.lock()) |
|
|
@ -641,7 +643,7 @@ bytes Host::saveNetwork() const |
|
|
|
peers.push_back(*p.second); |
|
|
|
} |
|
|
|
peers.sort(); |
|
|
|
|
|
|
|
|
|
|
|
RLPStream network; |
|
|
|
int count = 0; |
|
|
|
{ |
|
|
@ -683,7 +685,7 @@ bytes Host::saveNetwork() const |
|
|
|
count++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
RLPStream ret(3); |
|
|
|
ret << 1 << m_alias.secret(); |
|
|
|
ret.appendList(count).appendRaw(network.out(), count); |
|
|
@ -695,7 +697,7 @@ void Host::restoreNetwork(bytesConstRef _b) |
|
|
|
// nodes can only be added if network is added
|
|
|
|
if (!isStarted()) |
|
|
|
BOOST_THROW_EXCEPTION(NetworkStartRequired()); |
|
|
|
|
|
|
|
|
|
|
|
RecursiveGuard l(x_sessions); |
|
|
|
RLP r(_b); |
|
|
|
if (r.itemCount() > 0 && r[0].isInt() && r[0].toInt<int>() == 1) |
|
|
|