chainselection

package
v0.22.1 Latest Latest
Warning

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

Go to latest
Published: Mar 8, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	PeerTipUpdateEventType  event.EventType = "chainselection.peer_tip_update"
	ChainSwitchEventType    event.EventType = "chainselection.chain_switch"
	ChainSelectionEventType event.EventType = "chainselection.selection"
	PeerEvictedEventType    event.EventType = "chainselection.peer_evicted"
)
View Source
const (

	// DefaultMaxTrackedPeers is the maximum number of peers tracked by
	// the ChainSelector. When a new peer is added and the limit is reached,
	// the least-recently-updated peer is evicted. This bounds memory usage
	// and CPU cost of chain selection, preventing Sybil-based resource
	// exhaustion.
	DefaultMaxTrackedPeers = 200
)
View Source
const VRFOutputSize = 64

VRFOutputSize is the expected size of a VRF output in bytes.

Variables

This section is empty.

Functions

func GetVRFOutput

func GetVRFOutput(header ledger.BlockHeader) []byte

GetVRFOutput extracts the VRF output from a block header. Returns nil if the header type is not supported or doesn't contain VRF data.

The VRF output is used for tie-breaking in chain selection when two chains have equal block numbers and equal density. The chain with the LOWER VRF output (lexicographically) wins.

func IsBetterChain

func IsBetterChain(newTip, currentTip ochainsync.Tip) bool

IsBetterChain returns true if newTip represents a better chain than currentTip according to Ouroboros Praos rules.

func IsBetterChainWithDensity

func IsBetterChainWithDensity(
	newTip, currentTip ochainsync.Tip,
	blocksInWindowNew, blocksInWindowCurrent uint64,
) bool

IsBetterChainWithDensity returns true if newTip represents a better chain than currentTip according to Ouroboros Praos density-based rules.

func IsBetterChainWithVRF

func IsBetterChainWithVRF(
	newTip, currentTip ochainsync.Tip,
	blocksInWindowNew, blocksInWindowCurrent uint64,
	vrfOutputNew, vrfOutputCurrent []byte,
) bool

IsBetterChainWithVRF returns true if newTip represents a better chain than currentTip according to the complete Ouroboros Praos rules including VRF.

func IsSignificantlyBetter

func IsSignificantlyBetter(
	newTip, currentTip ochainsync.Tip,
	minBlockDiff uint64,
) bool

IsSignificantlyBetter returns true if newTip is better than currentTip by at least the specified minimum block difference. This can be used to avoid frequent chain switches for marginal improvements.

Types

type ChainComparisonResult

type ChainComparisonResult int

ChainComparisonResult indicates the result of comparing two chains.

const (
	ChainEqual             ChainComparisonResult = 0
	ChainABetter           ChainComparisonResult = 1
	ChainBBetter           ChainComparisonResult = -1
	ChainComparisonUnknown ChainComparisonResult = 2
)

func CompareChains

func CompareChains(tipA, tipB ochainsync.Tip) ChainComparisonResult

CompareChains compares two chain tips according to Ouroboros Praos rules: 1. Higher block number wins (longer chain) 2. At equal block number, lower slot wins (denser chain)

Returns:

  • ChainABetter (1) if tipA represents a better chain
  • ChainBBetter (-1) if tipB represents a better chain
  • ChainEqual (0) if they are equal

func CompareChainsWithDensity

func CompareChainsWithDensity(
	tipA, tipB ochainsync.Tip,
	blocksInWindowA, blocksInWindowB uint64,
) ChainComparisonResult

CompareChainsWithDensity compares two chain tips using density-based comparison according to Ouroboros Praos rules: 1. Higher block number wins (longer chain) 2. At equal block number, higher density wins (more blocks in 3k/f window) 3. At equal density, lower slot wins (tie-breaker)

The blocksInWindowA and blocksInWindowB parameters represent the number of blocks in the stability window (3k/f slots) for each chain tip. These values should be computed by counting blocks within the window ending at each tip.

Returns:

  • ChainABetter (1) if tipA represents a better chain
  • ChainBBetter (-1) if tipB represents a better chain
  • ChainEqual (0) if they are equal

func CompareChainsWithVRF

func CompareChainsWithVRF(
	tipA, tipB ochainsync.Tip,
	blocksInWindowA, blocksInWindowB uint64,
	vrfOutputA, vrfOutputB []byte,
) ChainComparisonResult

CompareChainsWithVRF compares two chain tips using the complete Ouroboros Praos chain selection algorithm including VRF tie-breaking: 1. Higher block number wins (longer chain) 2. At equal block number, higher density wins (more blocks in 3k/f window) 3. At equal density, lower VRF output wins (deterministic tie-breaker)

The vrfOutputA and vrfOutputB parameters are the VRF outputs from the tip blocks of each chain. These should be extracted using GetVRFOutput() from the block headers. If VRF outputs are nil, falls back to slot-based comparison.

Returns:

  • ChainABetter (1) if tipA represents a better chain
  • ChainBBetter (-1) if tipB represents a better chain
  • ChainEqual (0) if they are equal

func CompareHeaders

func CompareHeaders(headerA, headerB ledger.BlockHeader) ChainComparisonResult

CompareHeaders compares two block headers for chain selection tie-breaking using their VRF outputs. This is a convenience wrapper around GetVRFOutput and CompareVRFOutputs.

func CompareVRFOutputs

func CompareVRFOutputs(vrfA, vrfB []byte) ChainComparisonResult

CompareVRFOutputs compares two VRF outputs for chain selection tie-breaking. Returns:

  • ChainABetter (1) if vrfA is lower (A wins)
  • ChainBBetter (-1) if vrfB is lower (B wins)
  • ChainEqual (0) if equal, either is nil, or either has invalid length

Per Ouroboros Praos: the chain with the LOWER VRF output wins the tie-break. Both outputs must be exactly VRFOutputSize (64) bytes; outputs of any other length are treated the same as nil to prevent truncated values from winning.

type ChainSelectionEvent

type ChainSelectionEvent struct {
	BestConnectionId ouroboros.ConnectionId
	BestTip          ochainsync.Tip
	PeerCount        int
	SwitchOccurred   bool
}

ChainSelectionEvent is published when chain selection evaluation completes.

type ChainSelector

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

ChainSelector tracks chain tips from multiple peers and selects the best chain according to Ouroboros Praos rules.

func NewChainSelector

func NewChainSelector(cfg ChainSelectorConfig) *ChainSelector

NewChainSelector creates a new ChainSelector with the given configuration.

func (*ChainSelector) EvaluateAndSwitch

func (cs *ChainSelector) EvaluateAndSwitch() bool

EvaluateAndSwitch evaluates all peer tips and switches to the best chain if it differs from the current best. Returns true if a switch occurred.

func (*ChainSelector) GetAllPeerTips

func (cs *ChainSelector) GetAllPeerTips() map[ouroboros.ConnectionId]*PeerChainTip

GetAllPeerTips returns a deep copy of all tracked peer tips.

func (*ChainSelector) GetBestPeer

func (cs *ChainSelector) GetBestPeer() *ouroboros.ConnectionId

GetBestPeer returns the connection ID of the peer with the best chain, or nil if no suitable peer is available.

func (*ChainSelector) GetPeerTip

func (cs *ChainSelector) GetPeerTip(
	connId ouroboros.ConnectionId,
) *PeerChainTip

GetPeerTip returns a deep copy of the chain tip for a specific peer. Returns nil if the peer is not tracked.

func (*ChainSelector) HandlePeerTipUpdateEvent

func (cs *ChainSelector) HandlePeerTipUpdateEvent(evt event.Event)

HandlePeerTipUpdateEvent handles PeerTipUpdateEvent from the event bus.

func (*ChainSelector) PeerCount

func (cs *ChainSelector) PeerCount() int

PeerCount returns the number of peers being tracked.

func (*ChainSelector) RemovePeer

func (cs *ChainSelector) RemovePeer(connId ouroboros.ConnectionId)

RemovePeer removes a peer from tracking.

func (*ChainSelector) SelectBestChain

func (cs *ChainSelector) SelectBestChain() *ouroboros.ConnectionId

SelectBestChain evaluates all peer tips and returns the connection ID of the peer with the best chain.

func (*ChainSelector) SetLocalTip

func (cs *ChainSelector) SetLocalTip(tip ochainsync.Tip)

SetLocalTip updates the local chain tip for comparison.

func (*ChainSelector) SetSecurityParam

func (cs *ChainSelector) SetSecurityParam(k uint64)

SetSecurityParam updates the security parameter (k) dynamically. This allows the selector to use protocol parameters for density-based comparison.

func (*ChainSelector) Start

func (cs *ChainSelector) Start(ctx context.Context) error

Start begins the chain selector's background evaluation loop and subscribes to relevant events.

func (*ChainSelector) Stop

func (cs *ChainSelector) Stop()

Stop stops the chain selector.

func (*ChainSelector) UpdatePeerTip

func (cs *ChainSelector) UpdatePeerTip(
	connId ouroboros.ConnectionId,
	tip ochainsync.Tip,
	vrfOutput []byte,
) bool

UpdatePeerTip updates the chain tip for a specific peer and triggers evaluation if needed. The vrfOutput parameter is the VRF output from the tip block header, used for tie-breaking when chains have equal block number and slot.

Returns true if the tip was accepted, false if it was rejected as implausible. A tip is considered implausible if it claims a block number more than securityParam (k) blocks ahead of a reference point. For known peers, the reference is the peer's own previous tip; for new peers, the reference is the best known peer tip. This avoids rejecting legitimate peers during sync (where the local tip is far behind).

type ChainSelectorConfig

type ChainSelectorConfig struct {
	Logger             *slog.Logger
	EventBus           *event.EventBus
	EvaluationInterval time.Duration
	StaleTipThreshold  time.Duration
	SecurityParam      uint64
	MaxTrackedPeers    int // 0 means use DefaultMaxTrackedPeers
}

ChainSelectorConfig holds configuration for the ChainSelector.

type ChainSwitchEvent

type ChainSwitchEvent struct {
	PreviousConnectionId ouroboros.ConnectionId
	NewConnectionId      ouroboros.ConnectionId
	NewTip               ochainsync.Tip
	PreviousTip          ochainsync.Tip
	ComparisonResult     ChainComparisonResult
	BlockDifference      int64
}

ChainSwitchEvent is published when the chain selector decides to switch to a different peer's chain.

Fields:

  • PreviousConnectionId: The connection ID of the peer we were following.
  • NewConnectionId: The connection ID of the peer we are now following.
  • NewTip: The chain tip of the new peer.
  • PreviousTip: The chain tip of the previous peer at the time of the switch.
  • ComparisonResult: Why the new chain is better than the previous chain.
  • BlockDifference: NewTip.BlockNumber - PreviousTip.BlockNumber.

type PeerChainTip

type PeerChainTip struct {
	ConnectionId ouroboros.ConnectionId
	Tip          ochainsync.Tip
	VRFOutput    []byte // VRF output from tip block for tie-breaking
	LastUpdated  time.Time
}

PeerChainTip tracks the chain tip reported by a specific peer.

func NewPeerChainTip

func NewPeerChainTip(
	connId ouroboros.ConnectionId,
	tip ochainsync.Tip,
	vrfOutput []byte,
) *PeerChainTip

NewPeerChainTip creates a new PeerChainTip with the given connection ID, tip, and VRF output.

func (*PeerChainTip) IsStale

func (p *PeerChainTip) IsStale(threshold time.Duration) bool

IsStale returns true if the peer's tip hasn't been updated within the given duration.

func (*PeerChainTip) UpdateTip

func (p *PeerChainTip) UpdateTip(tip ochainsync.Tip, vrfOutput []byte)

UpdateTip updates the peer's chain tip, VRF output, and last updated timestamp.

type PeerEvictedEvent added in v0.22.0

type PeerEvictedEvent struct {
	ConnectionId ouroboros.ConnectionId
}

PeerEvictedEvent is published when a tracked peer is evicted from the chain selector to make room for a new peer. Subscribers (e.g. connection manager) can use this to close the evicted peer's connection.

type PeerTipUpdateEvent

type PeerTipUpdateEvent struct {
	ConnectionId ouroboros.ConnectionId
	Tip          ochainsync.Tip
	VRFOutput    []byte // VRF output from block header for tie-breaking
}

PeerTipUpdateEvent is published when a peer's chain tip is updated via chainsync roll forward.

Jump to

Keyboard shortcuts

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