node

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: 11 Imported by: 0

Documentation

Overview

Package node provides core types for representing network nodes.

A Node combines:

  • Identity: ENR record with node metadata
  • Network info: IP address, UDP/TCP ports
  • Statistics: Last seen, failure counts, RTT

Nodes are the fundamental unit in the discv5 peer discovery system.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidAddress is returned when a network address is invalid.
	ErrInvalidAddress = errors.New("node: invalid network address")

	// ErrInvalidPort is returned when a port number is invalid.
	ErrInvalidPort = errors.New("node: invalid port number")

	// ErrInvalidNodeID is returned when a node ID is invalid.
	ErrInvalidNodeID = errors.New("node: invalid node ID")

	// ErrInvalidENR is returned when an ENR record is invalid.
	ErrInvalidENR = errors.New("node: invalid ENR record")

	// ErrMulticastNotSupported is returned for multicast addresses.
	ErrMulticastNotSupported = errors.New("node: multicast addresses not supported")

	// ErrNodeNotFound is returned when a node is not in the database.
	ErrNodeNotFound = errors.New("node: node not found")

	// ErrNodeAlreadyExists is returned when trying to add a duplicate node.
	ErrNodeAlreadyExists = errors.New("node: node already exists")

	// ErrTimeout is returned when an operation times out.
	ErrTimeout = errors.New("node: operation timed out")

	// ErrNetworkError is returned for network-related errors.
	ErrNetworkError = errors.New("node: network error")

	// ErrProtocolError is returned for protocol violations.
	ErrProtocolError = errors.New("node: protocol error")
)

Functions

func BucketIndex

func BucketIndex(local, remote ID) int

BucketIndex returns the bucket index for a given distance.

This is the same as LogDistance but named more clearly for routing table use.

func BuildPeerID

func BuildPeerID(pubKey *ecdsa.PublicKey) string

func CloserTo

func CloserTo(target, a, b ID) bool

CloserTo checks if node a is closer to target than node b.

Equivalent to Compare(target, a, b) < 0, but more readable.

Example:

if CloserTo(target, node1.ID(), node2.ID()) {
    // node1 is closer to target
}

func Compare

func Compare(target, a, b ID) int

Compare compares the distance from target to a vs target to b.

Returns:

  • -1 if a is closer to target than b
  • 0 if a and b are equidistant from target
  • 1 if b is closer to target than a

This is used for sorting nodes by distance in discovery operations.

Example:

// Sort nodes by distance to target
sort.Slice(nodes, func(i, j int) bool {
    return Compare(target, nodes[i].ID(), nodes[j].ID()) < 0
})

func GetIPVersion

func GetIPVersion(ip net.IP) int

GetIPVersion returns the IP version (4 or 6) of an address.

Returns:

  • 4 for IPv4 addresses
  • 6 for IPv6 addresses
  • 0 for invalid/nil addresses

func IsLANAddress

func IsLANAddress(ip net.IP) bool

IsLANAddress checks if an IP address is a private/local address.

Returns true for:

  • 10.0.0.0/8 (RFC1918)
  • 172.16.0.0/12 (RFC1918)
  • 192.168.0.0/16 (RFC1918)
  • fc00::/7 (IPv6 ULA)
  • fe80::/10 (IPv6 link-local)
  • 127.0.0.0/8 (loopback)
  • ::1 (IPv6 loopback)

This is used to prevent serving LAN addresses to WAN peers.

Example:

if IsLANAddress(node.IP()) {
    // Don't serve to WAN requesters
}

func IsNetworkError

func IsNetworkError(err error) bool

IsNetworkError checks if an error is a network error.

func IsProtocolError

func IsProtocolError(err error) bool

IsProtocolError checks if an error is a protocol error.

func IsRoutableAddress

func IsRoutableAddress(ip net.IP) bool

IsRoutableAddress checks if an IP address is globally routable.

Returns false for:

  • Private addresses (RFC1918, ULA)
  • Loopback addresses
  • Link-local addresses
  • Multicast addresses
  • Unspecified addresses (0.0.0.0, ::)

Example:

if !IsRoutableAddress(node.IP()) {
    // Skip this node in public discovery
}

func IsSameNetwork

func IsSameNetwork(ip1, ip2 net.IP) bool

IsSameNetwork checks if two IP addresses are on the same network.

For IPv4, this checks if they're both LAN or both WAN. This is used to determine if nodes can directly communicate.

Example:

if !IsSameNetwork(localIP, remoteIP) {
    // May need NAT traversal
}

func IsTimeout

func IsTimeout(err error) bool

IsTimeout checks if an error is a timeout error.

func IsWANAddress

func IsWANAddress(ip net.IP) bool

IsWANAddress checks if an IP address is a publicly routable address.

This is the inverse of IsLANAddress - returns true for public internet IPs.

Example:

if IsWANAddress(requester.IP) && IsLANAddress(node.IP()) {
    // Don't serve LAN node to WAN requester
    return false
}

func LogDistance

func LogDistance(a, b ID) int

LogDistance calculates the logarithmic distance (bucket index) between two node IDs.

Returns the position of the most significant bit in the XOR distance, which determines the Kademlia bucket (0-255).

A distance of 0 means the nodes are identical (returns -1). A distance with MSB at position N means the nodes differ starting at bit N.

Example:

logDist := LogDistance(id1, id2)
// logDist is the bucket index (0-255) for the routing table

func NormalizeIP

func NormalizeIP(ip net.IP) net.IP

NormalizeIP normalizes an IP address for consistent comparison.

IPv4 addresses are returned in 4-byte form. IPv6 addresses are returned in 16-byte form.

func ParseNodeAddr

func ParseNodeAddr(addrStr string) (*net.UDPAddr, error)

ParseNodeAddr parses a node address string in the format "ip:port".

Example:

addr, err := ParseNodeAddr("192.168.1.1:9000")

func SameIP

func SameIP(ip1, ip2 net.IP) bool

SameIP checks if two IP addresses are the same.

This handles IPv4 vs IPv6 representation differences.

Example:

if SameIP(node1.IP(), node2.IP()) {
    // Same node or same host
}

func ValidateUDPAddr

func ValidateUDPAddr(addr *net.UDPAddr) error

ValidateUDPAddr checks if a UDP address is valid for discv5 communication.

Returns an error if:

  • Address is nil
  • IP is nil or unspecified
  • Port is 0
  • IP is multicast

Example:

if err := ValidateUDPAddr(addr); err != nil {
    return fmt.Errorf("invalid address: %w", err)
}

Types

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 ID

type ID [32]byte

ID represents a unique node identifier (32 bytes).

The node ID is derived from the node's public key:

nodeID = keccak256(uncompressed_pubkey[1:])

This ID is used in the Kademlia DHT for routing and distance calculations.

func Distance

func Distance(a, b ID) ID

Distance calculates the XOR distance between two node IDs.

In Kademlia, distance is defined as the XOR of two node IDs. This creates a metric space where:

  • d(x, x) = 0 (distance to self is zero)
  • d(x, y) = d(y, x) (symmetric)
  • d(x, z) <= d(x, y) + d(y, z) (triangle inequality)

The distance is returned as the ID itself (32 bytes).

Example:

id1 := PubkeyToID(pubKey1)
id2 := PubkeyToID(pubKey2)
dist := Distance(id1, id2)

func FindClosest

func FindClosest(target ID, nodes []ID, k int) []ID

FindClosest finds the k closest node IDs to the target from a list.

The result is sorted by distance (closest first). If the list contains fewer than k nodes, all nodes are returned.

Example:

target := localNode.ID()
nodeIDs := []ID{id1, id2, id3, id4, id5}
closest := FindClosest(target, nodeIDs, 3) // Get 3 closest

func PubkeyToID

func PubkeyToID(pub *ecdsa.PublicKey) ID

PubkeyToID converts a public key to a node ID.

Example:

privKey, _ := crypto.GenerateKey()
nodeID := PubkeyToID(&privKey.PublicKey)

func (ID) Bytes

func (id ID) Bytes() []byte

Bytes returns the byte slice representation of the node ID.

func (ID) String

func (id ID) String() string

String returns the hex representation of the node ID.

type Node

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

Node represents a network node in the discv5 protocol.

It combines the node's ENR record with additional runtime information like network statistics and last seen time.

func New

func New(record *enr.Record) (*Node, error)

New creates a new Node from an ENR record.

The node ID and network address are extracted from the ENR. Returns an error if the ENR is missing required fields.

Example:

privKey, _ := crypto.GenerateKey()
record, _ := enr.CreateSignedRecord(
    privKey,
    "ip", net.IPv4(192, 168, 1, 1),
    "udp", uint16(9000),
)
node, err := New(record)

func (*Node) Addr

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

Addr returns the node's UDP address.

func (*Node) CalculateScore

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

CalculateScore computes a quality score including fork digest compatibility.

The score considers:

  • RTT (lower is better): 30% weight
  • Success rate: 25% weight
  • Uptime (time since first seen): 15% weight
  • Recency (time since last seen): 10% weight
  • Fork digest compatibility: 20% weight

Fork digest scoring:

  • Current fork digest: 1.0 multiplier
  • Previous fork (in grace period): 0.8 multiplier
  • Genesis fork (syncing clients): 0.5 multiplier
  • Previous fork (expired grace): 0.2 multiplier (outdated clients)
  • Unknown/no fork data: 0.3 multiplier

Returns a score between 0.0 (worst) and 1.0 (best).

func (*Node) Digest

func (n *Node) Digest() [4]byte

Digest returns the node's fork digest.

func (*Node) GetStats

func (n *Node) GetStats() Stats

GetStats returns the current statistics for the node.

func (*Node) ID

func (n *Node) ID() ID

ID returns the node's unique identifier.

func (*Node) IP

func (n *Node) IP() net.IP

IP returns the node's IP address.

func (*Node) IncrementFailureCount

func (n *Node) IncrementFailureCount()

IncrementFailureCount increases the failure count by 1.

func (*Node) PeerID

func (n *Node) PeerID() string

PeerID returns the libp2p peer ID for this node.

The peer ID is constructed from the secp256k1 public key using the libp2p format:

  • Compressed secp256k1 public key (33 bytes)
  • Wrapped in libp2p PublicKey protobuf message:
  • Field 1 (Type): 0x08 0x02 (secp256k1 = 2)
  • Field 2 (Data): 0x12 0x21 [33 bytes of compressed key]
  • Wrapped in IDENTITY multihash (code 0x00)
  • Base58 encoded

Example output: "16Uiu2HAkyttpvpDTRdEnUqSPvbDpRgbSgrmeqTqi4R7EWECG5jso"

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 success count.

func (*Node) SetFailureCount

func (n *Node) SetFailureCount(count int)

SetFailureCount sets the failure count.

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 time.

func (*Node) SetStats added in v0.0.2

func (n *Node) SetStats(sharedStats *stats.SharedStats)

SetStats replaces the node's stats with a shared stats pointer. This allows the node to update stats owned by a parent node.

func (*Node) SetSuccessCount

func (n *Node) SetSuccessCount(count int)

SetSuccessCount sets the success count.

func (*Node) String

func (n *Node) String() string

String returns a human-readable representation of the node.

Format: Node[id=abc123..., addr=192.168.1.1:9000, seen=1m ago]

func (*Node) TCPPort

func (n *Node) TCPPort() uint16

TCPPort returns the node's TCP port (0 if not set).

func (*Node) UDPPort

func (n *Node) UDPPort() uint16

UDPPort returns the node's UDP port.

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, false if the current record is newer.

func (*Node) UpdateRTT

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

UpdateRTT updates the average RTT using exponential moving average.

type Stats

type Stats struct {
	FirstSeen    time.Time
	LastSeen     time.Time
	LastPing     time.Time
	FailureCount int
	SuccessCount int
	AvgRTT       time.Duration
	ENRSeq       uint64
}

Stats returns a snapshot of the node's statistics.

Jump to

Keyboard shortcuts

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