Documentation
¶
Overview ¶
Package services implements node discovery operations.
The services package provides:
- Iterative FINDNODE lookups (discv5 + discv4)
- PING/PONG operations (discv5 + discv4)
- Random walks for table maintenance
- ENR-based filtering during discovery
- Dual protocol support (prefers v5, falls back to v4)
Index ¶
- Constants
- type Config
- type IPDiscovery
- type IPDiscoveryConfig
- type IPDiscoveryStats
- type LookupService
- func (ls *LookupService) CleanupQueryHistory(maxAge time.Duration) int
- func (ls *LookupService) GetStats() LookupStats
- func (ls *LookupService) Lookup(ctx context.Context, target node.ID, k int) ([]*nodedb.Node, error)
- func (ls *LookupService) LookupWithFilter(ctx context.Context, target node.ID, k int, filter enr.ENRFilter) ([]*nodedb.Node, error)
- func (ls *LookupService) RandomWalk(ctx context.Context) ([]*nodedb.Node, error)
- type LookupStats
- type PingService
- func (ps *PingService) CheckProtocolSupport(n *nodedb.Node) (bool, bool, error)
- func (ps *PingService) CheckProtocolSupportMultiple(nodes []*nodedb.Node)
- func (ps *PingService) GetStats() PingStats
- func (ps *PingService) Ping(n *nodedb.Node) (bool, time.Duration, error)
- func (ps *PingService) PingMultiple(nodes []*nodedb.Node) map[[32]byte]bool
- type PingStats
Constants ¶
const DefaultAlpha = 3
DefaultAlpha is the concurrency factor for lookups (Kademlia parameter).
const DefaultLookupTimeout = 30 * time.Second
DefaultLookupTimeout is the timeout for a complete lookup operation.
const DefaultMajorityThreshold = 0.75
DefaultMajorityThreshold is the percentage threshold for IP consensus (0.0-1.0)
const DefaultMinDistinctIPs = 3
DefaultMinDistinctIPs is the minimum number of distinct reporter IPs needed
const DefaultMinReports = 5
DefaultMinReports is the minimum number of PONG responses needed before considering IP valid
const DefaultRecentWindow = 5 * time.Minute
DefaultRecentWindow is the time window to consider reports "recent" for IP change detection
const DefaultReportExpiry = 30 * time.Minute
DefaultReportExpiry is how long to keep IP reports before expiring them
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// LocalNode is our node information
LocalNode *nodedb.Node
// NodeDB is the node database
NodeDB *nodedb.NodeDB
// Table is the routing table
Table *nodedb.FlatTable
// V5Handler is the discv5 protocol handler (may be nil)
V5Handler *protocol.Handler
// V4Service is the discv4 service (may be nil)
V4Service *discv4.Service
// Database for bad node tracking (may be nil)
Database interface {
IsBadNode(nodeID []byte, layer db.NodeLayer, recheckInterval time.Duration) (isBad bool, shouldRecheck bool, reason string, err error)
}
// Layer is the layer being looked up
Layer db.NodeLayer
// Alpha is the concurrency factor (default 3)
Alpha int
// LookupTimeout is the timeout for lookup operations
LookupTimeout time.Duration
// OnNodeFound is called when a new node is discovered during lookup
// The callback should handle admission checks and add the node if valid
OnNodeFound func(*nodedb.Node) bool
// Logger for debug messages
Logger logrus.FieldLogger
}
Config contains configuration for the lookup service.
type IPDiscovery ¶
type IPDiscovery struct {
// contains filtered or unexported fields
}
IPDiscovery tracks external IP addresses and ports reported by peers via PONG messages.
It implements a consensus mechanism to detect the node's public IP address and port:
- Collects IP:Port from PONG responses (shows our address as seen by remote peer)
- Tracks IPv4 and IPv6 independently (separate consensus for each)
- Requires minimum number of reports before considering an address valid
- Requires minimum number of DISTINCT reporter IPs (prevents IP spoofing)
- Requires majority threshold (e.g., 75%) for consensus
- Expires old reports to handle IP/port changes
Works with both discv4 and discv5 protocols.
func NewIPDiscovery ¶
func NewIPDiscovery(cfg IPDiscoveryConfig) *IPDiscovery
NewIPDiscovery creates a new IP discovery service.
func (*IPDiscovery) GetConsensusIP ¶
func (ipd *IPDiscovery) GetConsensusIP() net.IP
GetConsensusIP returns the current consensus IPv4 address, or nil if no consensus. For IPv6, this returns nil. Use GetStats() for complete information.
func (*IPDiscovery) GetConsensusPort ¶
func (ipd *IPDiscovery) GetConsensusPort() uint16
GetConsensusPort returns the current consensus IPv4 port, or 0 if no consensus.
func (*IPDiscovery) GetStats ¶
func (ipd *IPDiscovery) GetStats() IPDiscoveryStats
GetStats returns current statistics.
func (*IPDiscovery) ReportIP ¶
ReportIP records an IP address and port from a PONG response.
Parameters:
- ip: The IP address as reported by the remote peer (our external address)
- port: The port as reported by the remote peer (our external port)
- reporterID: The node ID of the peer that sent the PONG (for tracking)
- reporterIP: The IP address of the reporter (to enforce distinct IP threshold)
func (*IPDiscovery) Reset ¶
func (ipd *IPDiscovery) Reset()
Reset clears all reports and resets consensus state. This can be used when the node's network changes.
type IPDiscoveryConfig ¶
type IPDiscoveryConfig struct {
// MinReports is the minimum number of PONG responses needed (default: 5)
MinReports int
// MinDistinctIPs is the minimum number of distinct reporter IPs needed (default: 3)
// This prevents a single malicious IP running multiple nodes from reaching consensus
MinDistinctIPs int
// MajorityThreshold is the percentage needed for consensus (default: 0.75)
MajorityThreshold float64
// ReportExpiry is how long to keep reports (default: 30 minutes)
ReportExpiry time.Duration
// RecentWindow is the time window to consider reports "recent" (default: 5 minutes)
// Used for detecting IP changes - recent reports get priority
RecentWindow time.Duration
// OnConsensusReached is called when IP:Port consensus is reached or changes
// isIPv6 indicates whether this is an IPv6 address (true) or IPv4 (false)
OnConsensusReached func(ip net.IP, port uint16, isIPv6 bool)
// Logger for debug messages
Logger logrus.FieldLogger
}
IPDiscoveryConfig contains configuration for IP discovery
type IPDiscoveryStats ¶
type IPDiscoveryStats struct {
TotalReportsIPv4 int
TotalReportsIPv6 int
UniqueIPv4Addrs int
UniqueIPv6Addrs int
ConsensusReachedIPv4 bool
ConsensusReachedIPv6 bool
ConsensusIPv4Addr string // "IP:Port" format
ConsensusIPv6Addr string // "IP:Port" format
IPv4Reports map[string]int // "IP:Port" -> count
IPv6Reports map[string]int // "IP:Port" -> count
}
IPDiscoveryStats contains statistics about IP discovery.
type LookupService ¶
type LookupService struct {
// contains filtered or unexported fields
}
LookupService manages node discovery operations. Supports both discv5 and discv4 protocols (prefers v5, falls back to v4).
func NewLookupService ¶
func NewLookupService(cfg Config) *LookupService
NewLookupService creates a new lookup service.
func (*LookupService) CleanupQueryHistory ¶
func (ls *LookupService) CleanupQueryHistory(maxAge time.Duration) int
CleanupQueryHistory removes stale entries from the query history. This should be called periodically to prevent unbounded growth. Entries older than the given duration are removed.
func (*LookupService) GetStats ¶
func (ls *LookupService) GetStats() LookupStats
GetStats returns discovery statistics.
func (*LookupService) Lookup ¶
Lookup performs a node lookup by querying random nodes from the table.
Since we use a flat table without buckets, we simply:
- Select 3 semi-random nodes (preferring alive ones)
- Query them concurrently for nodes near the target
- Add discovered nodes via callback
Parameters:
- ctx: Context for cancellation and timeout
- target: The target node ID to find
- k: The number of closest nodes to return
Returns discovered nodes that were added to the table.
func (*LookupService) LookupWithFilter ¶
func (ls *LookupService) LookupWithFilter(ctx context.Context, target node.ID, k int, filter enr.ENRFilter) ([]*nodedb.Node, error)
LookupWithFilter performs a lookup with an ENR filter.
Only nodes that pass the filter are included in the results.
func (*LookupService) RandomWalk ¶
RandomWalk performs a random walk to discover new nodes.
This is used for routing table maintenance and exploring the network.
type LookupStats ¶
type LookupStats struct {
LookupsStarted int
LookupsCompleted int
LookupsFailed int
NodesDiscovered int
LookupsV5 int // Lookups using discv5
LookupsV4 int // Lookups using discv4
}
GetStats returns statistics about discovery operations.
type PingService ¶
type PingService struct {
// contains filtered or unexported fields
}
PingService handles PING/PONG operations for liveness checks. Supports both discv5 and discv4 protocols (prefers v5, falls back to v4).
func NewPingService ¶
func NewPingService(v5Handler *protocol.Handler, v4Service *discv4.Service, logger logrus.FieldLogger) *PingService
NewPingService creates a new ping service with dual protocol support. At least one of v5Handler or v4Service must be provided.
func (*PingService) CheckProtocolSupport ¶
CheckProtocolSupport checks which protocols (v4 and/or v5) a node supports.
This pings the node on BOTH discv4 and discv5 to determine actual protocol support. The node's V4/V5 fields are updated based on which protocols respond successfully.
This should be called at a lower frequency than regular aliveness checks (e.g., every 30 minutes) to discover protocol capabilities without excessive overhead.
Returns (v4Supported, v5Supported, error)
func (*PingService) CheckProtocolSupportMultiple ¶
func (ps *PingService) CheckProtocolSupportMultiple(nodes []*nodedb.Node)
CheckProtocolSupportMultiple checks protocol support for multiple nodes in parallel.
This is useful for periodically verifying protocol capabilities across the table.
func (*PingService) GetStats ¶
func (ps *PingService) GetStats() PingStats
GetStats returns PING statistics.
func (*PingService) Ping ¶
Ping sends a PING to a node and waits for PONG.
Prefers discv5 if available, falls back to discv4. Returns true if the node responded, false on timeout. Also updates the node's RTT statistics.
func (*PingService) PingMultiple ¶
func (ps *PingService) PingMultiple(nodes []*nodedb.Node) map[[32]byte]bool
PingMultiple sends PINGs to multiple nodes in parallel.
Returns a map of node ID to ping result (success/failure).