Documentation
¶
Overview ¶
Package runner drives the consensus core in a node. It owns the consensus goroutine and its input queues, per-peer receive queues and checks, the timer scheduler, write-ahead logging and replay, calls to the private validator, message sending, the commit goroutine and the publication of state snapshots.
Index ¶
- Constants
- Variables
- func Handshake(appHead types.Height, st WALState, signHeight *types.Height) (HandshakeAction, *CommitRequest, error)
- type App
- type AppQueueSourceObserver
- type Chain
- type Clock
- type CommitRequest
- type Config
- type CoreEnvObservation
- type CoreStepMoment
- type CoreStepObservation
- type CoreStepObserver
- type Deps
- type EventSink
- type FinalizeSourceObserver
- type HandshakeAction
- type InboxFailureSourceObserver
- type InboxSourceObserver
- type InternalSourceObserver
- type ManualFinalizer
- type ManualProcessing
- type NextHeightLimits
- type ProcessingWork
- type ReceiveDispatchSourceObserver
- type ReceiveFailureDiagnosticObserver
- type ReplayInfo
- type RestoreSourceObserver
- type Runner
- func (r *Runner) Close() error
- func (r *Runner) EngineRun() uint64
- func (r *Runner) Halted() error
- func (r *Runner) InboundBytes() int64
- func (r *Runner) LastReplay() ReplayInfo
- func (r *Runner) NewHead(h *types.Header)
- func (r *Runner) NextHeightUsage() (uint64, uint64)
- func (r *Runner) PeerConnected(a types.Address)
- func (r *Runner) ProcessingPending() bool
- func (r *Runner) Pump() bool
- func (r *Runner) Receiver() transport.Receiver
- func (r *Runner) Running() bool
- func (r *Runner) Snapshot() *consensus.Snapshot
- func (r *Runner) Start(ctx context.Context, from types.Height) error
- func (r *Runner) Stop() error
- func (r *Runner) Submit(b *types.Block)
- func (r *Runner) Vars() *consensus.Vars
- type SelectionSourceObserver
- type SignerCallObserver
- type SourceFinalizer
- type Timer
- type TimerQueue
- func (q *TimerQueue) Arm(a consensus.ArmTimer, from time.Duration)
- func (q *TimerQueue) Cancel(kinds []consensus.TimerKind) []consensus.ArmTimer
- func (q *TimerQueue) Clear()
- func (q *TimerQueue) Pending() bool
- func (q *TimerQueue) Pop() (consensus.Timeout, bool)
- func (q *TimerQueue) PopRecorded(recorded consensus.Timeout) (consensus.Timeout, bool, error)
- func (q *TimerQueue) Status() TimerQueueStatus
- type TimerQueueStatus
- type TimerSourceObserver
- type ValidatorHistoryLimits
- type WALReadSourceObserver
- type WALState
Constants ¶
const ( ProcessingCheck = "receive_check" ProcessingCore = "core_input" )
const ( // WALSyncPeriod bounds how long a buffered write-ahead log record // stays unsynced when no own message forces a sync. WALSyncPeriod = 200 * time.Millisecond // HeadCheckPeriod is how often the head the core works on is compared // with the application's head. HeadCheckPeriod = 30 * time.Second )
Periods of the housekeeping tasks.
Variables ¶
var ( ErrRunning = errors.New("runner: already running") ErrNotRunning = errors.New("runner: not running") ErrHalted = errors.New("runner: consensus halted") ErrStartPoint = errors.New("runner: start height is not the head + 1") ErrClosed = errors.New("runner: closed") ErrFinalizePending = errors.New("runner: manual finalization pending") )
Errors of the runner.
var ErrAppBehind = fmt.Errorf("runner: the application is behind the write-ahead log or the sign state")
ErrAppBehind is the refusal of the handshake.
var ErrProcessingPending = errors.New("runner: manual core service pending")
Functions ¶
func Handshake ¶
func Handshake(appHead types.Height, st WALState, signHeight *types.Height) (HandshakeAction, *CommitRequest, error)
Handshake compares the application head appHead with the log state and the sign height (the highest signed height or the sign floor, nil if none) and returns the action and, for Refinalize, the block.
A commit request of height appHead + 1 is refinalized only when the log has not left a later height and nothing was signed beyond appHead + 2: the log keeps the requests of earlier heights, and an application that fell further behind than one height is refused even when the log still holds the request of its next height.
Types ¶
type App ¶
type App interface {
// ReadyToBuild tells the block builder that a round started and how
// long to wait before building; done is closed when the core leaves the
// height. It must not block.
ReadyToBuild(req consensus.RequestBuild, wait time.Duration, done <-chan struct{})
// FinalizeBlock imports a decided block with its seals. It is called
// from the commit goroutine, one block at a time in height order, and
// is idempotent for a block that is already the head.
FinalizeBlock(b *types.Block, round types.Round) error
}
App is the part of the application the runner drives.
type AppQueueSourceObserver ¶
type AppQueueSourceObserver interface{ ObserveAppQueueSource(journal.AppQueueRec) }
AppQueueSourceObserver receives detached application queue metadata after appMu is released. Calls can arrive concurrently and out of order; Sequence retains the actual mutex order. The observer must return promptly. Mutating its input bytes cannot change a queued input or functional journal record. This optional hook does not enable functional cache replay or app execution.
type Chain ¶
type Chain interface {
// Head returns the application's canonical head.
Head() *types.Header
// ValidatorsAt returns the validator set of block number whose parent
// has hash parent.
ValidatorsAt(number types.Height, parent types.Hash) (*validator.Set, error)
// ValidateProposal verifies a proposal (header.VerifyProposal); a
// proposal from the future fails with header.ErrFutureBlock and the time
// to wait.
ValidateProposal(b *types.Block) (time.Duration, error)
// IsBadBlock reports whether the block is recorded as bad.
IsBadBlock(h types.Hash) bool
}
Chain answers the core's questions about the application's chain. The runner records every answer so that a replay needs no application.
type Clock ¶
type Clock interface {
Now() time.Time
Mono() time.Duration
AfterFunc(d time.Duration, f func()) Timer
}
Clock is the runner's source of time. Durations are measured on the monotonic clock; the wall clock is read only for the wait of a block build request and for records.
type CommitRequest ¶
CommitRequest is a decided block the write-ahead log handed to the application.
type Config ¶
type Config struct {
// ValidatorHistory overrides the finite history cap of DeferValidatorStart.
// Nil takes that explicitly selected profile's supported defaults.
ValidatorHistory *ValidatorHistoryLimits `json:",omitempty"`
// ReceiveCache registers a received key only after a successful direct
// core ACCEPT. Nil preserves legacy precheck caching. This staged runtime
// API requires explicit limits and currently refuses message journals
// until a compatible cache-policy profile is available.
ReceiveCache *transport.ReceiveLedgerOptions `json:",omitempty"`
// ReceiveFailureCache separately memoizes classified failures within one
// immutable publication. Nil preserves the original receive path.
ReceiveFailureCache *transport.ReceiveFailureOptions `json:",omitempty"`
// Core configures the consensus core. Core.Self must be the signer's
// address, or zero for a node that never signs.
Core consensus.Options
// ReplayWAL replays the write-ahead log on Start to restore the lock,
// the prepared certificate, the sent votes and the round of the height
// being decided (a restart-safety rule).
ReplayWAL bool
// Manual runs no goroutine: the caller drives the runner with Pump.
// The deterministic simulator uses it.
Manual bool
// InboxMessages and InboxBytes bound the receive queue of one peer
// (defaults 256 and 32 MiB).
InboxMessages int
InboxBytes int64
// OverflowLimit is the number of dropped messages of one peer within
// OverflowWindow above which the peer is disconnected (defaults 1000
// and 10 s), unless every peer overflows.
OverflowLimit int
OverflowWindow time.Duration
// Workers is the number of receive-check workers (default GOMAXPROCS).
Workers int
// NextHeight is an opt-in next-sequence receive lane. It requires WAL
// replay and explicit positive limits; nil preserves the original path.
NextHeight *NextHeightLimits
}
Config configures a Runner.
type CoreEnvObservation ¶
type CoreEnvObservation = journal.CoreEnvObservation
Source values use the separate durable diagnostic schema, never StepRec.
type CoreStepMoment ¶
type CoreStepMoment = journal.CoreStepMoment
type CoreStepObservation ¶
type CoreStepObservation = journal.CoreStepRec
type CoreStepObserver ¶
type CoreStepObserver interface{ ObserveCoreStep(CoreStepObservation) }
CoreStepObserver must return promptly. It runs on the consensus loop after execution and may retain/alter its detached value without changing the core, WAL or functional journal. A nil observer avoids serialization and hashing.
type Deps ¶
type Deps struct {
// ObservationSeals receives detached verified extra-seal entries after an
// ordinary core step. It is a separately enabled functional witness sink,
// independent of diagnostic metadata and replay authority.
ObservationSeals func(prepared, committed []consensus.ExtraSealEntry)
// ReceiveCacheRecorded observes a direct core ACCEPT retained in the
// received cache. It runs after the cache mutex is released and must not
// block. Nil changes no functional cache ownership or state.
ReceiveCacheRecorded func(code uint64)
// ReceiveRecoveryChecked observes an actual complete Recover invocation,
// never a cache hit. It grants no signer or acceptance authority.
ReceiveRecoveryChecked func(code codec.Code, failure consensus.RecoveryFailure)
// ReceiveFailureHit observes reuse of an earlier failed check, not a new
// Recover, signing operation, or core acceptance. Callbacks must not block.
ReceiveFailureHit func(code uint64, class transport.ReceiveFailureClass)
Chain Chain
App App
// Transport sends and deduplicates; nil sends nothing.
Transport *transport.Dedup
// Net is the transport under Transport, used to disconnect peers; nil
// disconnects nothing.
Net transport.Transport
// Signer signs own messages; nil never signs.
Signer privval.Signer
// WAL is the write-ahead log; nil keeps none.
WAL *wal.Log
Clock Clock
// Events receives the event records; nil drops them.
Events EventSink
// Journal receives step and outcome records; nil keeps no journal.
Journal journal.Writer
// CoreSteps observes detached actual live core execution for diagnostics.
// It is independent of Journal and does not enable unsupported replay.
CoreSteps CoreStepObserver
// TimerSources observes detached scheduler operations after its mutex is
// released. Nil preserves the original journal and timer behavior.
TimerSources TimerSourceObserver
// AppQueueSources observes detached application-input queue mutations.
AppQueueSources AppQueueSourceObserver
// FinalizeSources observes detached commit-queue and application-call boundaries.
FinalizeSources FinalizeSourceObserver
// InboxSources observes detached receive queue and slot boundaries.
InboxSources InboxSourceObserver
// ReceiveFailureDiagnostics explicitly binds one declared source writer to
// core, inbox, accepted receive and separate failure observations.
ReceiveFailureDiagnostics ReceiveFailureDiagnosticObserver
// InboxFailureSources separately observes reuse of earlier failed checks.
// It does not declare a diagnostic profile or enable functional replay.
InboxFailureSources InboxFailureSourceObserver
// InternalSources observes original internal queue output/selection boundaries.
InternalSources InternalSourceObserver
// SelectionSources observes original conditional next probes and actual dispatch.
SelectionSources SelectionSourceObserver
// ReceiveDispatchSources observes original lookup ownership and actual completion.
ReceiveDispatchSources ReceiveDispatchSourceObserver
// WALReadSources observes original restore reads with explicit copy bounds.
SignerCalls SignerCallObserver
WALReadSources WALReadSourceObserver
WALReadSourceLimits wal.ReadSourceLimits
// Synchronising reports whether the application is synchronising the
// chain; consensus messages that arrive while the engine is stopped are
// then dropped instead of closing the connection.
Synchronising func() bool
// Rand draws the choice between the timer queue and the peer queues and
// the order of receive checks in manual mode; nil uses math/rand/v2.
Rand func(n int) int
// Faults is called at the crash points (builds with wbft_faults).
Faults faultpoint.Handler
// Logger logs; nil discards.
Logger *slog.Logger
// ModuleLogger, when set, gives the loggers of the log modules: the
// runner writes a line of the logcat catalog for each event record,
// under consensus.round or consensus.msg, when that module's level
// lets it through (observe.md 7.1).
ModuleLogger func(logcat.Module) *slog.Logger
// Refused, when set, is called for every signature privval refuses,
// with the code of the message (sign floor skips included; the HEALTH
// record of a skip is written once per height).
Refused func(code uint64)
// ManualProcessing is an optional deterministic service model. It is
// accepted only in manual mode and never changes the production loop.
ManualProcessing *ManualProcessing
}
Deps are the runner's collaborators.
type FinalizeSourceObserver ¶
type FinalizeSourceObserver interface{ ObserveFinalizeSource(journal.FinalizeRec) }
FinalizeSourceObserver receives detached call-boundary metadata after all source/commit/application mutexes are released. Calls may arrive out of order; Sequence retains instrumentation order. Observers must return promptly. This hook does not execute or authenticate the application, or enable cache replay.
type HandshakeAction ¶
type HandshakeAction uint8
HandshakeAction is what the node does before it starts the engine.
const ( // StartNormal: start at the application head + 1. StartNormal HandshakeAction = iota // Refinalize: the log handed a decided block of height head + 1 to the // application, which did not store it; finalize it again and start at // head + 2. Refinalize // StartAfterRollback: the log is one height ahead without a commit // request (the application rolled back); start at head + 1 and warn. StartAfterRollback // Refuse: the log or the sign state is more than one height ahead of // the application. Refuse )
Handshake actions.
type InboxFailureSourceObserver ¶
type InboxFailureSourceObserver interface {
ObserveInboxFailureHit(journal.InboxFailureHitRec)
}
InboxFailureSourceObserver receives detached evidence of a reused failure, never an executed recovery or successful signer. Like the ordinary inbox observer, callbacks run outside the mutex and may arrive out of order. This observer alone does not enable a supported diagnostic/replay profile.
type InboxSourceObserver ¶
InboxSourceObserver receives detached metadata after the inbox mutex is released. Delivery may be reordered; Sequence retains actual mutex order. Observers must return promptly and never become functional cache authority.
type InternalSourceObserver ¶
type InternalSourceObserver interface{ ObserveInternalSource(journal.InternalRec) }
InternalSourceObserver runs on the original consensus loop after a queue operation. Input bytes are detached; observers must return promptly. Nil neither encodes inputs nor retains diagnostic payloads.
type ManualFinalizer ¶
type ManualFinalizer interface {
FinalizeBlockManual(b *types.Block, round types.Round, complete func(error))
}
ManualFinalizer is an optional application actor for manual runners. It returns control to the external scheduler while modelling a blocking commit call. Complete must be invoked by that scheduler once; Pump pauses core input processing and Stop/Close refuse to discard the outstanding call meanwhile. Non-manual runners continue to use App.FinalizeBlock synchronously.
type ManualProcessing ¶
type ManualProcessing struct {
Delay func(ProcessingWork) time.Duration
Schedule func(time.Duration, func())
}
ManualProcessing reserves work, occupies its worker for Delay, then executes the existing atomic operation at completion. This is a declared pre-dispatch service approximation, not a claim about an operation's internal milestones. Schedule must serialize callbacks with Pump on the manual driver's goroutine; it must not invoke a positive-delay callback inline. Engine controls wait behind reserved core work; receive checks have independent worker capacity.
type NextHeightLimits ¶
type NextHeightLimits struct {
Entries uint64 `json:"entries,string"`
Bytes uint64 `json:"bytes,string"`
PerPeer uint64 `json:"per_peer,string"`
PerSource uint64 `json:"per_source,string"`
}
NextHeightLimits enables a separate receive lane for exactly the next sequence. All limits are explicit and positive; nil Config.NextHeight keeps the original receive path. Bytes counts owned raw payloads throughout their lifetime, including queued, checking and core-dispatch work. It is not a process heap, CPU or WAL disk bound. One additional receive worker performs outer authentication, independently of the current-height workers.
type ProcessingWork ¶
ProcessingWork identifies a reserved receive check or core input. Code is nonzero only for a wire message. Kind uses the inputlog kind vocabulary.
type ReceiveDispatchSourceObserver ¶
type ReceiveDispatchSourceObserver interface {
ObserveReceiveDispatch(journal.ReceiveDispatchRec)
}
ReceiveDispatchSourceObserver sees detached preparation/completion values after the original checking mutex is released. It must return promptly and cannot grant cache ownership or turn selection into completed core execution.
type ReceiveFailureDiagnosticObserver ¶
type ReceiveFailureDiagnosticObserver interface {
CaptureRun() string
CoreDeclaration() journal.CoreOptions
CoreStepObserver
InboxSourceObserver
InboxFailureSourceObserver
transport.ReceiveTransitionObserver
transport.ReceiveFailureObserver
}
ReceiveFailureDiagnosticObserver coordinates one explicit source writer. It observes original execution and separately retained failures; it does not authorize accepted inputs, functional replay, or physical reconstruction. CoreDeclaration must describe the frozen writer header, not mutable defaults.
type ReplayInfo ¶
type ReplayInfo struct {
Records int // input records fed to the core
Stopped string // why the replay stopped early; "" if it did not
Replayed bool // the core continues from the replayed state
}
ReplayInfo reports the WAL replay of the last Start.
type RestoreSourceObserver ¶
type RestoreSourceObserver interface{ ObserveRestore(journal.RestoreRec) }
RestoreSourceObserver is an optional extension of WALReadSourceObserver. Its detached terminal record is published before the next actual live step.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner drives one consensus core.
func (*Runner) Close ¶
Close stops the engine if it runs and ends the runner's goroutines. A later Start returns ErrClosed: it would hand its work to the loop that is gone and wait for it forever (a node whose sync loop restarts the engine while the node stops). Close refuses Start before it stops the engine, so no Start slips in between.
func (*Runner) InboundBytes ¶
InboundBytes returns the payload bytes waiting in the per-peer receive queues (the wbft_peer_inbound_queue_bytes metric).
func (*Runner) LastReplay ¶
func (r *Runner) LastReplay() ReplayInfo
LastReplay reports the WAL replay of the last Start.
func (*Runner) NewHead ¶
NewHead reports that the application's head changed. Notifications that arrive before the core handles the previous one are coalesced: the core sees the latest head.
func (*Runner) NextHeightUsage ¶
NextHeightUsage reports reserved raw payload entries and bytes in all stages.
func (*Runner) PeerConnected ¶
PeerConnected reports that a validator peer (re)connected.
func (*Runner) ProcessingPending ¶
ProcessingPending reports core work reserved by the manual driver. Like Pump and Vars, callers must serialize this with the manual scheduler.
func (*Runner) Pump ¶
Pump drives a manual runner until nothing is left to do: receive checks, core inputs and commits. It reports whether anything was done.
func (*Runner) Receiver ¶
Receiver returns the sink the transport adapter offers received messages to.
func (*Runner) Start ¶
Start starts the engine at height from, which must be the application's head + 1. With ReplayWAL it first replays the write-ahead log records of that height (restoring the lock, the prepared certificate, the sent votes and the round), re-arms the live timers and sends the last own messages of the current view again.
func (*Runner) Stop ¶
Stop stops the engine: the core state is discarded, armed timers and queued inputs are dropped. A later Start replays the write-ahead log.
type SelectionSourceObserver ¶
type SelectionSourceObserver interface{ ObserveInputSelection(journal.SelectionRec) }
SelectionSourceObserver sees detached original conditional probes and the selected input after all lane mutexes are released. It must return promptly. This hook neither merges those mutexes nor draws additional randomness.
type SignerCallObserver ¶
type SignerCallObserver interface{ ObserveSignerCall(journal.SignerCallRec) }
SignerCallObserver sees detached runner-to-signer/WAL call results, after the original operation. It does not enable functional replay or signing itself.
type SourceFinalizer ¶
type SourceFinalizer interface {
FinalizeBlockWithSource(*types.Block, types.Round, app.FinalizeSource) error
}
SourceFinalizer is an optional application bridge for original task ownership. The ordinary App and manual completion contracts remain unchanged. A bridge must preserve the original request and context, including cancellation.
type Timer ¶
type Timer interface {
// Stop prevents the timer from firing; it reports false if the timer
// already fired or was stopped.
Stop() bool
}
Timer is a timer of a Clock.
type TimerQueue ¶
type TimerQueue struct {
// contains filtered or unexported fields
}
TimerQueue uses the runner's timer reservation and expiry implementation. It preserves full timeout context and already-queued expiries after cancel. Arm and cancel calls are serialized by the execution owner; clock expiry callbacks may run concurrently with that owner.
func NewTimerQueue ¶
func NewTimerQueue(clock Clock) *TimerQueue
func (*TimerQueue) Cancel ¶
func (q *TimerQueue) Cancel(kinds []consensus.TimerKind) []consensus.ArmTimer
func (*TimerQueue) Clear ¶
func (q *TimerQueue) Clear()
func (*TimerQueue) Pending ¶
func (q *TimerQueue) Pending() bool
func (*TimerQueue) PopRecorded ¶
PopRecorded reproduces an observed dequeue order among already-queued expiries. Real timer callbacks can queue two overdue timers in a different order from their deadlines. This only consumes an existing full-context expiry; it never fires a timer, changes a deadline or manufactures input. Experiments without recorded ordering must use Pop instead.
func (*TimerQueue) Status ¶
func (q *TimerQueue) Status() TimerQueueStatus
type TimerQueueStatus ¶
type TimerQueueStatus struct{ Armed, Queued int }
TimerQueueStatus describes the finite execution horizon without advancing it.
type TimerSourceObserver ¶
TimerSourceObserver receives detached scheduler mutations independently of the functional journal. It runs after the timer mutex is released and must return promptly. Clock callbacks can deliver concurrently and out of order; Sequence records the original mutex order. Retaining or altering the value never changes timer ownership, input bytes or a legacy journal record. Zero Sequence marks exhausted observation metadata and is never reused. This optional diagnostic hook does not enable functional runtime replay.
type ValidatorHistoryLimits ¶
type ValidatorHistoryLimits struct {
Bytes uint64 `json:"bytes,string"`
Records uint64 `json:"records,string"`
}
ValidatorHistoryLimits bounds retained functional frames of an explicitly selected validator-start profile. It is not a heap, CPU or whole-disk cap. Reaching either bound halts progress while preserving the original history.
func DefaultValidatorHistoryLimits ¶
func DefaultValidatorHistoryLimits() ValidatorHistoryLimits
DefaultValidatorHistoryLimits returns the finite experimental history cap. Normal progress also consumes it; it is not an uptime recommendation.
func (ValidatorHistoryLimits) Validate ¶
func (limits ValidatorHistoryLimits) Validate() error
Validate checks explicit limits without changing them or selecting a profile.
type WALReadSourceObserver ¶
type WALReadSourceObserver interface{ ObserveWALRead(journal.WALReadRec) }
WALReadSourceObserver sees detached original restore reads. It must return promptly. Reading evidence does not enable functional cache replay or prove an independently executed restored prefix, signer state or application state.
type WALState ¶
type WALState struct {
// LastEnd is the height of the last end-of-height record: the highest
// height the core left after a new head; nil when there is none.
LastEnd *types.Height
// Commits are the commit requests in the log, in order, including those
// of heights the core has already left.
Commits []CommitRequest
// contains filtered or unexported fields
}
WALState is what the start-up handshake reads from the write-ahead log.
func InspectWAL ¶
InspectWAL reads the write-ahead log in dir for the start-up handshake.
func InspectWALForConfig ¶
InspectWALForConfig validates the retry profile before returning handshake actions. The caller must use the same configuration to start the runner; application refinalization must not precede this validation.
Source Files
¶
- app_queue_observer.go
- core_step_observer.go
- doc.go
- engine.go
- env.go
- finalize_source_observer.go
- inbox.go
- inbox_source_observer.go
- internal_source_observer.go
- lockorder.go
- loop.go
- manual_processing.go
- next_height.go
- prior_boundary.go
- receive_cache.go
- receive_context.go
- receive_dispatch_observer.go
- receive_failure.go
- receive_failure_diagnostics.go
- receive_failure_observer.go
- restore_observer.go
- retry_boundary.go
- round_change_retry.go
- runner.go
- scheduler.go
- selection_source_observer.go
- signer_observer.go
- snapshot_publication.go
- timer_source_observer.go
- validator_history.go
- validator_history_config.go
- validator_history_profile.go
- validator_history_replay.go
- validator_history_runtime.go
- validator_history_wal.go
- validator_history_writer.go
- validator_start.go
- wal_read_observer.go
- walrec.go