nodes

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2025 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// DefaultMaxActiveNodes is the maximum number of nodes to keep in the active state
	DefaultMaxActiveNodes = 500

	// DefaultPingRate is the maximum number of pings per minute
	DefaultPingRate = 400

	// DefaultSweepPercent is the percentage of active nodes to rotate during sweep
	DefaultSweepPercent = 10
)

FlatTable configuration constants

View Source
const (
	RejectionIPLimit   uint8 = 0x01
	RejectionAdmission uint8 = 0x02
)

Rejection reason flags

View Source
const DefaultMaxFailures = 3

DefaultMaxFailures is the maximum consecutive failures before considering a node dead.

View Source
const DefaultMaxNodeAge = 24 * time.Hour

DefaultMaxNodeAge is the maximum time since last seen before considering a node dead.

View Source
const DefaultMaxNodesPerIP = 10

DefaultMaxNodesPerIP is the default maximum nodes per IP address.

View Source
const DefaultPingInterval = 30 * time.Second

DefaultPingInterval is how often we PING nodes to check liveness.

View Source
const RejectionLogTTL = 12 * time.Hour

RejectionLogTTL is how long we remember that we logged a rejection for a node.

Variables

This section is empty.

Functions

func NewV4NodeFromRecord

func NewV4NodeFromRecord(record *enr.Record, addr *net.UDPAddr) (*node.Node, error)

NewV4NodeFromRecord creates a discv4 node from an ENR record and address. This is a helper for protocol support checks.

func NewV5NodeFromRecord

func NewV5NodeFromRecord(record *enr.Record) (*discv5node.Node, error)

NewV5NodeFromRecord creates a discv5 node from an ENR record. This is a helper for protocol support checks.

Types

type DB

type DB interface {
	StoreRejection(id [32]byte, reason uint8, timestamp time.Time) error
	LoadRejection(id [32]byte) (flags uint8, timestamp time.Time, found bool, err error)
	ExpireRejections(ttl time.Duration) (int, error)
}

DB is the interface for node database that supports rejection tracking.

type DirtyFlags

type DirtyFlags uint8

DirtyFlags represents which fields need database updates.

const (
	DirtyFull       DirtyFlags = 0x01 // Full upsert (initial add)
	DirtyENR        DirtyFlags = 0x02 // seq+enr+ip update
	DirtyLastActive DirtyFlags = 0x04 // last_active timestamp
	DirtyLastSeen   DirtyFlags = 0x08 // last_seen timestamp
	DirtyProtocol   DirtyFlags = 0x10 // has_v4/has_v5 flags
	DirtyStats      DirtyFlags = 0x20 // packet stats (success/failure/rtt)
)

type FlatTable

type FlatTable struct {
	// contains filtered or unexported fields
}

FlatTable is a flat node storage with capped active nodes.

Unlike the bucket-based Kademlia table, this maintains:

  • All nodes in DB (active and inactive)
  • Capped active nodes in memory (max 500)
  • Distributed ping scheduling
  • Periodic active/inactive rotation

func NewFlatTable

func NewFlatTable(cfg FlatTableConfig) (*FlatTable, error)

NewFlatTable creates a new flat node table.

func (*FlatTable) ActiveSize

func (t *FlatTable) ActiveSize() int

ActiveSize returns the number of active nodes.

func (*FlatTable) Add

func (t *FlatTable) Add(n *Node) bool

Add adds a node to the active pool.

This method handles adding nodes to the active in-memory pool with the following strategy: - For nodes that already exist in active pool: updates ENR if newer. - For new nodes: adds to active pool even if over capacity (up to hard limit). - Hard limit: 2x maxActiveNodes. If reached, triggers immediate sweep. - IP limiter is still enforced.

This allows newly discovered nodes (which may not be working) to be added without rejecting them immediately. The next sweep will clean up excess nodes.

DB writes must be handled by caller.

func (*FlatTable) CanAddNodeByIP

func (t *FlatTable) CanAddNodeByIP(n *Node) bool

CanAddNodeByIP checks if we can add a node based on IP limits. This checks against all nodes (active + inactive) in the DB.

func (*FlatTable) FindClosestNodes

func (t *FlatTable) FindClosestNodes(target [32]byte, k int) []*Node

FindClosestNodes finds the k closest active nodes to the target ID.

func (*FlatTable) Get

func (t *FlatTable) Get(nodeID [32]byte) *Node

Get retrieves a node by ID. First checks active nodes, then falls back to DB.

func (*FlatTable) GetActiveNodes

func (t *FlatTable) GetActiveNodes() []*Node

GetActiveNodes returns a copy of all active nodes.

func (*FlatTable) GetBucketNodes

func (t *FlatTable) GetBucketNodes(bucketIndex int) []*Node

GetBucketNodes is kept for compatibility but returns empty for flat table.

func (*FlatTable) GetNodesByDistance

func (t *FlatTable) GetNodesByDistance(targetID [32]byte, distances []uint, k int) []*Node

GetNodesByDistance returns nodes at specific distances with score-weighted random selection.

For each requested distance, it finds all matching nodes and selects up to k nodes with probability weighted by their score (RTT, success rate, fork compatibility).

This ensures:

  • Different results on each call (randomized)
  • Better nodes are returned more frequently (score-weighted)
  • Specific distances are respected

func (*FlatTable) GetNodesNeedingPing

func (t *FlatTable) GetNodesNeedingPing() []*Node

GetNodesNeedingPing returns active nodes that need a PING check.

This implements distributed ping scheduling by limiting the number of nodes returned.

func (*FlatTable) GetRandomActiveNodes

func (t *FlatTable) GetRandomActiveNodes(k int) []*Node

GetRandomActiveNodes returns up to k random active nodes.

func (*FlatTable) GetStats

func (t *FlatTable) GetStats() TableStats

GetStats returns statistics about the table.

func (*FlatTable) LoadInitialNodesFromDB

func (t *FlatTable) LoadInitialNodesFromDB() error

LoadInitialNodesFromDB loads random nodes from DB into the active pool.

func (*FlatTable) NumBucketsFilled

func (t *FlatTable) NumBucketsFilled() int

NumBucketsFilled returns a compatibility value for the flat table. Since we don't have buckets, we return 1 if we have any active nodes, 0 otherwise.

func (*FlatTable) PerformSweep

func (t *FlatTable) PerformSweep()

PerformSweep rotates nodes between active and inactive pools.

This should be called periodically (e.g., every 10 minutes). Only demotes nodes when over capacity. When at or under capacity, nodes are kept to allow newly discovered nodes to be tested before being demoted. Loads inactive nodes from DB and tries to promote them to fill available slots.

func (*FlatTable) SetForkScoringInfo

func (t *FlatTable) SetForkScoringInfo(info *ForkScoringInfo)

SetForkScoringInfo updates the fork scoring information used for node ranking. This should be called periodically to reflect fork changes.

func (*FlatTable) Size

func (t *FlatTable) Size() int

Size returns the total number of nodes (active + inactive).

type FlatTableConfig

type FlatTableConfig struct {
	// LocalID is our node ID
	LocalID [32]byte

	// DB is the primary node storage
	DB *NodeDB

	// MaxActiveNodes is the maximum number of active nodes (default 500)
	MaxActiveNodes int

	// MaxNodesPerIP is the maximum nodes allowed per IP address
	MaxNodesPerIP int

	// PingInterval is how often to ping nodes
	PingInterval time.Duration

	// PingRate is maximum pings per minute (default 400)
	PingRate int

	// MaxNodeAge is the maximum time since last seen
	MaxNodeAge time.Duration

	// MaxFailures is the maximum consecutive failures
	MaxFailures int

	// SweepPercent is percentage of nodes to rotate during sweep (default 10%)
	SweepPercent int

	// NodeChangedCallback is called when a node is added or updated
	NodeChangedCallback NodeChangedCallback

	// Logger for debug messages
	Logger logrus.FieldLogger
}

FlatTableConfig contains configuration for the flat table.

type ForkScoringInfo

type ForkScoringInfo struct {
	// CurrentForkDigest is the current expected fork digest
	CurrentForkDigest [4]byte

	// PreviousForkDigest is the previous fork digest (current - 1)
	PreviousForkDigest [4]byte

	// GenesisForkDigest is the genesis fork digest
	GenesisForkDigest [4]byte

	// GracePeriodEnd is when the grace period for the previous fork ends
	// If zero, there is no grace period active
	GracePeriodEnd time.Time
}

ForkScoringInfo contains fork digest information for node scoring.

type IPLimiter

type IPLimiter struct {
	// contains filtered or unexported fields
}

IPLimiter tracks and enforces per-IP node limits.

This prevents sybil attacks where an attacker tries to fill the routing table with many nodes from the same IP address.

func NewIPLimiter

func NewIPLimiter(maxNodesPerIP int) *IPLimiter

NewIPLimiter creates a new IP limiter.

Parameters:

  • maxNodesPerIP: Maximum nodes allowed per IP (0 = unlimited)

func (*IPLimiter) Add

func (l *IPLimiter) Add(n *Node) bool

Add registers a node with the IP limiter.

This should be called when a node is added to the routing table. Returns false if the IP limit is exceeded.

func (*IPLimiter) CanAdd

func (l *IPLimiter) CanAdd(n *Node) bool

CanAdd checks if a node can be added without exceeding IP limits.

Returns true if the node can be added, false if the IP limit is exceeded.

func (*IPLimiter) GetNodeCountForIP

func (l *IPLimiter) GetNodeCountForIP(ip net.IP) int

GetNodeCountForIP returns the number of nodes for a given IP.

func (*IPLimiter) GetStats

func (l *IPLimiter) GetStats() IPStats

GetStats returns detailed statistics about IP distribution.

func (*IPLimiter) GetTotalRejections

func (l *IPLimiter) GetTotalRejections() int

GetTotalRejections returns the total number of rejected nodes due to IP limits.

func (*IPLimiter) Remove

func (l *IPLimiter) Remove(nodeID [32]byte)

Remove unregisters a node from the IP limiter.

This should be called when a node is removed from the routing table.

type IPStats

type IPStats struct {
	UniqueIPs      int
	TotalNodes     int
	MaxNodesPerIP  int
	Rejections     int
	IPDistribution map[string]int // IP -> node count
}

GetStats returns statistics about IP usage.

type Node

type Node struct {
	// contains filtered or unexported fields
}

Node is a generic node type that can hold both discv4 and discv5 nodes.

A node can have:

  • Only v4 (legacy execution layer nodes)
  • Only v5 (consensus layer nodes)
  • Both v4 and v5 (modern execution layer nodes)

The node is identified by its node ID which is consistent across protocols.

func NewFromV4

func NewFromV4(v4 *node.Node, nodedb *NodeDB) *Node

NewFromV4 creates a generic Node from a discv4 node.

func NewFromV5

func NewFromV5(v5 *discv5node.Node, nodedb *NodeDB) *Node

NewFromV5 creates a generic Node from a discv5 node.

func (*Node) Addr

func (n *Node) Addr() *net.UDPAddr

Addr returns the node's UDP address.

func (*Node) AvgRTT

func (n *Node) AvgRTT() time.Duration

AvgRTT returns the average round-trip time.

func (*Node) CalculateScore

func (n *Node) CalculateScore(forkInfo *ForkScoringInfo) float64

CalculateScore computes a quality score for the node. Delegates to the v5 node if available.

func (*Node) ClearDirtyFlags

func (n *Node) ClearDirtyFlags()

ClearDirtyFlags clears all dirty flags.

func (*Node) ENR

func (n *Node) ENR() *enr.Record

ENR returns the node's ENR record.

func (*Node) Enode

func (n *Node) Enode() *enode.Enode

Enode returns the node's enode:// URL representation.

func (*Node) FailureCount

func (n *Node) FailureCount() int

FailureCount returns the number of failed communications.

func (*Node) FirstSeen

func (n *Node) FirstSeen() time.Time

FirstSeen returns when we first discovered this node.

func (*Node) GetDirtyFlags

func (n *Node) GetDirtyFlags() DirtyFlags

GetDirtyFlags returns and clears the dirty flags atomically.

func (*Node) GetStats

func (n *Node) GetStats() NodeStats

GetStats returns a snapshot of node statistics.

func (*Node) HasV4

func (n *Node) HasV4() bool

HasV4 returns true if this node supports discv4.

func (*Node) HasV5

func (n *Node) HasV5() bool

HasV5 returns true if this node supports discv5.

func (*Node) ID

func (n *Node) ID() [32]byte

ID returns the node identifier.

func (*Node) IDBytes

func (n *Node) IDBytes() []byte

IDBytes returns the node ID as a byte slice.

func (*Node) IncrementFailure

func (n *Node) IncrementFailure()

IncrementFailure increments the failure counter.

func (*Node) IncrementFailureCount

func (n *Node) IncrementFailureCount()

IncrementFailureCount increments the failure counter. Alias for IncrementFailure for consistency with other packages.

func (*Node) IncrementSuccess

func (n *Node) IncrementSuccess()

IncrementSuccess increments the success counter and updates last seen.

func (*Node) IsAlive

func (n *Node) IsAlive(maxAge time.Duration, maxFailures int) bool

IsAlive checks if the node is considered alive. A node is alive if it has been seen recently and has acceptable failure rate.

func (*Node) LastActive

func (n *Node) LastActive() time.Time

LastActive returns the last active timestamp.

func (*Node) LastPing

func (n *Node) LastPing() time.Time

LastPing returns when we last sent a PING to this node.

func (*Node) LastSeen

func (n *Node) LastSeen() time.Time

LastSeen returns when we last saw a packet from this node.

func (*Node) MarkDirty

func (n *Node) MarkDirty(flags DirtyFlags)

MarkDirty marks specific fields as dirty (needing database update).

func (*Node) NeedsPing

func (n *Node) NeedsPing(pingInterval time.Duration) bool

NeedsPing checks if the node needs a liveness check.

func (*Node) PeerID

func (n *Node) PeerID() string

PeerID returns the libp2p peer ID for this node. Delegates to the v5 node if available, otherwise builds it from the public key.

func (*Node) PublicKey

func (n *Node) PublicKey() *ecdsa.PublicKey

PublicKey returns the node's public key.

func (*Node) Record

func (n *Node) Record() *enr.Record

Record returns the node's ENR record.

func (*Node) ResetFailureCount

func (n *Node) ResetFailureCount()

ResetFailureCount resets the failure count to 0 and increments the success count.

func (*Node) SetAddr

func (n *Node) SetAddr(addr *net.UDPAddr)

SetAddr updates the node's address.

func (*Node) SetFailureCount

func (n *Node) SetFailureCount(count int)

SetFailureCount sets the failure count.

func (*Node) SetFirstSeen

func (n *Node) SetFirstSeen(t time.Time)

SetFirstSeen sets the first seen timestamp.

func (*Node) SetLastActive

func (n *Node) SetLastActive(t time.Time)

SetLastActive sets the last active timestamp and marks it dirty.

func (*Node) SetLastPing

func (n *Node) SetLastPing(t time.Time)

SetLastPing updates the last ping time.

func (*Node) SetLastSeen

func (n *Node) SetLastSeen(t time.Time)

SetLastSeen updates the last seen timestamp.

func (*Node) SetSuccessCount

func (n *Node) SetSuccessCount(count int)

SetSuccessCount sets the success count.

func (*Node) SetV4

func (n *Node) SetV4(v4 *node.Node)

SetV4 sets the discv4 node and marks protocol support dirty.

func (*Node) SetV5

func (n *Node) SetV5(v5 *discv5node.Node)

SetV5 sets the discv5 node and marks protocol support dirty.

func (*Node) String

func (n *Node) String() string

String returns a human-readable representation.

func (*Node) SuccessCount

func (n *Node) SuccessCount() int

SuccessCount returns the number of successful communications.

func (*Node) UpdateENR

func (n *Node) UpdateENR(newRecord *enr.Record) bool

UpdateENR updates the node's ENR record if the new one has a higher sequence number. Returns true if the record was updated.

func (*Node) UpdateLastSeen

func (n *Node) UpdateLastSeen()

UpdateLastSeen updates the last seen timestamp to now.

func (*Node) UpdateRTT

func (n *Node) UpdateRTT(rtt time.Duration)

UpdateRTT updates the average RTT with exponential moving average.

func (*Node) V4

func (n *Node) V4() *node.Node

V4 returns the discv4 node if available.

func (*Node) V5

func (n *Node) V5() *discv5node.Node

V5 returns the discv5 node if available.

type NodeChangedCallback

type NodeChangedCallback func(*Node)

NodeChangedCallback is called when a node is added or updated in the table.

type NodeDB

type NodeDB struct {
	// contains filtered or unexported fields
}

NodeDB wraps the Database and provides node storage with async updates. It operates on either el or cl table based on the layer configuration.

func NewNodeDB

func NewNodeDB(ctx context.Context, database *db.Database, layer db.NodeLayer, logger logrus.FieldLogger) *NodeDB

NewNodeDB creates a new node database wrapper for the specified layer. layer should be either db.LayerEL or db.LayerCL.

func (*NodeDB) Close

func (ndb *NodeDB) Close()

Close stops the update queue processor and waits for pending updates.

func (*NodeDB) Count

func (ndb *NodeDB) Count() int

Count returns the total number of nodes in the database.

func (*NodeDB) GetStats

func (ndb *NodeDB) GetStats() NodeDBStats

GetStats returns current database statistics.

func (*NodeDB) List

func (ndb *NodeDB) List() []*Node

List loads all nodes (alias for LoadAll).

func (*NodeDB) Load

func (ndb *NodeDB) Load(id [32]byte) (*Node, error)

Load retrieves a node by ID.

func (*NodeDB) LoadAll

func (ndb *NodeDB) LoadAll() ([]*Node, error)

LoadAll loads all nodes for this layer.

func (*NodeDB) LoadInactiveNodes

func (ndb *NodeDB) LoadInactiveNodes(limit int) []*Node

LoadInactiveNodes loads inactive nodes (not seen recently).

func (*NodeDB) LoadRandom

func (ndb *NodeDB) LoadRandom(limit int) ([]*Node, error)

LoadRandom loads a random sample of nodes (up to limit).

func (*NodeDB) LoadRandomNodes

func (ndb *NodeDB) LoadRandomNodes(limit int) []*Node

LoadRandomNodes loads a random sample of nodes (alias for LoadRandom).

func (*NodeDB) QueueUpdate

func (ndb *NodeDB) QueueUpdate(n *Node) error

QueueUpdate queues a node for database update. The node's dirty flags determine what gets updated.

type NodeDBStats

type NodeDBStats struct {
	QueueSize        int   // Current number of pending updates in queue
	ProcessedUpdates int64 // Total updates processed
	MergedUpdates    int64 // Total updates merged with existing pending
	FailedUpdates    int64 // Total updates that failed
	Transactions     int64 // Total database transactions executed
	TotalQueries     int64 // Total database queries executed
	OpenConnections  int   // Current number of open DB connections
}

NodeDBStats contains statistics about database operations.

type NodeStats

type NodeStats struct {
	FirstSeen    time.Time
	LastSeen     time.Time
	SuccessCount int
	FailureCount int
	AvgRTT       time.Duration
}

NodeStats contains statistics about a node.

type TableStats

type TableStats struct {
	TotalNodes          int
	ActiveNodes         int
	BucketsFilled       int
	AdmissionRejections int
	IPLimitRejections   int
	DeadNodesRemoved    int
	IPStats             IPStats
}

TableStats contains statistics about the routing table.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL