@ -1,16 +1,16 @@
/*
/*
This file is part of cpp - ethereum .
This file is part of cpp - ethereum .
cpp - ethereum is free software : you can redistribute it and / or modify
cpp - ethereum is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
( at your option ) any later version .
cpp - ethereum is distributed in the hope that it will be useful ,
cpp - ethereum is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with cpp - ethereum . If not , see < http : //www.gnu.org/licenses/>.
along with cpp - ethereum . If not , see < http : //www.gnu.org/licenses/>.
*/
*/
@ -23,7 +23,9 @@
# include <algorithm>
# include <algorithm>
# include <deque>
# include <deque>
# include <boost/integer/static_log2.hpp>
# include <boost/integer/static_log2.hpp>
# include <libp2p/UDP.h>
# include <libp2p/UDP.h>
# include "Common.h"
# include "Common.h"
@ -54,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 ( )
@ -73,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 ;
@ -84,16 +86,16 @@ 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
* Host whenever a node is added or removed to / from the table .
* Host whenever a node is added or removed to / from the table .
*
*
* Thread - safety is ensured by modifying NodeEntry details via
* Thread - safety is ensured by modifying NodeEntry details via
* shared_ptr replacement instead of mutating values .
* shared_ptr replacement instead of mutating values .
*
*
* NodeTable accepts a port for UDP and will listen to the port on all available
* NodeTable accepts a port for UDP and will listen to the port on all available
@ -130,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 ;
@ -200,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 ) ;
@ -251,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.
@ -308,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 ;
@ -333,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 > ( ) ; }
} ;
} ;
@ -357,10 +359,10 @@ 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 ;
void streamRLP ( RLPStream & _s ) const { _s . appendList ( 2 ) ; _s < < target < < expiration ; }
void streamRLP ( RLPStream & _s ) const { _s . appendList ( 2 ) ; _s < < target < < expiration ; }
void interpretRLP ( bytesConstRef _bytes ) { RLP r ( _bytes ) ; target = r [ 0 ] . toHash < h512 > ( ) ; expiration = r [ 1 ] . toInt < unsigned > ( ) ; }
void interpretRLP ( bytesConstRef _bytes ) { RLP r ( _bytes ) ; target = r [ 0 ] . toHash < h512 > ( ) ; expiration = r [ 1 ] . toInt < unsigned > ( ) ; }
} ;
} ;
@ -383,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 ) ) )
{
{
@ -397,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 ;