NodeTable.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*
  2. This file is part of cpp-ethereum.
  3. cpp-ethereum is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. cpp-ethereum is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. /** @file NodeTable.h
  15. * @author Alex Leverington <nessence@gmail.com>
  16. * @date 2014
  17. */
  18. #pragma once
  19. #include <algorithm>
  20. #include <deque>
  21. #include <boost/integer/static_log2.hpp>
  22. #include <libp2p/UDP.h>
  23. #include "Common.h"
  24. namespace dev
  25. {
  26. namespace p2p
  27. {
  28. /**
  29. * NodeEntry
  30. * @brief Entry in Node Table
  31. */
  32. struct NodeEntry: public Node
  33. {
  34. NodeEntry(NodeID const& _src, Public const& _pubk, NodeIPEndpoint const& _gw);
  35. unsigned const distance; ///< Node's distance (xor of _src as integer).
  36. bool pending = true; ///< Node will be ignored until Pong is received
  37. };
  38. enum NodeTableEventType
  39. {
  40. NodeEntryAdded,
  41. NodeEntryDropped
  42. };
  43. class NodeTable;
  44. class NodeTableEventHandler
  45. {
  46. friend class NodeTable;
  47. public:
  48. virtual void processEvent(NodeID const& _n, NodeTableEventType const& _e) = 0;
  49. protected:
  50. /// Called by NodeTable on behalf of an implementation (Host) to process new events without blocking nodetable.
  51. void processEvents()
  52. {
  53. std::list<std::pair<NodeID, NodeTableEventType>> events;
  54. {
  55. Guard l(x_events);
  56. if (!m_nodeEventHandler.size())
  57. return;
  58. m_nodeEventHandler.unique();
  59. for (auto const& n: m_nodeEventHandler)
  60. events.push_back(std::make_pair(n,m_events[n]));
  61. m_nodeEventHandler.clear();
  62. m_events.clear();
  63. }
  64. for (auto const& e: events)
  65. processEvent(e.first, e.second);
  66. }
  67. /// Called by NodeTable to append event.
  68. virtual void appendEvent(NodeID _n, NodeTableEventType _e) { Guard l(x_events); m_nodeEventHandler.push_back(_n); m_events[_n] = _e; }
  69. Mutex x_events;
  70. std::list<NodeID> m_nodeEventHandler;
  71. std::unordered_map<NodeID, NodeTableEventType> m_events;
  72. };
  73. class NodeTable;
  74. inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable);
  75. /**
  76. * NodeTable using modified kademlia for node discovery and preference.
  77. * Node table requires an IO service, creates a socket for incoming
  78. * UDP messages and implements a kademlia-like protocol. Node requests and
  79. * responses are used to build a node table which can be queried to
  80. * obtain a list of potential nodes to connect to, and, passes events to
  81. * Host whenever a node is added or removed to/from the table.
  82. *
  83. * Thread-safety is ensured by modifying NodeEntry details via
  84. * shared_ptr replacement instead of mutating values.
  85. *
  86. * NodeTable accepts a port for UDP and will listen to the port on all available
  87. * interfaces.
  88. *
  89. * [Optimization]
  90. * @todo serialize evictions per-bucket
  91. * @todo store evictions in map, unit-test eviction logic
  92. * @todo store root node in table
  93. * @todo encapsulate discover into NetworkAlgorithm (task)
  94. * @todo expiration and sha3(id) 'to' for messages which are replies (prevents replay)
  95. * @todo cache Ping and FindSelf
  96. *
  97. * [Networking]
  98. * @todo eth/upnp/natpmp/stun/ice/etc for public-discovery
  99. * @todo firewall
  100. *
  101. * [Protocol]
  102. * @todo optimize knowledge at opposite edges; eg, s_bitsPerStep lookups. (Can be done via pointers to NodeBucket)
  103. * @todo ^ s_bitsPerStep = 8; // Denoted by b in [Kademlia]. Bits by which address space is divided.
  104. */
  105. class NodeTable: UDPSocketEvents, public std::enable_shared_from_this<NodeTable>
  106. {
  107. friend std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable);
  108. using NodeSocket = UDPSocket<NodeTable, 1280>;
  109. using TimePoint = std::chrono::steady_clock::time_point; ///< Steady time point.
  110. using NodeIdTimePoint = std::pair<NodeID, TimePoint>;
  111. using EvictionTimeout = std::pair<NodeIdTimePoint, NodeID>; ///< First NodeID (NodeIdTimePoint) may be evicted and replaced with second NodeID.
  112. public:
  113. enum NodeRelation { Unknown = 0, Known };
  114. enum DiscoverType { Random = 0 };
  115. /// Constructor requiring host for I/O, credentials, and IP Address and port to listen on.
  116. NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled = true);
  117. ~NodeTable();
  118. /// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
  119. static unsigned distance(NodeID const& _a, NodeID const& _b) { u256 d = sha3(_a) ^ sha3(_b); unsigned ret; for (ret = 0; d >>= 1; ++ret) {}; return ret; }
  120. /// Set event handler for NodeEntryAdded and NodeEntryDropped events.
  121. void setEventHandler(NodeTableEventHandler* _handler) { m_nodeEventHandler.reset(_handler); }
  122. /// Called by implementation which provided handler to process NodeEntryAdded/NodeEntryDropped events. Events are coalesced by type whereby old events are ignored.
  123. void processEvents();
  124. /// Add node. Node will be pinged and empty shared_ptr is returned if node has never been seen or NodeID is empty.
  125. std::shared_ptr<NodeEntry> addNode(Node const& _node, NodeRelation _relation = NodeRelation::Unknown);
  126. /// Returns list of node ids active in node table.
  127. std::list<NodeID> nodes() const;
  128. /// Returns node count.
  129. unsigned count() const { return m_nodes.size(); }
  130. /// Returns snapshot of table.
  131. std::list<NodeEntry> snapshot() const;
  132. /// Returns true if node id is in node table.
  133. bool haveNode(NodeID const& _id) { Guard l(x_nodes); return m_nodes.count(_id) > 0; }
  134. /// Returns the Node to the corresponding node id or the empty Node if that id is not found.
  135. Node node(NodeID const& _id);
  136. #if defined(BOOST_AUTO_TEST_SUITE) || defined(_MSC_VER) // MSVC includes access specifier in symbol name
  137. protected:
  138. #else
  139. private:
  140. #endif
  141. /// Constants for Kademlia, derived from address space.
  142. static unsigned const s_addressByteSize = h256::size; ///< Size of address type in bytes.
  143. static unsigned const s_bits = 8 * s_addressByteSize; ///< Denoted by n in [Kademlia].
  144. static unsigned const s_bins = s_bits - 1; ///< Size of m_state (excludes root, which is us).
  145. static unsigned const s_maxSteps = boost::static_log2<s_bits>::value; ///< Max iterations of discovery. (discover)
  146. /// Chosen constants
  147. static unsigned const s_bucketSize = 16; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket.
  148. static unsigned const s_alpha = 3; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests.
  149. /// Intervals
  150. /* todo: replace boost::posix_time; change constants to upper camelcase */
  151. std::chrono::milliseconds const c_evictionCheckInterval = std::chrono::milliseconds(75); ///< Interval at which eviction timeouts are checked.
  152. std::chrono::milliseconds const c_reqTimeout = std::chrono::milliseconds(300); ///< How long to wait for requests (evict, find iterations).
  153. std::chrono::milliseconds const c_bucketRefresh = std::chrono::milliseconds(7200); ///< Refresh interval prevents bucket from becoming stale. [Kademlia]
  154. struct NodeBucket
  155. {
  156. unsigned distance;
  157. std::list<std::weak_ptr<NodeEntry>> nodes;
  158. };
  159. /// Used to ping endpoint.
  160. void ping(NodeIPEndpoint _to) const;
  161. /// Used ping known node. Used by node table when refreshing buckets and as part of eviction process (see evict).
  162. void ping(NodeEntry* _n) const;
  163. /// Returns center node entry which describes this node and used with dist() to calculate xor metric for node table nodes.
  164. NodeEntry center() const { return NodeEntry(m_node.id, m_node.publicKey(), m_node.endpoint); }
  165. /// Used by asynchronous operations to return NodeEntry which is active and managed by node table.
  166. std::shared_ptr<NodeEntry> nodeEntry(NodeID _id);
  167. /// Used to discovery nodes on network which are close to the given target.
  168. /// Sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to s_maxSteps rounds.
  169. void doDiscover(NodeID _target, unsigned _round = 0, std::shared_ptr<std::set<std::shared_ptr<NodeEntry>>> _tried = std::shared_ptr<std::set<std::shared_ptr<NodeEntry>>>());
  170. /// Returns nodes from node table which are closest to target.
  171. std::vector<std::shared_ptr<NodeEntry>> nearestNodeEntries(NodeID _target);
  172. /// Asynchronously drops _leastSeen node if it doesn't reply and adds _new node, otherwise _new node is thrown away.
  173. void evict(std::shared_ptr<NodeEntry> _leastSeen, std::shared_ptr<NodeEntry> _new);
  174. /// Called whenever activity is received from a node in order to maintain node table.
  175. void noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _endpoint);
  176. /// Used to drop node when timeout occurs or when evict() result is to keep previous node.
  177. void dropNode(std::shared_ptr<NodeEntry> _n);
  178. /// Returns references to bucket which corresponds to distance of node id.
  179. /// @warning Only use the return reference locked x_state mutex.
  180. // TODO p2p: Remove this method after removing offset-by-one functionality.
  181. NodeBucket& bucket_UNSAFE(NodeEntry const* _n);
  182. /// General Network Events
  183. /// Called by m_socket when packet is received.
  184. void onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet);
  185. /// Called by m_socket when socket is disconnected.
  186. void onDisconnected(UDPSocketFace*) {}
  187. /// Tasks
  188. /// Called by evict() to ensure eviction check is scheduled to run and terminates when no evictions remain. Asynchronous.
  189. void doCheckEvictions();
  190. /// Looks up a random node at @c_bucketRefresh interval.
  191. void doDiscovery();
  192. std::unique_ptr<NodeTableEventHandler> m_nodeEventHandler; ///< Event handler for node events.
  193. Node m_node; ///< This node. LOCK x_state if endpoint access or mutation is required. Do not modify id.
  194. Secret m_secret; ///< This nodes secret key.
  195. mutable Mutex x_nodes; ///< LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const.
  196. std::unordered_map<NodeID, std::shared_ptr<NodeEntry>> m_nodes; ///< Known Node Endpoints
  197. mutable Mutex x_state; ///< LOCK x_state first if both x_nodes and x_state locks are required.
  198. std::array<NodeBucket, s_bins> m_state; ///< State of p2p node network.
  199. Mutex x_evictions; ///< LOCK x_evictions first if both x_nodes and x_evictions locks are required.
  200. std::deque<EvictionTimeout> m_evictions; ///< Eviction timeouts.
  201. Mutex x_pubkDiscoverPings; ///< LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required.
  202. std::unordered_map<bi::address, TimePoint> m_pubkDiscoverPings; ///< List of pending pings where node entry wasn't created due to unkown pubk.
  203. Mutex x_findNodeTimeout;
  204. std::list<NodeIdTimePoint> m_findNodeTimeout; ///< Timeouts for pending Ping and FindNode requests.
  205. std::shared_ptr<NodeSocket> m_socket; ///< Shared pointer for our UDPSocket; ASIO requires shared_ptr.
  206. NodeSocket* m_socketPointer; ///< Set to m_socket.get(). Socket is created in constructor and disconnected in destructor to ensure access to pointer is safe.
  207. DeadlineOps m_timers; ///< this should be the last member - it must be destroyed first
  208. };
  209. inline std::ostream& operator<<(std::ostream& _out, NodeTable const& _nodeTable)
  210. {
  211. _out << _nodeTable.center().address() << "\t" << "0\t" << _nodeTable.center().endpoint.address << ":" << _nodeTable.center().endpoint.udpPort << std::endl;
  212. auto s = _nodeTable.snapshot();
  213. for (auto n: s)
  214. _out << n.address() << "\t" << n.distance << "\t" << n.endpoint.address << ":" << n.endpoint.udpPort << std::endl;
  215. return _out;
  216. }
  217. struct DiscoveryDatagram: public RLPXDatagramFace
  218. {
  219. /// Constructor used for sending.
  220. DiscoveryDatagram(bi::udp::endpoint const& _to): RLPXDatagramFace(_to), ts(futureFromEpoch(std::chrono::seconds(60))) {}
  221. /// Constructor used for parsing inbound packets.
  222. DiscoveryDatagram(bi::udp::endpoint const& _from, NodeID const& _fromid, h256 const& _echo): RLPXDatagramFace(_from), sourceid(_fromid), echo(_echo) {}
  223. // These two are set for inbound packets only.
  224. NodeID sourceid; // sender public key (from signature)
  225. h256 echo; // hash of encoded packet, for reply tracking
  226. // All discovery packets carry a timestamp, which must be greater
  227. // than the current local time. This prevents replay attacks.
  228. uint32_t ts = 0;
  229. bool isExpired() const { return secondsSinceEpoch() > ts; }
  230. /// Decodes UDP packets.
  231. static std::unique_ptr<DiscoveryDatagram> interpretUDP(bi::udp::endpoint const& _from, bytesConstRef _packet);
  232. };
  233. /**
  234. * Ping packet: Sent to check if node is alive.
  235. * PingNode is cached and regenerated after ts + t, where t is timeout.
  236. *
  237. * Ping is used to implement evict. When a new node is seen for
  238. * a given bucket which is full, the least-responsive node is pinged.
  239. * If the pinged node doesn't respond, then it is removed and the new
  240. * node is inserted.
  241. */
  242. struct PingNode: DiscoveryDatagram
  243. {
  244. using DiscoveryDatagram::DiscoveryDatagram;
  245. PingNode(NodeIPEndpoint const& _src, NodeIPEndpoint const& _dest): DiscoveryDatagram(_dest), source(_src), destination(_dest) {}
  246. PingNode(bi::udp::endpoint const& _from, NodeID const& _fromid, h256 const& _echo): DiscoveryDatagram(_from, _fromid, _echo) {}
  247. static const uint8_t type = 1;
  248. uint8_t packetType() const { return type; }
  249. unsigned version = 0;
  250. NodeIPEndpoint source;
  251. NodeIPEndpoint destination;
  252. void streamRLP(RLPStream& _s) const
  253. {
  254. _s.appendList(4);
  255. _s << dev::p2p::c_protocolVersion;
  256. source.streamRLP(_s);
  257. destination.streamRLP(_s);
  258. _s << ts;
  259. }
  260. void interpretRLP(bytesConstRef _bytes)
  261. {
  262. RLP r(_bytes, RLP::AllowNonCanon|RLP::ThrowOnFail);
  263. version = r[0].toInt<unsigned>();
  264. source.interpretRLP(r[1]);
  265. destination.interpretRLP(r[2]);
  266. ts = r[3].toInt<uint32_t>();
  267. }
  268. };
  269. /**
  270. * Pong packet: Sent in response to ping
  271. */
  272. struct Pong: DiscoveryDatagram
  273. {
  274. Pong(NodeIPEndpoint const& _dest): DiscoveryDatagram((bi::udp::endpoint)_dest), destination(_dest) {}
  275. Pong(bi::udp::endpoint const& _from, NodeID const& _fromid, h256 const& _echo): DiscoveryDatagram(_from, _fromid, _echo) {}
  276. static const uint8_t type = 2;
  277. uint8_t packetType() const { return type; }
  278. NodeIPEndpoint destination;
  279. void streamRLP(RLPStream& _s) const
  280. {
  281. _s.appendList(3);
  282. destination.streamRLP(_s);
  283. _s << echo;
  284. _s << ts;
  285. }
  286. void interpretRLP(bytesConstRef _bytes)
  287. {
  288. RLP r(_bytes, RLP::AllowNonCanon|RLP::ThrowOnFail);
  289. destination.interpretRLP(r[0]);
  290. echo = (h256)r[1];
  291. ts = r[2].toInt<uint32_t>();
  292. }
  293. };
  294. /**
  295. * FindNode Packet: Request k-nodes, closest to the target.
  296. * FindNode is cached and regenerated after ts + t, where t is timeout.
  297. * FindNode implicitly results in finding neighbours of a given node.
  298. *
  299. * RLP Encoded Items: 2
  300. * Minimum Encoded Size: 21 bytes
  301. * Maximum Encoded Size: 30 bytes
  302. *
  303. * target: NodeID of node. The responding node will send back nodes closest to the target.
  304. *
  305. */
  306. struct FindNode: DiscoveryDatagram
  307. {
  308. FindNode(bi::udp::endpoint _to, h512 _target): DiscoveryDatagram(_to), target(_target) {}
  309. FindNode(bi::udp::endpoint const& _from, NodeID const& _fromid, h256 const& _echo): DiscoveryDatagram(_from, _fromid, _echo) {}
  310. static const uint8_t type = 3;
  311. uint8_t packetType() const { return type; }
  312. h512 target;
  313. void streamRLP(RLPStream& _s) const
  314. {
  315. _s.appendList(2); _s << target << ts;
  316. }
  317. void interpretRLP(bytesConstRef _bytes)
  318. {
  319. RLP r(_bytes, RLP::AllowNonCanon|RLP::ThrowOnFail);
  320. target = r[0].toHash<h512>();
  321. ts = r[1].toInt<uint32_t>();
  322. }
  323. };
  324. /**
  325. * Node Packet: One or more node packets are sent in response to FindNode.
  326. */
  327. struct Neighbours: DiscoveryDatagram
  328. {
  329. Neighbours(bi::udp::endpoint _to, std::vector<std::shared_ptr<NodeEntry>> const& _nearest, unsigned _offset = 0, unsigned _limit = 0): DiscoveryDatagram(_to)
  330. {
  331. auto limit = _limit ? std::min(_nearest.size(), (size_t)(_offset + _limit)) : _nearest.size();
  332. for (auto i = _offset; i < limit; i++)
  333. neighbours.push_back(Neighbour(*_nearest[i]));
  334. }
  335. Neighbours(bi::udp::endpoint const& _to): DiscoveryDatagram(_to) {}
  336. Neighbours(bi::udp::endpoint const& _from, NodeID const& _fromid, h256 const& _echo): DiscoveryDatagram(_from, _fromid, _echo) {}
  337. struct Neighbour
  338. {
  339. Neighbour(Node const& _node): endpoint(_node.endpoint), node(_node.id) {}
  340. Neighbour(RLP const& _r): endpoint(_r) { node = h512(_r[3].toBytes()); }
  341. NodeIPEndpoint endpoint;
  342. NodeID node;
  343. void streamRLP(RLPStream& _s) const { _s.appendList(4); endpoint.streamRLP(_s, NodeIPEndpoint::StreamInline); _s << node; }
  344. };
  345. static const uint8_t type = 4;
  346. uint8_t packetType() const { return type; }
  347. std::vector<Neighbour> neighbours;
  348. void streamRLP(RLPStream& _s) const
  349. {
  350. _s.appendList(2);
  351. _s.appendList(neighbours.size());
  352. for (auto const& n: neighbours)
  353. n.streamRLP(_s);
  354. _s << ts;
  355. }
  356. void interpretRLP(bytesConstRef _bytes)
  357. {
  358. RLP r(_bytes, RLP::AllowNonCanon|RLP::ThrowOnFail);
  359. for (auto const& n: r[0])
  360. neighbours.emplace_back(n);
  361. ts = r[1].toInt<uint32_t>();
  362. }
  363. };
  364. struct NodeTableWarn: public LogChannel { static const char* name(); static const int verbosity = 0; };
  365. struct NodeTableNote: public LogChannel { static const char* name(); static const int verbosity = 1; };
  366. struct NodeTableMessageSummary: public LogChannel { static const char* name(); static const int verbosity = 2; };
  367. struct NodeTableMessageDetail: public LogChannel { static const char* name(); static const int verbosity = 5; };
  368. struct NodeTableConnect: public LogChannel { static const char* name(); static const int verbosity = 10; };
  369. struct NodeTableEvent: public LogChannel { static const char* name(); static const int verbosity = 10; };
  370. struct NodeTableTimer: public LogChannel { static const char* name(); static const int verbosity = 10; };
  371. struct NodeTableUpdate: public LogChannel { static const char* name(); static const int verbosity = 10; };
  372. struct NodeTableTriviaSummary: public LogChannel { static const char* name(); static const int verbosity = 10; };
  373. struct NodeTableTriviaDetail: public LogChannel { static const char* name(); static const int verbosity = 11; };
  374. struct NodeTableAllDetail: public LogChannel { static const char* name(); static const int verbosity = 13; };
  375. struct NodeTableEgress: public LogChannel { static const char* name(); static const int verbosity = 14; };
  376. struct NodeTableIngress: public LogChannel { static const char* name(); static const int verbosity = 15; };
  377. }
  378. }