rcl

package
v0.0.0-...-e581afd Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: ISC Imports: 18 Imported by: 0

Documentation

Overview

Package rcl implements the Ripple Consensus Ledger algorithm. This is the default consensus algorithm used by the XRP Ledger.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsCurrent

func IsCurrent(now, signTime, seenTime time.Time) bool

IsCurrent reports whether a validation's sign-time and seen-time are close enough to now to be considered "current" in rippled's sense. Exact mirror of Validations.h:148-166 isCurrent:

signTime > (now - validationCURRENT_EARLY) &&
signTime < (now + validationCURRENT_WALL) &&
(seenTime == 0 || seenTime < (now + validationCURRENT_LOCAL))

Note on constant names: rippled's EARLY bounds the PAST on signTime (not "early" in the usual sense of future-side); WALL bounds the FUTURE on signTime. The prior go-xrpl implementation had the two swapped and used a past-bound on seenTime — three wire-parity bugs that would desync freshness decisions between Go and rippled peers under clock skew.

`now` is the network-adjusted time from the adaptor so the freshness window honors the close-offset consensus has converged on.

Types

type AncestryProvider

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

AncestryProvider satisfies LedgerAncestryProvider. It materialises a ledger's ancestor slice once per LedgerID and caches it in an LRU, avoiding O(depth²) ParentHash walks across the trie's many Ancestor(s) calls. Partial chains carry a retry marker and are rechecked before reuse so acquisition can repair them in place.

func NewAncestryProvider

func NewAncestryProvider(svc *service.Service) *AncestryProvider

NewAncestryProvider wraps the ledger service. A nil svc returns a disabled provider that always reports (nil, false).

func (*AncestryProvider) LedgerByID

LedgerByID implements LedgerAncestryProvider.

type Config

type Config struct {
	Timing     consensus.Timing
	Thresholds consensus.Thresholds

	// Clock overrides the wall-clock source for duration metrics. Nil means
	// time.Now; csf injects a virtual clock for deterministic runs.
	Clock func() time.Time

	// ManualTick disables the heartbeat goroutine; the caller drives ticks
	// via TimerEntry. Used by csf.
	ManualTick bool
}

Config holds RCL engine configuration.

func DefaultConfig

func DefaultConfig() Config

type Engine

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

Engine implements the RCL consensus algorithm.

func NewEngine

func NewEngine(adaptor consensus.Adaptor, config Config) *Engine

func (*Engine) BuildingLedgerSeq

func (e *Engine) BuildingLedgerSeq() uint32

BuildingLedgerSeq returns the ledger sequence being built after the open phase, or zero when no ledger build is active.

func (*Engine) CanAcceptLedger

func (e *Engine) CanAcceptLedger(id consensus.LedgerID) (bool, error)

func (*Engine) CurrentRound

func (e *Engine) CurrentRound() (consensus.RoundID, bool)

CurrentRound returns the selected round without exposing mutable engine state.

func (*Engine) Done

func (e *Engine) Done() <-chan error

func (*Engine) GetJSON

func (e *Engine) GetJSON(full bool) map[string]any

GetJSON returns the consensus-round state as a JSON map. Backs the consensus_info RPC (always full).

func (*Engine) GetLastCloseInfo

func (e *Engine) GetLastCloseInfo() (proposers int, convergeTime time.Duration)

GetLastCloseInfo returns the proposer count and convergence time for server_info.last_close: the last accepted round's snapshot, or — before any round is accepted — a freshness-bounded count of recent trusted proposers so a cold start doesn't report 0 while peers propose.

func (*Engine) IsProposing

func (e *Engine) IsProposing() bool

IsProposing reports whether we're actively proposing (lock-free atomic read; called on the RPC hot path under ledger.service.s.mu — see modeAtomic).

func (*Engine) IsValidating

func (e *Engine) IsValidating() bool

IsValidating reports whether the node is eligible to issue validations in this round. Sync state only determines whether those validations are full or partial. Takes no engine lock, safe on the server_info hot path.

func (*Engine) Mode

func (e *Engine) Mode() consensus.Mode

Mode returns the current consensus mode via the lock-free atomic mirror (see modeAtomic).

func (*Engine) OnLedgerAcquireFailed

func (e *Engine) OnLedgerAcquireFailed(id consensus.LedgerID)

OnLedgerAcquireFailed reports that an acquisition was invalidated by a topology change. If pinned in wrongLedger on id, un-pin so the router can re-resolve and retry without resuming consensus on the stale LCL.

func (*Engine) OnProposal

func (e *Engine) OnProposal(proposal *consensus.Proposal, originPeer uint64) error

OnProposal handles an incoming proposal. originPeer (0 = self) is excluded from the RelayProposal gossip forward.

func (*Engine) OnTxSet

func (e *Engine) OnTxSet(id consensus.TxSetID, txs [][]byte) error

OnTxSet handles receiving a transaction set we requested.

func (*Engine) OnValidation

func (e *Engine) OnValidation(validation *consensus.Validation, originPeer uint64) error

OnValidation is the synchronous compatibility path used by direct engine callers. The network router verifies validations on its worker queues and calls ProcessVerifiedValidation instead.

func (*Engine) Phase

func (e *Engine) Phase() consensus.Phase

func (*Engine) ProcessVerifiedValidation

func (e *Engine) ProcessVerifiedValidation(
	validation *consensus.Validation,
	origin consensus.ValidationOrigin,
) (consensus.ValidationDisposition, error)

ProcessVerifiedValidation applies a signature-verified validation to local consensus state. It deliberately performs no network I/O; the router acts on the returned disposition after this method releases the engine lock.

func (*Engine) RestartRound

func (e *Engine) RestartRound(proposing bool) error

RestartRound starts a fresh round from the adaptor's current LCL after re-evaluating the trusted-validation preference. It is used by externally driven consensus loops that stop their timer between bounded runs.

func (*Engine) SetArchive

func (e *Engine) SetArchive(a ValidationArchive)

SetArchive wires (or, with nil, detaches) the validation archive. Detach clears the onStale callback so the archive can be Close()d without a use-after-close send. Safe before or after Start; callers must not replace the owned archive concurrently with Start or Stop.

func (*Engine) SetInMemoryLedgers

func (e *Engine) SetInMemoryLedgers(n uint32)

SetInMemoryLedgers sets how many fully-validated ledgers of validation history the tracker keeps; older validations are evicted to the archive. Zero disables auto-eviction.

func (*Engine) SetLedgerAncestryProvider

func (e *Engine) SetLedgerAncestryProvider(p LedgerAncestryProvider)

SetLedgerAncestryProvider installs the trie's ancestry provider. Safe before or after Start; nil reverts to flat-count support.

func (*Engine) SetStallPing

func (e *Engine) SetStallPing(ping func())

SetStallPing installs the stall watchdog's heartbeat callback, invoked once per run-loop iteration. Nil disables. Must be cheap and non-blocking — it runs inside the consensus loop.

func (*Engine) Start

func (e *Engine) Start(ctx context.Context) error

func (*Engine) StartRound

func (e *Engine) StartRound(round consensus.RoundID, proposing bool) error

func (*Engine) Stop

func (e *Engine) Stop() error

Stop shuts down the engine. A wired archive is drained and committed before return, and any terminal durability failure is returned.

func (*Engine) Subscribe

func (e *Engine) Subscribe(sub consensus.EventSubscriber)

func (*Engine) TimerEntry

func (e *Engine) TimerEntry()

TimerEntry runs one heartbeat dispatch synchronously. For ManualTick mode: an external driver (csf) advances the state machine.

func (*Engine) TrySwitchToLedger

func (e *Engine) TrySwitchToLedger(id consensus.LedgerID) (consensus.LedgerSwitchResult, error)

TrySwitchToLedger synchronously evaluates and adopts a locally-held ledger selected by consensus recovery, validation, or the current network view.

type LedgerAncestryProvider

type LedgerAncestryProvider interface {
	LedgerByID(id consensus.LedgerID) (ledgertrie.Ledger, bool)
}

LedgerAncestryProvider resolves a LedgerID to a ledgertrie.Ledger carrying its ancestry. Returns (nil, false) when the ledger's history is not locally known.

type ValidationArchive

type ValidationArchive interface {
	OnStale(*consensus.Validation)
	NoteFullyValidated(seq uint32)
	Close(ctx context.Context) error
}

ValidationArchive is the archive API subset the engine consumes, decoupling rcl from the concrete archive type.

type ValidationTracker

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

ValidationTracker tracks validations and determines ledger finality.

func NewValidationTracker

func NewValidationTracker(quorum int) *ValidationTracker

NewValidationTracker creates a new validation tracker. The tracker's freshness clock defaults to time.Now; wire it to adaptor.Now via SetNow before use so isCurrent honors the network close-time offset.

func (*ValidationTracker) Add

func (vt *ValidationTracker) Add(validation *consensus.Validation) bool

Add adds a validation to the tracker. Returns true if this is a new validation (not duplicate).

func (*ValidationTracker) ExpireOld

func (vt *ValidationTracker) ExpireOld(minSeq uint32)

ExpireOld drops validations below minSeq from every index and fires onStale outside the mutex. Trie tips for dropped validators are also removed so phantom branchSupport doesn't linger on stale ancestors.

A set created or read within validationSetExpires is retained even below the sequence floor — rippled's access-age expiry (validationSET_EXPIRES with byLedger touch) — so hot ledgers stay queryable for RPC and late peers. Memory stays bounded: Add rejects below the engine's minSeq gate, so a below-floor set cannot reheat from the network, and it drops on the first ExpireOld after going cold. The seq floor coarsely protects the recent (negative-UNL voting) window, but is anchored at the validated tip; SetSeqToKeep pins the exact flag-ledger window so a fast-advancing tip can't outrun the vote's read.

func (*ValidationTracker) GetJSONTrie

func (vt *ValidationTracker) GetJSONTrie() map[string]any

GetJSONTrie returns a JSON-serializable snapshot of the ancestry trie's support state for diagnosing preferred-ledger divergence. Returns nil when the trie is disabled (no ancestry provider wired) or a serialization panic is trapped. Guarded by vt.mu.

func (*ValidationTracker) GetPreferred

func (vt *ValidationTracker) GetPreferred(largestIssued uint32) (consensus.LedgerID, uint32, bool)

GetPreferred returns the network-preferred ledger ID and sequence as decided by the ancestry trie. Parked validations whose ledger has been acquired since the last poll are replayed into the trie first, so the trie decides unconditionally (rippled getPreferred via withTrie, Validations.h:849-879). When the trie yields no tip at all, falls back to the majority over still-acquiring ledgers; ok is false only when the trie is unwired or both sources are empty. largestIssued is the highest sequence this node has validated; it seeds uncommitted support from earlier seqs.

func (*ValidationTracker) GetTrustedFullValidations

func (vt *ValidationTracker) GetTrustedFullValidations(
	ledgerID consensus.LedgerID,
	ledgerSeq uint32,
) []*consensus.Validation

GetTrustedFullValidations returns deep-cloned trusted full validations for exactly (ledgerID, ledgerSeq). The sequence filter is deliberately enforced inside the tracker so every protocol voting caller sees the same evidence.

func (*ValidationTracker) PreferredFromValidations

func (vt *ValidationTracker) PreferredFromValidations(minSeq uint32) (consensus.LedgerID, uint32, bool)

PreferredFromValidations returns the most-popular trusted-validator tip at seq >= minSeq, ignoring local ancestry — the no-trie fallback for GetPreferred during deep catch-up. negUNL validators are counted, matching the trie-backed GetPreferred (rippled steers on trusted() alone). Ties resolved by higher seq then lexicographic ID.

func (*ValidationTracker) RecheckFullyValidated

func (vt *ValidationTracker) RecheckFullyValidated(
	ledgerID consensus.LedgerID,
	seq uint32,
) ([]*consensus.Validation, int, bool)

RecheckFullyValidated returns the validations that currently count toward finality for ledgerID together with the quorum from the same tracker state. When the set no longer reaches quorum it removes the prior firing marker before unlocking, so a concurrent or later validation can notify again.

func (*ValidationTracker) SetFullyValidatedCallback

func (vt *ValidationTracker) SetFullyValidatedCallback(fn func(ledgerID consensus.LedgerID, ledgerSeq uint32))

SetFullyValidatedCallback sets the callback for when a ledger is fully validated. Fired once per ledger the first time trusted-validation count crosses the quorum threshold. Seq is passed alongside the ledger ID so the callee can look up or stamp the ledger without a secondary map lookup.

func (*ValidationTracker) SetNow

func (vt *ValidationTracker) SetNow(fn func() time.Time)

SetNow replaces the clock used by isCurrent for freshness checks. Production use: wire to adaptor.Now so the freshness window honors the network-adjusted close offset (matches rippled's app_.timeKeeper().closeTime() usage in Validations.h). Tests pass fixed-time functions to get deterministic accept/reject behavior. Passing nil resets to time.Now.

func (*ValidationTracker) SetOnStale

func (vt *ValidationTracker) SetOnStale(fn func(*consensus.Validation))

SetOnStale installs a callback invoked once per validation dropped by ExpireOld. Mirrors rippled's Validations<Adaptor>::onStale contract — consumed by the on-disk validation archive to persist stale validations before they leave memory. Callback runs outside the tracker's mutex so it may do blocking work (channel send to a batched writer); callers must ensure it does not call back into the tracker. Pass nil to disable.

func (*ValidationTracker) SetSeqToKeep

func (vt *ValidationTracker) SetSeqToKeep(low, high uint32)

SetSeqToKeep pins validations in [low, high) so ExpireOld will not drop them, even once the retention floor advances past them. The negative-UNL vote calls this before scanning the flag-ledger window so a fast-advancing tip can't prune its low end mid-scan. Unlike the seq floor — which is anchored at the validated tip — the pin is anchored at the flag ledger, so it holds regardless of how far the tip has raced ahead or how small the configured retention is. Mirrors rippled's setSeqToKeep; a range with high <= low clears the pin.

func (*ValidationTracker) SetTrustedAndQuorum

func (vt *ValidationTracker) SetTrustedAndQuorum(nodes []consensus.NodeID, quorum int)

SetTrustedAndQuorum updates the trusted set and its quorum atomically.

Jump to

Keyboard shortcuts

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