routing

package
v0.1.8 Latest Latest
Warning

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

Go to latest
Published: May 2, 2026 License: MPL-2.0 Imports: 7 Imported by: 0

Documentation

Index

Constants

View Source
const (
	K = 16
)

Variables

This section is empty.

Functions

func BucketIndex

func BucketIndex(self, peer a2al.NodeID) int

BucketIndex is the K-bucket slot for peer relative to self: shared MSB prefix length in [0,255]. Returns -1 if peer equals self (spec §4.1, Step 5).

func LessXORDistance

func LessXORDistance(a, b, target a2al.NodeID) bool

LessXORDistance reports whether a is strictly closer to target than b under XOR metric (lexicographic compare of Distance(a,target) vs Distance(b,target)).

Types

type EntryMeta added in v0.1.7

type EntryMeta struct {
	// VerifiedAt is the last moment at which we had direct-contact evidence
	// for this node (inbound message or successful outbound RPC).
	// Zero value means the node has never been directly verified by us.
	VerifiedAt time.Time

	// LearnedFrom is the NodeID of the peer that told us about this node via a
	// FIND_NODE response.  Zero value means we discovered the node directly
	// (i.e., it contacted us or we contacted it successfully).
	LearnedFrom a2al.NodeID
}

EntryMeta holds routing-quality metadata for a routing table entry. Values are supplied by the dht layer; the routing layer only stores and reads them. The routing layer never calls time.Now() directly.

type MaintenanceWork added in v0.1.7

type MaintenanceWork struct {
	// PendingToProbe contains nodes in pending lists that need a PING to be
	// verified and potentially promoted to the main bucket.
	PendingToProbe []protocol.NodeInfo

	// StaleToProbe contains main-bucket entries that have VerifiedAt.IsZero()
	// and have been in the table long enough to warrant a probe.
	StaleToProbe []protocol.NodeInfo

	// BucketsToRefill holds bucket indices where the number of Verified-Fresh
	// nodes is below K/2.  The dht layer should issue FindNode(random-in-bucket)
	// for each.
	BucketsToRefill []int
}

MaintenanceWork holds work items collected by CollectMaintenanceWork for the dht layer's routing maintenance loop.

type PeerDebugRow

type PeerDebugRow struct {
	Bucket        int    `json:"bucket"`
	XORDistToSelf string `json:"xor_distance_to_self_hex"`
	AddressHex    string `json:"address_hex"`
	NodeIDHex     string `json:"node_id_hex"`
	IP            string `json:"ip"`
	Port          uint16 `json:"port"`
	VerifiedAgo   string `json:"verified_ago,omitempty"` // human-readable age of VerifiedAt; empty if unverified
}

PeerDebugRow is a JSON-friendly routing row (spec §3.6).

type PingFunc

type PingFunc func(protocol.NodeInfo) bool

PingFunc probes whether a candidate entry is still alive (spec Step 5).

type Table

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

Table is a 256-bucket Kademlia routing table (Phase 1: single LRU zone per bucket).

func NewTable

func NewTable(self a2al.NodeID, ping PingFunc) *Table

NewTable constructs an empty table for the local node. ping may be nil (treated as always true on eviction probe).

func (*Table) Add

func (t *Table) Add(n protocol.NodeInfo, meta EntryMeta, addedAt time.Time) bool

Add inserts or refreshes a peer. Returns false if the peer is self, invalid, or rejected (full bucket with a responsive LRU peer for direct contacts; added to the pending list for hearsay contacts).

meta.VerifiedAt should be the current time for direct-contact entries; leave zero for nodes learned from third-party FIND_NODE responses. addedAt is passed through to the bucket entry so the routing layer does not need to call time.Now().

func (*Table) AddPunched added in v0.1.8

func (t *Table) AddPunched(n protocol.NodeInfo, meta EntryMeta, addedAt time.Time) bool

AddPunched inserts or refreshes a punched entry into the appropriate bucket.

Punched entries are admitted only into spare slots (bucket len < K). A full bucket silently rejects the entry — the next time a direct-contact node arrives it will evict a punched entry (via Add), freeing a slot.

Returns true if the entry was admitted or refreshed.

func (*Table) AllPeers

func (t *Table) AllPeers() []protocol.NodeInfo

AllPeers returns every distinct peer in the main bucket table (unordered). Excludes self.

func (*Table) BucketDiscoveryCount added in v0.1.7

func (t *Table) BucketDiscoveryCount(bi int) int

BucketDiscoveryCount returns the total number of distinct peers known to bucket bi, counting both main-bucket entries (verified or stale) and pending-list entries (unverified hearsay awaiting a probe).

This is the metric the dht layer uses before/after a refill FindNode to decide whether the query actually discovered new peers. verifiedFreshCount would be the wrong choice here: FindNode results enter the table as hearsay (VerifiedAt = 0) via absorbNodeInfo, so they cannot influence verifiedFreshCount until a later PING promotes them — by which point the outcome record is long since written.

Caller must hold the table lock.

func (*Table) BucketIndexOf

func (t *Table) BucketIndexOf(peer a2al.NodeID) int

BucketIndexOf returns BucketIndex(t.self, peer).

func (*Table) CollectMaintenanceWork added in v0.1.7

func (t *Table) CollectMaintenanceWork(now, freshCutoff, staleCutoff time.Time) MaintenanceWork

CollectMaintenanceWork scans all buckets and returns work items for the routing maintenance loop.

  • now: current time (used for pending expiry)
  • freshCutoff: entries with VerifiedAt.After(freshCutoff) are "fresh"
  • staleCutoff: unverified main-bucket entries added before staleCutoff need probing

As a side effect this call expires pending entries older than pendingTTL.

func (*Table) Contains

func (t *Table) Contains(id a2al.NodeID) bool

Contains reports whether id is present in any main bucket.

func (*Table) DebugPeerRows

func (t *Table) DebugPeerRows() []PeerDebugRow

DebugPeerRows returns a flat list of peers with bucket index and XOR distance. Caller must serialise table access if concurrent.

func (*Table) EstimatedNetworkSize added in v0.1.3

func (t *Table) EstimatedNetworkSize() int

EstimatedNetworkSize estimates the total number of nodes using bucket density. For a bucket at CPL c with m nodes the estimate is m×2^(c+1). Includes all main-bucket entries (verified and unverified). For a freshness-filtered estimate with confidence, use EstimatedNetworkSizeFiltered.

func (*Table) EstimatedNetworkSizeFiltered added in v0.1.7

func (t *Table) EstimatedNetworkSizeFiltered(cutoff time.Time) (estimate int, confidence float64)

EstimatedNetworkSizeFiltered estimates network size using only fresh verified nodes (VerifiedAt.After(cutoff)). It scans up to 5 non-empty verified buckets from the highest CPL downward, computes m_i × 2^(c_i+1) for each, and returns the median estimate together with a confidence score in [0, 1] where 1 means 5 or more valid sample buckets were available.

cutoff is provided by the caller; pass time.Time{} to count all verified nodes regardless of age.

func (*Table) GetEntryMeta added in v0.1.7

func (t *Table) GetEntryMeta(id a2al.NodeID) (EntryMeta, bool)

GetEntryMeta returns the EntryMeta for a main-bucket entry. Returns false if the node is not in the main bucket.

func (*Table) IsPunched added in v0.1.8

func (t *Table) IsPunched(peer a2al.NodeID) bool

IsPunched reports whether the given peer is currently in the main bucket and has its isPunched flag set (i.e., was admitted via ICE hole-punching rather than a direct UDP contact).

Returns false when the peer is not in any main bucket or when it has been promoted to a direct-contact entry (isPunched cleared by addOrTouch). Used by the dht layer to classify repSet members into XOR-set vs direct-set.

func (*Table) Len

func (t *Table) Len() int

Len returns the total number of stored peers in main buckets.

func (*Table) MarkPendingFailed added in v0.1.7

func (t *Table) MarkPendingFailed(id a2al.NodeID)

MarkPendingFailed removes a pending entry (called after a failed PING).

func (*Table) MarkPendingVerified added in v0.1.7

func (t *Table) MarkPendingVerified(id a2al.NodeID, meta EntryMeta, addedAt time.Time) bool

MarkPendingVerified attempts to promote a pending entry to the main bucket using meta (typically VerifiedAt=now after a successful PING). Returns true if the entry was moved; false if the main bucket is full (the entry remains in pending and the caller may retry after a slot opens).

func (*Table) NearestN

func (t *Table) NearestN(target a2al.NodeID, n int) []protocol.NodeInfo

NearestN returns up to n peers with smallest XOR distance to target (excluding self). Results sorted closest-first. Includes all main-bucket entries regardless of verification status.

func (*Table) NearestNVerified added in v0.1.7

func (t *Table) NearestNVerified(target a2al.NodeID, n int, cutoff time.Time) []protocol.NodeInfo

NearestNVerified returns up to n main-bucket peers that have been directly verified (VerifiedAt != zero), sorted by freshness then XOR distance. Verified-Fresh entries (VerifiedAt.After(cutoff)) are returned before Verified-Stale entries. Unverified (VerifiedAt.IsZero()) entries are excluded.

cutoff is provided by the caller; the routing layer does not call time.Now().

func (*Table) OldestInBucket

func (t *Table) OldestInBucket(peer a2al.NodeID) (protocol.NodeInfo, bool)

OldestInBucket returns the LRU entry in the main bucket that would hold peer.

func (*Table) OldestPunchedInBucket added in v0.1.8

func (t *Table) OldestPunchedInBucket(peer a2al.NodeID) (protocol.NodeInfo, bool)

OldestPunchedInBucket returns the NodeInfo of the oldest (LRU-position) punched entry in the bucket that would hold peer, together with its NodeID. Returns (NodeInfo{}, false) when no punched entry exists in that bucket.

Used by the dht layer's tabAdd to evict a punched node before triggering a liveness ping on a direct-contact entry (punched nodes are evicted first).

func (*Table) PeerBucketLen

func (t *Table) PeerBucketLen(peer a2al.NodeID) int

PeerBucketLen returns the number of entries in the main bucket for peer's CPL slot.

func (*Table) RecordRefillOutcome added in v0.1.7

func (t *Table) RecordRefillOutcome(bi int, improved bool)

RecordRefillOutcome updates the futile-attempt counter for bucket bi based on whether a recent FindNode improved its verifiedFreshCount.

  • improved=true: reset futileCount to 0 (back to normal cadence)
  • improved=false: increment futileCount (longer wait before next attempt)

Called by the dht layer after a maintenance FindNode goroutine completes. Caller must hold the table write lock.

func (*Table) Remove

func (t *Table) Remove(id a2al.NodeID)

Remove deletes a peer by NodeID if present.

func (*Table) Self

func (t *Table) Self() a2al.NodeID

Self returns the local NodeID.

func (*Table) UpdateVerifiedAt added in v0.1.7

func (t *Table) UpdateVerifiedAt(id a2al.NodeID, verifiedAt time.Time)

UpdateVerifiedAt updates the VerifiedAt timestamp for an existing main-bucket entry. Called by the dht layer after a successful outbound RPC. No-op if the node is not currently in the main bucket.

Jump to

Keyboard shortcuts

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