Documentation
¶
Overview ¶
Package chainselection implements multi-peer chain selection. It tracks the tip reported by each connected peer and, for Shelley-family headers, uses the Praos select view projected from the observed header: longer chain first, then the reference implementation's equal-length opcert/VRF tiebreaker when it is armed.
Genesis selection mode can be enabled at startup. The selector's rolling observed density ranks peers during bootstrap; when fetched candidates fork, ledger resolution performs the authoritative density comparison from their exact common intersection. Both automatically fall back to Praos once the local tip is close enough to the best observed peer tip.
ChainSelector is the top-level type. It consumes PeerTipUpdateEvent events from chainsync, compares peer tips against the local tip, and emits ChainSwitchEvent on the EventBus when the selected peer changes. The node's Run loop listens for those events and repoints the active chainsync stream at the new best peer.
This package does not perform any block validation; it only selects which peer's chain to follow. Validation happens downstream in the ledger package after blocks are fetched from the selected peer.
Index ¶
- Constants
- Variables
- func DensityFromIntersection(intersectionSlot uint64, window uint64, slots []uint64) uint64
- func GenesisCandidateHeaderLimit(windowSlots, securityParam uint64) int
- func GenesisWindowSlotsForParams(securityParam uint64, activeSlotsCoeff *big.Rat) uint64
- func GetVRFOutput(header gledger.BlockHeader) []byte
- func PreferPraosCandidate(ours, cand PraosTiebreakerView) bool
- type CandidateFragment
- type ChainComparisonResult
- type ChainSelectedNoneEvent
- type ChainSelectionEvent
- type ChainSelector
- func (cs *ChainSelector) AcceptPeerTipUpdate(e PeerTipUpdateEvent) bool
- func (cs *ChainSelector) AwaitCatchUpBand(ctx context.Context, connId ouroboros.ConnectionId, observedTip ochainsync.Tip) error
- func (cs *ChainSelector) AwaitEagernessLimit(ctx context.Context, connId ouroboros.ConnectionId, blockNumber uint64, ...) error
- func (cs *ChainSelector) AwaitIngressCorroboration(ctx context.Context, connId ouroboros.ConnectionId) (bool, error)
- func (cs *ChainSelector) CandidateFragments() map[ouroboros.ConnectionId]CandidateFragment
- func (cs *ChainSelector) CommitPeerTipAdmission(update PeerTipUpdateEvent) bool
- func (cs *ChainSelector) CommitPeerTipAdmissionPinned(update PeerTipUpdateEvent) bool
- func (cs *ChainSelector) EagernessLimit() EagernessLimit
- func (cs *ChainSelector) EagernessPaused(connId ouroboros.ConnectionId) bool
- func (cs *ChainSelector) EvaluateAndSwitch() bool
- func (cs *ChainSelector) GenesisCorroborationActive() bool
- func (cs *ChainSelector) GenesisSelectionState() (bool, uint64)
- func (cs *ChainSelector) GenesisStatus() GenesisStatus
- func (cs *ChainSelector) GenesisWindowSlots() uint64
- func (cs *ChainSelector) GetAllPeerTips() map[ouroboros.ConnectionId]*PeerChainTip
- func (cs *ChainSelector) GetBestPeer() *ouroboros.ConnectionId
- func (cs *ChainSelector) GetCandidateFragment(connId ouroboros.ConnectionId) (CandidateFragment, bool)
- func (cs *ChainSelector) GetPeerSyncTarget(connId ouroboros.ConnectionId) (ochainsync.Tip, bool)
- func (cs *ChainSelector) GetPeerTip(connId ouroboros.ConnectionId) *PeerChainTip
- func (cs *ChainSelector) HandlePeerActivityEvent(evt event.Event)
- func (cs *ChainSelector) HandlePeerRollbackEvent(evt event.Event)
- func (cs *ChainSelector) HandlePeerTipUpdateEvent(evt event.Event)
- func (cs *ChainSelector) ObservePeerTipUpdate(e PeerTipUpdateEvent) PeerTipUpdateDisposition
- func (cs *ChainSelector) PeerCount() int
- func (cs *ChainSelector) PrepareForecastHorizonWitness(update PeerTipUpdateEvent) []ouroboros.ConnectionId
- func (cs *ChainSelector) PreparePeerTipAdmission(update PeerTipUpdateEvent) bool
- func (cs *ChainSelector) PreparePeerTipAdmissionDisposition(update PeerTipUpdateEvent) PeerTipUpdateDisposition
- func (cs *ChainSelector) RecordDeliveredPeerRollback(e PeerRollbackEvent)
- func (cs *ChainSelector) RecordDeliveredPeerTip(update PeerTipUpdateEvent)
- func (cs *ChainSelector) RejectPeerTipAdmission(update PeerTipUpdateEvent)
- func (cs *ChainSelector) RemoveForecastHorizonWitness(update PeerTipUpdateEvent)
- func (cs *ChainSelector) RemovePeer(connId ouroboros.ConnectionId)
- func (cs *ChainSelector) ResolvePeerTipAdmission(update PeerTipUpdateEvent, admitted bool)
- func (cs *ChainSelector) SelectBestChain() *ouroboros.ConnectionId
- func (cs *ChainSelector) SelectedTip() (ochainsync.Tip, bool)
- func (cs *ChainSelector) SelectionMode() SelectionMode
- func (cs *ChainSelector) SetConnectionEligible(connId ouroboros.ConnectionId, eligible bool)
- func (cs *ChainSelector) SetConnectionPriority(connId ouroboros.ConnectionId, priority int)
- func (cs *ChainSelector) SetLocalTip(tip ochainsync.Tip)
- func (cs *ChainSelector) SetPeerIdling(connId ouroboros.ConnectionId, point ocommon.Point)
- func (cs *ChainSelector) SetSecurityParam(k uint64)
- func (cs *ChainSelector) ShouldApplyIngress(connId ouroboros.ConnectionId) bool
- func (cs *ChainSelector) Start(ctx context.Context) error
- func (cs *ChainSelector) Stop()
- func (cs *ChainSelector) SyncTargetForPeerTipUpdate(update PeerTipUpdateEvent) (ochainsync.Tip, bool)
- func (cs *ChainSelector) TouchPeerActivity(connId ouroboros.ConnectionId)
- func (cs *ChainSelector) UpdatePeerTip(connId ouroboros.ConnectionId, tip ochainsync.Tip, vrfOutput []byte) bool
- type ChainSelectorConfig
- type ChainSwitchEvent
- type EagernessLimit
- type EvaluationPanicEvent
- type GenesisCorroborationFailedEvent
- type GenesisDensityDisconnect
- type GenesisModeExitedEvent
- type GenesisPeerStatus
- type GenesisStatus
- type PeerActivityEvent
- type PeerChainTip
- func (p *PeerChainTip) ApplyRollback(point ocommon.Point, tip ochainsync.Tip)
- func (p *PeerChainTip) AwaitingFirstHeader() bool
- func (p *PeerChainTip) CandidateFragment() CandidateFragment
- func (p *PeerChainTip) IsStale(threshold time.Duration) bool
- func (p *PeerChainTip) SelectionTip() ochainsync.Tip
- func (p *PeerChainTip) Touch()
- func (p *PeerChainTip) UpdateTip(tip ochainsync.Tip, vrfOutput []byte)
- func (p *PeerChainTip) UpdateTipWithObserved(tip ochainsync.Tip, observedTip ochainsync.Tip, vrfOutput []byte)
- func (p *PeerChainTip) UpdateTipWithObservedPraosView(tip ochainsync.Tip, observedTip ochainsync.Tip, vrfOutput []byte, ...)
- type PeerEvictedEvent
- type PeerRollbackEvent
- type PeerRollbackHandlerPanicEvent
- type PeerTipRejection
- type PeerTipUpdateDisposition
- type PeerTipUpdateEvent
- type PraosTiebreakerConfig
- type PraosTiebreakerFlavor
- type PraosTiebreakerView
- func GetPraosTiebreakerView(header gledger.BlockHeader) (PraosTiebreakerView, bool)
- func NewPraosTiebreakerViewFull(tip ochainsync.Tip, issuer []byte, issueNo uint64, vrfOutput []byte, ...) PraosTiebreakerView
- func PraosTiebreakerViewFromTip(tip ochainsync.Tip, vrfOutput []byte, config PraosTiebreakerConfig) PraosTiebreakerView
- type RollbackRegistrationOutcome
- type SelectionMode
Constants ¶
const ( ChainEqual = praos.ChainEqual ChainABetter = praos.ChainABetter ChainBBetter = praos.ChainBBetter ChainComparisonUnknown = praos.ChainComparisonUnknown )
const ( PeerTipUpdateEventType event.EventType = "chainselection.peer_tip_update" PeerActivityEventType event.EventType = "chainselection.peer_activity" PeerRollbackEventType event.EventType = "chainselection.peer_rollback" ChainSwitchEventType event.EventType = "chainselection.chain_switch" ChainSelectionEventType event.EventType = "chainselection.selection" PeerEvictedEventType event.EventType = "chainselection.peer_evicted" // GenesisCorroborationFailedEventType is published when the densest // Genesis fast source cannot be corroborated by the configured minimum // number of independent peers, so it is denied chain selection (stalls) // rather than steering the local chain. Subscribers (peer governance, // operators) can use this to demote or investigate the source. GenesisCorroborationFailedEventType event.EventType = "chainselection.genesis_corroboration_failed" // GenesisModeExitedEventType is published when the selector transitions // from Genesis density-based selection back to Praos selection because the // local tip has caught up to within the Genesis window of the best known // peer tip. GenesisModeExitedEventType event.EventType = "chainselection.genesis_mode_exited" // ChainSelectedNoneEventType is published when the selector transitions to // having no selectable best peer (e.g. an uncorroborated Genesis fast source // is denied). It is the explicit selected-to-none transition that the // ChainSwitchEvent (which always carries a non-nil replacement) cannot // express, so subscribers can observe that selection has stalled. Ledger // application from uncorroborated peers is separately gated via // ShouldApplyIngress; this event is for observability of the stall. ChainSelectedNoneEventType event.EventType = "chainselection.selected_none" // PeerRollbackHandlerPanicEventType is published when the // PeerRollbackEvent handler registered in NewChainSelector panics. The // EventBus subscription that delivers PeerRollbackEvent is torn down // immediately afterward (see event.EventBus.SubscribeFuncStrict), so // this is the durable signal that rollback handling for this selector // has stopped rather than a swallowed panic followed by business as // usual. PeerRollbackHandlerPanicEventType event.EventType = "chainselection.peer_rollback_handler_panic" // EvaluationPanicEventType is published when a background evaluation // tick or triggered evaluation panics. The evaluation loop itself keeps // running (see ChainSelector.recoverEvaluationPanic), but the specific // best-peer transition that evaluation would have produced is dropped; // this event is the only remaining signal that it happened. EvaluationPanicEventType event.EventType = "chainselection.evaluation_panic" )
const ( PraosTiebreakerUnknown = praos.PraosTiebreakerUnknown PraosTiebreakerUnrestricted = praos.PraosTiebreakerUnrestricted PraosTiebreakerRestricted = praos.PraosTiebreakerRestricted )
const DefaultGenesisWindowSlots uint64 = 6480
DefaultGenesisWindowSlots is the window the selector uses when neither GenesisWindowSlots nor a security parameter is known (3 * 2160). It is a stand-in for exit-horizon and density bookkeeping, not a Genesis window: the Genesis Density Disconnector stays off unless GenesisWindowSlots is set.
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 )
const GenesisDensityEvaluationInterval = time.Second
GenesisDensityEvaluationInterval is the minimum time between Genesis Density Disconnector evaluations. The pairwise comparison is quadratic in the number of tracked peers, so it is not run on every peer tip update.
const VRFOutputSize = praos.VRFOutputSize
VRFOutputSize is re-exported from consensus/praos.
Variables ¶
var ErrEagernessPeerUntracked = errors.New("eagerness peer untracked")
ErrEagernessPeerUntracked means the sender is no longer a tracked candidate when its header reaches the Limit on Eagerness gate. The caller must discard the header.
Functions ¶
func DensityFromIntersection ¶ added in v0.69.0
DensityFromIntersection counts candidate blocks in the Genesis window that starts immediately after the common intersection and ends at intersectionSlot+window, inclusive. The intersection block itself belongs to both candidates and is therefore excluded.
Slots must describe one candidate path in ascending order. Values outside the window are ignored so callers may pass the complete fetched fragment.
func GenesisCandidateHeaderLimit ¶ added in v0.90.2
GenesisCandidateHeaderLimit converts the Genesis window, measured in slots, into a header count for retaining or walking a peer's candidate ancestry. A candidate fragment holds up to k headers on the local side of the intersection plus every header inside the forecast range. The 3k figure in the reference is only the expectation at density f; the range spans one window of slots and a candidate may be denser, so the hard bound is one header per window slot. Truncating below that loses the intersection of a dense but forecastable candidate. Memory stays bounded independently by the per-connection and total byte budgets on retained headers. Without a known k (zero) the window is returned unchanged. The result saturates at math.MaxInt.
func GenesisWindowSlotsForParams ¶ added in v0.37.0
GenesisWindowSlotsForParams returns the Genesis density window in slots. Shelley-style networks use ceil(3k/f), where k is the security parameter and f is the active slot coefficient, matching the reference node's computeStabilityWindow. The division is exact: f must be the genesis rational, because a float64 approximation of a value such as 3/10000 rounds the quotient across an integer boundary and widens the window by one slot. It returns 0 when k is zero or f is nil or not positive, since no Genesis window exists without both; it saturates at math.MaxUint64 and does not modify activeSlotsCoeff.
func GetVRFOutput ¶
func GetVRFOutput(header gledger.BlockHeader) []byte
GetVRFOutput delegates to consensus/praos.
func PreferPraosCandidate ¶ added in v0.46.0
func PreferPraosCandidate( ours, cand PraosTiebreakerView, ) bool
PreferPraosCandidate delegates to consensus/praos.
Types ¶
type CandidateFragment ¶ added in v0.70.6
type CandidateFragment struct {
// contains filtered or unexported fields
}
CandidateFragment is a bounded, immutable snapshot of the chain fragment a chainsync-eligible peer has actually delivered, ordered from its anchor (the oldest retained point) to its most recently delivered point.
This is Dingo's analogue of the upstream consensus interface readCandidateChains :: STM m (Map peer (AnchoredFragment header)); see ARCHITECTURE.md's "Ouroboros Genesis trust model" section. As with the upstream contract, entries are derived only from headers that have already passed chain-selection observation (see LedgerState.ValidateChainSelectionHeaderCrypto in ARCHITECTURE.md), the fragment may hold less than a full window, and its anchor is not required to intersect the primary chain or any other peer's fragment.
The fragment snapshots PeerChainTip's hash-bearing Genesis frontier. That frontier has capacity for one Genesis window plus the k+1-header eagerness suffix. The separate k+1 Tip history stays small and serves rollback restoration. Entries are kept in strictly ascending slot order.
func (CandidateFragment) Anchor ¶ added in v0.70.6
func (f CandidateFragment) Anchor() ocommon.Point
Anchor returns the fragment's oldest retained point. Per the upstream AnchoredFragment contract, the anchor is not guaranteed to intersect the primary chain or any other peer's fragment — it is simply the oldest point this snapshot still remembers. Anchor returns the zero Point when the fragment is empty.
func (CandidateFragment) HeadPoint ¶ added in v0.70.6
func (f CandidateFragment) HeadPoint() ocommon.Point
HeadPoint returns the fragment's most recently delivered point, or the zero Point when the fragment is empty.
func (CandidateFragment) Intersect ¶ added in v0.70.6
func (f CandidateFragment) Intersect( other CandidateFragment, ) (ocommon.Point, bool)
Intersect returns the highest point present in both fragments, comparing by (slot, hash). Both fragments must already be in ascending slot order, which recordObservedPoint guarantees. This is the primitive the Limit on Eagerness and the Genesis Density Disconnector need to compute the intersection across candidate fragments (see ARCHITECTURE.md); it does not itself implement either.
func (CandidateFragment) Len ¶ added in v0.70.6
func (f CandidateFragment) Len() int
Len returns the number of points retained in the fragment.
func (CandidateFragment) Points ¶ added in v0.70.6
func (f CandidateFragment) Points() []ocommon.Point
Points returns the fragment's retained points, including the anchor, in ascending slot order. The caller receives an independent copy.
type ChainComparisonResult ¶
type ChainComparisonResult = praos.ChainComparisonResult
ChainComparisonResult is aliased from consensus/praos.
func ComparePraosTips ¶ added in v0.46.0
func ComparePraosTips( tipA, tipB ochainsync.Tip, viewA, viewB PraosTiebreakerView, ) ChainComparisonResult
ComparePraosTips delegates to consensus/praos.
func CompareVRFOutputs ¶
func CompareVRFOutputs(vrfA, vrfB []byte) ChainComparisonResult
CompareVRFOutputs delegates to consensus/praos.
type ChainSelectedNoneEvent ¶ added in v0.67.0
type ChainSelectedNoneEvent struct {
PreviousConnectionId ouroboros.ConnectionId
GenesisCorroboration bool
}
ChainSelectedNoneEvent is published when best-peer selection transitions from some peer to none (selection stalled).
Fields:
- PreviousConnectionId: the peer that was selected before the stall (zero value if none was selected).
- GenesisCorroboration: true when the stall is due to the Genesis corroboration gate (no corroborated peer), as opposed to simply having no eligible peers.
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 using the active mode's comparison rules.
func NewChainSelector ¶
func NewChainSelector(cfg ChainSelectorConfig) *ChainSelector
NewChainSelector creates a new ChainSelector with the given configuration.
func (*ChainSelector) AcceptPeerTipUpdate ¶ added in v0.83.0
func (cs *ChainSelector) AcceptPeerTipUpdate(e PeerTipUpdateEvent) bool
AcceptPeerTipUpdate synchronously applies a peer tip update and reports whether the selector accepted the candidate.
func (*ChainSelector) AwaitCatchUpBand ¶ added in v0.90.0
func (cs *ChainSelector) AwaitCatchUpBand( ctx context.Context, connId ouroboros.ConnectionId, observedTip ochainsync.Tip, ) error
AwaitCatchUpBand blocks while connId's next delivered frontier is held by catchUpBandHoldLocked, and returns the context's error if it is cancelled first. It returns at once when the frontier is not held, so only a connection whose header is past an admission bound because the ledger lags waits.
The wait reads selector state and records nothing: no frontier, claim, or witness, so a waiting connection can neither be selected nor corroborate another. It takes the selector lock only for each check, never while parked. The reference, the applied tip, and the connection set are re-read on every check, so the wait ends when the ledger advances, the reference moves, or the reference peers go away. The caller bounds it with the context.
The caller's chainsync callback blocks here, which stops that connection's header requests without discarding a header: the cursor stays put and the header is processed in full once it is admissible.
func (*ChainSelector) AwaitEagernessLimit ¶ added in v0.83.0
func (cs *ChainSelector) AwaitEagernessLimit( ctx context.Context, connId ouroboros.ConnectionId, blockNumber uint64, appliedTip func() ochainsync.Tip, ) error
AwaitEagernessLimit blocks until a header numbered blockNumber, delivered by connId, is within the Limit on Eagerness, and returns the context's error if it is cancelled first. It returns ErrEagernessPeerUntracked when connId is not tracked at entry or disappears while waiting. It returns immediately when the cap is inactive or the header is within the limit.
appliedTip, when non-nil, supplies the applied ledger tip that the limit is measured from before the candidates have shared a point, and is re-read on every check. A running node gives the selector its local tip only at startup and then on the stall recycler's tick, which would hold a paused peer for the length of that interval; nil uses the selector's own local tip. The sender counts as a candidate even when stale, so a lone peer is never held back by the limit that its own fragment defines.
The caller's chainsync callback blocks here, which stops that peer's header stream without discarding a header: the peer's cursor stays put and the header is delivered once the limit allows it.
func (*ChainSelector) AwaitIngressCorroboration ¶ added in v0.87.1
func (cs *ChainSelector) AwaitIngressCorroboration( ctx context.Context, connId ouroboros.ConnectionId, ) (bool, error)
AwaitIngressCorroboration holds a header from connId that ShouldApplyIngress denies only because the peer is not yet corroborated, and reports whether the header may then be applied. It returns false at once when the denial has another cause (Genesis corroboration is off, the advertised tip is stale, or the peer is untracked), and returns the context's error if it is cancelled while waiting.
The caller's chainsync callback blocks here while witnesses can advance. The caller bounds that wait; on expiry it withholds the header but keeps the session and staged candidate frontier so another peer can deliver the same point. Re-intersecting immediately from the applied tip discards that frontier and repeats the hold. Staged observations remain bounded, so a peer that runs beyond the admission window cannot corroborate its entire prefix.
func (*ChainSelector) CandidateFragments ¶ added in v0.70.6
func (cs *ChainSelector) CandidateFragments() map[ouroboros.ConnectionId]CandidateFragment
CandidateFragments returns a snapshot of the candidate chain fragment maintained for every currently tracked peer. This is the Dingo equivalent of the upstream readCandidateChains query: a fragment exists only while its peer's connection is tracked (i.e. chainsync-eligible per peer governance, the sole gate on entry into peerTips — see ouroboros/chainsync.go), and RemovePeer drops it along with the rest of that peer's state.
This intentionally does not consult the eligible map (SetConnectionEligible / isConnectionEligible): that flag disqualifies a peer from being chosen as best peer or counted as a corroboration witness, but it is not a disconnect and does not stop the peer from chainsyncing or being tracked here, so filtering on it would hide a still-connected candidate's fragment and would make this accessor inconsistent with its sibling GetAllPeerTips, which does not filter on it either. A future consumer that needs only selection-eligible fragments can intersect this result with isConnectionEligible/GetPeerTip itself.
func (*ChainSelector) CommitPeerTipAdmission ¶ added in v0.76.0
func (cs *ChainSelector) CommitPeerTipAdmission( update PeerTipUpdateEvent, ) bool
CommitPeerTipAdmission promotes an admitted candidate into normal peer selection. Events without a staged candidate retain the legacy update path.
func (*ChainSelector) CommitPeerTipAdmissionPinned ¶ added in v0.86.0
func (cs *ChainSelector) CommitPeerTipAdmissionPinned( update PeerTipUpdateEvent, ) bool
CommitPeerTipAdmissionPinned promotes a candidate while the ledger keeps the producer-authorization generation pinned across this call.
func (*ChainSelector) EagernessLimit ¶ added in v0.83.0
func (cs *ChainSelector) EagernessLimit() EagernessLimit
EagernessLimit returns the current Limit on Eagerness.
func (*ChainSelector) EagernessPaused ¶ added in v0.83.0
func (cs *ChainSelector) EagernessPaused(connId ouroboros.ConnectionId) bool
EagernessPaused reports whether connId's header stream is currently held at the Limit on Eagerness.
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) GenesisCorroborationActive ¶ added in v0.67.0
func (cs *ChainSelector) GenesisCorroborationActive() bool
GenesisCorroborationActive reports whether the Genesis corroboration gate is currently in effect. Callers use it to decide whether tip observation must be ordered synchronously with the apply-eligibility gate (see ShouldApplyIngress): the gate is only non-trivial while corroboration is active.
func (*ChainSelector) GenesisSelectionState ¶ added in v0.69.0
func (cs *ChainSelector) GenesisSelectionState() (bool, uint64)
GenesisSelectionState returns an atomic snapshot of whether Genesis selection is active and the density window it is using. Composition code injects this narrow state query into fork resolution so the authoritative local decision follows the selector's one-way Genesis-to-Praos transition.
This deliberately does not take cs.mutex. Callers are function-valued callbacks wired into other subsystems' hot paths (chainsync.State.RecordObservedHeader, LedgerState.recordPeerHeaderHistory), which hold their own lock (chainsync.State.clientConnIdMutex) across the call. Taking cs.mutex.RLock() here previously created a lock-order inversion against a separate path that holds cs.mutex (write) and calls back into chainsync.State.BlockfetchLatency (clientConnIdMutex.RLock), which deadlocked chain sync under real peer traffic. The snapshot below is kept current by refreshGenesisSelectionSnapshotLocked, called under cs.mutex at every point that can change it.
func (*ChainSelector) GenesisStatus ¶ added in v0.67.0
func (cs *ChainSelector) GenesisStatus() GenesisStatus
GenesisStatus returns a snapshot of current selection mode, Genesis window, selected fast source, and per-peer density/corroboration for observability.
func (*ChainSelector) GenesisWindowSlots ¶ added in v0.37.0
func (cs *ChainSelector) GenesisWindowSlots() uint64
GenesisWindowSlots returns the slot window used for Genesis density checks.
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) GetCandidateFragment ¶ added in v0.70.6
func (cs *ChainSelector) GetCandidateFragment( connId ouroboros.ConnectionId, ) (CandidateFragment, bool)
GetCandidateFragment returns the candidate chain fragment for a specific peer. The second return value is false when the peer is not tracked (never observed, or removed on disconnect).
func (*ChainSelector) GetPeerSyncTarget ¶ added in v0.70.3
func (cs *ChainSelector) GetPeerSyncTarget( connId ouroboros.ConnectionId, ) (ochainsync.Tip, bool)
GetPeerSyncTarget returns a peer's advertised head only after its delivered frontier is close enough to corroborate that advertisement.
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) HandlePeerActivityEvent ¶ added in v0.27.7
func (cs *ChainSelector) HandlePeerActivityEvent(evt event.Event)
HandlePeerActivityEvent refreshes a peer's liveness on non-tip protocol activity such as keepalive responses.
func (*ChainSelector) HandlePeerRollbackEvent ¶ added in v0.61.2
func (cs *ChainSelector) HandlePeerRollbackEvent(evt event.Event)
HandlePeerRollbackEvent trims Genesis observed history and refreshes the tracked peer tip after a rollback. When the connection has no tracked entry it registers one from the rollback (see registerPeerFromRollbackLocked), because a rollback for an untracked connection is the normal post- FindIntersect MsgRollBackward of a fresh or recycled connection and dropping it leaves the peer unselectable until its next MsgRollForward.
func (*ChainSelector) HandlePeerTipUpdateEvent ¶
func (cs *ChainSelector) HandlePeerTipUpdateEvent(evt event.Event)
HandlePeerTipUpdateEvent handles PeerTipUpdateEvent from the event bus.
func (*ChainSelector) ObservePeerTipUpdate ¶ added in v0.83.0
func (cs *ChainSelector) ObservePeerTipUpdate( e PeerTipUpdateEvent, ) PeerTipUpdateDisposition
ObservePeerTipUpdate synchronously applies a peer tip update and reports whether it was accepted, retained as a pending far-tip claim, or rejected.
func (*ChainSelector) PeerCount ¶
func (cs *ChainSelector) PeerCount() int
PeerCount returns the number of peers being tracked.
func (*ChainSelector) PrepareForecastHorizonWitness ¶ added in v0.86.0
func (cs *ChainSelector) PrepareForecastHorizonWitness( update PeerTipUpdateEvent, ) []ouroboros.ConnectionId
PrepareForecastHorizonWitness records one connected header solely as a Genesis corroboration witness while its ChainSync callback waits for the ledger forecast horizon. The header has not passed consensus verification, so it is kept outside every selection and admission path. The returned connections have verified staged headers that the witness now corroborates and should be re-intersected so those headers are delivered again.
func (*ChainSelector) PreparePeerTipAdmission ¶ added in v0.76.0
func (cs *ChainSelector) PreparePeerTipAdmission( update PeerTipUpdateEvent, ) bool
PreparePeerTipAdmission records an observed header as a candidate. Genesis corroboration may use it as a witness, but it cannot become a selectable source until CommitPeerTipAdmission.
func (*ChainSelector) PreparePeerTipAdmissionDisposition ¶ added in v0.84.0
func (cs *ChainSelector) PreparePeerTipAdmissionDisposition( update PeerTipUpdateEvent, ) PeerTipUpdateDisposition
PreparePeerTipAdmissionDisposition stages a candidate and distinguishes plausible headers from far-tip claims that must remain withheld.
func (*ChainSelector) RecordDeliveredPeerRollback ¶ added in v0.90.0
func (cs *ChainSelector) RecordDeliveredPeerRollback(e PeerRollbackEvent)
RecordDeliveredPeerRollback trims the delivered history of a peer after a rollback. It touches only an existing delivered record: it never registers the connection, so it is safe for peers that are not admitted to selection.
func (*ChainSelector) RecordDeliveredPeerTip ¶ added in v0.90.0
func (cs *ChainSelector) RecordDeliveredPeerTip(update PeerTipUpdateEvent)
RecordDeliveredPeerTip keeps a bounded corroboration history separate from the admission frontier, whose contents can be discarded after rejection or rebuilt from a later intersection.
func (*ChainSelector) RejectPeerTipAdmission ¶ added in v0.76.0
func (cs *ChainSelector) RejectPeerTipAdmission( update PeerTipUpdateEvent, )
RejectPeerTipAdmission removes a candidate and later candidate headers from the same peer. Later headers depend on the rejected chain prefix and must be observed again after the peer resynchronizes. Staged copies of the same point on other peers are removed the same way: a header the ledger declined is resolved once for the point, so a duplicate whose own publish was suppressed must not remain behind as corroboration evidence.
func (*ChainSelector) RemoveForecastHorizonWitness ¶ added in v0.86.0
func (cs *ChainSelector) RemoveForecastHorizonWitness( update PeerTipUpdateEvent, )
RemoveForecastHorizonWitness removes the temporary witness created for the matching admission wait. A later wait on the same connection is unaffected.
func (*ChainSelector) RemovePeer ¶
func (cs *ChainSelector) RemovePeer(connId ouroboros.ConnectionId)
RemovePeer removes a peer from tracking.
func (*ChainSelector) ResolvePeerTipAdmission ¶ added in v0.76.0
func (cs *ChainSelector) ResolvePeerTipAdmission( update PeerTipUpdateEvent, admitted bool, )
ResolvePeerTipAdmission promotes an accepted header or removes a rejected candidate. It is the composition-layer callback for ledger admission.
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) SelectedTip ¶ added in v0.83.0
func (cs *ChainSelector) SelectedTip() (ochainsync.Tip, bool)
SelectedTip returns the tip of the currently selected peer's chain truncated at the Limit on Eagerness: the highest point of that peer's retained fragment not past the limit, or the local tip when the fragment holds none. It returns false when no peer is selected.
func (*ChainSelector) SelectionMode ¶ added in v0.37.0
func (cs *ChainSelector) SelectionMode() SelectionMode
SelectionMode returns the selector's current mode.
func (*ChainSelector) SetConnectionEligible ¶ added in v0.55.0
func (cs *ChainSelector) SetConnectionEligible( connId ouroboros.ConnectionId, eligible bool, )
SetConnectionEligible marks whether a peer connection is eligible for chain selection. Ineligible peers are skipped during best-peer evaluation. Calling this triggers a re-evaluation of the best peer.
eligible=true is the default (absent key), so we delete instead of storing it. This prevents a stale entry when an out-of-order eligible=true event arrives after RemovePeer has already cleaned up the maps.
func (*ChainSelector) SetConnectionPriority ¶ added in v0.55.0
func (cs *ChainSelector) SetConnectionPriority( connId ouroboros.ConnectionId, priority int, )
SetConnectionPriority sets the selection priority for a peer connection. When two peers advertise the same chain tip, the peer with the higher priority wins. Calling this triggers a re-evaluation of the best peer.
priority=0 is the default (absent key), so we delete instead of storing it. This prevents a stale entry when an out-of-order priority=0 event arrives after RemovePeer has already cleaned up the maps.
func (*ChainSelector) SetLocalTip ¶
func (cs *ChainSelector) SetLocalTip(tip ochainsync.Tip)
SetLocalTip updates the local chain tip for comparison.
It also records the last time the APPLIED local tip moved FORWARD (its block number advanced). The anti-flap incumbent pin uses this timestamp as a progress-aware escape: if the applied tip stops advancing while the pin is engaged, the pin releases. Same-tip updates and rollbacks deliberately do NOT reset the stall clock, so a stalled incumbent cannot keep the pin alive by re-reporting an unchanged tip. Forward progress after a rollback does reset the clock because the comparison is against the previous applied tip, not an all-time high-water mark.
func (*ChainSelector) SetPeerIdling ¶ added in v0.82.0
func (cs *ChainSelector) SetPeerIdling( connId ouroboros.ConnectionId, point ocommon.Point, )
SetPeerIdling records an explicit ChainSync MsgAwaitReply at point. A later roll-forward or rollback clears it; a delayed event for an older point is ignored.
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) ShouldApplyIngress ¶ added in v0.67.0
func (cs *ChainSelector) ShouldApplyIngress( connId ouroboros.ConnectionId, ) bool
ShouldApplyIngress reports whether headers/blocks from connId may be applied to the ledger. It returns false only while Genesis corroboration is active (Genesis mode with a positive threshold) and the peer is not corroborated, so an uncorroborated or divergent fast source is OBSERVED — its tips still feed corroboration via PeerTipUpdateEvent — but its blocks are not applied and it cannot steer the ledger. Enforcing the stall here (ledger application) rather than only at peer selection is required because ingress is gated on peer- governance eligibility, independent of the selected best peer: clearing the active chainsync driver alone would not stop an eligible uncorroborated peer's headers from being applied.
An unknown peer (no tips observed yet) is treated as uncorroborated and denied while the gate is active — fail closed until corroboration forms. Outside Genesis corroboration it returns true unless the peer's advertised tip is more than a Genesis window older than the wall clock, which is denied in Genesis mode regardless of corroboration (see advertisedTipStaleLocked).
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) SyncTargetForPeerTipUpdate ¶ added in v0.70.3
func (cs *ChainSelector) SyncTargetForPeerTipUpdate( update PeerTipUpdateEvent, ) (ochainsync.Tip, bool)
SyncTargetForPeerTipUpdate applies the bounded-target policy to the exact observed/advertised pair carried by one chainsync event. It intentionally does not read the mutable per-peer tip map.
func (*ChainSelector) TouchPeerActivity ¶ added in v0.27.7
func (cs *ChainSelector) TouchPeerActivity(connId ouroboros.ConnectionId)
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 its delivered frontier was rejected as implausible. The remote advertised tip is untrusted and may be arbitrarily far ahead while the peer legitimately serves the next block the node needs, so plausibility is checked against the locally observed header. For known peers, the reference is the peer's previous observed frontier; for new peers, it is the best observed peer frontier. This avoids rejecting legitimate peers during sync while still bounding delivered block-number jumps.
type ChainSelectorConfig ¶
type ChainSelectorConfig struct {
Consensus nodeconsensus.Provider
Logger *slog.Logger
EventBus *event.EventBus
EvaluationInterval time.Duration
StaleTipThreshold time.Duration
SecurityParam uint64
GenesisMode bool
GenesisWindowSlots uint64
// WallClockSlot returns the current wall-clock slot and whether it is
// known. In Genesis mode a peer whose advertised tip slot plus the
// Genesis window is below the wall-clock slot is treated as serving an
// abandoned fork: it is not selectable and its headers are not applied.
// Other peers' tips play no part in the decision. Optional; the rule is
// inactive while nil or while it reports false.
WallClockSlot func() (uint64, bool)
// AppliedTip reads the live applied ledger tip. When set, peer-tip
// admission bounds delivered frontiers against it rather than against the
// tip last passed to SetLocalTip, which the node refreshes only on the
// stall recycler's tick while the ledger may apply hundreds of blocks in
// between. It reports false while no ledger is available, which keeps
// the last SetLocalTip value. It must not block or take locks shared with
// the selector.
AppliedTip func() (ochainsync.Tip, bool)
// PointOnLocalChain confirms a point against the current applied chain.
// The Genesis ingress gate calls it without holding the selector mutex.
PointOnLocalChain func(ocommon.Point) bool
// HeaderCryptoVerificationGenerationCurrent reports whether a nonzero
// producer-authorization generation still matches the live ledger state.
// It is optional for callers that do not stage cryptographically verified
// headers.
HeaderCryptoVerificationGenerationCurrent func(uint64) bool
// CurrentHeaderCryptoVerificationGeneration returns the current nonzero
// ledger snapshot generation, or zero while publication is in flight.
CurrentHeaderCryptoVerificationGeneration func() uint64
// PinHeaderCryptoVerificationGeneration keeps a current generation stable
// until the returned release function is called. Selector promotion uses
// it to order a committed tip before any ledger snapshot publication that
// invalidates its producer-authorization verdict.
PinHeaderCryptoVerificationGeneration func(uint64) (func(), bool)
// MinCorroboratingPeers is the number of distinct other eligible peers
// that must report the same recent blocks as a candidate before that
// candidate can drive chain selection in Genesis mode. It implements the
// Ouroboros Genesis trust property that a fast (shallow) block source is
// followed only while corroborated by independent peers. 0 disables
// corroboration (density-only Genesis selection, the historical default).
MinCorroboratingPeers int
ConnectionLive func(ouroboros.ConnectionId) bool
// PeerIdentity maps a connection to a control identity used to
// de-duplicate corroborating witnesses. A connection for which it returns
// "" has no trustworthy independence evidence and
// fails closed in corroboration: it is not corroborated as a candidate
// and does not count as a witness. A nil hook groups by remote host.
PeerIdentity func(ouroboros.ConnectionId) string
ConnectionEligible func(ouroboros.ConnectionId) bool
ConnectionPriority func(ouroboros.ConnectionId) int
MaxTrackedPeers int // 0 means use DefaultMaxTrackedPeers
// DisableEventSubscriptions leaves EventBus configured for publishing
// selector events but skips automatic input subscriptions. This is useful
// for deterministic replay harnesses that feed input events synchronously.
DisableEventSubscriptions bool
// BlockfetchLatency returns the EWMA first-block latency for a
// connection and whether any samples exist. Used only to choose a
// peer when two peers advertise the exact same selected block.
BlockfetchLatency func(ouroboros.ConnectionId) (time.Duration, bool)
// OnRollbackRegistration is called, outside the selector lock, with the
// outcome of every attempt to register a peer from a chainsync rollback for
// an untracked connection. Optional; the composition layer uses it to
// export a counter.
OnRollbackRegistration func(RollbackRegistrationOutcome)
// OnPeerTipRejected is called once for every peer tip refused as
// implausible, with the bound that refused it. It runs with the selector
// lock held, so it must only do non-blocking work such as incrementing a
// counter and must not call back into the selector. Optional.
OnPeerTipRejected func(PeerTipRejection)
// SwitchBackCooldown bounds the rate of active-connection handoffs
// driven by the anti-flap pin's discretionary escapes -- longer-chain
// and progress-stall (pinIncumbentDuringCatchUpLocked). Once the active
// connection moves away from a peer, that peer cannot reclaim it through
// either escape until this much time has passed, and no discretionary
// hand-off to any connection happens within this much time of the
// previous one, even if the challenger currently leads by more than
// catchUpPinHeadMargin. This is a rate limit, not a correctness rule --
// the challenger is still adopted once the cooldown expires, and the
// mandatory incumbent-unselectable release is never gated -- and it
// exists because peers whose delivered frontiers repeatedly leapfrog each
// other by more than the margin can otherwise hand the chainsync/
// blockfetch pipeline back and forth on every such crossing. 0 (the
// zero value) is replaced with defaultSwitchBackCooldown by
// NewChainSelector, matching EvaluationInterval and StaleTipThreshold.
SwitchBackCooldown time.Duration
// OnGenesisDensityDisconnect is called, outside the selector lock, once
// per peer the Genesis Density Disconnector finds provably sparser than
// another candidate chain. The composition layer disconnects and denies
// the peer. The disconnector is inactive while this is nil, outside
// Genesis mode, or while GenesisWindowSlots is zero: the 3k-slot
// fallback is too short to compare densities. Evaluation runs at most
// once per GenesisDensityEvaluationInterval.
OnGenesisDensityDisconnect func(GenesisDensityDisconnect)
}
ChainSelectorConfig holds configuration for the ChainSelector.
type ChainSwitchEvent ¶
type ChainSwitchEvent struct {
PreviousConnectionId ouroboros.ConnectionId
NewConnectionId ouroboros.ConnectionId
NewTip ochainsync.Tip
PreviousTip ochainsync.Tip
NewObservedTip ochainsync.Tip
NewObservedTipSet bool
PreviousObservedTip ochainsync.Tip
ComparisonResult ChainComparisonResult
BlockDifference int64
RollbackPoint *ocommon.Point
}
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 advertised chain tip of the new peer.
- PreviousTip: The advertised chain tip of the previous peer at switch time.
- NewObservedTip: The delivered frontier of the new peer. A zero value means the peer delivered nothing, which is distinct from absent.
- NewObservedTipSet: Whether NewObservedTip was populated. Producers in this package always set it. Events built elsewhere (older producers, direct unit-test and integration constructors) leave it false, and only those fall back to the advertised NewTip.
- PreviousObservedTip: The delivered frontier of the previous peer.
- ComparisonResult: Why the new chain is better than the previous chain.
- BlockDifference: NewTip.BlockNumber - PreviousTip.BlockNumber.
- RollbackPoint: The highest point the previous and new peers' candidate fragments share, including when the previous peer was just removed on disconnect or stale cleanup. It is not intersected with the local chain and can lie above the local tip, so a consumer rolling the local chain back must intersect it with that chain first. Nil when the fragments share no retained point.
type EagernessLimit ¶ added in v0.83.0
type EagernessLimit struct {
// Active reports whether the cap applies. It is false outside Genesis
// selection mode (the node is caught up), and for a selector without a
// security parameter.
Active bool
// Intersected is true when the limit was derived from a point common to
// every candidate fragment, and false when the candidates share no
// retained point and the limit fell back to the last point they shared,
// or to the local tip if they never shared one.
Intersected bool
// Point is the common point of the candidate fragments, or the last one
// they shared when not Intersected. It is the zero Point when the limit
// is measured from the local tip.
Point ocommon.Point
// BlockNumber is the highest block number selection may reach: k past the
// intersection or the last shared point, or k past the local tip when the
// candidates never shared a point. It is never below k past the immutable
// tip (the local tip less k).
BlockNumber uint64
}
EagernessLimit is the Limit on Eagerness: the highest block number chain selection may reach while the cap is active.
type EvaluationPanicEvent ¶ added in v0.70.5
EvaluationPanicEvent is published when a background evaluation tick or triggered evaluation panics.
Fields:
- Panic: the recovered panic value, for diagnostics.
- Triggered: true when the panic occurred in runTriggeredEvaluation (the evaluationLoop select case draining evaluationTrigger, fed by SetConnectionEligible/SetConnectionPriority's triggerEvaluation calls); false for the periodic ticker tick (runEvaluationTick), which also runs cleanupStalePeers first. Panics from EvaluateAndSwitch called directly outside evaluationLoop (e.g. from UpdatePeerTip, RemovePeer, SetLocalTip, or an event handler) are not covered by this event -- only the two evaluationLoop paths recover and surface panics here.
type GenesisCorroborationFailedEvent ¶ added in v0.67.0
type GenesisCorroborationFailedEvent struct {
ConnectionId ouroboros.ConnectionId
ObservedDensity uint64
CorroboratingPeers int
RequiredPeers int
GenesisWindowSlots uint64
}
GenesisCorroborationFailedEvent is published when the densest Genesis fast source lacks the configured minimum corroboration from independent peers.
Fields:
- ConnectionId: the uncorroborated fast source that was denied selection.
- ObservedDensity: its observed block density within the Genesis window.
- CorroboratingPeers: how many independent peers actually corroborate it.
- RequiredPeers: the configured MinCorroboratingPeers threshold.
- GenesisWindowSlots: the active Genesis density window in slots.
type GenesisDensityDisconnect ¶ added in v0.81.0
type GenesisDensityDisconnect struct {
// ConnectionId is the peer to disconnect.
ConnectionId ouroboros.ConnectionId
// DominatingConnectionId is the peer whose chain is provably denser.
DominatingConnectionId ouroboros.ConnectionId
// Intersection is the highest block the two candidates share.
Intersection ocommon.Point
// WindowSlots is the Genesis window that starts after Intersection.
WindowSlots uint64
// DominatingDensity is the blocks the dominating peer has delivered in
// the window.
DominatingDensity uint64
// MaxDensity is the density bound ConnectionId was compared against. For
// an idling peer this is its delivered density; otherwise it includes all
// still-possible slots through the window end.
MaxDensity uint64
// EagernessStandoff is true when ConnectionId was removed to resolve two
// forks whose header streams were both held at the Limit on Eagerness.
EagernessStandoff bool
}
GenesisDensityDisconnect describes a peer the Genesis Density Disconnector found provably sparser than another candidate chain.
type GenesisModeExitedEvent ¶ added in v0.67.0
type GenesisModeExitedEvent struct {
LocalSlot uint64
BestKnownSlot uint64
GenesisWindowSlots uint64
}
GenesisModeExitedEvent is published when the selector leaves Genesis mode.
Fields:
- LocalSlot: the local tip slot at the time of exit.
- BestKnownSlot: the best known selectable peer tip slot.
- GenesisWindowSlots: the active Genesis density window in slots.
type GenesisPeerStatus ¶ added in v0.67.0
type GenesisPeerStatus struct {
ConnectionId ouroboros.ConnectionId
ObservedDensity uint64
CorroboratingPeers int
Corroborated bool
Selectable bool
}
GenesisPeerStatus is the per-peer Genesis observability snapshot.
type GenesisStatus ¶ added in v0.67.0
type GenesisStatus struct {
Mode SelectionMode
WindowSlots uint64
MinCorroboratingPeers int
BestSource *ouroboros.ConnectionId
Peers []GenesisPeerStatus
}
GenesisStatus is a point-in-time snapshot of Genesis selection state for observability (metrics, logs, operator tooling).
type PeerActivityEvent ¶ added in v0.27.7
type PeerActivityEvent struct {
ConnectionId ouroboros.ConnectionId
}
PeerActivityEvent is published when a peer has recent protocol activity (for example, a keepalive response) without a tip change. This refreshes selector liveness for healthy but temporarily quiet peers.
type PeerChainTip ¶
type PeerChainTip struct {
ConnectionId ouroboros.ConnectionId
// Tip is the remote peer's advertised chain tip. It is untrusted and may
// be far ahead of the headers the peer has actually delivered.
Tip ochainsync.Tip
// ObservedTip is the latest header locally delivered by this peer. Chain
// comparison and handoff decisions use this frontier.
ObservedTip ochainsync.Tip
VRFOutput []byte // VRF output from tip block for tie-breaking
PraosView PraosTiebreakerView
LastUpdated time.Time
// contains filtered or unexported fields
}
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) ApplyRollback ¶ added in v0.37.0
func (p *PeerChainTip) ApplyRollback( point ocommon.Point, tip ochainsync.Tip, )
ApplyRollback trims observed history at the rollback point and refreshes the peer tip to the chainsync tip reported with the rollback.
func (*PeerChainTip) AwaitingFirstHeader ¶ added in v0.70.15
func (p *PeerChainTip) AwaitingFirstHeader() bool
AwaitingFirstHeader reports whether chain selection registered this peer from a chainsync rollback (newPeerChainTipFromRollback, the post-FindIntersect MsgRollBackward on a connection it was not tracking) and the peer has not delivered a header since. For such a peer SelectionTip is the point its session intersected at, carrying no block number, and it does not move until the peer's first RollForward arrives: it is evidence of the intersection and nothing else. Callers that reason about how far a peer has got need to tell that apart from a delivered frontier.
It reads the awaitingFirstHeader flag rather than testing for a zero delivered block number, because the two are not the same property. A tracked peer that has delivered headers and then rolls back to a point outside its retained delivered-header history is also left with a zero block number (ApplyRollback keeps the point and cannot recover a block number for it), but it has delivered headers on this connection and is not awaiting its first one. So is a peer whose delivered frontier genuinely is origin.
func (*PeerChainTip) CandidateFragment ¶ added in v0.70.6
func (p *PeerChainTip) CandidateFragment() CandidateFragment
CandidateFragment returns a snapshot of this peer's candidate chain fragment. The caller receives an independent copy.
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) SelectionTip ¶ added in v0.27.7
func (p *PeerChainTip) SelectionTip() ochainsync.Tip
SelectionTip returns the best locally observed frontier for this peer. When available, prefer the latest block the peer has actually delivered to us over its remote advertised tip. This avoids switching to peers whose far-end tip is high while their chainsync cursor is still lagging.
func (*PeerChainTip) Touch ¶ added in v0.27.7
func (p *PeerChainTip) Touch()
Touch marks the peer as recently active without changing its advertised tip.
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.
func (*PeerChainTip) UpdateTipWithObserved ¶ added in v0.27.7
func (p *PeerChainTip) UpdateTipWithObserved( tip ochainsync.Tip, observedTip ochainsync.Tip, vrfOutput []byte, )
UpdateTipWithObserved updates both the remote advertised tip and the latest locally observed frontier for the peer.
func (*PeerChainTip) UpdateTipWithObservedPraosView ¶ added in v0.46.0
func (p *PeerChainTip) UpdateTipWithObservedPraosView( tip ochainsync.Tip, observedTip ochainsync.Tip, vrfOutput []byte, praosView PraosTiebreakerView, )
UpdateTipWithObservedPraosView updates the remote advertised tip, the latest locally observed frontier, the VRF output, and the Praos tiebreaker view for the peer. Callers must provide a PraosTiebreakerView derived from observedTip and the supplied vrfOutput when that VRF output participates in the view. The view is stored as supplied; this method does not validate consistency, so an inconsistent view can make later chain-selection comparisons incorrect.
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 PeerRollbackEvent ¶ added in v0.37.0
type PeerRollbackEvent struct {
ConnectionId ouroboros.ConnectionId
Point ocommon.Point
Tip ochainsync.Tip
}
PeerRollbackEvent is published when an ingress-eligible chainsync peer reports a rollback. Point is the rollback point; Tip is the peer's current chainsync tip after the rollback.
type PeerRollbackHandlerPanicEvent ¶ added in v0.70.5
type PeerRollbackHandlerPanicEvent struct {
Panic any
}
PeerRollbackHandlerPanicEvent is published when HandlePeerRollbackEvent panics. Panic carries the recovered panic value for diagnostics.
type PeerTipRejection ¶ added in v0.81.2
type PeerTipRejection string
PeerTipRejection says which plausibility bound refused a peer's tip. It is reported to ChainSelectorConfig.OnPeerTipRejected so the composition layer can count rejections without chainselection depending on a metrics library.
const ( // PeerTipRejectedObservedFrontier means the peer's delivered frontier // jumped too far ahead of the trusted reference. PeerTipRejectedObservedFrontier PeerTipRejection = "observed_frontier" // PeerTipRejectedAdvertisedTip means the peer's advertised tip exceeded // the bootstrap bound while no local block had been applied. PeerTipRejectedAdvertisedTip PeerTipRejection = "advertised_tip" )
type PeerTipUpdateDisposition ¶ added in v0.83.0
type PeerTipUpdateDisposition uint8
PeerTipUpdateDisposition describes synchronous handling of a delivered peer tip at the ChainSync admission boundary.
const ( // PeerTipUpdateUnhandled leaves the update on the asynchronous event path. PeerTipUpdateUnhandled PeerTipUpdateDisposition = iota // PeerTipUpdateAccepted records the update as a selectable candidate. PeerTipUpdateAccepted // PeerTipUpdatePending retains a connected far-tip claim without making it // selectable until enough consecutive headers establish plausibility. PeerTipUpdatePending // PeerTipUpdateRejected refuses an update that cannot become a candidate. PeerTipUpdateRejected )
type PeerTipUpdateEvent ¶
type PeerTipUpdateEvent struct {
ConnectionId ouroboros.ConnectionId
// Tip is the untrusted remote advertised tip.
Tip ochainsync.Tip
// ObservedTip is the header frontier actually delivered by the peer.
ObservedTip ochainsync.Tip
VRFOutput []byte // VRF output from observed block header for tie-breaking
PraosView PraosTiebreakerView
// AdmissionID identifies one delivered header. A candidate may contribute
// to Genesis corroboration, but cannot be a selectable peer until admitted.
AdmissionID uint64
// HeaderCryptoVerificationGeneration identifies the ledger snapshot that
// authorized this header. The selector must ignore a staged candidate once
// that snapshot is no longer current.
HeaderCryptoVerificationGeneration uint64
Admitted bool
// Rejected removes the candidate after definite ledger rejection.
Rejected bool
// ObservedPrevHash is the hash of the parent the delivered header names.
// It lets the selector require that far frontiers claimed by one
// connection form a connected header chain; empty means unknown.
ObservedPrevHash []byte
// ObservedBoundary is set when the delivered header is a Byron epoch
// boundary block, whose block number equals its parent's.
ObservedBoundary bool
}
PeerTipUpdateEvent is published when a peer's chain tip is updated via chainsync roll forward.
type PraosTiebreakerConfig ¶ added in v0.46.0
type PraosTiebreakerConfig = praos.PraosTiebreakerConfig
PraosTiebreakerConfig is aliased from consensus/praos.
func PraosTiebreakerConfigBeforeConway ¶ added in v0.46.0
func PraosTiebreakerConfigBeforeConway() PraosTiebreakerConfig
PraosTiebreakerConfigBeforeConway delegates to consensus/praos.
func PraosTiebreakerConfigConway ¶ added in v0.46.0
func PraosTiebreakerConfigConway() PraosTiebreakerConfig
PraosTiebreakerConfigConway delegates to consensus/praos.
func PraosTiebreakerConfigUnknown ¶ added in v0.46.0
func PraosTiebreakerConfigUnknown() PraosTiebreakerConfig
PraosTiebreakerConfigUnknown delegates to consensus/praos.
type PraosTiebreakerFlavor ¶ added in v0.46.0
type PraosTiebreakerFlavor = praos.PraosTiebreakerFlavor
PraosTiebreakerFlavor and its constants are type-aliased from consensus/praos so that chainselection.PraosTiebreakerFlavor and praos.PraosTiebreakerFlavor are the same type.
type PraosTiebreakerView ¶ added in v0.46.0
type PraosTiebreakerView = praos.PraosTiebreakerView
PraosTiebreakerView is aliased from consensus/praos.
func GetPraosTiebreakerView ¶ added in v0.46.0
func GetPraosTiebreakerView( header gledger.BlockHeader, ) (PraosTiebreakerView, bool)
GetPraosTiebreakerView delegates to consensus/praos.
func NewPraosTiebreakerViewFull ¶ added in v0.55.0
func NewPraosTiebreakerViewFull( tip ochainsync.Tip, issuer []byte, issueNo uint64, vrfOutput []byte, config PraosTiebreakerConfig, ) PraosTiebreakerView
NewPraosTiebreakerViewFull delegates to consensus/praos.
func PraosTiebreakerViewFromTip ¶ added in v0.46.0
func PraosTiebreakerViewFromTip( tip ochainsync.Tip, vrfOutput []byte, config PraosTiebreakerConfig, ) PraosTiebreakerView
PraosTiebreakerViewFromTip delegates to consensus/praos.
type RollbackRegistrationOutcome ¶ added in v0.70.14
type RollbackRegistrationOutcome string
RollbackRegistrationOutcome is the result of handling a chainsync rollback for a connection the selector is not tracking (see registerPeerFromRollbackLocked). It is reported to ChainSelectorConfig.OnRollbackRegistration so the composition layer can count registrations and refusals without chainselection depending on a metrics library.
const ( // RollbackRegistrationRegistered means the peer was registered from the // rollback and is now visible to chain selection. RollbackRegistrationRegistered RollbackRegistrationOutcome = "registered" // RollbackRegistrationClosedConnection means the rollback arrived for a // connection that is no longer live, so no entry was created. RollbackRegistrationClosedConnection RollbackRegistrationOutcome = "rejected_closed_connection" // RollbackRegistrationImplausibleTip means the advertised tip failed the // same plausibility bound the roll-forward path applies to a new peer. RollbackRegistrationImplausibleTip RollbackRegistrationOutcome = "rejected_implausible_tip" // RollbackRegistrationAtCapacity means the tracked-peer table was full and // no peer could be evicted to make room. RollbackRegistrationAtCapacity RollbackRegistrationOutcome = "rejected_at_capacity" )
type SelectionMode ¶ added in v0.37.0
type SelectionMode uint8
SelectionMode describes the chain-selection strategy currently in use.
const ( SelectionModePraos SelectionMode = iota SelectionModeGenesis )
func (SelectionMode) String ¶ added in v0.37.0
func (m SelectionMode) String() string