Fabcoin Core  0.16.2
P2P Digital Currency
NodeTable.cpp
Go to the documentation of this file.
1 /*
2  This file is part of cpp-ethereum.
3 
4  cpp-ethereum is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  cpp-ethereum is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
16  */
22 #include "NodeTable.h"
23 using namespace std;
24 using namespace dev;
25 using namespace dev::p2p;
26 
27 const char* NodeTableWarn::name() { return "!P!"; }
28 const char* NodeTableNote::name() { return "*P*"; }
29 const char* NodeTableMessageSummary::name() { return "-P-"; }
30 const char* NodeTableMessageDetail::name() { return "=P="; }
31 const char* NodeTableConnect::name() { return "+P+"; }
32 const char* NodeTableEvent::name() { return "+P+"; }
33 const char* NodeTableTimer::name() { return "+P+"; }
34 const char* NodeTableUpdate::name() { return "+P+"; }
35 const char* NodeTableTriviaSummary::name() { return "-P-"; }
36 const char* NodeTableTriviaDetail::name() { return "=P="; }
37 const char* NodeTableAllDetail::name() { return "=P="; }
38 const char* NodeTableEgress::name() { return ">>P"; }
39 const char* NodeTableIngress::name() { return "<<P"; }
40 
41 NodeEntry::NodeEntry(NodeID const& _src, Public const& _pubk, NodeIPEndpoint const& _gw): Node(_pubk, _gw), distance(NodeTable::distance(_src, _pubk)) {}
42 
43 NodeTable::NodeTable(ba::io_service& _io, KeyPair const& _alias, NodeIPEndpoint const& _endpoint, bool _enabled):
44  m_node(Node(_alias.pub(), _endpoint)),
45  m_secret(_alias.secret()),
46  m_socket(make_shared<NodeSocket>(_io, *reinterpret_cast<UDPSocketEvents*>(this), (bi::udp::endpoint)m_node.endpoint)),
47  m_socketPointer(m_socket.get()),
48  m_timers(_io)
49 {
50  for (unsigned i = 0; i < s_bins; i++)
51  m_state[i].distance = i;
52 
53  if (!_enabled)
54  return;
55 
56  try
57  {
59  doDiscovery();
60  }
61  catch (std::exception const& _e)
62  {
63  clog(NetWarn) << "Exception connecting NodeTable socket: " << _e.what();
64  clog(NetWarn) << "Discovery disabled.";
65  }
66 }
67 
69 {
71  m_timers.stop();
72 }
73 
75 {
77  m_nodeEventHandler->processEvents();
78 }
79 
80 shared_ptr<NodeEntry> NodeTable::addNode(Node const& _node, NodeRelation _relation)
81 {
82  if (_relation == Known)
83  {
84  auto ret = make_shared<NodeEntry>(m_node.id, _node.id, _node.endpoint);
85  ret->pending = false;
87  m_nodes[_node.id] = ret;
88  noteActiveNode(_node.id, _node.endpoint);
89  return ret;
90  }
91 
92  if (!_node.endpoint)
93  return shared_ptr<NodeEntry>();
94 
95  // ping address to recover nodeid if nodeid is empty
96  if (!_node.id)
97  {
99  clog(NodeTableConnect) << "Sending public key discovery Ping to" << (bi::udp::endpoint)_node.endpoint << "(Advertising:" << (bi::udp::endpoint)m_node.endpoint << ")";
101  m_pubkDiscoverPings[_node.endpoint.address] = std::chrono::steady_clock::now();
102  ping(_node.endpoint);
103  return shared_ptr<NodeEntry>();
104  }
105 
107  if (m_nodes.count(_node.id))
108  return m_nodes[_node.id];
109 
110  auto ret = make_shared<NodeEntry>(m_node.id, _node.id, _node.endpoint);
112  m_nodes[_node.id] = ret;
113  clog(NodeTableConnect) << "addNode pending for" << _node.endpoint;
114  ping(_node.endpoint);
115  return ret;
116 }
117 
118 list<NodeID> NodeTable::nodes() const
119 {
120  list<NodeID> nodes;
122  for (auto& i: m_nodes)
123  nodes.push_back(i.second->id);
124  return nodes;
125 }
126 
127 list<NodeEntry> NodeTable::snapshot() const
128 {
129  list<NodeEntry> ret;
131  for (auto const& s: m_state)
132  for (auto const& np: s.nodes)
133  if (auto n = np.lock())
134  ret.push_back(*n);
135  return ret;
136 }
137 
139 {
140  Guard l(x_nodes);
141  if (m_nodes.count(_id))
142  {
143  auto entry = m_nodes[_id];
144  return Node(_id, entry->endpoint, entry->peerType);
145  }
146  return UnspecifiedNode;
147 }
148 
149 shared_ptr<NodeEntry> NodeTable::nodeEntry(NodeID _id)
150 {
151  Guard l(x_nodes);
152  return m_nodes.count(_id) ? m_nodes[_id] : shared_ptr<NodeEntry>();
153 }
154 
155 void NodeTable::doDiscover(NodeID _node, unsigned _round, shared_ptr<set<shared_ptr<NodeEntry>>> _tried)
156 {
157  // NOTE: ONLY called by doDiscovery!
158 
159  if (!m_socketPointer->isOpen())
160  return;
161 
162  if (_round == s_maxSteps)
163  {
164  clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds.";
165  doDiscovery();
166  return;
167  }
168  else if (!_round && !_tried)
169  // initialized _tried on first round
170  _tried = make_shared<set<shared_ptr<NodeEntry>>>();
171 
172  auto nearest = nearestNodeEntries(_node);
173  list<shared_ptr<NodeEntry>> tried;
174  for (unsigned i = 0; i < nearest.size() && tried.size() < s_alpha; i++)
175  if (!_tried->count(nearest[i]))
176  {
177  auto r = nearest[i];
178  tried.push_back(r);
179  FindNode p(r->endpoint, _node);
180  p.sign(m_secret);
182  m_findNodeTimeout.push_back(make_pair(r->id, chrono::steady_clock::now()));
183  m_socketPointer->send(p);
184  }
185 
186  if (tried.empty())
187  {
188  clog(NodeTableEvent) << "Terminating discover after " << _round << " rounds.";
189  doDiscovery();
190  return;
191  }
192 
193  while (!tried.empty())
194  {
195  _tried->insert(tried.front());
196  tried.pop_front();
197  }
198 
199  m_timers.schedule(c_reqTimeout.count() * 2, [this, _node, _round, _tried](boost::system::error_code const& _ec)
200  {
201  if (_ec)
202  clog(NodeTableMessageDetail) << "Discovery timer was probably cancelled: " << _ec.value() << _ec.message();
203 
204  if (_ec.value() == boost::asio::error::operation_aborted || m_timers.isStopped())
205  return;
206 
207  // error::operation_aborted means that the timer was probably aborted.
208  // It usually happens when "this" object is deallocated, in which case
209  // subsequent call to doDiscover() would cause a crash. We can not rely on
210  // m_timers.isStopped(), because "this" pointer was captured by the lambda,
211  // and therefore, in case of deallocation m_timers object no longer exists.
212 
213  doDiscover(_node, _round + 1, _tried);
214  });
215 }
216 
217 vector<shared_ptr<NodeEntry>> NodeTable::nearestNodeEntries(NodeID _target)
218 {
219  // send s_alpha FindNode packets to nodes we know, closest to target
220  static unsigned lastBin = s_bins - 1;
221  unsigned head = distance(m_node.id, _target);
222  unsigned tail = head == 0 ? lastBin : (head - 1) % s_bins;
223 
224  map<unsigned, list<shared_ptr<NodeEntry>>> found;
225  unsigned count = 0;
226 
227  // if d is 0, then we roll look forward, if last, we reverse, else, spread from d
228  if (head > 1 && tail != lastBin)
229  while (head != tail && head < s_bins && count < s_bucketSize)
230  {
231  Guard l(x_state);
232  for (auto const& n: m_state[head].nodes)
233  if (auto p = n.lock())
234  {
235  if (count < s_bucketSize)
236  found[distance(_target, p->id)].push_back(p);
237  else
238  break;
239  }
240 
241  if (count < s_bucketSize && tail)
242  for (auto const& n: m_state[tail].nodes)
243  if (auto p = n.lock())
244  {
245  if (count < s_bucketSize)
246  found[distance(_target, p->id)].push_back(p);
247  else
248  break;
249  }
250 
251  head++;
252  if (tail)
253  tail--;
254  }
255  else if (head < 2)
256  while (head < s_bins && count < s_bucketSize)
257  {
258  Guard l(x_state);
259  for (auto const& n: m_state[head].nodes)
260  if (auto p = n.lock())
261  {
262  if (count < s_bucketSize)
263  found[distance(_target, p->id)].push_back(p);
264  else
265  break;
266  }
267  head++;
268  }
269  else
270  while (tail > 0 && count < s_bucketSize)
271  {
272  Guard l(x_state);
273  for (auto const& n: m_state[tail].nodes)
274  if (auto p = n.lock())
275  {
276  if (count < s_bucketSize)
277  found[distance(_target, p->id)].push_back(p);
278  else
279  break;
280  }
281  tail--;
282  }
283 
284  vector<shared_ptr<NodeEntry>> ret;
285  for (auto& nodes: found)
286  for (auto const& n: nodes.second)
287  if (ret.size() < s_bucketSize && !!n->endpoint && n->endpoint.isAllowed())
288  ret.push_back(n);
289  return ret;
290 }
291 
293 {
294  NodeIPEndpoint src;
296  src = m_node.endpoint;
297  PingNode p(src, _to);
298  p.sign(m_secret);
299  m_socketPointer->send(p);
300 }
301 
302 void NodeTable::ping(NodeEntry* _n) const
303 {
304  if (_n)
305  ping(_n->endpoint);
306 }
307 
308 void NodeTable::evict(shared_ptr<NodeEntry> _leastSeen, shared_ptr<NodeEntry> _new)
309 {
310  if (!m_socketPointer->isOpen())
311  return;
312 
313  unsigned evicts = 0;
315  {
316  m_evictions.push_back(EvictionTimeout(make_pair(_leastSeen->id,chrono::steady_clock::now()), _new->id));
317  evicts = m_evictions.size();
318  }
319 
320  if (evicts == 1)
322  ping(_leastSeen.get());
323 }
324 
325 void NodeTable::noteActiveNode(Public const& _pubk, bi::udp::endpoint const& _endpoint)
326 {
327  if (_pubk == m_node.address() || !NodeIPEndpoint(_endpoint.address(), _endpoint.port(), _endpoint.port()).isAllowed())
328  return;
329 
330  shared_ptr<NodeEntry> node = nodeEntry(_pubk);
331  if (!!node && !node->pending)
332  {
333  clog(NodeTableConnect) << "Noting active node:" << _pubk << _endpoint.address().to_string() << ":" << _endpoint.port();
334  node->endpoint.address = _endpoint.address();
335  node->endpoint.udpPort = _endpoint.port();
336 
337  shared_ptr<NodeEntry> contested;
338  {
339  Guard l(x_state);
340  NodeBucket& s = bucket_UNSAFE(node.get());
341  bool removed = false;
342  s.nodes.remove_if([&node, &removed](weak_ptr<NodeEntry> const& n)
343  {
344  if (n.lock() == node)
345  removed = true;
346  return removed;
347  });
348 
349  if (s.nodes.size() >= s_bucketSize)
350  {
351  if (removed)
352  clog(NodeTableWarn) << "DANGER: Bucket overflow when swapping node position.";
353 
354  // It's only contested iff nodeentry exists
355  contested = s.nodes.front().lock();
356  if (!contested)
357  {
358  s.nodes.pop_front();
359  s.nodes.push_back(node);
360  if (!removed && m_nodeEventHandler)
361  m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
362  }
363  }
364  else
365  {
366  s.nodes.push_back(node);
367  if (!removed && m_nodeEventHandler)
368  m_nodeEventHandler->appendEvent(node->id, NodeEntryAdded);
369  }
370  }
371 
372  if (contested)
373  evict(contested, node);
374  }
375 }
376 
377 void NodeTable::dropNode(shared_ptr<NodeEntry> _n)
378 {
379  // remove from nodetable
380  {
381  Guard l(x_state);
382  NodeBucket& s = bucket_UNSAFE(_n.get());
383  s.nodes.remove_if([&_n](weak_ptr<NodeEntry> n) { return n.lock() == _n; });
384  }
385 
386  // notify host
387  clog(NodeTableUpdate) << "p2p.nodes.drop " << _n->id;
388  if (m_nodeEventHandler)
389  m_nodeEventHandler->appendEvent(_n->id, NodeEntryDropped);
390 }
391 
393 {
394  return m_state[_n->distance - 1];
395 }
396 
397 void NodeTable::onReceived(UDPSocketFace*, bi::udp::endpoint const& _from, bytesConstRef _packet)
398 {
399  try {
400  unique_ptr<DiscoveryDatagram> packet = DiscoveryDatagram::interpretUDP(_from, _packet);
401  if (!packet)
402  return;
403  if (packet->isExpired())
404  {
405  clog(NodeTableWarn) << "Invalid packet (timestamp in the past) from " << _from.address().to_string() << ":" << _from.port();
406  return;
407  }
408 
409  switch (packet->packetType())
410  {
411  case Pong::type:
412  {
413  auto in = dynamic_cast<Pong const&>(*packet);
414  // whenever a pong is received, check if it's in m_evictions
415  bool found = false;
416  EvictionTimeout evictionEntry;
418  for (auto it = m_evictions.begin(); it != m_evictions.end(); ++it)
419  if (it->first.first == in.sourceid && it->first.second > std::chrono::steady_clock::now())
420  {
421  found = true;
422  evictionEntry = *it;
423  m_evictions.erase(it);
424  break;
425  }
426  if (found)
427  {
428  if (auto n = nodeEntry(evictionEntry.second))
429  dropNode(n);
430  if (auto n = nodeEntry(evictionEntry.first.first))
431  n->pending = false;
432  }
433  else
434  {
435  // if not, check if it's known/pending or a pubk discovery ping
436  if (auto n = nodeEntry(in.sourceid))
437  n->pending = false;
438  else
439  {
441  {
442  if (!m_pubkDiscoverPings.count(_from.address()))
443  return; // unsolicited pong; don't note node as active
444  m_pubkDiscoverPings.erase(_from.address());
445  }
446  if (!haveNode(in.sourceid))
447  addNode(Node(in.sourceid, NodeIPEndpoint(_from.address(), _from.port(), _from.port())));
448  }
449  }
450 
451  // update our endpoint address and UDP port
453  {
454  if ((!m_node.endpoint || !m_node.endpoint.isAllowed()) && isPublicAddress(in.destination.address))
455  m_node.endpoint.address = in.destination.address;
456  m_node.endpoint.udpPort = in.destination.udpPort;
457  }
458 
459  clog(NodeTableConnect) << "PONG from " << in.sourceid << _from;
460  break;
461  }
462 
463  case Neighbours::type:
464  {
465  auto in = dynamic_cast<Neighbours const&>(*packet);
466  bool expected = false;
467  auto now = chrono::steady_clock::now();
469  m_findNodeTimeout.remove_if([&](NodeIdTimePoint const& t)
470  {
471  if (t.first == in.sourceid && now - t.second < c_reqTimeout)
472  expected = true;
473  else if (t.first == in.sourceid)
474  return true;
475  return false;
476  });
477  if (!expected)
478  {
479  clog(NetConnect) << "Dropping unsolicited neighbours packet from " << _from.address();
480  break;
481  }
482 
483  for (auto n: in.neighbours)
484  addNode(Node(n.node, n.endpoint));
485  break;
486  }
487 
488  case FindNode::type:
489  {
490  auto in = dynamic_cast<FindNode const&>(*packet);
491  vector<shared_ptr<NodeEntry>> nearest = nearestNodeEntries(in.target);
492  static unsigned const nlimit = (m_socketPointer->maxDatagramSize - 109) / 90;
493  for (unsigned offset = 0; offset < nearest.size(); offset += nlimit)
494  {
495  Neighbours out(_from, nearest, offset, nlimit);
496  out.sign(m_secret);
497  if (out.data.size() > 1280)
498  clog(NetWarn) << "Sending truncated datagram, size: " << out.data.size();
499  m_socketPointer->send(out);
500  }
501  break;
502  }
503 
504  case PingNode::type:
505  {
506  auto in = dynamic_cast<PingNode const&>(*packet);
507  in.source.address = _from.address();
508  in.source.udpPort = _from.port();
509  addNode(Node(in.sourceid, in.source));
510 
511  Pong p(in.source);
512  p.echo = sha3(in.echo);
513  p.sign(m_secret);
514  m_socketPointer->send(p);
515  break;
516  }
517  }
518 
519  noteActiveNode(packet->sourceid, _from);
520  }
521  catch (std::exception const& _e)
522  {
523  clog(NodeTableWarn) << "Exception processing message from " << _from.address().to_string() << ":" << _from.port() << ": " << _e.what();
524  }
525  catch (...)
526  {
527  clog(NodeTableWarn) << "Exception processing message from " << _from.address().to_string() << ":" << _from.port();
528  }
529 }
530 
532 {
533  m_timers.schedule(c_evictionCheckInterval.count(), [this](boost::system::error_code const& _ec)
534  {
535  if (_ec)
536  clog(NodeTableMessageDetail) << "Check Evictions timer was probably cancelled: " << _ec.value() << _ec.message();
537 
538  if (_ec.value() == boost::asio::error::operation_aborted || m_timers.isStopped())
539  return;
540 
541  bool evictionsRemain = false;
542  list<shared_ptr<NodeEntry>> drop;
543  {
544  Guard le(x_evictions);
545  Guard ln(x_nodes);
546  for (auto& e: m_evictions)
547  if (chrono::steady_clock::now() - e.first.second > c_reqTimeout)
548  if (m_nodes.count(e.second))
549  drop.push_back(m_nodes[e.second]);
550  evictionsRemain = (m_evictions.size() - drop.size() > 0);
551  }
552 
553  drop.unique();
554  for (auto n: drop)
555  dropNode(n);
556 
557  if (evictionsRemain)
559  });
560 }
561 
563 {
564  m_timers.schedule(c_bucketRefresh.count(), [this](boost::system::error_code const& _ec)
565  {
566  if (_ec)
567  clog(NodeTableMessageDetail) << "Discovery timer was probably cancelled: " << _ec.value() << _ec.message();
568 
569  if (_ec.value() == boost::asio::error::operation_aborted || m_timers.isStopped())
570  return;
571 
572  clog(NodeTableEvent) << "performing random discovery";
573  NodeID randNodeId;
574  crypto::Nonce::get().ref().copyTo(randNodeId.ref().cropped(0, h256::size));
576  doDiscover(randNodeId);
577  });
578 }
579 
580 unique_ptr<DiscoveryDatagram> DiscoveryDatagram::interpretUDP(bi::udp::endpoint const& _from, bytesConstRef _packet)
581 {
582  unique_ptr<DiscoveryDatagram> decoded;
583  // h256 + Signature + type + RLP (smallest possible packet is empty neighbours packet which is 3 bytes)
584  if (_packet.size() < h256::size + Signature::size + 1 + 3)
585  {
586  clog(NodeTableWarn) << "Invalid packet (too small) from " << _from.address().to_string() << ":" << _from.port();
587  return decoded;
588  }
589  bytesConstRef hashedBytes(_packet.cropped(h256::size, _packet.size() - h256::size));
590  bytesConstRef signedBytes(hashedBytes.cropped(Signature::size, hashedBytes.size() - Signature::size));
591  bytesConstRef signatureBytes(_packet.cropped(h256::size, Signature::size));
592  bytesConstRef bodyBytes(_packet.cropped(h256::size + Signature::size + 1));
593 
594  h256 echo(sha3(hashedBytes));
595  if (!_packet.cropped(0, h256::size).contentsEqual(echo.asBytes()))
596  {
597  clog(NodeTableWarn) << "Invalid packet (bad hash) from " << _from.address().to_string() << ":" << _from.port();
598  return decoded;
599  }
600  Public sourceid(dev::recover(*(Signature const*)signatureBytes.data(), sha3(signedBytes)));
601  if (!sourceid)
602  {
603  clog(NodeTableWarn) << "Invalid packet (bad signature) from " << _from.address().to_string() << ":" << _from.port();
604  return decoded;
605  }
606  switch (signedBytes[0])
607  {
608  case PingNode::type:
609  decoded.reset(new PingNode(_from, sourceid, echo));
610  break;
611  case Pong::type:
612  decoded.reset(new Pong(_from, sourceid, echo));
613  break;
614  case FindNode::type:
615  decoded.reset(new FindNode(_from, sourceid, echo));
616  break;
617  case Neighbours::type:
618  decoded.reset(new Neighbours(_from, sourceid, echo));
619  break;
620  default:
621  clog(NodeTableWarn) << "Invalid packet (unknown packet type) from " << _from.address().to_string() << ":" << _from.port();
622  return decoded;
623  }
624  decoded->interpretRLP(bodyBytes);
625  return decoded;
626 }
bool send(UDPDatagram const &_datagram)
Send datagram.
Definition: UDP.h:181
void evict(std::shared_ptr< NodeEntry > _leastSeen, std::shared_ptr< NodeEntry > _new)
Asynchronously drops _leastSeen node if it doesn&#39;t reply and adds _new node, otherwise _new node is t...
Definition: NodeTable.cpp:308
void onReceived(UDPSocketFace *, bi::udp::endpoint const &_from, bytesConstRef _packet)
General Network Events.
Definition: NodeTable.cpp:397
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
Definition: Arith256.cpp:15
bool isAllowed() const
Definition: Common.h:196
std::shared_ptr< NodeEntry > addNode(Node const &_node, NodeRelation _relation=NodeRelation::Unknown)
Add node. Node will be pinged and empty shared_ptr is returned if node has never been seen or NodeID ...
Definition: NodeTable.cpp:80
std::list< NodeIdTimePoint > m_findNodeTimeout
Timeouts for pending Ping and FindNode requests.
Definition: NodeTable.h:262
NodeEntry.
Definition: NodeTable.h:41
bi::address address
Definition: Common.h:209
static const uint8_t type
Definition: NodeTable.h:315
std::shared_ptr< NodeEntry > nodeEntry(NodeID _id)
Used by asynchronous operations to return NodeEntry which is active and managed by node table...
Definition: NodeTable.cpp:149
std::pair< NodeIdTimePoint, NodeID > EvictionTimeout
First NodeID (NodeIdTimePoint) may be evicted and replaced with second NodeID.
Definition: NodeTable.h:127
Node node(NodeID const &_id)
Returns the Node to the corresponding node id or the empty Node if that id is not found...
Definition: NodeTable.cpp:138
static const uint8_t type
Definition: NodeTable.h:386
Node m_node
This node. LOCK x_state if endpoint access or mutation is required. Do not modify id...
Definition: NodeTable.h:246
UDP Interface Handler must implement UDPSocketEvents.
Definition: UDP.h:107
Simple class that represents a "key pair".
Definition: Common.h:150
bool isPublicAddress(bi::address const &_addressToCheck)
Definition: Common.cpp:76
std::unordered_map< bi::address, TimePoint > m_pubkDiscoverPings
List of pending pings where node entry wasn&#39;t created due to unkown pubk.
Definition: NodeTable.h:259
std::pair< NodeID, TimePoint > NodeIdTimePoint
Definition: NodeTable.h:126
std::hash for asio::adress
Definition: Common.h:323
NodeSocket * m_socketPointer
Set to m_socket.get(). Socket is created in constructor and disconnected in destructor to ensure acce...
Definition: NodeTable.h:265
IPv4,UDP/TCP endpoints.
Definition: Common.h:175
void disconnect()
Disconnect socket.
Definition: UDP.h:130
vector_ref< _T > cropped(size_t _begin, size_t _count) const
Definition: vector_ref.h:62
static const uint8_t type
Definition: NodeTable.h:426
if(a.IndicesBefore(b, len, lenIndices))
Definition: equihash.cpp:243
unsigned const distance
Node&#39;s distance (xor of _src as integer).
Definition: NodeTable.h:44
bool contentsEqual(std::vector< mutable_value_type > const &_c) const
Definition: vector_ref.h:43
static Secret get()
Returns the next nonce (might be read from a file).
Definition: Common.h:205
bool isStopped() const
Definition: Common.h:297
static unsigned distance(NodeID const &_a, NodeID const &_b)
Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
Definition: NodeTable.h:138
void processEvents()
Called by implementation which provided handler to process NodeEntryAdded/NodeEntryDropped events...
Definition: NodeTable.cpp:74
#define DEV_GUARDED(MUTEX)
Simple block guard.
Definition: Guards.h:144
bool isOpen()
Returns if socket is open.
Definition: UDP.h:127
std::vector< std::shared_ptr< NodeEntry > > nearestNodeEntries(NodeID _target)
Returns nodes from node table which are closest to target.
Definition: NodeTable.cpp:217
Mutex x_pubkDiscoverPings
LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required.
Definition: NodeTable.h:258
bytesConstRef ref() const
Definition: FixedHash.h:292
std::list< std::weak_ptr< NodeEntry > > nodes
Definition: NodeTable.h:192
NodeIPEndpoint source
Definition: NodeTable.h:319
std::chrono::milliseconds const c_bucketRefresh
Refresh interval prevents bucket from becoming stale. [Kademlia].
Definition: NodeTable.h:187
static unsigned const s_maxSteps
Max iterations of discovery. (discover)
Definition: NodeTable.h:175
bytesRef ref()
Definition: FixedHash.h:133
Ping packet: Sent to check if node is alive.
Definition: NodeTable.h:309
void doCheckEvictions()
Tasks.
Definition: NodeTable.cpp:531
std::lock_guard< std::mutex > Guard
Definition: Guards.h:41
void noteActiveNode(Public const &_pubk, bi::udp::endpoint const &_endpoint)
Called whenever activity is received from a node in order to maintain node table. ...
Definition: NodeTable.cpp:325
void doDiscovery()
Looks up a random node at interval.
Definition: NodeTable.cpp:562
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 >>>())
Used to discovery nodes on network which are close to the given target.
Definition: NodeTable.cpp:155
const char * name
Definition: rest.cpp:36
NodeTable using modified kademlia for node discovery and preference.
Definition: NodeTable.h:121
static unsigned const s_bins
Size of m_state (excludes root, which is us).
Definition: NodeTable.h:174
Public recover(Signature const &_sig, h256 const &_hash)
Recovers Public key from signed message hash.
Definition: Common.cpp:203
const Node UnspecifiedNode
Definition: Common.cpp:33
NodeTable(ba::io_service &_io, KeyPair const &_alias, NodeIPEndpoint const &_endpoint, bool _enabled=true)
Constructor requiring host for I/O, credentials, and IP Address and port to listen on...
Definition: NodeTable.cpp:43
Mutex x_evictions
LOCK x_evictions first if both x_nodes and x_evictions locks are required.
Definition: NodeTable.h:255
NodeID id
Definition: Common.h:254
Mutex x_nodes
LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const...
Definition: NodeTable.h:249
bool haveNode(NodeID const &_id)
Returns true if node id is in node table.
Definition: NodeTable.h:159
void schedule(unsigned _msInFuture, std::function< void(boost::system::error_code const &)> const &_f)
Definition: Common.h:293
std::unique_ptr< NodeTableEventHandler > m_nodeEventHandler
Event handler for node events.
Definition: NodeTable.h:244
std::chrono::milliseconds const c_reqTimeout
How long to wait for requests (evict, find iterations).
Definition: NodeTable.h:186
virtual h256 sign(Secret const &_from)
Definition: UDP.cpp:30
size_t size() const
Definition: vector_ref.h:55
DeadlineOps m_timers
this should be the last member - it must be destroyed first
Definition: NodeTable.h:267
static const uint8_t type
Definition: NodeTable.h:348
virtual NodeID const & address() const
Definition: Common.h:248
void copyTo(vector_ref< typename std::remove_const< _T >::type > _t) const
Copies the contents of this vector_ref to the contents of _t, up to the max size of _t...
Definition: vector_ref.h:69
Secret m_secret
This nodes secret key.
Definition: NodeTable.h:247
bool sha3(bytesConstRef _input, bytesRef o_output)
Calculate SHA3-256 hash of the given input and load it into the given output.
Definition: SHA3.cpp:214
unsigned count() const
Returns node count.
Definition: NodeTable.h:153
NodeBucket & bucket_UNSAFE(NodeEntry const *_n)
Returns references to bucket which corresponds to distance of node id.
Definition: NodeTable.cpp:392
std::chrono::milliseconds const c_evictionCheckInterval
Intervals.
Definition: NodeTable.h:185
#define clog(X)
Definition: Log.h:295
std::list< NodeID > nodes() const
Returns list of node ids active in node table.
Definition: NodeTable.cpp:118
#define e(i)
Definition: sha.cpp:733
static unsigned const s_bucketSize
Chosen constants.
Definition: NodeTable.h:179
static unsigned const s_alpha
Denoted by in [Kademlia]. Number of concurrent FindNode requests.
Definition: NodeTable.h:180
std::array< NodeBucket, s_bins > m_state
State of p2p node network.
Definition: NodeTable.h:253
Interface which UDPSocket will implement.
Definition: UDP.h:84
void ping(NodeIPEndpoint _to) const
Used to ping endpoint.
Definition: NodeTable.cpp:292
void connect()
Socket will begin listening for and delivering packets.
Definition: UDP.h:156
Pong packet: Sent in response to ping.
Definition: NodeTable.h:343
static std::unique_ptr< DiscoveryDatagram > interpretUDP(bi::udp::endpoint const &_from, bytesConstRef _packet)
Decodes UDP packets.
Definition: NodeTable.cpp:580
void dropNode(std::shared_ptr< NodeEntry > _n)
Used to drop node when timeout occurs or when evict() result is to keep previous node.
Definition: NodeTable.cpp:377
UniValue echo(const JSONRPCRequest &request)
Definition: misc.cpp:639
Mutex x_state
LOCK x_state first if both x_nodes and x_state locks are required.
Definition: NodeTable.h:252
FindNode Packet: Request k-nodes, closest to the target.
Definition: NodeTable.h:381
std::list< NodeEntry > snapshot() const
Returns snapshot of table.
Definition: NodeTable.cpp:127
std::unordered_map< NodeID, std::shared_ptr< NodeEntry > > m_nodes
Known Node Endpoints.
Definition: NodeTable.h:250
std::deque< EvictionTimeout > m_evictions
Eviction timeouts.
Definition: NodeTable.h:256
Node Packet: One or more node packets are sent in response to FindNode.
Definition: NodeTable.h:406
Definition: internal.h:25
NodeIPEndpoint endpoint
Endpoints by which we expect to reach node.
Definition: Common.h:258
Interface which a UDPSocket&#39;s owner must implement.
Definition: UDP.h:93