TARAXA
dev::p2p::NodeTable Class Reference

#include <NodeTable.h>

Collaboration diagram for dev::p2p::NodeTable:

Classes

struct  NodeBucket
 
struct  NodeValidation
 

Public Member Functions

 NodeTable (ba::io_context &_io, KeyPair const &_alias, NodeIPEndpoint const &_endpoint, ENR const &_enr, bool _enabled=true, bool _allowLocalDiscovery=false, bool is_boot_node=false, uint32_t chain_id=0)
 
 ~NodeTable ()
 
void setEventHandler (NodeTableEventHandler *_handler)
 Set event handler for NodeEntryAdded and NodeEntryDropped events. More...
 
void processEvents ()
 
bool addNode (Node const &_node)
 
bool addKnownNode (Node const &_node, uint32_t _lastPongReceivedTime, uint32_t _lastPongSentTime)
 
std::list< NodeIDnodes () const
 Returns list of node ids active in node table. More...
 
unsigned count () const
 Returns node count. More...
 
std::list< NodeEntrysnapshot () const
 Returns snapshot of table. More...
 
bool haveNode (NodeID const &_id)
 Returns true if node id is in node table. More...
 
Node node (NodeID const &_id)
 
void invalidateNode (NodeID const &_id)
 
ENR hostENR () const
 
void runBackgroundTask (std::chrono::milliseconds const &_period, std::shared_ptr< ba::steady_timer > _timer, std::function< void()> _f)
 
void cancelTimer (std::shared_ptr< ba::steady_timer > _timer)
 

Static Public Member Functions

static int distance (h256 const &_a, h256 const &_b)
 

Static Public Attributes

static constexpr uint32_t c_bondingTimeSeconds {12 * 60 * 60}
 

Protected Member Functions

bool isValidNode (Node const &_node) const
 
void ping (Node const &_node, std::shared_ptr< NodeEntry > _replacementNodeEntry={})
 
void schedulePing (Node const &_node)
 
std::shared_ptr< NodeEntrynodeEntry (NodeID const &_id)
 
void doDiscoveryRound (NodeID _target, unsigned _round, std::shared_ptr< std::set< std::shared_ptr< NodeEntry >>> _tried)
 
std::vector< std::shared_ptr< NodeEntry > > nearestNodeEntries (NodeID const &_target)
 Returns s_bucketSize nodes from node table which are closest to target. More...
 
void evict (NodeEntry const &_leastSeen, std::shared_ptr< NodeEntry > _replacement)
 
void noteActiveNode (std::shared_ptr< NodeEntry > _nodeEntry)
 
void dropNode (std::shared_ptr< NodeEntry > _n)
 
NodeBucketbucket_UNSAFE (NodeEntry const *_n)
 
void onPacketReceived (UDPSocketFace *, bi::udp::endpoint const &_from, bytesConstRef _packet) override
 General Network Events. More...
 
std::shared_ptr< NodeEntryhandlePong (bi::udp::endpoint const &_from, DiscoveryDatagram const &_packet)
 
std::shared_ptr< NodeEntryhandleNeighbours (bi::udp::endpoint const &_from, DiscoveryDatagram const &_packet)
 
std::shared_ptr< NodeEntryhandleFindNode (bi::udp::endpoint const &_from, DiscoveryDatagram const &_packet)
 
std::shared_ptr< NodeEntryhandlePingNode (bi::udp::endpoint const &_from, DiscoveryDatagram const &_packet)
 
std::shared_ptr< NodeEntryhandleENRRequest (bi::udp::endpoint const &_from, DiscoveryDatagram const &_packet)
 
std::shared_ptr< NodeEntryhandleENRResponse (bi::udp::endpoint const &_from, DiscoveryDatagram const &_packet)
 
void onSocketDisconnected (UDPSocketFace *) override
 Called by m_socket when socket is disconnected. More...
 
void doDiscovery ()
 Tasks. More...
 
void doHandleTimeouts ()
 
void doEndpointTracking ()
 
uint32_t nextRequestExpirationTime () const
 
bool isAllowedEndpoint (NodeIPEndpoint const &_endpointToCheck) const
 
NodeIPEndpoint getSourceEndpoint (bi::udp::endpoint const &from, PingNode const &packet)
 

Protected Attributes

unsigned s_bucketSize = NODE_BUCKET_SIZE
 
ba::strand< ba::io_context::executor_type > strand_
 
std::unique_ptr< NodeTableEventHandlerm_nodeEventHandler
 Event handler for node events. More...
 
const NodeID m_hostNodeID
 
const h256 m_hostNodeIDHash
 
const bool m_allowLocalDiscovery
 
bi::address const m_hostStaticIP
 
NodeIPEndpoint m_hostNodeEndpoint
 
ENR m_hostENR
 
Mutex m_hostENRMutex
 
Secret m_secret
 This nodes secret key. More...
 
Mutex x_nodes
 
std::unordered_map< NodeID, std::shared_ptr< NodeEntry > > m_allNodes
 
Mutex x_state
 
std::unordered_map< NodeID, bi::udp::endpoint > m_id2IpMap
 This map is used for maping between node id and real network IP. More...
 
std::unordered_map< bi::udp::endpoint, NodeIPEndpointm_ipMappings
 This map keeps tracking between real IP and IP reported by node (external IP) More...
 
Mutex x_ips
 
std::array< NodeBucket, s_binsm_buckets
 
std::list< NodeIdTimePointm_sentFindNodes
 Timeouts for FindNode requests. More...
 
std::shared_ptr< NodeSocketm_socket
 
std::unordered_map< bi::udp::endpoint, NodeValidationm_sentPings
 
const std::chrono::seconds m_requestTimeToLive
 
Logger m_logger {createLogger(VerbosityDebug, "discov")}
 
EndpointTracker m_endpointTracker
 
std::shared_ptr< ba::steady_timer > m_discoveryTimer
 
std::shared_ptr< ba::steady_timer > m_timeoutsTimer
 
std::shared_ptr< ba::steady_timer > m_endpointTrackingTimer
 
const bool is_boot_node_ = false
 
const uint32_t chain_id_ = 0
 

Static Protected Attributes

static constexpr unsigned s_addressByteSize = h256::size
 Constants for Kademlia, derived from address space. More...
 
static constexpr unsigned s_bits = 8 * s_addressByteSize
 Denoted by n in [Kademlia]. More...
 
static constexpr unsigned s_bins = s_bits - 1
 Size of m_buckets (excludes root, which is us). More...
 
static constexpr unsigned s_maxSteps = boost::static_log2<s_bits>::value
 
static constexpr size_t BOOT_NODE_BUCKET_SIZE = 256
 Chosen constants. More...
 
static constexpr size_t NODE_BUCKET_SIZE = 16
 
static constexpr unsigned s_alpha = 3
 
static constexpr std::chrono::milliseconds c_reqTimeoutMs {600}
 Intervals. More...
 
static constexpr std::chrono::milliseconds c_discoveryRoundIntervalMs {c_reqTimeoutMs * 2}
 How long to wait before starting a new discovery round. More...
 
static constexpr std::chrono::milliseconds c_bucketRefreshMs {7200}
 Refresh interval prevents bucket from becoming stale. [Kademlia]. More...
 

Private Types

using NodeSocket = UDPSocket< NodeTable, 1280 >
 
using TimePoint = std::chrono::steady_clock::time_point
 Steady time point. More...
 
using NodeIdTimePoint = std::pair< NodeID, TimePoint >
 

Friends

std::ostream & operator<< (std::ostream &_out, NodeTable const &_nodeTable)
 

Additional Inherited Members

- Private Member Functions inherited from dev::p2p::UDPSocketEvents
virtual ~UDPSocketEvents ()=default
 

Detailed Description

NodeTable using modified kademlia for node discovery and preference. Node table requires an IO service, creates a socket for incoming UDP messages and implements a kademlia-like protocol. Node requests and responses are used to build a node table which can be queried to obtain a list of potential nodes to connect to, and, passes events to Host whenever a node is added or removed to/from the table.

Thread-safety is ensured by modifying NodeEntry details via shared_ptr replacement instead of mutating values.

NodeTable accepts a port for UDP and will listen to the port on all available interfaces.

[Optimization]

Todo:

serialize evictions per-bucket

store evictions in map, unit-test eviction logic

store root node in table

encapsulate discover into NetworkAlgorithm (task)

expiration and sha3(id) 'to' for messages which are replies (prevents replay)

cache Ping and FindSelf

[Networking]

Todo:

eth/upnp/natpmp/stun/ice/etc for public-discovery

firewall

[Protocol]

Todo:

optimize knowledge at opposite edges; eg, s_bitsPerStep lookups. (Can be done via pointers to NodeBucket)

^ s_bitsPerStep = 8; // Denoted by b in [Kademlia]. Bits by which address space is divided.


Class Documentation

◆ dev::p2p::NodeTable::NodeBucket

struct dev::p2p::NodeTable::NodeBucket
Class Members
unsigned distance
list< weak_ptr< NodeEntry > > nodes

Member Typedef Documentation

◆ NodeIdTimePoint

◆ NodeSocket

◆ TimePoint

using dev::p2p::NodeTable::TimePoint = std::chrono::steady_clock::time_point
private

Steady time point.

Constructor & Destructor Documentation

◆ NodeTable()

dev::p2p::NodeTable::NodeTable ( ba::io_context &  _io,
KeyPair const &  _alias,
NodeIPEndpoint const &  _endpoint,
ENR const &  _enr,
bool  _enabled = true,
bool  _allowLocalDiscovery = false,
bool  is_boot_node = false,
uint32_t  chain_id = 0 
)

Constructor requiring host for I/O, credentials, and IP Address, port to listen on and host ENR.

◆ ~NodeTable()

dev::p2p::NodeTable::~NodeTable ( )
inline

Member Function Documentation

◆ addKnownNode()

bool dev::p2p::NodeTable::addKnownNode ( Node const &  _node,
uint32_t  _lastPongReceivedTime,
uint32_t  _lastPongSentTime 
)

Add node to the list of all nodes and add it to the node table. In case the node's endpoint proof expired, pings it. In case the nodes is already in the node table, ignores add request.

Returns
True if the node is valid.

◆ addNode()

bool dev::p2p::NodeTable::addNode ( Node const &  _node)

Starts async node add to the node table by pinging it to trigger the endpoint proof. In case the node is already in the node table, pings only if the endpoint proof expired.

Returns
True if the node is valid.

◆ bucket_UNSAFE()

NodeTable::NodeBucket & dev::p2p::NodeTable::bucket_UNSAFE ( NodeEntry const *  _n)
protected

Returns references to bucket which corresponds to distance of node id.

Warning
Only use the return reference locked x_state mutex.

◆ cancelTimer()

void dev::p2p::NodeTable::cancelTimer ( std::shared_ptr< ba::steady_timer >  _timer)

◆ count()

unsigned dev::p2p::NodeTable::count ( ) const
inline

Returns node count.

◆ distance()

static int dev::p2p::NodeTable::distance ( h256 const &  _a,
h256 const &  _b 
)
inlinestatic

Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.

◆ doDiscovery()

void dev::p2p::NodeTable::doDiscovery ( )
protected

Tasks.

Looks up a random node at @c_bucketRefresh interval.

◆ doDiscoveryRound()

void dev::p2p::NodeTable::doDiscoveryRound ( NodeID  _target,
unsigned  _round,
std::shared_ptr< std::set< std::shared_ptr< NodeEntry >>>  _tried 
)
protected

Used to discovery nodes on network which are close to the given target. Sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to s_maxSteps rounds.

◆ doEndpointTracking()

void dev::p2p::NodeTable::doEndpointTracking ( )
protected

◆ doHandleTimeouts()

void dev::p2p::NodeTable::doHandleTimeouts ( )
protected

Clear timed-out pings and drop nodes from the node table which haven't responded to ping and bring in their replacements

◆ dropNode()

void dev::p2p::NodeTable::dropNode ( std::shared_ptr< NodeEntry _n)
protected

Used to drop node when timeout occurs or when evict() result is to keep previous node.

◆ evict()

void dev::p2p::NodeTable::evict ( NodeEntry const &  _leastSeen,
std::shared_ptr< NodeEntry _replacement 
)
protected

Asynchronously drops _leastSeen node if it doesn't reply and adds _replacement node, otherwise _replacement is thrown away.

◆ getSourceEndpoint()

NodeIPEndpoint dev::p2p::NodeTable::getSourceEndpoint ( bi::udp::endpoint const &  from,
PingNode const &  packet 
)
protected

◆ handleENRRequest()

std::shared_ptr< NodeEntry > dev::p2p::NodeTable::handleENRRequest ( bi::udp::endpoint const &  _from,
DiscoveryDatagram const &  _packet 
)
protected

◆ handleENRResponse()

std::shared_ptr< NodeEntry > dev::p2p::NodeTable::handleENRResponse ( bi::udp::endpoint const &  _from,
DiscoveryDatagram const &  _packet 
)
protected

◆ handleFindNode()

std::shared_ptr< NodeEntry > dev::p2p::NodeTable::handleFindNode ( bi::udp::endpoint const &  _from,
DiscoveryDatagram const &  _packet 
)
protected

◆ handleNeighbours()

std::shared_ptr< NodeEntry > dev::p2p::NodeTable::handleNeighbours ( bi::udp::endpoint const &  _from,
DiscoveryDatagram const &  _packet 
)
protected

◆ handlePingNode()

std::shared_ptr< NodeEntry > dev::p2p::NodeTable::handlePingNode ( bi::udp::endpoint const &  _from,
DiscoveryDatagram const &  _packet 
)
protected

◆ handlePong()

std::shared_ptr< NodeEntry > dev::p2p::NodeTable::handlePong ( bi::udp::endpoint const &  _from,
DiscoveryDatagram const &  _packet 
)
protected

◆ haveNode()

bool dev::p2p::NodeTable::haveNode ( NodeID const &  _id)
inline

Returns true if node id is in node table.

◆ hostENR()

ENR dev::p2p::NodeTable::hostENR ( ) const
inline

◆ invalidateNode()

void dev::p2p::NodeTable::invalidateNode ( NodeID const &  _id)

◆ isAllowedEndpoint()

bool dev::p2p::NodeTable::isAllowedEndpoint ( NodeIPEndpoint const &  _endpointToCheck) const
inlineprotected

Determines if a node with the supplied endpoint is allowed to participate in discovery.

◆ isValidNode()

bool dev::p2p::NodeTable::isValidNode ( Node const &  _node) const
protected
Returns
true if the node is valid to be added to the node table. (validates node ID and endpoint)

◆ nearestNodeEntries()

std::vector< std::shared_ptr< NodeEntry > > dev::p2p::NodeTable::nearestNodeEntries ( NodeID const &  _target)
protected

Returns s_bucketSize nodes from node table which are closest to target.

◆ nextRequestExpirationTime()

uint32_t dev::p2p::NodeTable::nextRequestExpirationTime ( ) const
inlineprotected

◆ node()

Node dev::p2p::NodeTable::node ( NodeID const &  _id)

Returns the Node to the corresponding node id or the empty Node if that id is not found.

◆ nodeEntry()

std::shared_ptr< NodeEntry > dev::p2p::NodeTable::nodeEntry ( NodeID const &  _id)
protected

Used by asynchronous operations to return NodeEntry which is active and managed by node table.

◆ nodes()

std::list< NodeID > dev::p2p::NodeTable::nodes ( ) const

Returns list of node ids active in node table.

◆ noteActiveNode()

void dev::p2p::NodeTable::noteActiveNode ( std::shared_ptr< NodeEntry _nodeEntry)
protected

Called whenever activity is received from a node in order to maintain node table. Only called for nodes for which we've completed an endpoint proof.

◆ onPacketReceived()

void dev::p2p::NodeTable::onPacketReceived ( UDPSocketFace ,
bi::udp::endpoint const &  _from,
bytesConstRef  _packet 
)
overrideprotectedvirtual

General Network Events.

Called by m_socket when packet is received.

Implements dev::p2p::UDPSocketEvents.

◆ onSocketDisconnected()

void dev::p2p::NodeTable::onSocketDisconnected ( UDPSocketFace )
inlineoverrideprotectedvirtual

Called by m_socket when socket is disconnected.

Reimplemented from dev::p2p::UDPSocketEvents.

◆ ping()

void dev::p2p::NodeTable::ping ( Node const &  _node,
std::shared_ptr< NodeEntry _replacementNodeEntry = {} 
)
protected

Used to ping a node to initiate the endpoint proof. Used when contacting neighbours if they don't have a valid endpoint proof (see doDiscoveryRound), refreshing buckets and as part of eviction process (see evict). Synchronous, has to be called only from the network thread.

◆ processEvents()

void dev::p2p::NodeTable::processEvents ( )

Called by implementation which provided handler to process NodeEntryAdded/NodeEntryDropped events. Events are coalesced by type whereby old events are ignored.

◆ runBackgroundTask()

void dev::p2p::NodeTable::runBackgroundTask ( std::chrono::milliseconds const &  _period,
std::shared_ptr< ba::steady_timer >  _timer,
std::function< void()>  _f 
)

◆ schedulePing()

void dev::p2p::NodeTable::schedulePing ( Node const &  _node)
protected

Schedules ping() method to be called from the network thread. Not synchronous - the ping operation is queued via a timer.

◆ setEventHandler()

void dev::p2p::NodeTable::setEventHandler ( NodeTableEventHandler _handler)
inline

Set event handler for NodeEntryAdded and NodeEntryDropped events.

◆ snapshot()

std::list< NodeEntry > dev::p2p::NodeTable::snapshot ( ) const

Returns snapshot of table.

Friends And Related Function Documentation

◆ operator<<

std::ostream& operator<< ( std::ostream &  _out,
NodeTable const &  _nodeTable 
)
friend

Member Data Documentation

◆ BOOT_NODE_BUCKET_SIZE

constexpr size_t dev::p2p::NodeTable::BOOT_NODE_BUCKET_SIZE = 256
staticconstexprprotected

Chosen constants.

◆ c_bondingTimeSeconds

constexpr uint32_t dev::p2p::NodeTable::c_bondingTimeSeconds {12 * 60 * 60}
staticconstexpr

◆ c_bucketRefreshMs

constexpr std::chrono::milliseconds dev::p2p::NodeTable::c_bucketRefreshMs {7200}
staticconstexprprotected

Refresh interval prevents bucket from becoming stale. [Kademlia].

◆ c_discoveryRoundIntervalMs

constexpr std::chrono::milliseconds dev::p2p::NodeTable::c_discoveryRoundIntervalMs {c_reqTimeoutMs * 2}
staticconstexprprotected

How long to wait before starting a new discovery round.

◆ c_reqTimeoutMs

constexpr std::chrono::milliseconds dev::p2p::NodeTable::c_reqTimeoutMs {600}
staticconstexprprotected

Intervals.

How long to wait for requests (evict, find iterations).

◆ chain_id_

const uint32_t dev::p2p::NodeTable::chain_id_ = 0
protected

◆ is_boot_node_

const bool dev::p2p::NodeTable::is_boot_node_ = false
protected

◆ m_allNodes

std::unordered_map<NodeID, std::shared_ptr<NodeEntry> > dev::p2p::NodeTable::m_allNodes
protected

Node endpoints. Includes all nodes that were added into node table's buckets and have not been evicted yet.

◆ m_allowLocalDiscovery

const bool dev::p2p::NodeTable::m_allowLocalDiscovery
protected

Allow nodes with local addresses to be included in the discovery process

◆ m_buckets

std::array<NodeBucket, s_bins> dev::p2p::NodeTable::m_buckets
protected

State of p2p node network. Only includes nodes for which we've completed the endpoint proof

◆ m_discoveryTimer

std::shared_ptr<ba::steady_timer> dev::p2p::NodeTable::m_discoveryTimer
protected

◆ m_endpointTracker

EndpointTracker dev::p2p::NodeTable::m_endpointTracker
protected

◆ m_endpointTrackingTimer

std::shared_ptr<ba::steady_timer> dev::p2p::NodeTable::m_endpointTrackingTimer
protected

◆ m_hostENR

ENR dev::p2p::NodeTable::m_hostENR
protected

◆ m_hostENRMutex

Mutex dev::p2p::NodeTable::m_hostENRMutex
mutableprotected

◆ m_hostNodeEndpoint

NodeIPEndpoint dev::p2p::NodeTable::m_hostNodeEndpoint
protected

◆ m_hostNodeID

const NodeID dev::p2p::NodeTable::m_hostNodeID
protected

◆ m_hostNodeIDHash

const h256 dev::p2p::NodeTable::m_hostNodeIDHash
protected

◆ m_hostStaticIP

bi::address const dev::p2p::NodeTable::m_hostStaticIP
protected

◆ m_id2IpMap

std::unordered_map<NodeID, bi::udp::endpoint> dev::p2p::NodeTable::m_id2IpMap
protected

This map is used for maping between node id and real network IP.

◆ m_ipMappings

std::unordered_map<bi::udp::endpoint, NodeIPEndpoint> dev::p2p::NodeTable::m_ipMappings
protected

This map keeps tracking between real IP and IP reported by node (external IP)

◆ m_logger

Logger dev::p2p::NodeTable::m_logger {createLogger(VerbosityDebug, "discov")}
mutableprotected

◆ m_nodeEventHandler

std::unique_ptr<NodeTableEventHandler> dev::p2p::NodeTable::m_nodeEventHandler
protected

Event handler for node events.

◆ m_requestTimeToLive

const std::chrono::seconds dev::p2p::NodeTable::m_requestTimeToLive
protected

◆ m_secret

Secret dev::p2p::NodeTable::m_secret
protected

This nodes secret key.

◆ m_sentFindNodes

std::list<NodeIdTimePoint> dev::p2p::NodeTable::m_sentFindNodes
protected

Timeouts for FindNode requests.

◆ m_sentPings

std::unordered_map<bi::udp::endpoint, NodeValidation> dev::p2p::NodeTable::m_sentPings
protected

◆ m_socket

std::shared_ptr<NodeSocket> dev::p2p::NodeTable::m_socket
protected

Shared pointer for our UDPSocket; ASIO requires shared_ptr.

◆ m_timeoutsTimer

std::shared_ptr<ba::steady_timer> dev::p2p::NodeTable::m_timeoutsTimer
protected

◆ NODE_BUCKET_SIZE

constexpr size_t dev::p2p::NodeTable::NODE_BUCKET_SIZE = 16
staticconstexprprotected

◆ s_addressByteSize

constexpr unsigned dev::p2p::NodeTable::s_addressByteSize = h256::size
staticconstexprprotected

Constants for Kademlia, derived from address space.

Size of address type in bytes.

◆ s_alpha

constexpr unsigned dev::p2p::NodeTable::s_alpha = 3
staticconstexprprotected

Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests.

◆ s_bins

constexpr unsigned dev::p2p::NodeTable::s_bins = s_bits - 1
staticconstexprprotected

Size of m_buckets (excludes root, which is us).

◆ s_bits

constexpr unsigned dev::p2p::NodeTable::s_bits = 8 * s_addressByteSize
staticconstexprprotected

Denoted by n in [Kademlia].

◆ s_bucketSize

unsigned dev::p2p::NodeTable::s_bucketSize = NODE_BUCKET_SIZE
protected

Denoted by k in [Kademlia]. Number of nodes stored in each bucket.

◆ s_maxSteps

constexpr unsigned dev::p2p::NodeTable::s_maxSteps = boost::static_log2<s_bits>::value
staticconstexprprotected

Max iterations of discovery. (discover)

◆ strand_

ba::strand<ba::io_context::executor_type> dev::p2p::NodeTable::strand_
protected

◆ x_ips

Mutex dev::p2p::NodeTable::x_ips
mutableprotected

◆ x_nodes

Mutex dev::p2p::NodeTable::x_nodes
mutableprotected

LOCK x_state first if both locks are required. Mutable for thread-safe copy in nodes() const.

◆ x_state

Mutex dev::p2p::NodeTable::x_state
mutableprotected

LOCK x_state first if both x_nodes and x_state locks are required.


The documentation for this class was generated from the following files: