@ -24,8 +24,6 @@
# include <algorithm>
# include <algorithm>
# include <deque>
# include <deque>
// Make sure boost/asio.hpp is included before windows.h.
# include <boost/asio.hpp>
# include <boost/integer/static_log2.hpp>
# include <boost/integer/static_log2.hpp>
# include <libp2p/UDP.h>
# include <libp2p/UDP.h>
@ -58,7 +56,7 @@ class NodeTableEventHandler
friend class NodeTable ;
friend class NodeTable ;
public :
public :
virtual void processEvent ( NodeId const & _n , NodeTableEventType const & _e ) = 0 ;
virtual void processEvent ( NodeId const & _n , NodeTableEventType const & _e ) = 0 ;
protected :
protected :
/// Called by NodeTable on behalf of an implementation (Host) to process new events without blocking nodetable.
/// Called by NodeTable on behalf of an implementation (Host) to process new events without blocking nodetable.
void processEvents ( )
void processEvents ( )
@ -77,10 +75,10 @@ protected:
for ( auto const & e : events )
for ( auto const & e : events )
processEvent ( e . first , e . second ) ;
processEvent ( e . first , e . second ) ;
}
}
/// Called by NodeTable to append event.
/// Called by NodeTable to append event.
virtual void appendEvent ( NodeId _n , NodeTableEventType _e ) { Guard l ( x_events ) ; m_nodeEventHandler . push_back ( _n ) ; m_events [ _n ] = _e ; }
virtual void appendEvent ( NodeId _n , NodeTableEventType _e ) { Guard l ( x_events ) ; m_nodeEventHandler . push_back ( _n ) ; m_events [ _n ] = _e ; }
Mutex x_events ;
Mutex x_events ;
std : : list < NodeId > m_nodeEventHandler ;
std : : list < NodeId > m_nodeEventHandler ;
std : : map < NodeId , NodeTableEventType > m_events ;
std : : map < NodeId , NodeTableEventType > m_events ;
@ -88,10 +86,10 @@ protected:
class NodeTable ;
class NodeTable ;
inline std : : ostream & operator < < ( std : : ostream & _out , NodeTable const & _nodeTable ) ;
inline std : : ostream & operator < < ( std : : ostream & _out , NodeTable const & _nodeTable ) ;
/**
/**
* NodeTable using modified kademlia for node discovery and preference .
* NodeTable using modified kademlia for node discovery and preference .
* Node table requires an IO service , creates a socket for incoming
* Node table requires an IO service , creates a socket for incoming
* UDP messages and implements a kademlia - like protocol . Node requests and
* UDP messages and implements a kademlia - like protocol . Node requests and
* responses are used to build a node table which can be queried to
* 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
* obtain a list of potential nodes to connect to , and , passes events to
@ -134,69 +132,69 @@ class NodeTable: UDPSocketEvents, public std::enable_shared_from_this<NodeTable>
using NodeSocket = UDPSocket < NodeTable , 1280 > ;
using NodeSocket = UDPSocket < NodeTable , 1280 > ;
using TimePoint = std : : chrono : : steady_clock : : time_point ;
using TimePoint = std : : chrono : : steady_clock : : time_point ;
using EvictionTimeout = std : : pair < std : : pair < NodeId , TimePoint > , NodeId > ; ///< First NodeId may be evicted and replaced with second NodeId.
using EvictionTimeout = std : : pair < std : : pair < NodeId , TimePoint > , NodeId > ; ///< First NodeId may be evicted and replaced with second NodeId.
public :
public :
NodeTable ( ba : : io_service & _io , KeyPair _alias , uint16_t _udpPort = 30303 ) ;
NodeTable ( ba : : io_service & _io , KeyPair _alias , uint16_t _udpPort = 30303 ) ;
~ NodeTable ( ) ;
~ NodeTable ( ) ;
/// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
/// Returns distance based on xor metric two node ids. Used by NodeEntry and NodeTable.
static unsigned distance ( NodeId const & _a , NodeId const & _b ) { u512 d = _a ^ _b ; unsigned ret ; for ( ret = 0 ; d > > = 1 ; + + ret ) { } ; return ret ; }
static unsigned distance ( NodeId const & _a , NodeId const & _b ) { u512 d = _a ^ _b ; unsigned ret ; for ( ret = 0 ; d > > = 1 ; + + ret ) { } ; return ret ; }
/// Set event handler for NodeEntryAdded and NodeEntryRemoved events.
/// Set event handler for NodeEntryAdded and NodeEntryRemoved events.
void setEventHandler ( NodeTableEventHandler * _handler ) { m_nodeEventHandler . reset ( _handler ) ; }
void setEventHandler ( NodeTableEventHandler * _handler ) { m_nodeEventHandler . reset ( _handler ) ; }
/// Called by implementation which provided handler to process NodeEntryAdded/NodeEntryRemoved events. Events are coalesced by type whereby old events are ignored.
/// Called by implementation which provided handler to process NodeEntryAdded/NodeEntryRemoved events. Events are coalesced by type whereby old events are ignored.
void processEvents ( ) ;
void processEvents ( ) ;
/// Add node. Node will be pinged if it's not already known.
/// Add node. Node will be pinged if it's not already known.
std : : shared_ptr < NodeEntry > addNode ( Public const & _pubk , bi : : udp : : endpoint const & _udp , bi : : tcp : : endpoint const & _tcp ) ;
std : : shared_ptr < NodeEntry > addNode ( Public const & _pubk , bi : : udp : : endpoint const & _udp , bi : : tcp : : endpoint const & _tcp ) ;
/// Add node. Node will be pinged if it's not already known.
/// Add node. Node will be pinged if it's not already known.
std : : shared_ptr < NodeEntry > addNode ( Node const & _node ) ;
std : : shared_ptr < NodeEntry > addNode ( Node const & _node ) ;
/// To be called when node table is empty. Runs node discovery with m_node.id as the target in order to populate node-table.
/// To be called when node table is empty. Runs node discovery with m_node.id as the target in order to populate node-table.
void discover ( ) ;
void discover ( ) ;
/// Returns list of node ids active in node table.
/// Returns list of node ids active in node table.
std : : list < NodeId > nodes ( ) const ;
std : : list < NodeId > nodes ( ) const ;
/// Returns node count.
/// Returns node count.
unsigned count ( ) const { return m_nodes . size ( ) ; }
unsigned count ( ) const { return m_nodes . size ( ) ; }
/// Returns snapshot of table.
/// Returns snapshot of table.
std : : list < NodeEntry > snapshot ( ) const ;
std : : list < NodeEntry > snapshot ( ) const ;
/// Returns true if node id is in node table.
/// Returns true if node id is in node table.
bool haveNode ( NodeId const & _id ) { Guard l ( x_nodes ) ; return m_nodes . count ( _id ) > 0 ; }
bool haveNode ( NodeId const & _id ) { Guard l ( x_nodes ) ; return m_nodes . count ( _id ) > 0 ; }
/// Returns the Node to the corresponding node id or the empty Node if that id is not found.
/// Returns the Node to the corresponding node id or the empty Node if that id is not found.
Node node ( NodeId const & _id ) ;
Node node ( NodeId const & _id ) ;
# if defined(BOOST_AUTO_TEST_SUITE) || defined(_MSC_VER) // MSVC includes access specifier in symbol name
# if defined(BOOST_AUTO_TEST_SUITE) || defined(_MSC_VER) // MSVC includes access specifier in symbol name
protected :
protected :
# else
# else
private :
private :
# endif
# endif
/// Constants for Kademlia, derived from address space.
/// Constants for Kademlia, derived from address space.
static unsigned const s_addressByteSize = sizeof ( NodeId ) ; ///< Size of address type in bytes.
static unsigned const s_addressByteSize = sizeof ( NodeId ) ; ///< Size of address type in bytes.
static unsigned const s_bits = 8 * s_addressByteSize ; ///< Denoted by n in [Kademlia].
static unsigned const s_bits = 8 * s_addressByteSize ; ///< Denoted by n in [Kademlia].
static unsigned const s_bins = s_bits - 1 ; ///< Size of m_state (excludes root, which is us).
static unsigned const s_bins = s_bits - 1 ; ///< Size of m_state (excludes root, which is us).
static unsigned const s_maxSteps = boost : : static_log2 < s_bits > : : value ; ///< Max iterations of discovery. (discover)
static unsigned const s_maxSteps = boost : : static_log2 < s_bits > : : value ; ///< Max iterations of discovery. (discover)
/// Chosen constants
/// Chosen constants
static unsigned const s_bucketSize = 16 ; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket.
static unsigned const s_bucketSize = 16 ; ///< Denoted by k in [Kademlia]. Number of nodes stored in each bucket.
static unsigned const s_alpha = 3 ; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests.
static unsigned const s_alpha = 3 ; ///< Denoted by \alpha in [Kademlia]. Number of concurrent FindNode requests.
/// Intervals
/// Intervals
/* todo: replace boost::posix_time; change constants to upper camelcase */
/* todo: replace boost::posix_time; change constants to upper camelcase */
boost : : posix_time : : milliseconds const c_evictionCheckInterval = boost : : posix_time : : milliseconds ( 75 ) ; ///< Interval at which eviction timeouts are checked.
boost : : posix_time : : milliseconds const c_evictionCheckInterval = boost : : posix_time : : milliseconds ( 75 ) ; ///< Interval at which eviction timeouts are checked.
std : : chrono : : milliseconds const c_reqTimeout = std : : chrono : : milliseconds ( 300 ) ; ///< How long to wait for requests (evict, find iterations).
std : : chrono : : milliseconds const c_reqTimeout = std : : chrono : : milliseconds ( 300 ) ; ///< How long to wait for requests (evict, find iterations).
std : : chrono : : seconds const c_bucketRefresh = std : : chrono : : seconds ( 3600 ) ; ///< Refresh interval prevents bucket from becoming stale. [Kademlia]
std : : chrono : : seconds const c_bucketRefresh = std : : chrono : : seconds ( 3600 ) ; ///< Refresh interval prevents bucket from becoming stale. [Kademlia]
struct NodeBucket
struct NodeBucket
{
{
unsigned distance ;
unsigned distance ;
@ -204,50 +202,50 @@ private:
std : : list < std : : weak_ptr < NodeEntry > > nodes ;
std : : list < std : : weak_ptr < NodeEntry > > nodes ;
void touch ( ) { modified = std : : chrono : : steady_clock : : now ( ) ; }
void touch ( ) { modified = std : : chrono : : steady_clock : : now ( ) ; }
} ;
} ;
/// Used to ping endpoint.
/// Used to ping endpoint.
void ping ( bi : : udp : : endpoint _to ) const ;
void ping ( bi : : udp : : endpoint _to ) const ;
/// Used ping known node. Used by node table when refreshing buckets and as part of eviction process (see evict).
/// Used ping known node. Used by node table when refreshing buckets and as part of eviction process (see evict).
void ping ( NodeEntry * _n ) const ;
void ping ( NodeEntry * _n ) const ;
/// Returns center node entry which describes this node and used with dist() to calculate xor metric for node table nodes.
/// Returns center node entry which describes this node and used with dist() to calculate xor metric for node table nodes.
NodeEntry center ( ) const { return NodeEntry ( m_node , m_node . publicKey ( ) , m_node . endpoint . udp ) ; }
NodeEntry center ( ) const { return NodeEntry ( m_node , m_node . publicKey ( ) , m_node . endpoint . udp ) ; }
/// Used by asynchronous operations to return NodeEntry which is active and managed by node table.
/// Used by asynchronous operations to return NodeEntry which is active and managed by node table.
std : : shared_ptr < NodeEntry > nodeEntry ( NodeId _id ) ;
std : : shared_ptr < NodeEntry > nodeEntry ( NodeId _id ) ;
/// Used to discovery nodes on network which are close to the given target.
/// 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.
/// Sends s_alpha concurrent requests to nodes nearest to target, for nodes nearest to target, up to s_maxSteps rounds.
void discover ( NodeId _target , unsigned _round = 0 , std : : shared_ptr < std : : set < std : : shared_ptr < NodeEntry > > > _tried = std : : shared_ptr < std : : set < std : : shared_ptr < NodeEntry > > > ( ) ) ;
void discover ( NodeId _target , unsigned _round = 0 , std : : shared_ptr < std : : set < std : : shared_ptr < NodeEntry > > > _tried = std : : shared_ptr < std : : set < std : : shared_ptr < NodeEntry > > > ( ) ) ;
/// Returns nodes from node table which are closest to target.
/// Returns nodes from node table which are closest to target.
std : : vector < std : : shared_ptr < NodeEntry > > nearestNodeEntries ( NodeId _target ) ;
std : : vector < std : : shared_ptr < NodeEntry > > nearestNodeEntries ( NodeId _target ) ;
/// Asynchronously drops _leastSeen node if it doesn't reply and adds _new node, otherwise _new node is thrown away.
/// Asynchronously drops _leastSeen node if it doesn't reply and adds _new node, otherwise _new node is thrown away.
void evict ( std : : shared_ptr < NodeEntry > _leastSeen , std : : shared_ptr < NodeEntry > _new ) ;
void evict ( std : : shared_ptr < NodeEntry > _leastSeen , std : : shared_ptr < NodeEntry > _new ) ;
/// Called whenever activity is received from an unknown node in order to maintain node table.
/// Called whenever activity is received from an unknown node in order to maintain node table.
void noteActiveNode ( Public const & _pubk , bi : : udp : : endpoint const & _endpoint ) ;
void noteActiveNode ( Public const & _pubk , bi : : udp : : endpoint const & _endpoint ) ;
/// Used to drop node when timeout occurs or when evict() result is to keep previous node.
/// Used to drop node when timeout occurs or when evict() result is to keep previous node.
void dropNode ( std : : shared_ptr < NodeEntry > _n ) ;
void dropNode ( std : : shared_ptr < NodeEntry > _n ) ;
/// Returns references to bucket which corresponds to distance of node id.
/// Returns references to bucket which corresponds to distance of node id.
/// @warning Only use the return reference locked x_state mutex.
/// @warning Only use the return reference locked x_state mutex.
// TODO p2p: Remove this method after removing offset-by-one functionality.
// TODO p2p: Remove this method after removing offset-by-one functionality.
NodeBucket & bucket_UNSAFE ( NodeEntry const * _n ) ;
NodeBucket & bucket_UNSAFE ( NodeEntry const * _n ) ;
/// General Network Events
/// General Network Events
/// Called by m_socket when packet is received.
/// Called by m_socket when packet is received.
void onReceived ( UDPSocketFace * , bi : : udp : : endpoint const & _from , bytesConstRef _packet ) ;
void onReceived ( UDPSocketFace * , bi : : udp : : endpoint const & _from , bytesConstRef _packet ) ;
/// Called by m_socket when socket is disconnected.
/// Called by m_socket when socket is disconnected.
void onDisconnected ( UDPSocketFace * ) { }
void onDisconnected ( UDPSocketFace * ) { }
/// Tasks
/// Tasks
/// Called by evict() to ensure eviction check is scheduled to run and terminates when no evictions remain. Asynchronous.
/// Called by evict() to ensure eviction check is scheduled to run and terminates when no evictions remain. Asynchronous.
void doCheckEvictions ( boost : : system : : error_code const & _ec ) ;
void doCheckEvictions ( boost : : system : : error_code const & _ec ) ;
@ -255,7 +253,7 @@ private:
void doRefreshBuckets ( boost : : system : : error_code const & _ec ) ;
void doRefreshBuckets ( boost : : system : : error_code const & _ec ) ;
std : : unique_ptr < NodeTableEventHandler > m_nodeEventHandler ; ///< Event handler for node events.
std : : unique_ptr < NodeTableEventHandler > m_nodeEventHandler ; ///< Event handler for node events.
Node m_node ; ///< This node.
Node m_node ; ///< This node.
Secret m_secret ; ///< This nodes secret key.
Secret m_secret ; ///< This nodes secret key.
@ -312,7 +310,7 @@ struct PingNode: RLPXDatagram<PingNode>
PingNode ( bi : : udp : : endpoint _ep , std : : string _src , uint16_t _srcPort , std : : chrono : : seconds _expiration = std : : chrono : : seconds ( 60 ) ) : RLPXDatagram < PingNode > ( _ep ) , ipAddress ( _src ) , port ( _srcPort ) , expiration ( futureFromEpoch ( _expiration ) ) { }
PingNode ( bi : : udp : : endpoint _ep , std : : string _src , uint16_t _srcPort , std : : chrono : : seconds _expiration = std : : chrono : : seconds ( 60 ) ) : RLPXDatagram < PingNode > ( _ep ) , ipAddress ( _src ) , port ( _srcPort ) , expiration ( futureFromEpoch ( _expiration ) ) { }
static const uint8_t type = 1 ;
static const uint8_t type = 1 ;
unsigned version = 1 ;
unsigned version = 1 ;
std : : string ipAddress ;
std : : string ipAddress ;
unsigned port ;
unsigned port ;
@ -337,7 +335,7 @@ struct Pong: RLPXDatagram<Pong>
h256 echo ; ///< MCD of PingNode
h256 echo ; ///< MCD of PingNode
unsigned expiration ;
unsigned expiration ;
void streamRLP ( RLPStream & _s ) const { _s . appendList ( 2 ) ; _s < < echo < < expiration ; }
void streamRLP ( RLPStream & _s ) const { _s . appendList ( 2 ) ; _s < < echo < < expiration ; }
void interpretRLP ( bytesConstRef _bytes ) { RLP r ( _bytes ) ; echo = ( h256 ) r [ 0 ] ; expiration = r [ 1 ] . toInt < unsigned > ( ) ; }
void interpretRLP ( bytesConstRef _bytes ) { RLP r ( _bytes ) ; echo = ( h256 ) r [ 0 ] ; expiration = r [ 1 ] . toInt < unsigned > ( ) ; }
} ;
} ;
@ -361,7 +359,7 @@ struct FindNode: RLPXDatagram<FindNode>
FindNode ( bi : : udp : : endpoint _ep , NodeId _target , std : : chrono : : seconds _expiration = std : : chrono : : seconds ( 30 ) ) : RLPXDatagram < FindNode > ( _ep ) , target ( _target ) , expiration ( futureFromEpoch ( _expiration ) ) { }
FindNode ( bi : : udp : : endpoint _ep , NodeId _target , std : : chrono : : seconds _expiration = std : : chrono : : seconds ( 30 ) ) : RLPXDatagram < FindNode > ( _ep ) , target ( _target ) , expiration ( futureFromEpoch ( _expiration ) ) { }
static const uint8_t type = 3 ;
static const uint8_t type = 3 ;
h512 target ;
h512 target ;
unsigned expiration ;
unsigned expiration ;
@ -387,7 +385,7 @@ struct Neighbours: RLPXDatagram<Neighbours>
void streamRLP ( RLPStream & _s ) const { _s . appendList ( 3 ) ; _s < < ipAddress < < port < < node ; }
void streamRLP ( RLPStream & _s ) const { _s . appendList ( 3 ) ; _s < < ipAddress < < port < < node ; }
void interpretRLP ( RLP const & _r ) { ipAddress = _r [ 0 ] . toString ( ) ; port = _r [ 1 ] . toInt < unsigned > ( ) ; node = h512 ( _r [ 2 ] . toBytes ( ) ) ; }
void interpretRLP ( RLP const & _r ) { ipAddress = _r [ 0 ] . toString ( ) ; port = _r [ 1 ] . toInt < unsigned > ( ) ; node = h512 ( _r [ 2 ] . toBytes ( ) ) ; }
} ;
} ;
Neighbours ( bi : : udp : : endpoint _ep ) : RLPXDatagram < Neighbours > ( _ep ) , expiration ( futureFromEpoch ( std : : chrono : : seconds ( 30 ) ) ) { }
Neighbours ( bi : : udp : : endpoint _ep ) : RLPXDatagram < Neighbours > ( _ep ) , expiration ( futureFromEpoch ( std : : chrono : : seconds ( 30 ) ) ) { }
Neighbours ( bi : : udp : : endpoint _to , std : : vector < std : : shared_ptr < NodeEntry > > const & _nearest , unsigned _offset = 0 , unsigned _limit = 0 ) : RLPXDatagram < Neighbours > ( _to ) , expiration ( futureFromEpoch ( std : : chrono : : seconds ( 30 ) ) )
Neighbours ( bi : : udp : : endpoint _to , std : : vector < std : : shared_ptr < NodeEntry > > const & _nearest , unsigned _offset = 0 , unsigned _limit = 0 ) : RLPXDatagram < Neighbours > ( _to ) , expiration ( futureFromEpoch ( std : : chrono : : seconds ( 30 ) ) )
{
{
@ -401,7 +399,7 @@ struct Neighbours: RLPXDatagram<Neighbours>
nodes . push_back ( node ) ;
nodes . push_back ( node ) ;
}
}
}
}
static const uint8_t type = 4 ;
static const uint8_t type = 4 ;
std : : vector < Node > nodes ;
std : : vector < Node > nodes ;
unsigned expiration = 1 ;
unsigned expiration = 1 ;