44 m_node(
Node(_alias.pub(), _endpoint)),
45 m_secret(_alias.secret()),
47 m_socketPointer(m_socket.get()),
50 for (
unsigned i = 0; i <
s_bins; i++)
61 catch (std::exception
const& _e)
63 clog(
NetWarn) <<
"Exception connecting NodeTable socket: " << _e.what();
82 if (_relation ==
Known)
93 return shared_ptr<NodeEntry>();
103 return shared_ptr<NodeEntry>();
123 nodes.push_back(i.second->id);
132 for (
auto const& np: s.nodes)
133 if (
auto n = np.lock())
144 return Node(_id, entry->endpoint, entry->peerType);
152 return m_nodes.count(_id) ?
m_nodes[_id] : shared_ptr<NodeEntry>();
168 else if (!_round && !_tried)
170 _tried = make_shared<set<shared_ptr<NodeEntry>>>();
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]))
193 while (!tried.empty())
195 _tried->insert(tried.front());
204 if (_ec.value() == boost::asio::error::operation_aborted ||
m_timers.
isStopped())
220 static unsigned lastBin =
s_bins - 1;
222 unsigned tail = head == 0 ? lastBin : (head - 1) %
s_bins;
224 map<unsigned, list<shared_ptr<NodeEntry>>> found;
228 if (head > 1 && tail != lastBin)
233 if (
auto p = n.lock())
236 found[
distance(_target, p->id)].push_back(p);
242 for (
auto const& n:
m_state[tail].nodes)
243 if (
auto p = n.lock())
246 found[
distance(_target, p->id)].push_back(p);
260 if (
auto p = n.lock())
263 found[
distance(_target, p->id)].push_back(p);
274 if (
auto p = n.lock())
277 found[
distance(_target, p->id)].push_back(p);
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())
322 ping(_leastSeen.get());
331 if (!!node && !node->pending)
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();
337 shared_ptr<NodeEntry> contested;
341 bool removed =
false;
342 s.
nodes.remove_if([&node, &removed](weak_ptr<NodeEntry>
const& n)
344 if (n.lock() ==
node)
355 contested = s.
nodes.front().lock();
359 s.
nodes.push_back(node);
366 s.
nodes.push_back(node);
373 evict(contested, node);
383 s.
nodes.remove_if([&_n](weak_ptr<NodeEntry> n) {
return n.lock() == _n; });
403 if (packet->isExpired())
405 clog(
NodeTableWarn) <<
"Invalid packet (timestamp in the past) from " << _from.address().to_string() <<
":" << _from.port();
409 switch (packet->packetType())
413 auto in =
dynamic_cast<Pong const&
>(*packet);
419 if (it->first.first == in.sourceid && it->first.second > std::chrono::steady_clock::now())
428 if (
auto n =
nodeEntry(evictionEntry.second))
430 if (
auto n =
nodeEntry(evictionEntry.first.first))
465 auto in =
dynamic_cast<Neighbours const&
>(*packet);
466 bool expected =
false;
467 auto now = chrono::steady_clock::now();
471 if (t.first == in.sourceid && now - t.second <
c_reqTimeout)
473 else if (t.first == in.sourceid)
479 clog(
NetConnect) <<
"Dropping unsolicited neighbours packet from " << _from.address();
483 for (
auto n: in.neighbours)
490 auto in =
dynamic_cast<FindNode const&
>(*packet);
493 for (
unsigned offset = 0; offset < nearest.size(); offset += nlimit)
495 Neighbours out(_from, nearest, offset, nlimit);
497 if (out.
data.size() > 1280)
498 clog(
NetWarn) <<
"Sending truncated datagram, size: " << out.
data.size();
506 auto in =
dynamic_cast<PingNode const&
>(*packet);
508 in.source.udpPort = _from.port();
521 catch (std::exception
const& _e)
523 clog(
NodeTableWarn) <<
"Exception processing message from " << _from.address().to_string() <<
":" << _from.port() <<
": " << _e.what();
527 clog(
NodeTableWarn) <<
"Exception processing message from " << _from.address().to_string() <<
":" << _from.port();
538 if (_ec.value() == boost::asio::error::operation_aborted ||
m_timers.
isStopped())
541 bool evictionsRemain =
false;
542 list<shared_ptr<NodeEntry>> drop;
547 if (chrono::steady_clock::now() -
e.first.second >
c_reqTimeout)
550 evictionsRemain = (m_evictions.size() - drop.size() > 0);
569 if (_ec.value() == boost::asio::error::operation_aborted ||
m_timers.
isStopped())
582 unique_ptr<DiscoveryDatagram> decoded;
586 clog(
NodeTableWarn) <<
"Invalid packet (too small) from " << _from.address().to_string() <<
":" << _from.port();
597 clog(
NodeTableWarn) <<
"Invalid packet (bad hash) from " << _from.address().to_string() <<
":" << _from.port();
603 clog(
NodeTableWarn) <<
"Invalid packet (bad signature) from " << _from.address().to_string() <<
":" << _from.port();
606 switch (signedBytes[0])
609 decoded.reset(
new PingNode(_from, sourceid, echo));
612 decoded.reset(
new Pong(_from, sourceid, echo));
615 decoded.reset(
new FindNode(_from, sourceid, echo));
618 decoded.reset(
new Neighbours(_from, sourceid, echo));
621 clog(
NodeTableWarn) <<
"Invalid packet (unknown packet type) from " << _from.address().to_string() <<
":" << _from.port();
624 decoded->interpretRLP(bodyBytes);
bool send(UDPDatagram const &_datagram)
Send datagram.
void evict(std::shared_ptr< NodeEntry > _leastSeen, std::shared_ptr< NodeEntry > _new)
Asynchronously drops _leastSeen node if it doesn't reply and adds _new node, otherwise _new node is t...
void onReceived(UDPSocketFace *, bi::udp::endpoint const &_from, bytesConstRef _packet)
General Network Events.
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
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 ...
std::list< NodeIdTimePoint > m_findNodeTimeout
Timeouts for pending Ping and FindNode requests.
static const uint8_t type
std::shared_ptr< NodeEntry > nodeEntry(NodeID _id)
Used by asynchronous operations to return NodeEntry which is active and managed by node table...
std::pair< NodeIdTimePoint, NodeID > EvictionTimeout
First NodeID (NodeIdTimePoint) may be evicted and replaced with second NodeID.
Node node(NodeID const &_id)
Returns the Node to the corresponding node id or the empty Node if that id is not found...
static const uint8_t type
Node m_node
This node. LOCK x_state if endpoint access or mutation is required. Do not modify id...
UDP Interface Handler must implement UDPSocketEvents.
Simple class that represents a "key pair".
bool isPublicAddress(bi::address const &_addressToCheck)
std::unordered_map< bi::address, TimePoint > m_pubkDiscoverPings
List of pending pings where node entry wasn't created due to unkown pubk.
std::pair< NodeID, TimePoint > NodeIdTimePoint
std::hash for asio::adress
NodeSocket * m_socketPointer
Set to m_socket.get(). Socket is created in constructor and disconnected in destructor to ensure acce...
void disconnect()
Disconnect socket.
vector_ref< _T > cropped(size_t _begin, size_t _count) const
static const uint8_t type
if(a.IndicesBefore(b, len, lenIndices))
unsigned const distance
Node's distance (xor of _src as integer).
bool contentsEqual(std::vector< mutable_value_type > const &_c) const
static Secret get()
Returns the next nonce (might be read from a file).
static unsigned distance(NodeID const &_a, NodeID const &_b)
Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
void processEvents()
Called by implementation which provided handler to process NodeEntryAdded/NodeEntryDropped events...
#define DEV_GUARDED(MUTEX)
Simple block guard.
bool isOpen()
Returns if socket is open.
std::vector< std::shared_ptr< NodeEntry > > nearestNodeEntries(NodeID _target)
Returns nodes from node table which are closest to target.
Mutex x_pubkDiscoverPings
LOCK x_nodes first if both x_nodes and x_pubkDiscoverPings locks are required.
bytesConstRef ref() const
std::list< std::weak_ptr< NodeEntry > > nodes
std::chrono::milliseconds const c_bucketRefresh
Refresh interval prevents bucket from becoming stale. [Kademlia].
static unsigned const s_maxSteps
Max iterations of discovery. (discover)
Ping packet: Sent to check if node is alive.
void doCheckEvictions()
Tasks.
std::lock_guard< std::mutex > Guard
void noteActiveNode(Public const &_pubk, bi::udp::endpoint const &_endpoint)
Called whenever activity is received from a node in order to maintain node table. ...
void doDiscovery()
Looks up a random node at interval.
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.
NodeTable using modified kademlia for node discovery and preference.
static unsigned const s_bins
Size of m_state (excludes root, which is us).
Public recover(Signature const &_sig, h256 const &_hash)
Recovers Public key from signed message hash.
const Node UnspecifiedNode
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...
Mutex x_evictions
LOCK x_evictions first if both x_nodes and x_evictions locks are required.
Mutex x_nodes
LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const...
bool haveNode(NodeID const &_id)
Returns true if node id is in node table.
void schedule(unsigned _msInFuture, std::function< void(boost::system::error_code const &)> const &_f)
std::unique_ptr< NodeTableEventHandler > m_nodeEventHandler
Event handler for node events.
std::chrono::milliseconds const c_reqTimeout
How long to wait for requests (evict, find iterations).
virtual h256 sign(Secret const &_from)
DeadlineOps m_timers
this should be the last member - it must be destroyed first
static const uint8_t type
virtual NodeID const & address() const
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...
Secret m_secret
This nodes secret key.
bool sha3(bytesConstRef _input, bytesRef o_output)
Calculate SHA3-256 hash of the given input and load it into the given output.
unsigned count() const
Returns node count.
NodeBucket & bucket_UNSAFE(NodeEntry const *_n)
Returns references to bucket which corresponds to distance of node id.
std::chrono::milliseconds const c_evictionCheckInterval
Intervals.
std::list< NodeID > nodes() const
Returns list of node ids active in node table.
static unsigned const s_bucketSize
Chosen constants.
static unsigned const s_alpha
Denoted by in [Kademlia]. Number of concurrent FindNode requests.
std::array< NodeBucket, s_bins > m_state
State of p2p node network.
Interface which UDPSocket will implement.
void ping(NodeIPEndpoint _to) const
Used to ping endpoint.
void connect()
Socket will begin listening for and delivering packets.
Pong packet: Sent in response to ping.
static std::unique_ptr< DiscoveryDatagram > interpretUDP(bi::udp::endpoint const &_from, bytesConstRef _packet)
Decodes UDP packets.
void dropNode(std::shared_ptr< NodeEntry > _n)
Used to drop node when timeout occurs or when evict() result is to keep previous node.
UniValue echo(const JSONRPCRequest &request)
Mutex x_state
LOCK x_state first if both x_nodes and x_state locks are required.
FindNode Packet: Request k-nodes, closest to the target.
std::list< NodeEntry > snapshot() const
Returns snapshot of table.
std::unordered_map< NodeID, std::shared_ptr< NodeEntry > > m_nodes
Known Node Endpoints.
std::deque< EvictionTimeout > m_evictions
Eviction timeouts.
Node Packet: One or more node packets are sent in response to FindNode.
NodeIPEndpoint endpoint
Endpoints by which we expect to reach node.
Interface which a UDPSocket's owner must implement.