ceremonyclient/consensus/consensus_timeout.go
Cassandra Heart c797d482f9
v2.1.0.5 (#457)
* wip: conversion of hotstuff from flow into Q-oriented model

* bulk of tests

* remaining non-integration tests

* add integration test, adjust log interface, small tweaks

* further adjustments, restore full pacemaker shape

* add component lifecycle management+supervisor

* further refinements

* resolve timeout hanging

* mostly finalized state for consensus

* bulk of engine swap out

* lifecycle-ify most types

* wiring nearly complete, missing needed hooks for proposals

* plugged in, vetting message validation paths

* global consensus, plugged in and verified

* app shard now wired in too

* do not decode empty keys.yml (#456)

* remove obsolete engine.maxFrames config parameter (#454)

* default to Info log level unless debug is enabled (#453)

* respect config's  "logging" section params, remove obsolete single-file logging (#452)

* Trivial code cleanup aiming to reduce Go compiler warnings (#451)

* simplify range traversal

* simplify channel read for single select case

* delete rand.Seed() deprecated in Go 1.20 and no-op as of Go 1.24

* simplify range traversal

* simplify channel read for single select case

* remove redundant type from array

* simplify range traversal

* simplify channel read for single select case

* RC slate

* finalize 2.1.0.5

* Update comments in StrictMonotonicCounter

Fix comment formatting and clarify description.

---------

Co-authored-by: Black Swan <3999712+blacks1ne@users.noreply.github.com>
2025-11-11 05:00:17 -06:00

128 lines
6.2 KiB
Go

package consensus
import (
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
"source.quilibrium.com/quilibrium/monorepo/lifecycle"
)
// TimeoutAggregator verifies and aggregates timeout states to build timeout
// certificates [TCs]. When enough timeout states are collected, it builds a TC
// and sends it to the EventLoop TimeoutAggregator also detects protocol
// violation, including invalid timeouts, double timeout, etc and notifies a
// HotStuff consumer for slashing.
type TimeoutAggregator[VoteT models.Unique] interface {
lifecycle.Component
// AddTimeout verifies and aggregates a timeout state.
// This method can be called concurrently, timeouts will be queued and
// processed asynchronously.
AddTimeout(timeoutState *models.TimeoutState[VoteT])
// PruneUpToRank deletes all `TimeoutCollector`s _below_ to the given rank, as
// well as related indices. We only retain and process `TimeoutCollector`s,
// whose rank is equal or larger than `lowestRetainedRank`. If
// `lowestRetainedRank` is smaller than the previous value, the previous value
// is kept and the method call is a NoOp. This value should be set to the
// latest active rank maintained by `Pacemaker`.
PruneUpToRank(lowestRetainedRank uint64)
}
// TimeoutCollector collects all timeout states for a specified rank. On the
// happy path, it generates a TimeoutCertificate when enough timeouts have been
// collected. The TimeoutCollector is a higher-level structure that orchestrates
// deduplication, caching and processing of timeouts, delegating those tasks to
// underlying modules (such as TimeoutProcessor). Implementations of
// TimeoutCollector must be concurrency safe.
type TimeoutCollector[VoteT models.Unique] interface {
// AddTimeout adds a Timeout State to the collector. When TSs from
// strictly more than 1/3 of consensus participants (measured by weight) were
// collected, the callback for partial TC will be triggered. After collecting
// TSs from a supermajority, a TC will be created and passed to the EventLoop.
// Expected error returns during normal operations:
// * timeoutcollector.ErrTimeoutForIncompatibleRank - submitted timeout for
// incompatible rank
// All other exceptions are symptoms of potential state corruption.
AddTimeout(timeoutState *models.TimeoutState[VoteT]) error
// Rank returns the rank that this instance is collecting timeouts for.
// This method is useful when adding the newly created timeout collector to
// timeout collectors map.
Rank() uint64
}
// TimeoutProcessor ingests Timeout States for a particular rank. It
// implements the algorithms for validating TSs, orchestrates their low-level
// aggregation and emits `OnPartialTimeoutCertificateCreated` and `OnTimeoutCertificateConstructedFromTimeouts`
// notifications. TimeoutProcessor cannot deduplicate TSs (this should be
// handled by the higher-level TimeoutCollector) and errors instead. Depending
// on their implementation, a TimeoutProcessor might drop timeouts or attempt to
// construct a TC.
type TimeoutProcessor[VoteT models.Unique] interface {
// Process performs processing of single timeout state. This function is safe
// to call from multiple goroutines. Expected error returns during normal
// operations:
// * timeoutcollector.ErrTimeoutForIncompatibleRank - submitted timeout for
// incompatible rank
// * models.InvalidTimeoutError - submitted invalid timeout(invalid structure
// or invalid signature)
// * models.DuplicatedSignerError if a timeout from the same signer was
// previously already added. It does _not necessarily_ imply that the
// timeout is invalid or the sender is equivocating.
// All other errors should be treated as exceptions.
Process(timeout *models.TimeoutState[VoteT]) error
}
// TimeoutCollectorFactory performs creation of TimeoutCollector for a given
// rank
type TimeoutCollectorFactory[VoteT models.Unique] interface {
// Create is a factory method to generate a TimeoutCollector for a given rank
// Expected error returns during normal operations:
// * models.ErrRankUnknown no rank containing the given rank is known
// All other errors should be treated as exceptions.
Create(rank uint64) (TimeoutCollector[VoteT], error)
}
// TimeoutProcessorFactory performs creation of TimeoutProcessor for a given
// rank
type TimeoutProcessorFactory[VoteT models.Unique] interface {
// Create is a factory method to generate a TimeoutProcessor for a given rank
// Expected error returns during normal operations:
// * models.ErrRankUnknown no rank containing the given rank is known
// All other errors should be treated as exceptions.
Create(rank uint64) (TimeoutProcessor[VoteT], error)
}
// TimeoutCollectors encapsulates the functionality to generate, store and prune
// `TimeoutCollector` instances (one per rank). Its main purpose is to provide a
// higher-level API to `TimeoutAggregator` for managing and interacting with the
// rank-specific `TimeoutCollector` instances. Implementations are concurrency
// safe.
type TimeoutCollectors[VoteT models.Unique] interface {
// GetOrCreateCollector retrieves the TimeoutCollector for the specified
// rank or creates one if none exists. When creating a timeout collector,
// the rank is used to query the consensus committee for the respective
// Rank the rank belongs to.
// It returns:
// - (collector, true, nil) if no collector can be found by the rank, and a
// new collector was created.
// - (collector, false, nil) if the collector can be found by the rank.
// - (nil, false, error) if running into any exception creating the timeout
// collector.
// Expected error returns during normal operations:
// * models.BelowPrunedThresholdError if rank is below the pruning threshold
// * models.ErrRankUnknown if rank is not yet pruned but no rank containing
// the given rank is known
GetOrCreateCollector(rank uint64) (
collector TimeoutCollector[VoteT],
created bool,
err error,
)
// PruneUpToRank prunes the timeout collectors with ranks _below_ the given
// value, i.e. we only retain and process timeout collectors, whose ranks are
// equal or larger than `lowestRetainedRank`. If `lowestRetainedRank` is
// smaller than the previous value, the previous value is kept and the method
// call is a NoOp.
PruneUpToRank(lowestRetainedRank uint64)
}