snapshot

package
v0.70.13 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package snapshot provides stake snapshot management for Ouroboros Praos leader election. It captures stake distribution at epoch boundaries and maintains the Mark/Set/Go snapshot rotation model.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HandleGenesisSnapshotError added in v0.66.0

func HandleGenesisSnapshotError(
	blockProducer bool,
	logger *slog.Logger,
	err error,
) error

HandleGenesisSnapshotError applies the standard policy for a failed genesis (epoch 0) mark-snapshot capture: it is fatal for a block producer (which cannot elect leaders without the snapshot) and a warning for a relay or replay-only node (which does not perform leader election). A nil err returns nil, so callers can forward the CaptureGenesisSnapshot result directly.

Types

type Calculator

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

Calculator calculates stake distribution from the current ledger state.

func NewCalculator

func NewCalculator(db *database.Database) *Calculator

NewCalculator creates a new stake calculator.

func (*Calculator) CalculateStakeDistribution

func (c *Calculator) CalculateStakeDistribution(
	ctx context.Context,
	slot uint64,
) (dist *StakeDistribution, err error)

CalculateStakeDistribution calculates the stake distribution at a given slot. Pool selection and stake totals are both slot-aware. Reward input rows are only available from the live epoch-boundary path, so this public historical query returns pool totals and delegator counts without per-credential inputs.

func (*Calculator) CalculateStakeDistributionInTxn added in v0.70.12

func (c *Calculator) CalculateStakeDistributionInTxn(
	ctx context.Context,
	txn *database.Txn,
	slot uint64,
) (*StakeDistribution, error)

CalculateStakeDistributionInTxn is CalculateStakeDistribution for a caller that already holds an open transaction and needs this calculation to observe the exact same database snapshot as the rest of its own work, rather than whatever is current when a fresh transaction is opened here. Same plain "stake at slot" semantics: no epoch-boundary reward inputs, no CIP-0163 inactivity gate.

type Manager

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

Manager handles stake snapshot capture and rotation at epoch boundaries. It subscribes to EpochTransitionEvents and orchestrates the snapshot lifecycle according to the Ouroboros Praos specification.

func NewManager

func NewManager(
	db *database.Database,
	eventBus *event.EventBus,
	logger *slog.Logger,
) *Manager

NewManager creates a new snapshot manager.

func (*Manager) CaptureEpochBoundarySnapshot added in v0.66.0

func (m *Manager) CaptureEpochBoundarySnapshot(
	ctx context.Context,
	txn *database.Txn,
	evt event.EpochTransitionEvent,
) error

CaptureEpochBoundarySnapshot persists the Mark snapshot using the caller's open epoch-boundary transaction. Ledger invokes this hook last in the rollover ordering, after POOLREAP, governance enactment, donation accounting, and the new epoch row have been applied, because the row it writes needs the new epoch's nonce and the post-enactment protocol version.

The stake distribution it persists is the one ComputeEpochBoundarySnapshot read at the SNAP point earlier in this same transaction. When no matching SNAP-point distribution exists — the compute hook is not installed, or its read failed — it reconstructs the exact boundary with slot-aware reward semantics. It never falls back to the live aggregate, whose post-SNAP credits would corrupt the Mark snapshot.

func (*Manager) CaptureGenesisSnapshot

func (m *Manager) CaptureGenesisSnapshot(ctx context.Context) error

CaptureGenesisSnapshot captures the initial stake distribution as mark snapshots. For a fresh sync this seeds epoch 0. After a Mithril bootstrap the node starts at a much later epoch, so the method also seeds the recent historical window (epochs N, N-1, N-2).

func (*Manager) ComputeEpochBoundarySnapshot added in v0.69.0

func (m *Manager) ComputeEpochBoundarySnapshot(
	ctx context.Context,
	txn *database.Txn,
	evt event.EpochTransitionEvent,
) error

ComputeEpochBoundarySnapshot computes the Mark snapshot's stake distribution at the SNAP point of the caller's open epoch-boundary transaction and holds it for the matching CaptureEpochBoundarySnapshot call later in the same rollover. It writes nothing.

cardano-ledger runs SNAP before POOLREAP and before governance enactment, so a mark snapshot must reflect stake as of the boundary with only the pre-SNAP boundary rules applied (applyRUpd and MIR). dingo's authoritative capture reads the live reward aggregate, which has no slot predicate, and used to run at the very end of the rollover — so the mark snapshot also absorbed every credit cardano-ledger applies after SNAP: POOLREAP deposit refunds, enacted treasury withdrawals and proposal-deposit refunds, all recorded at the boundary slot.

Splitting the capture is what fixes that without subtracting those credits back out: the stake read happens at the reference SNAP point (after applyStakeRewards and applyMIRCerts, before applyPoolRetirements), while the write stays at the end where the new epoch row, its nonce and the post-enactment protocol version exist. Both phases run in the one rollover transaction, so a rollback or replay of the boundary re-executes the same deterministic read and reproduces the same snapshot.

func (*Manager) DelegatorInactivityConfig added in v0.69.0

func (m *Manager) DelegatorInactivityConfig() (enabled bool, period uint64)

DelegatorInactivityConfig returns the CIP-0163 reward-account inactivity gate and window currently configured on this manager, as last set by SetDelegatorInactivity (mirrors LedgerState.DelegatorInactivityConfig's identical pattern) — used to verify a live restore/truncate's rebuilt snapshot manager actually picked up the operator's configured value.

func (*Manager) RewardAccountOutputRetentionUnbounded added in v0.70.9

func (m *Manager) RewardAccountOutputRetentionUnbounded() bool

RewardAccountOutputRetentionUnbounded reports the current value set by SetRewardAccountOutputRetentionUnbounded, for tests and diagnostics.

func (*Manager) SetDelegatorInactivity added in v0.67.0

func (m *Manager) SetDelegatorInactivity(
	enabled bool,
	inactivityPeriod uint64,
) error

SetDelegatorInactivity mirrors the CIP-0163 reward-account inactivity gate and inactivity window from LedgerStateConfig into the snapshot manager. It must be called before snapshot capture begins (i.e. before CaptureGenesisSnapshot/Start), matching how node.go and load.go configure the ledger. Once capture can begin, the configuration is permanently locked. Default (unset) is gate off, which keeps snapshot capture byte-identical to the pre-CIP behavior.

func (*Manager) SetPoolSnapshotRetentionGuard added in v0.70.6

func (m *Manager) SetPoolSnapshotRetentionGuard(g PoolSnapshotRetentionGuard)

SetPoolSnapshotRetentionGuard installs the guard cleanupOldSnapshots uses to prune pool snapshots atomically with the deferred-header retention floor, so a snapshot a queued/deferred header still needs is retained beyond the default currentEpoch-3 window until the header resolves (issue #3727). Pass nil to clear it. It should be set before Start; a nil guard (the default) preserves the original pruning behaviour exactly.

func (*Manager) SetPromRegistry added in v0.37.0

func (m *Manager) SetPromRegistry(reg prometheus.Registerer)

SetPromRegistry enables snapshot manager metrics.

func (*Manager) SetRewardAccountOutputRetentionUnbounded added in v0.70.9

func (m *Manager) SetRewardAccountOutputRetentionUnbounded(enabled bool)

SetRewardAccountOutputRetentionUnbounded mirrors whether the in-process Koios parity observer (dingo #3098) is enabled into the snapshot manager's cleanup path. When enabled is true, cleanupOldSnapshots retains reward_account_output without bound in CORE storage mode, matching API storage mode's existing unbounded retention (dingo #1875).

The Koios parity observer validates each closed epoch against Koios only after fetching and comparing over the network, which can fall arbitrarily far behind chain progression during a from-genesis or catch-up sync — unlike the fixed, small rotation/reward-replay window CORE mode's cleanupOldSnapshots otherwise prunes to. Without this, reward_account_output for an epoch is routinely pruned before the observer ever reads it, and the koios-parity check for that epoch fails permanently with a reward_account_output row that genuinely no longer exists (dingo #4188).

Not consensus-affecting — it only widens local historical retention — so unlike SetDelegatorInactivity this is not gated by configurationLocked and may be called or changed at any time, including after Start.

func (*Manager) Start

func (m *Manager) Start(ctx context.Context) error

Start begins listening for epoch transitions and capturing snapshots. The provided context is used as the parent for the manager's internal context; cancelling it will stop all snapshot operations.

func (*Manager) Stop

func (m *Manager) Stop() error

Stop stops the snapshot manager.

type PoolSnapshotRetentionGuard added in v0.70.6

type PoolSnapshotRetentionGuard func(
	defaultBefore uint64,
	minBefore uint64,
	prune func(before uint64) error,
) error

PoolSnapshotRetentionGuard runs the caller's pool-snapshot prune with the deferred-header set held stable, returning the boundary to prune below. defaultBefore is cleanup's default currentEpoch-3 pool boundary; the guard lowers it to the retention floor a queued/deferred header still requires (or to 0 = retain everything while any deferred slot is unmappable), then clamps it UP to minBefore, a hard backstop bounding how many historical epochs the pin can ever hold. All of this — plus eviction of deferred headers the apply cursor has passed — happens under one lock so admission cannot interleave (issue #3727). prune must delete AND commit before returning.

type StakeDistribution

type StakeDistribution struct {
	StakeInputs    []StakeInput
	Slot           uint64                         // Slot at which distribution was captured
	PoolStakes     map[lcommon.PoolKeyHash]uint64 // pool key hash -> total stake
	DelegatorCount map[lcommon.PoolKeyHash]uint64 // pool key hash -> delegator count
	TotalStake     uint64                         // Sum of all pool stakes
	TotalPools     uint64                         // Number of active pools
}

StakeDistribution represents the stake distribution at a point in time. Uses ledger types for interoperability between database and ledger layers.

type StakeInput added in v0.66.0

type StakeInput struct {
	PoolKeyHash   []byte
	CredentialTag uint8
	StakingKey    []byte
	Stake         uint64
	Registered    bool
}

StakeInput is a per-stake-credential snapshot input owned by the snapshot package. Persistence code converts it to database reward-state rows.

Jump to

Keyboard shortcuts

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