From e9dfa8642efff90d7fb7106ec234bd46f6b4fe29 Mon Sep 17 00:00:00 2001
From: subtly <subtly@users.noreply.github.com>
Date: Tue, 21 Apr 2015 05:30:41 -0400
Subject: [PATCH] compiles (v4 endpoint changes)

---
 libp2p/Common.cpp    |  2 +-
 libp2p/Common.h      | 11 ++++++++
 libp2p/Host.cpp      | 44 +++++++++++++++----------------
 libp2p/NodeTable.cpp | 45 +++++++++++++-------------------
 libp2p/NodeTable.h   | 62 +++++++++++++-------------------------------
 5 files changed, 69 insertions(+), 95 deletions(-)

diff --git a/libp2p/Common.cpp b/libp2p/Common.cpp
index 1853679cf..cb39fc051 100644
--- a/libp2p/Common.cpp
+++ b/libp2p/Common.cpp
@@ -24,7 +24,7 @@ using namespace std;
 using namespace dev;
 using namespace dev::p2p;
 
-const unsigned dev::p2p::c_protocolVersion = 3;
+const unsigned dev::p2p::c_protocolVersion = 4;
 const unsigned dev::p2p::c_defaultIPPort = 30303;
 
 const dev::p2p::NodeIPEndpoint dev::p2p::UnspecifiedNodeIPEndpoint = NodeIPEndpoint(bi::address(), 0, 0);
diff --git a/libp2p/Common.h b/libp2p/Common.h
index 378064e7d..baf90b4db 100644
--- a/libp2p/Common.h
+++ b/libp2p/Common.h
@@ -36,6 +36,7 @@
 #include <libdevcrypto/Common.h>
 #include <libdevcore/Log.h>
 #include <libdevcore/Exceptions.h>
+#include <libdevcore/RLP.h>
 namespace ba = boost::asio;
 namespace bi = boost::asio::ip;
 
@@ -162,10 +163,17 @@ using PeerSessionInfos = std::vector<PeerSessionInfo>;
  */
 struct NodeIPEndpoint
 {
+	enum InlineRLP
+	{
+		CreateList,
+		InlineList
+	};
+	
 	/// Setting true causes isAllowed to return true for all addresses. (Used by test fixtures)
 	static bool test_allowLocal;
 
 	NodeIPEndpoint(bi::address _addr, uint16_t _udp, uint16_t _tcp): address(_addr), udpPort(_udp), tcpPort(_tcp) {}
+	NodeIPEndpoint(RLP const& _r) { interpretRLP(_r); }
 
 	bi::address address;
 	uint16_t udpPort;
@@ -177,6 +185,9 @@ struct NodeIPEndpoint
 	operator bool() const { return !address.is_unspecified() && udpPort > 0 && tcpPort > 0; }
 	
 	bool isAllowed() const { return NodeIPEndpoint::test_allowLocal ? !address.is_unspecified() : isPublicAddress(address); }
+	
+	void streamRLP(RLPStream& _s, bool _inline = CreateList) const { if (_inline == CreateList) _s.appendList(3); if (address.is_v4()) _s << address.to_v4().to_bytes(); else _s << address.to_v6().to_bytes(); _s << udpPort << tcpPort; }
+	void interpretRLP(RLP const& _r) { if (_r[0].size() == 4) address = bi::address_v4(_r[0].toArray<byte, 4>()); else address = bi::address_v6(_r[0].toArray<byte, 16>()); udpPort = _r[1].toInt<uint16_t>(); tcpPort = _r[2].toInt<uint16_t>(); }
 };
 	
 struct Node
diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp
index fff718295..08289bea8 100644
--- a/libp2p/Host.cpp
+++ b/libp2p/Host.cpp
@@ -678,15 +678,16 @@ bytes Host::saveNetwork() const
 	int count = 0;
 	for (auto const& p: peers)
 	{
-		// Only save peers which have connected within 2 days, with properly-advertised port and public IP address
-		// todo: e2e ipv6 support
+		// todo: ipv6
 		if (!p.endpoint.address.is_v4())
 			continue;
 
-		if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && p.endpoint.tcpPort > 0 && p.id != id() && (p.required || p.endpoint.isAllowed()))
+		// Only save peers which have connected within 2 days, with properly-advertised port and public IP address
+		if (chrono::system_clock::now() - p.m_lastConnected < chrono::seconds(3600 * 48) && !!p.endpoint && p.id != id() && (p.required || p.endpoint.isAllowed()))
 		{
-			network.appendList(10);
-			network << p.endpoint.address.to_v4().to_bytes() << p.endpoint.tcpPort << p.id << p.required
+			network.appendList(11);
+			p.endpoint.streamRLP(network, NodeIPEndpoint::InlineList);
+			network << p.id << p.required
 				<< chrono::duration_cast<chrono::seconds>(p.m_lastConnected.time_since_epoch()).count()
 				<< chrono::duration_cast<chrono::seconds>(p.m_lastAttempted.time_since_epoch()).count()
 				<< p.m_failedAttempts << (unsigned)p.m_lastDisconnect << p.m_score << p.m_rating;
@@ -700,12 +701,9 @@ bytes Host::saveNetwork() const
 		state.sort();
 		for (auto const& entry: state)
 		{
-			network.appendList(3);
-			if (entry.endpoint.address.is_v4())
-				network << entry.endpoint.address.to_v4().to_bytes();
-			else
-				network << entry.endpoint.address.to_v6().to_bytes();
-			network << entry.endpoint.tcpPort << entry.id;
+			network.appendList(4);
+			entry.endpoint.streamRLP(network, NodeIPEndpoint::InlineList);
+			network << entry.id;
 			count++;
 		}
 	}
@@ -738,25 +736,25 @@ void Host::restoreNetwork(bytesConstRef _b)
 
 		for (auto i: r[2])
 		{
+			// todo: ipv6
 			if (i[0].itemCount() != 4)
 				continue;
-			
-			// todo: ipv6, bi::address_v6(i[0].toArray<byte, 16>()
-			Node n((NodeId)i[2], NodeIPEndpoint(bi::address_v4(i[0].toArray<byte, 4>()), i[1].toInt<uint16_t>(), i[1].toInt<uint16_t>()));
-			if (i.itemCount() == 3 && n.endpoint.isAllowed())
+
+			Node n((NodeId)i[3], NodeIPEndpoint(i));
+			if (i.itemCount() == 4 && n.endpoint.isAllowed())
 				m_nodeTable->addNode(n);
-			else if (i.itemCount() == 10)
+			else if (i.itemCount() == 11)
 			{
-				n.required = i[3].toInt<bool>();
+				n.required = i[4].toInt<bool>();
 				if (!n.endpoint.isAllowed() && !n.required)
 					continue;
 				shared_ptr<Peer> p = make_shared<Peer>(n);
-				p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[4].toInt<unsigned>()));
-				p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
-				p->m_failedAttempts = i[6].toInt<unsigned>();
-				p->m_lastDisconnect = (DisconnectReason)i[7].toInt<unsigned>();
-				p->m_score = (int)i[8].toInt<unsigned>();
-				p->m_rating = (int)i[9].toInt<unsigned>();
+				p->m_lastConnected = chrono::system_clock::time_point(chrono::seconds(i[5].toInt<unsigned>()));
+				p->m_lastAttempted = chrono::system_clock::time_point(chrono::seconds(i[6].toInt<unsigned>()));
+				p->m_failedAttempts = i[7].toInt<unsigned>();
+				p->m_lastDisconnect = (DisconnectReason)i[8].toInt<unsigned>();
+				p->m_score = (int)i[9].toInt<unsigned>();
+				p->m_rating = (int)i[10].toInt<unsigned>();
 				m_peers[p->id] = p;
 				if (p->required)
 					requirePeer(p->id, n.endpoint);
diff --git a/libp2p/NodeTable.cpp b/libp2p/NodeTable.cpp
index 35db7fbac..e7f4d1f33 100644
--- a/libp2p/NodeTable.cpp
+++ b/libp2p/NodeTable.cpp
@@ -99,9 +99,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node)
 			Guard l(x_pubkDiscoverPings);
 			m_pubkDiscoverPings[_node.endpoint.address] = std::chrono::steady_clock::now();
 		}
-		PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
-		p.sign(m_secret);
-		m_socketPointer->send(p);
+		ping(_node.endpoint);
 		return move(shared_ptr<NodeEntry>());
 	}
 	
@@ -114,9 +112,7 @@ shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node)
 	shared_ptr<NodeEntry> ret(new NodeEntry(m_node, _node.id, _node.endpoint));
 	m_nodes[_node.id] = ret;
 	clog(NodeTableConnect) << "addNode pending for" << _node.endpoint;
-	PingNode p(_node.endpoint, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
-	p.sign(m_secret);
-	m_socketPointer->send(p);
+	ping(_node.endpoint);
 	return ret;
 }
 
@@ -291,9 +287,9 @@ vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeId _target)
 	return move(ret);
 }
 
-void NodeTable::ping(bi::udp::endpoint _to) const
+void NodeTable::ping(NodeIPEndpoint _to) const
 {
-	PingNode p(_to, m_node.endpoint.address.to_string(), m_node.endpoint.udpPort);
+	PingNode p(m_node.endpoint, _to);
 	p.sign(m_secret);
 	m_socketPointer->send(p);
 }
@@ -488,8 +484,8 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
 				}
 				
 				Neighbours in = Neighbours::fromBytesConstRef(_from, rlpBytes);
-				for (auto n: in.nodes)
-					addNode(n.node, NodeIPEndpoint(bi::address::from_string(n.ipAddress), n.udpPort, n.udpPort));
+				for (auto n: in.neighbours)
+					addNode(n.node, n.endpoint);
 				break;
 			}
 
@@ -520,10 +516,9 @@ void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytes
 					return;
 				}
 				
-				// TODO: Feedback if _from.address() != in.ipAddress
-				addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), in.tcpPort));
-				
-				Pong p(_from);
+				// TODO: Feedback if _from.address() != in.ipAddress, or, _from.por() != in.source.udpPort
+				auto node = addNode(nodeid, NodeIPEndpoint(_from.address(), _from.port(), in.source.tcpPort));
+				Pong p(node->endpoint);
 				p.echo = sha3(rlpBytes);
 				p.sign(m_secret);
 				m_socketPointer->send(p);
@@ -599,26 +594,22 @@ void NodeTable::doRefreshBuckets(boost::system::error_code const& _ec)
 void PingNode::streamRLP(RLPStream& _s) const
 {
 	_s.appendList(4);
-	_s << dev::p2p::c_protocolVersion << ipAddress << tcpPort << ts;
+	_s << dev::p2p::c_protocolVersion;
+	source.streamRLP(_s);
+	destination.streamRLP(_s);
+	_s << ts;
 }
 
 void PingNode::interpretRLP(bytesConstRef _bytes)
 {
 	RLP r(_bytes);
-	if (r.itemCountStrict() == 3)
-	{
-		version = 2;
-		ipAddress = r[0].toString();
-		tcpPort = r[1].toInt<unsigned>(RLP::Strict);
-		ts = r[2].toInt<unsigned>(RLP::Strict);
-	}
-	else if (r.itemCountStrict() == 4)
+	if (r.itemCountStrict() == 4 && r[0].isInt() && r[0].toInt<unsigned>(RLP::Strict) == dev::p2p::c_protocolVersion)
 	{
-		version = r[0].toInt<unsigned>(RLP::Strict);
-		ipAddress = r[1].toString();
-		tcpPort = r[2].toInt<unsigned>(RLP::Strict);
+		version = dev::p2p::c_protocolVersion;
+		source.interpretRLP(r[1]);
+		destination.interpretRLP(r[2]);
 		ts = r[3].toInt<unsigned>(RLP::Strict);
 	}
 	else
-		BOOST_THROW_EXCEPTION(InvalidRLP());
+		version = 0;
 }
diff --git a/libp2p/NodeTable.h b/libp2p/NodeTable.h
index 07247462f..c96abde86 100644
--- a/libp2p/NodeTable.h
+++ b/libp2p/NodeTable.h
@@ -204,7 +204,7 @@ private:
 	};
 
 	/// Used to ping endpoint.
-	void ping(bi::udp::endpoint _to) const;
+	void ping(NodeIPEndpoint _to) const;
 
 	/// Used ping known node. Used by node table when refreshing buckets and as part of eviction process (see evict).
 	void ping(NodeEntry* _n) const;
@@ -299,29 +299,17 @@ struct InvalidRLP: public Exception {};
  * a given bucket which is full, the least-responsive node is pinged.
  * If the pinged node doesn't respond, then it is removed and the new
  * node is inserted.
- *
- * RLP Encoded Items: 3
- * Minimum Encoded Size: 18 bytes
- * Maximum Encoded Size:  bytes // todo after u128 addresses
- *
- * signature: Signature of message.
- * ipAddress: Our IP address.
- * port: Our port.
- *
- * @todo uint128_t for ip address (<->integer ipv4/6, asio-address, asio-endpoint)
- *
  */
 struct PingNode: RLPXDatagram<PingNode>
 {
-	PingNode(bi::udp::endpoint _ep): RLPXDatagram<PingNode>(_ep) {}
-	PingNode(bi::udp::endpoint _ep, std::string _src, uint16_t _srcPort, std::chrono::seconds _ts = std::chrono::seconds(60)): RLPXDatagram<PingNode>(_ep), ipAddress(_src), tcpPort(_srcPort), ts(futureFromEpoch(_ts)) {}
+	PingNode(bi::udp::endpoint _ep): RLPXDatagram<PingNode>(_ep), source(UnspecifiedNodeIPEndpoint), destination(UnspecifiedNodeIPEndpoint) {}
+	PingNode(NodeIPEndpoint _src, NodeIPEndpoint _dest): RLPXDatagram<PingNode>(_dest), source(_src), destination(_dest), ts(futureFromEpoch(std::chrono::seconds(60))) {}
 
 	static const uint8_t type = 1;
 
 	unsigned version = 0;
-	std::string ipAddress;
-//	uint16_t udpPort;
-	uint16_t tcpPort;
+	NodeIPEndpoint source;
+	NodeIPEndpoint destination;
 	unsigned ts;
 
 	void streamRLP(RLPStream& _s) const override;
@@ -330,17 +318,15 @@ struct PingNode: RLPXDatagram<PingNode>
 
 /**
  * Pong packet: Sent in response to ping
- *
- * RLP Encoded Items: 2
- * Minimum Encoded Size: 33 bytes
- * Maximum Encoded Size: 33 bytes
  */
 struct Pong: RLPXDatagram<Pong>
 {
-	Pong(bi::udp::endpoint _ep): RLPXDatagram<Pong>(_ep), ts(futureFromEpoch(std::chrono::seconds(60))) {}
+	Pong(bi::udp::endpoint _ep): RLPXDatagram<Pong>(_ep), destination(UnspecifiedNodeIPEndpoint) {}
+	Pong(NodeIPEndpoint _dest): RLPXDatagram<Pong>((bi::udp::endpoint)_dest), destination(_dest), ts(futureFromEpoch(std::chrono::seconds(60))) {}
 
 	static const uint8_t type = 2;
 
+	NodeIPEndpoint destination;
 	h256 echo;				///< MCD of PingNode
 	unsigned ts;
 
@@ -375,23 +361,17 @@ struct FindNode: RLPXDatagram<FindNode>
 };
 
 /**
- * Node Packet: Multiple node packets are sent in response to FindNode.
- *
- * RLP Encoded Items: 2 (first item is list)
- * Minimum Encoded Size: 10 bytes
+ * Node Packet: One or more node packets are sent in response to FindNode.
  */
 struct Neighbours: RLPXDatagram<Neighbours>
 {
-	struct Node
+	struct Neighbour
 	{
-		Node() = default;
-		Node(RLP const& _r) { interpretRLP(_r); }
-		std::string ipAddress;
-		uint16_t udpPort;
-//		uint16_t tcpPort;
+		Neighbour(Node const& _node): endpoint(_node.endpoint), node(_node.id) {}
+		Neighbour(RLP const& _r): endpoint(_r) { node = h512(_r[3].toBytes()); }
+		NodeIPEndpoint endpoint;
 		NodeId node;
-		void streamRLP(RLPStream& _s) const { _s.appendList(3); _s << ipAddress << udpPort << node; }
-		void interpretRLP(RLP const& _r) { ipAddress = _r[0].toString(); udpPort = _r[1].toInt<uint16_t>(); node = h512(_r[2].toBytes()); }
+		void streamRLP(RLPStream& _s) const { _s.appendList(4); endpoint.streamRLP(_s, NodeIPEndpoint::InlineList); _s << node; }
 	};
 
 	Neighbours(bi::udp::endpoint _ep): RLPXDatagram<Neighbours>(_ep), ts(futureFromEpoch(std::chrono::seconds(30))) {}
@@ -399,21 +379,15 @@ struct Neighbours: RLPXDatagram<Neighbours>
 	{
 		auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size();
 		for (auto i = _offset; i < limit; i++)
-		{
-			Node node;
-			node.ipAddress = _nearest[i]->endpoint.address.to_string();
-			node.udpPort = _nearest[i]->endpoint.udpPort;
-			node.node = _nearest[i]->publicKey();
-			nodes.push_back(node);
-		}
+			neighbours.push_back(Neighbour(*_nearest[i]));
 	}
 
 	static const uint8_t type = 4;
-	std::vector<Node> nodes;
+	std::vector<Neighbour> neighbours;
 	unsigned ts = 1;
 
-	void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(nodes.size()); for (auto& n: nodes) n.streamRLP(_s); _s << ts; }
-	void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) nodes.push_back(Node(n)); ts = r[1].toInt<unsigned>(); }
+	void streamRLP(RLPStream& _s) const { _s.appendList(2); _s.appendList(neighbours.size()); for (auto& n: neighbours) n.streamRLP(_s); _s << ts; }
+	void interpretRLP(bytesConstRef _bytes) { RLP r(_bytes); for (auto n: r[0]) neighbours.push_back(Neighbour(n)); ts = r[1].toInt<unsigned>(); }
 };
 
 struct NodeTableWarn: public LogChannel { static const char* name(); static const int verbosity = 0; };