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 ¶
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.
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 ¶
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.
type EraSummary ¶
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 ¶
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 ¶
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 ¶
SlotToEpoch converts an absolute slot number to an EpochInfo.
func (*Summary) SlotToTime ¶
SlotToTime converts an absolute slot number to its wall-clock start time.
func (*Summary) TimeToSlot ¶
TimeToSlot converts a wall-clock time to the slot in which it falls. Returns ErrBeforeGenesis if t is before SystemStart.
func (*Summary) Validate ¶
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 )