ecfinality

package
v1.9.2 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: Apache-2.0, MIT Imports: 6 Imported by: 0

Documentation

Overview

Package ecfinality implements the FRC-0089 EC finality calculator (#96).

The calculator computes an upper bound on the probability that a confirmed tipset could be reorganized out of the canonical chain by an adversarial fork, using OBSERVED chain data (block counts per epoch). Under healthy network conditions (~5 blocks/epoch), the 2^-30 finality guarantee (roughly one-in-a-billion chance of reorg) is typically achieved within ~30 epochs (~15 minutes), compared to the static 900-epoch (~7.5 hour) EC finality assumption which is based on worst-case conditions.

Why Lantern wants this: F3 gives BLS-verified finality certificates, but where F3 finality is not available at the tip (or not live on a network), the fallback is the static 900-epoch worst case. FRC-0089 gives a principled, locally-computed finality depth with no external trust: it quantifies how deep a tip-range eclipse could plausibly reorg us. It feeds the head-trust story (#100), retention pruning depth (#92), and the v2 "finalized"/"safe" selector semantics (#99: finalized = max(ec-finalized, F3-finalized)).

This is a port of lotus chain/ecfinality (itself a Go port of the FRC-0089 Python reference), adapted to Lantern's header store. The math is kept verbatim so the lotus/Python test vectors apply unchanged.

Reference: https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0089.md Python reference: https://github.com/consensus-shipyard/ec-finality-calculator

Index

Constants

View Source
const (
	// BisectLow and BisectHigh define the search range for the bisect
	// algorithm that finds the epoch depth at which the finality guarantee
	// is met. A low bound of 3 avoids evaluating trivially shallow depths;
	// a high bound of 450 accommodates degraded chains that take longer to
	// finalize.
	BisectLow  = 3
	BisectHigh = 450

	// DefaultBlocksPerEpoch is the Filecoin expected block production rate
	// (mainnet and calibration both target 5).
	DefaultBlocksPerEpoch = 5.0

	// DefaultByzantineFraction is the standard Filecoin security assumption
	// for adversarial mining power.
	DefaultByzantineFraction = 0.3

	// DefaultSafetyExponent is the target reorg probability as a power of 2.
	// 2^-30 (~one-in-a-billion) is the standard Filecoin finality guarantee.
	DefaultSafetyExponent = -30
)
View Source
const MinWindow = 30

MinWindow is the minimum number of epochs of observed history required before the calculator reports a threshold at all. Below this the L distribution has too little data to be meaningful, so Status reports depth -1 (not computable) rather than an over-confident number. 30 epochs (~15 min of history) is the depth at which a healthy chain typically meets 2^-30 in the first place.

Variables

This section is empty.

Functions

func CalcValidatorProb

func CalcValidatorProb(chain []int, finality int, blocksPerEpoch float64, byzantineFraction float64, currentEpoch int, targetEpoch int) float64

CalcValidatorProb computes the upper-bound probability that a confirmed tipset could be reorganized out of the canonical chain. Verbatim port of the FRC-0089 reference (finality_calc_validator.py) via lotus.

Parameters:

  • chain: block counts per epoch (index 0 = earliest epoch; null rounds appear as 0 entries)
  • finality: lookback depth for the L distribution (900 on mainnet)
  • blocksPerEpoch: expected blocks per epoch (5 for mainnet)
  • byzantineFraction: upper bound on adversarial power fraction (e.g. 0.3)
  • currentEpoch: index into chain for the current epoch
  • targetEpoch: index into chain for the epoch being evaluated

func FindThresholdDepth

func FindThresholdDepth(chain []int, finality int, blocksPerEpoch float64, byzantineFraction float64, guarantee float64) int

FindThresholdDepth performs a bisect search to find the shallowest depth at which the reorg probability drops below the given guarantee. Returns -1 if the guarantee is not met within the search range.

Types

type Cache

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

Cache computes the FRC-0089 threshold for the current head and caches the result, recomputing only when the head changes. Computation is on-demand (dashboard/stats pulls), NOT on every head change: the cost is dominated by the Skellam PMF loops, so an idle node pays nothing. Safe for concurrent use.

func NewCache

func NewCache(src HeaderSource, finality int) *Cache

NewCache builds a Cache over the given header source. finality is the lookback depth for the L distribution (900 for both mainnet and calibration).

func (*Cache) Stats

func (c *Cache) Stats() (calls, recomputes uint64)

Stats returns (lifetime Status calls, lifetime recomputes).

func (*Cache) Status

func (c *Cache) Status() (*Status, error)

Status returns the EC finality state for the current head, cached per head tipset key. Errors are not cached; a transient store failure allows retry on the next call.

type HeaderSource

type HeaderSource interface {
	Head() *ltypes.TipSet
	GetTipSet(tsk ltypes.TipSetKey) (*ltypes.TipSet, error)
}

HeaderSource is the minimal store surface the cache walks. Lantern's chain/header/store.Store satisfies it.

type Status

type Status struct {
	// ThresholdDepth is the shallowest epoch depth at which the reorg
	// probability drops below 2^-30. -1 when not met or not computable
	// (degraded chain, or observed window < MinWindow).
	ThresholdDepth int
	// FinalizedEpoch is head.Height() - ThresholdDepth, or -1 when
	// ThresholdDepth is -1.
	FinalizedEpoch abi.ChainEpoch
	// HeadEpoch is the head height the computation ran against.
	HeadEpoch abi.ChainEpoch
	// WindowEpochs is how many epochs of observed history the calculator
	// actually had (honesty signal: a freshly-booted node has only its
	// anchor depth until #91/#92 give it a deeper tail).
	WindowEpochs int
}

Status is the resolved EC finality state for a given head.

Directories

Path Synopsis
tools
refgen command
refgen regenerates the reference reorg-probability vectors used by TestCalcValidatorProb_PythonReference against the calculator's current implementation on the CPU it runs on.
refgen regenerates the reference reorg-probability vectors used by TestCalcValidatorProb_PythonReference against the calculator's current implementation on the CPU it runs on.

Jump to

Keyboard shortcuts

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