diff --git a/alethzero/Main.ui b/alethzero/Main.ui
index 5e86ee59a..5a6eb6f32 100644
--- a/alethzero/Main.ui
+++ b/alethzero/Main.ui
@@ -1480,6 +1480,40 @@ font-size: 14pt
+
+
+ QDockWidget::DockWidgetFeatureMask
+
+
+ Nodes
+
+
+ 2
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::NoFrame
+
+
+
+
+
+
&Quit
diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp
index 7d3fe4085..77384d7ea 100644
--- a/alethzero/MainWin.cpp
+++ b/alethzero/MainWin.cpp
@@ -750,11 +750,33 @@ void Main::refreshBalances()
void Main::refreshNetwork()
{
auto ps = web3()->peers();
-
ui->peerCount->setText(QString::fromStdString(toString(ps.size())) + " peer(s)");
ui->peers->clear();
for (PeerInfo const& i: ps)
- ui->peers->addItem(QString("[%7] %3 ms - %1:%2 - %4 %5 %6").arg(i.host.c_str()).arg(i.port).arg(chrono::duration_cast(i.lastPing).count()).arg(i.clientVersion.c_str()).arg(QString::fromStdString(toString(i.caps))).arg(QString::fromStdString(toString(i.notes))).arg(i.socket));
+ ui->peers->addItem(QString("[%8 %7] %3 ms - %1:%2 - %4 %5 %6")
+ .arg(QString::fromStdString(i.host))
+ .arg(i.port)
+ .arg(chrono::duration_cast(i.lastPing).count())
+ .arg(QString::fromStdString(i.clientVersion))
+ .arg(QString::fromStdString(toString(i.caps)))
+ .arg(QString::fromStdString(toString(i.notes)))
+ .arg(i.socket)
+ .arg(QString::fromStdString(i.id.abridged())));
+
+ auto ns = web3()->nodes();
+ ui->nodes->clear();
+ for (p2p::Node const& i: ns)
+ ui->nodes->addItem(QString("[%1%3] %2 - ( =%5s | /%4s | %6 | %7x ) - *%8 $%9")
+ .arg(QString::fromStdString(i.id.abridged()))
+ .arg(QString::fromStdString(toString(i.address)))
+ .arg(i.id == web3()->id() ? " self" : i.isOffline() ? "" : " PEER")
+ .arg(i.secondsSinceLastAttempted())
+ .arg(i.secondsSinceLastConnected())
+ .arg(QString::fromStdString(reasonOf(i.lastDisconnect)))
+ .arg(i.failedAttempts)
+ .arg(i.rating)
+ .arg((int)i.idOrigin)
+ );
}
void Main::refreshAll()
diff --git a/libdevcrypto/MemoryDB.h b/libdevcrypto/MemoryDB.h
index 59435d3bf..446a947ec 100644
--- a/libdevcrypto/MemoryDB.h
+++ b/libdevcrypto/MemoryDB.h
@@ -32,7 +32,7 @@ namespace dev
namespace eth
{
-struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 12; };
+struct DBChannel: public LogChannel { static const char* name() { return "TDB"; } static const int verbosity = 18; };
#define dbdebug clog(DBChannel)
diff --git a/libdevcrypto/TrieDB.h b/libdevcrypto/TrieDB.h
index 16e2684fc..3dd76899a 100644
--- a/libdevcrypto/TrieDB.h
+++ b/libdevcrypto/TrieDB.h
@@ -42,7 +42,7 @@ namespace dev
namespace eth
{
-struct TrieDBChannel: public LogChannel { static const char* name() { return "-T-"; } static const int verbosity = 6; };
+struct TrieDBChannel: public LogChannel { static const char* name() { return "-T-"; } static const int verbosity = 17; };
#define tdebug clog(TrieDBChannel)
struct InvalidTrie: virtual dev::Exception {};
diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp
index 540f285c1..8a3b27ef0 100644
--- a/libp2p/Common.cpp
+++ b/libp2p/Common.cpp
@@ -64,7 +64,12 @@ std::string p2p::reasonOf(DisconnectReason _r)
case TooManyPeers: return "Peer had too many connections.";
case DuplicatePeer: return "Peer was already connected.";
case IncompatibleProtocol: return "Peer protocol versions are incompatible.";
+ case NullIdentity: return "Null identity given.";
case ClientQuit: return "Peer is exiting.";
+ case UnexpectedIdentity: return "Unexpected identity given.";
+ case LocalIdentity: return "Connected to ourselves.";
+ case UserReason: return "Subprotocol reason.";
+ case NoDisconnect: return "(No disconnect has happened.)";
default: return "Unknown reason.";
}
}
diff --git a/libp2p/Common.h b/libp2p/Common.h
index 7ef25ca6e..04154c9a0 100644
--- a/libp2p/Common.h
+++ b/libp2p/Common.h
@@ -87,9 +87,27 @@ enum DisconnectReason
NullIdentity,
ClientQuit,
UnexpectedIdentity,
- UserReason = 0x10
+ LocalIdentity,
+ UserReason = 0x10,
+ NoDisconnect = 0xffff
};
+inline bool isPermanentProblem(DisconnectReason _r)
+{
+ switch (_r)
+ {
+ case DisconnectRequested:
+ case DuplicatePeer:
+ case IncompatibleProtocol:
+ case NullIdentity:
+ case UnexpectedIdentity:
+ case LocalIdentity:
+ return true;
+ default:
+ return false;
+ }
+}
+
/// @returns the string form of the given disconnection reason.
std::string reasonOf(DisconnectReason _r);
diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp
index bb9901428..82de1727e 100644
--- a/libp2p/Host.cpp
+++ b/libp2p/Host.cpp
@@ -66,7 +66,6 @@ Host::Host(std::string const& _clientVersion, NetworkPreferences const& _n, bool
m_key(KeyPair::create())
{
populateAddresses();
- m_lastPeersRequest = chrono::steady_clock::time_point::min();
clog(NetNote) << "Id:" << id().abridged();
if (_start)
start();
@@ -112,7 +111,6 @@ void Host::start()
if (!m_public.address().is_unspecified() && (m_nodes.empty() || m_nodes[m_nodesList[0]]->id != id()))
noteNode(id(), m_public, Origin::Perfect, false);
- m_lastPeersRequest = chrono::steady_clock::time_point::min();
clog(NetNote) << "Id:" << id().abridged();
for (auto const& h: m_capabilities)
@@ -349,6 +347,10 @@ shared_ptr Host::noteNode(NodeId _id, bi::tcp::endpoint const& _a, Origin
{
RecursiveGuard l(x_peers);
cnote << "Node:" << _id.abridged() << _a << (_ready ? "ready" : "used") << _oldId.abridged() << (m_nodes.count(_id) ? "[have]" : "[NEW]");
+ if (!_a.port())
+ {
+ cwarn << "PORT IS INVALID!";
+ }
unsigned i;
if (!m_nodes.count(_id))
{
@@ -367,7 +369,7 @@ shared_ptr Host::noteNode(NodeId _id, bi::tcp::endpoint const& _a, Origin
m_nodesList.push_back(_id);
m_nodes[_id] = make_shared();
}
- m_nodes[_id]->address = m_public;
+ m_nodes[_id]->address = _a;
m_nodes[_id]->index = i;
m_nodes[_id]->id = _id;
m_nodes[_id]->idOrigin = _o;
@@ -390,6 +392,8 @@ shared_ptr Host::noteNode(NodeId _id, bi::tcp::endpoint const& _a, Origin
cnote << m_nodes[_id]->index << ":" << m_ready;
+ m_hadNewNodes = true;
+
return m_nodes[_id];
}
@@ -398,7 +402,8 @@ Nodes Host::potentialPeers(RangeMask const& _known)
RecursiveGuard l(x_peers);
Nodes ret;
- for (auto i: m_ready - (m_private + _known))
+ auto ns = (m_netPrefs.localNetworking ? _known : (m_private + _known)).inverted();
+ for (auto i: ns)
ret.push_back(*m_nodes[m_nodesList[i]]);
return ret;
}
@@ -494,7 +499,7 @@ void Host::connect(bi::tcp::endpoint const& _ep)
void Node::connect(Host* _h)
{
- clog(NetConnect) << "Attempting connection to node" << id.abridged() << "@" << address;
+ clog(NetConnect) << "Attempting connection to node" << id.abridged() << "@" << address << "from" << _h->id().abridged();
_h->m_ready -= index;
bi::tcp::socket* s = new bi::tcp::socket(_h->m_ioService);
s->async_connect(address, [=](boost::system::error_code const& ec)
@@ -566,23 +571,16 @@ void Host::growPeers()
else
{
ensureAccepting();
- if (chrono::steady_clock::now() > m_lastPeersRequest + chrono::seconds(10))
- requestPeers();
+ requestNodes();
}
}
}
-void Host::requestPeers()
+void Host::requestNodes()
{
- RLPStream s;
- bytes b;
- Session::prep(s, GetPeersPacket).swapOut(b);
- seal(b);
for (auto const& i: m_peers)
if (auto p = i.second.lock())
- if (p->isOpen())
- p->send(&b);
- m_lastPeersRequest = chrono::steady_clock::now();
+ p->ensureNodesRequested();
}
void Host::prunePeers()
@@ -637,6 +635,16 @@ void Host::doWork()
{
growPeers();
prunePeers();
+
+ if (m_hadNewNodes)
+ {
+ for (auto p: m_peers)
+ if (auto pp = p.second.lock())
+ pp->serviceNodesRequest();
+
+ m_hadNewNodes = false;
+ }
+
m_ioService.poll();
}
@@ -667,7 +675,7 @@ bytes Host::saveNodes() const
nodes << n.address.port() << n.id << (int)n.idOrigin
<< std::chrono::duration_cast(n.lastConnected.time_since_epoch()).count()
<< std::chrono::duration_cast(n.lastAttempted.time_since_epoch()).count()
- << n.failedAttempts << n.lastDisconnect << n.score << n.rating;
+ << n.failedAttempts << (unsigned)n.lastDisconnect << n.score << n.rating;
count++;
}
}
@@ -686,7 +694,11 @@ void Host::restoreNodes(bytesConstRef _b)
switch (r[0].toInt())
{
case 0:
+ {
+ auto oldId = id();
m_key = KeyPair(r[1].toHash());
+ noteNode(id(), m_public, Origin::Perfect, false, oldId);
+
for (auto i: r[2])
{
bi::tcp::endpoint ep;
@@ -702,11 +714,12 @@ void Host::restoreNodes(bytesConstRef _b)
n->lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt()));
n->lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt()));
n->failedAttempts = i[6].toInt();
- n->lastDisconnect = (int)i[7].toInt();
+ n->lastDisconnect = (DisconnectReason)i[7].toInt();
n->score = (int)i[8].toInt();
n->rating = (int)i[9].toInt();
}
}
+ }
default:;
}
else
diff --git a/libp2p/Host.h b/libp2p/Host.h
index 723cc5cba..43a5afb25 100644
--- a/libp2p/Host.h
+++ b/libp2p/Host.h
@@ -67,16 +67,19 @@ struct Node
std::chrono::system_clock::time_point lastConnected;
std::chrono::system_clock::time_point lastAttempted;
unsigned failedAttempts = 0;
- int lastDisconnect = -1; ///< Reason for disconnect that happened last.
+ DisconnectReason lastDisconnect = NoDisconnect; ///< Reason for disconnect that happened last.
- Origin idOrigin = Origin::Unknown; ///< Thirdparty
+ Origin idOrigin = Origin::Unknown; ///< How did we get to know this node's id?
- bool offline() const { return lastDisconnect == -1 || lastAttempted > lastConnected; }
+ int secondsSinceLastConnected() const { return lastConnected == std::chrono::system_clock::time_point() ? -1 : (int)std::chrono::duration_cast(std::chrono::system_clock::now() - lastConnected).count(); }
+ int secondsSinceLastAttempted() const { return lastAttempted == std::chrono::system_clock::time_point() ? -1 : (int)std::chrono::duration_cast(std::chrono::system_clock::now() - lastAttempted).count(); }
+
+ bool isOffline() const { return lastDisconnect == -1 || lastAttempted > lastConnected; }
bool operator<(Node const& _n) const
{
- if (offline() != _n.offline())
- return offline();
- else if (offline())
+ if (isOffline() != _n.isOffline())
+ return isOffline();
+ else if (isOffline())
if (lastAttempted == _n.lastAttempted)
return failedAttempts < _n.failedAttempts;
else
@@ -165,6 +168,8 @@ public:
/// Deserialise the data and populate the set of known peers.
void restoreNodes(bytesConstRef _b);
+ Nodes nodes() const { RecursiveGuard l(x_peers); Nodes ret; for (auto const& i: m_nodes) ret.push_back(*i.second); return ret; }
+
void setNetworkPreferences(NetworkPreferences const& _p) { stop(); m_netPrefs = _p; start(); }
void start();
@@ -194,7 +199,7 @@ private:
std::shared_ptr noteNode(NodeId _id, bi::tcp::endpoint const& _a, Origin _o, bool _ready, NodeId _oldId = h256());
Nodes potentialPeers(RangeMask const& _known);
- void requestPeers();
+ void requestNodes();
std::string m_clientVersion; ///< Our version string.
@@ -211,6 +216,8 @@ private:
bi::tcp::endpoint m_public; ///< Our public listening endpoint.
KeyPair m_key; ///< Our unique ID.
+ bool m_hadNewNodes = false;
+
mutable RecursiveMutex x_peers;
/// The nodes to which we are currently connected.
@@ -227,8 +234,6 @@ private:
RangeMask m_ready; ///< Indices into m_nodesList over to which nodes we are not currently connected, connecting or otherwise ignoring.
RangeMask m_private; ///< Indices into m_nodesList over to which nodes are private.
- std::chrono::steady_clock::time_point m_lastPeersRequest; ///< Last time we asked for some peers - don't want to do this too often. TODO: peers should be pushed, not polled.
-
unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to.
// Our addresses.
diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp
index 65517c0d1..d647d97dc 100644
--- a/libp2p/Session.cpp
+++ b/libp2p/Session.cpp
@@ -62,8 +62,8 @@ Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const&
Session::~Session()
{
- if (id())
- m_server->noteNode(id(), m_manualEndpoint, Origin::Unknown, true);
+ if (id() && !isPermanentProblem(m_node->lastDisconnect))
+ m_server->m_ready += m_node->index;
// Read-chain finished for one reason or another.
for (auto& i: m_capabilities)
@@ -125,6 +125,47 @@ template vector randomSelection(vector const& _t, unsigned _n)
return ret;
}
+void Session::ensureNodesRequested()
+{
+ if (isOpen() && !m_weRequestedNodes)
+ {
+ m_weRequestedNodes = true;
+ RLPStream s;
+ sealAndSend(prep(s, GetPeersPacket));
+ }
+}
+
+void Session::serviceNodesRequest()
+{
+ if (!m_theyRequestedNodes)
+ return;
+
+ auto peers = m_server->potentialPeers(m_knownNodes);
+ if (peers.empty())
+ {
+ addNote("peers", "requested");
+ return;
+ }
+
+ // note this should cost them...
+ RLPStream s;
+ prep(s, PeersPacket, min(10, peers.size()));
+ auto rs = randomSelection(peers, 10);
+ for (auto const& i: rs)
+ {
+ clogS(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.address;
+ if (i.address.address().is_v4())
+ s.appendList(3) << bytesConstRef(i.address.address().to_v4().to_bytes().data(), 4) << i.address.port() << i.id;
+ else// if (i.second.address().is_v6()) - assumed
+ s.appendList(3) << bytesConstRef(i.address.address().to_v6().to_bytes().data(), 16) << i.address.port() << i.id;
+ m_knownNodes.extendAll(i.index);
+ m_knownNodes.unionWith(i.index);
+ }
+ sealAndSend(s);
+ m_theyRequestedNodes = false;
+ addNote("peers", "done");
+}
+
bool Session::interpret(RLP const& _r)
{
clogS(NetRight) << _r;
@@ -136,7 +177,7 @@ bool Session::interpret(RLP const& _r)
case HelloPacket:
{
if (m_node)
- m_node->lastDisconnect = -1;
+ m_node->lastDisconnect = NoDisconnect;
m_protocolVersion = _r[1].toInt();
auto clientVersion = _r[2].toString();
@@ -152,6 +193,14 @@ bool Session::interpret(RLP const& _r)
clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << id.abridged() << showbase << capslog.str() << dec << listenPort;
+ if (m_server->id() == id)
+ {
+ // Already connected.
+ clogS(NetWarn) << "Connected to ourself under a false pretext. We were told this peer was id" << m_info.id.abridged();
+ disconnect(LocalIdentity);
+ return false;
+ }
+
if (m_server->havePeer(id))
{
// Already connected.
@@ -221,27 +270,13 @@ bool Session::interpret(RLP const& _r)
case GetPeersPacket:
{
clogS(NetTriviaSummary) << "GetPeers";
- auto peers = m_server->potentialPeers(m_knownNodes);
- if (peers.empty())
- break;
- RLPStream s;
- prep(s, PeersPacket, min(10, peers.size()));
- auto rs = randomSelection(peers, 10);
- for (auto const& i: rs)
- {
- clogS(NetTriviaDetail) << "Sending peer " << i.id.abridged() << i.address;
- if (i.address.address().is_v4())
- s.appendList(3) << bytesConstRef(i.address.address().to_v4().to_bytes().data(), 4) << i.address.port() << i.id;
- else// if (i.second.address().is_v6()) - assumed
- s.appendList(3) << bytesConstRef(i.address.address().to_v6().to_bytes().data(), 16) << i.address.port() << i.id;
- m_knownNodes.extendAll(i.index);
- m_knownNodes.unionWith(i.index);
- }
- sealAndSend(s);
+ m_theyRequestedNodes = true;
+ serviceNodesRequest();
break;
}
case PeersPacket:
clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)";
+ m_weRequestedNodes = false;
for (unsigned i = 1; i < _r.itemCount(); ++i)
{
bi::address peerAddress;
@@ -329,12 +364,6 @@ void Session::ping()
m_ping = std::chrono::steady_clock::now();
}
-void Session::getPeers()
-{
- RLPStream s;
- sealAndSend(prep(s, GetPeersPacket));
-}
-
RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args)
{
return prep(_s).appendList(_args + 1).append((unsigned)_id);
@@ -449,7 +478,7 @@ void Session::dropped()
catch (...) {}
}
-void Session::disconnect(int _reason)
+void Session::disconnect(DisconnectReason _reason)
{
clogS(NetConnect) << "Disconnecting (reason:" << reasonOf((DisconnectReason)_reason) << ")";
@@ -481,8 +510,6 @@ void Session::start()
<< m_server->id();
sealAndSend(s);
ping();
- getPeers();
-
doRead();
}
diff --git a/libp2p/Session.h b/libp2p/Session.h
index ff05be81b..e76edd8b1 100644
--- a/libp2p/Session.h
+++ b/libp2p/Session.h
@@ -55,7 +55,7 @@ public:
virtual ~Session();
void start();
- void disconnect(int _reason);
+ void disconnect(DisconnectReason _reason);
void ping();
@@ -82,6 +82,9 @@ public:
PeerInfo const& info() const { return m_info; }
+ void ensureNodesRequested();
+ void serviceNodesRequest();
+
private:
void dropped();
void doRead();
@@ -89,7 +92,6 @@ private:
void writeImpl(bytes& _buffer);
void write();
- void getPeers();
bool interpret(RLP const& _r);
/// @returns true iff the _msg forms a valid message for sending or receiving on the network.
@@ -110,6 +112,9 @@ private:
bi::tcp::endpoint m_manualEndpoint;
bool m_force = false; /// If true, ignore IDs being different. This could open you up to MitM attacks.
+ bool m_theyRequestedNodes = false;
+ bool m_weRequestedNodes = false;
+
std::chrono::steady_clock::time_point m_ping;
std::chrono::steady_clock::time_point m_connect;
std::chrono::steady_clock::time_point m_disconnect;
diff --git a/libwebthree/WebThree.h b/libwebthree/WebThree.h
index fd0991eaa..32bbe0b31 100644
--- a/libwebthree/WebThree.h
+++ b/libwebthree/WebThree.h
@@ -108,6 +108,11 @@ public:
void setNetworkPreferences(p2p::NetworkPreferences const& _n) { auto had = haveNetwork(); if (had) stopNetwork(); m_net.setNetworkPreferences(_n); if (had) startNetwork(); }
+ p2p::NodeId id() const { return m_net.id(); }
+
+ /// Gets the nodes.
+ p2p::Nodes nodes() const { return m_net.nodes(); }
+
/// Start the network subsystem.
void startNetwork() { m_net.start(); }