hardfork

package
v0.61.2 Latest Latest
Warning

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

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

Documentation

Overview

Package hardfork provides the HardFork Combinator primitives used by the ledger to reason about multi-era chain time, epoch, and slot conversions.

This package mirrors the structure of Ouroboros.Consensus.HardFork.History and Ouroboros.Consensus.HardFork.Combinator in the Haskell consensus implementation, but is deliberately decoupled from any specific era or genesis configuration. Callers build an EraParams + Shape + Summary from their own sources and then use the conversion methods on Summary.

Index

Constants

This section is empty.

Variables

View Source
var ErrBeforeGenesis = errors.New("hardfork: time is before system start")

ErrBeforeGenesis is returned by TimeToSlot when the given time is before the chain's SystemStart.

View Source
var ErrPastHorizon = errors.New("hardfork: slot/time past era horizon")

ErrPastHorizon is returned when a slot or time falls past the last bounded era's end and no unbounded era follows. Mirrors Haskell's PastHorizonException.

Functions

This section is empty.

Types

type Bound

type Bound struct {
	RelativeTime time.Duration
	Slot         uint64
	Epoch        uint64
}

Bound identifies a point in chain coordinates along all three axes: relative time (since SystemStart), absolute slot, and absolute epoch. Every EraSummary has a Start Bound, and a closed era also has an End Bound.

type EpochInfo

type EpochInfo struct {
	Epoch         uint64
	StartSlot     uint64
	LengthInSlots uint64
	SlotLength    time.Duration
	EraID         uint
}

EpochInfo is a lightweight SlotToEpoch answer combining the absolute epoch number with the era parameters needed to reason about it.

type EraParams

type EraParams struct {
	// EpochSize is the number of slots per epoch in this era.
	EpochSize uint64
	// SlotLength is the wall-clock duration of one slot.
	SlotLength time.Duration
	// SafeZoneSlots is the number of slots from the ledger tip (or era start)
	// within which no hard fork can occur. Zero means unbounded (the era has
	// no forecasted end).
	SafeZoneSlots uint64
	// GenesisWindow is the Ouroboros Genesis density-disconnection window,
	// also expressed in slots. Unused by Summary today but carried for
	// future callers.
	GenesisWindow uint64
}

EraParams describes the fixed per-era protocol parameters needed to translate between slot, epoch, and wall-clock time.

SafeZoneSlots == 0 denotes UnsafeIndefiniteSafeZone: the era has no known upper bound, and Summary will produce a nil End for it.

func (EraParams) Validate

func (p EraParams) Validate() error

Validate returns nil if the EraParams values are internally consistent.

type EraSummary

type EraSummary struct {
	EraID  uint
	Start  Bound
	End    *Bound // nil ⇒ unbounded
	Params EraParams
}

EraSummary describes the bounds and parameters of a single era within a chain's confirmed history.

End == nil denotes an unbounded era (the current era under UnsafeIndefiniteSafeZone). Every past era MUST have a non-nil End.

type Shape

type Shape struct {
	SystemStart time.Time
	Eras        []ShapeEntry
}

Shape is the static, compile-time-known era table: the set of eras this node binary knows how to handle, in chronological order. Mirrors Haskell's Ouroboros.Consensus.HardFork.History.Summary.Shape.

func (Shape) EraForID

func (s Shape) EraForID(eraID uint) (ShapeEntry, bool)

EraForID returns the ShapeEntry with the given EraID, or (_, false).

func (Shape) EraForVersion

func (s Shape) EraForVersion(majorVersion uint) (ShapeEntry, bool)

EraForVersion returns the ShapeEntry whose version range contains majorVersion, or (_, false) if no era covers it.

func (Shape) EraIndex

func (s Shape) EraIndex(eraID uint) (int, bool)

EraIndex returns the zero-based position of the era with the given EraID in the shape, or (_, false) if no such era is present.

func (Shape) Validate

func (s Shape) Validate() error

Validate returns nil if the Shape is structurally consistent:

  • At least one era entry.
  • Each entry's protocol-version range is well-formed (min ≤ max).
  • Consecutive entries' version ranges meet without gap and without overlap (cur.Min == prev.Max + 1).
  • Each entry's EraParams validate.
  • The final entry carries TriggerNotDuringThisExecution; no other entry does.

type ShapeEntry

type ShapeEntry struct {
	EraID           uint
	EraName         string
	MinMajorVersion uint
	MaxMajorVersion uint
	Params          EraParams
	NextEraTrigger  TriggerHardFork
}

ShapeEntry describes one era's static identity and parameters: the era ID, the span of protocol major versions it covers, its EraParams, and the trigger that arms the transition OUT of this era into its successor.

Protocol major version ranges must be contiguous and non-overlapping across a valid Shape. NextEraTrigger mirrors Haskell's per-era TriggerHardFork: the final entry must be TriggerNotDuringThisExecution; every other entry should carry either TriggerAtVersion (the default, keyed on the next era's MinMajorVersion) or TriggerAtEpoch (the TestXHardForkAtEpoch override).

type Summary

type Summary struct {
	SystemStart time.Time
	Eras        []EraSummary // chronological; last element is the current era
	Transition  TransitionInfo
}

Summary is the confirmed per-chain era history. It is derived from the ledger state + TransitionInfo by BuildSummary and then consulted for all slot/epoch/time conversions.

Summary mirrors the semantics of Ouroboros.Consensus.HardFork.History.Summary but in a Go-idiomatic, non-generic form.

func BuildSummary

func BuildSummary(
	shape Shape,
	past []EraSummary,
	current EraSummary,
	tipSlot uint64,
	transition TransitionInfo,
) (Summary, error)

BuildSummary constructs a Summary from a Shape, the confirmed closed past eras, the current (open) era, the ledger tip slot, and the current TransitionInfo.

The current era's End is computed per the HFC safe-zone rules:

  • TransitionUnknown — apply SafeZoneSlots from max(tipSlot+1, era start), then snap up to the next epoch boundary.
  • TransitionKnown — pin the end at KnownEpoch's start (mkUpperBound).
  • TransitionImpossible — apply SafeZoneSlots from the era start, snapped.

If Params.SafeZoneSlots == 0 the era is treated as UnsafeIndefiniteSafeZone and its End remains nil.

Mirrors Ouroboros.Consensus.HardFork.Combinator.State.Infra.reconstructSummary.

func (*Summary) CurrentEra

func (s *Summary) CurrentEra() *EraSummary

CurrentEra returns a pointer to the last (open) era, or nil if the Summary has no eras.

func (*Summary) SlotToEpoch

func (s *Summary) SlotToEpoch(slot uint64) (EpochInfo, error)

SlotToEpoch converts an absolute slot number to an EpochInfo.

func (*Summary) SlotToTime

func (s *Summary) SlotToTime(slot uint64) (time.Time, error)

SlotToTime converts an absolute slot number to its wall-clock start time.

func (*Summary) TimeToSlot

func (s *Summary) TimeToSlot(t time.Time) (uint64, error)

TimeToSlot converts a wall-clock time to the slot in which it falls. Returns ErrBeforeGenesis if t is before SystemStart.

func (*Summary) Validate

func (s *Summary) Validate() error

Validate checks structural invariants:

  • Summary has at least one era.
  • Each non-first era's Start equals the previous era's End.
  • Only the last era may have a nil End.

It does NOT check the per-era INV-1b / INV-2b invariants (end.Slot == start.Slot + epochs*epochSize, etc.) — those are left to the builder.

type TransitionInfo

type TransitionInfo struct {
	State      TransitionState
	KnownEpoch uint64 // valid when State == TransitionKnown
}

TransitionInfo describes the current era's known-transition state. It mirrors Haskell's HFC TransitionInfo but uses an explicit tag + payload rather than a sum type.

func NewTransitionImpossible

func NewTransitionImpossible() TransitionInfo

NewTransitionImpossible constructs the TransitionImpossible variant.

func NewTransitionKnown

func NewTransitionKnown(epoch uint64) TransitionInfo

NewTransitionKnown constructs the TransitionKnown variant with the given target epoch (the first epoch of the next era).

func NewTransitionUnknown

func NewTransitionUnknown() TransitionInfo

NewTransitionUnknown constructs the TransitionUnknown variant.

type TransitionState

type TransitionState uint8

TransitionState encodes which of the three HFC TransitionInfo cases currently applies to the open (current) era. Mirrors Haskell's Ouroboros.Consensus.HardFork.Combinator.State.Types.TransitionInfo.

const (
	// TransitionUnknown means no confirmed upcoming hard fork is known. The
	// open era's EraEnd will be capped at tipSlot + SafeZoneSlots, snapped to
	// the next epoch boundary.
	TransitionUnknown TransitionState = iota
	// TransitionKnown means the epoch-rollover logic has observed a
	// protocol-version bump confirmed to be stable. KnownEpoch is the first
	// epoch of the next era; its start slot is the exact era boundary.
	TransitionKnown
	// TransitionImpossible means a hard fork cannot happen within the current
	// safe zone (e.g. we're past the epoch's voting deadline, or in the
	// final known era). The safe zone applies from the era start.
	TransitionImpossible
)

func (TransitionState) String

func (s TransitionState) String() string

String returns a human-readable label for the state.

type TriggerHardFork

type TriggerHardFork struct {
	Kind    TriggerKind
	Version uint   // valid when Kind == TriggerAtVersion
	Epoch   uint64 // valid when Kind == TriggerAtEpoch
}

TriggerHardFork describes how a transition OUT of a given era is armed. It is stored per-era on ShapeEntry and is the Go equivalent of Haskell's TriggerHardFork sum type.

func NewTriggerAtEpoch

func NewTriggerAtEpoch(epoch uint64) TriggerHardFork

NewTriggerAtEpoch returns a TriggerAtEpoch variant pinning the transition to the given epoch. This corresponds to the TestXHardForkAtEpoch YAML override.

func NewTriggerAtVersion

func NewTriggerAtVersion(majorVersion uint) TriggerHardFork

NewTriggerAtVersion returns a TriggerAtVersion variant targeting the given next-era major protocol version.

func NewTriggerNotDuringThisExecution

func NewTriggerNotDuringThisExecution() TriggerHardFork

NewTriggerNotDuringThisExecution returns the variant used by the last era a binary knows about: no successor, so no in-execution transition.

func (TriggerHardFork) String

func (t TriggerHardFork) String() string

String returns a human-readable form, e.g. "AtVersion(4)", "AtEpoch(500)", "NotDuringThisExecution".

type TriggerKind

type TriggerKind uint8

TriggerKind identifies which of the three TriggerHardFork variants applies. Mirrors Haskell's Ouroboros.Consensus.HardFork.Simple.TriggerHardFork.

const (
	// TriggerAtVersion means the transition fires when the on-chain pparams
	// advertise the configured next-era major protocol version (and, in the
	// full HFC, the vote has stabilised). Version holds that major version.
	TriggerAtVersion TriggerKind = iota
	// TriggerAtEpoch means the transition is administratively pinned to a
	// specific epoch (the "TestXHardForkAtEpoch" override). Epoch holds
	// that epoch ID. The pparams major-version bump is bypassed.
	TriggerAtEpoch
	// TriggerNotDuringThisExecution means no transition out of this era can
	// occur under the currently-running binary — the node does not know
	// about the successor era.
	TriggerNotDuringThisExecution
)

Jump to

Keyboard shortcuts

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