forging

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: 44 Imported by: 0

Documentation

Overview

Package forging contains types and utilities for block production.

Block Propagation

Block propagation to peers is handled automatically by the chain package. When a forged block is added via chain.AddLocalBlock(), the method closes the chain's waitingChan (see chain/chain.go lines 174-180), which signals any blocking ChainIterators. The ouroboros chainsync server (see ouroboros/chainsync.go chainsyncServerRequestNext) waits on these iterators via ChainIterator.Next(true), which blocks on waitingChan when at chain tip. When the channel is closed, iterators wake up and deliver the new block to connected peers via RollForward messages.

This means there is no need for explicit propagation logic when forging blocks - adding the block to the chain automatically triggers delivery to all subscribed chainsync clients.

Package forging provides block production functionality for Cardano SPOs.

Index

Constants

View Source
const SlotBattleEventType = event.EventType("forging.slot_battle")

SlotBattleEventType is the event type for slot battles (competing blocks)

Variables

View Source
var ErrOpCertEraUnevaluable = errors.New(
	"operational certificate era rule not evaluable",
)

ErrOpCertEraUnevaluable reports that the era in effect at a startup slot could not be resolved, so the era-scoped no-gap operational-certificate counter rule was not evaluable there.

It is never returned as the error result of ValidateAgainstLedgerAtSlot. An unresolved era means the rule was not evaluated, not that it was violated, and refusing startup on it strands a producer that is merely behind: the node cannot then sync to the state that would resolve the era. The forge loop re-applies the full rule for every won leader slot (BlockForger.checkOpCertSequence) with both operands read from near-tip state and fails closed per slot, so a gapped counter still cannot produce a block.

View Source
var (
	ErrVRFKeyHashMismatch = errors.New("VRF key hash mismatch")
)

Functions

func ComputeConwayBlockBodyHash added in v0.69.0

func ComputeConwayBlockBodyHash(
	txBodies []conway.ConwayTransactionBody,
	witnessSets []conway.ConwayTransactionWitnessSet,
	metadataSet lcommon.TransactionMetadataSet,
) (lcommon.Blake2b256, uint64, error)

ComputeConwayBlockBodyHash computes the block body hash for a Conway-era block per the Cardano spec (see computeBlockBodyHash), given the typed transaction bodies/witness sets that will be placed into the block. Each element is CBOR-encoded individually before hashing, matching the byte form the Conway block's own MarshalCBOR produces for that same element.

Exported for the dev-mode forging path (ledger/state.go), which builds blocks directly from typed mempool transactions rather than through Builder.

func CurrentKESPeriod added in v0.43.0

func CurrentKESPeriod(
	currentSlot uint64,
	slotsPerKESPeriod uint64,
) (uint64, error)

CurrentKESPeriod returns the KES period containing currentSlot.

Semantics:

  • currentSlot must be the era-aware absolute slot number from the slot clock. Reconstructing it from Shelley wall-clock slot length is wrong on networks with a Byron prefix.
  • Returns an error if slotsPerKESPeriod is zero.

func CurrentKESPeriodFromGenesis added in v0.63.0

func CurrentKESPeriodFromGenesis(
	genesis *shelley.ShelleyGenesis,
	currentSlot uint64,
) (uint64, error)

CurrentKESPeriodFromGenesis returns the KES period containing currentSlot, using slotsPerKESPeriod from Shelley genesis.

func WallClockKESPeriod added in v0.63.0

func WallClockKESPeriod(
	genesis *shelley.ShelleyGenesis,
	now time.Time,
) (uint64, error)

WallClockKESPeriod returns the KES period implied by Shelley genesis wall time. It is retained for tests and diagnostics on networks without a Byron prefix. Block production startup must use CurrentKESPeriodFromGenesis with an era-aware slot clock instead.

Types

type BlockBroadcaster

type BlockBroadcaster interface {
	// AddBlock adds a block to the local chain and propagates to peers.
	AddBlock(block ledger.Block, cbor []byte) error
}

BlockBroadcaster submits built blocks to the chain.

type BlockBuilder

type BlockBuilder interface {
	// BuildBlock creates a new block for the given slot.
	// Returns the block and its CBOR encoding.
	BuildBlock(slot uint64, kesPeriod uint64) (ledger.Block, []byte, error)
}

BlockBuilder constructs blocks from mempool transactions.

type BlockBuilderConfig

type BlockBuilderConfig struct {
	Logger          *slog.Logger
	Mempool         MempoolProvider
	PParamsProvider ProtocolParamsProvider
	ChainTip        ChainTipProvider
	EpochNonce      EpochNonceProvider
	Credentials     *PoolCredentials
	// TxValidator optionally re-validates each transaction against
	// the current ledger state before including it in a block.
	// When nil, ledger-level re-validation is skipped (but
	// intra-block double-spend detection still applies).
	TxValidator TxValidator
}

BlockBuilderConfig holds configuration for the DefaultBlockBuilder.

type BlockForgedObserver added in v0.46.2

type BlockForgedObserver func(
	block ledger.Block,
	cbor []byte,
	latency time.Duration,
)

BlockForgedObserver observes blocks after they are successfully built and chain adoption has been attempted.

type BlockForger

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

BlockForger coordinates block production for a stake pool.

func NewBlockForger

func NewBlockForger(cfg ForgerConfig) (*BlockForger, error)

NewBlockForger creates a new block forger.

func (*BlockForger) IsRunning

func (f *BlockForger) IsRunning() bool

IsRunning returns true if the forger is currently running.

func (*BlockForger) RecordSlotBattle

func (f *BlockForger) RecordSlotBattle()

RecordSlotBattle increments the slot battles counter. This is called from external components (e.g., LedgerState) when a slot battle is detected.

func (*BlockForger) SignBlockHeader

func (f *BlockForger) SignBlockHeader(
	kesPeriod uint64,
	headerBytes []byte,
) ([]byte, error)

SignBlockHeader signs a block header with KES.

func (*BlockForger) SlotTracker

func (f *BlockForger) SlotTracker() *SlotTracker

SlotTracker returns the forger's slot tracker, which can be used by other components (e.g., chainsync) to detect slot battles.

func (*BlockForger) Start

func (f *BlockForger) Start(ctx context.Context) error

Start begins the block forging process. The provided context controls the forger's lifecycle.

func (*BlockForger) Stop

func (f *BlockForger) Stop()

Stop stops the block forging process. It blocks until the runLoop goroutine has exited.

func (*BlockForger) VRFProofForSlot

func (f *BlockForger) VRFProofForSlot(
	slot uint64,
	epochNonce []byte,
) ([]byte, []byte, error)

VRFProofForSlot generates a VRF proof for leader election at the given slot. Returns (proof, output, error).

type BlockValidator added in v0.58.0

type BlockValidator interface {
	ValidateForgedBlock(block ledger.Block, blockCbor []byte) error
}

BlockValidator validates a locally-forged block before it is adopted onto the local chain and diffused to peers. If ValidateForgedBlock returns a non-nil error the block is dropped and neither adopted nor diffused.

type ChainTipHashProvider added in v0.70.7

type ChainTipHashProvider interface {
	// ChainTipHash returns the block hash of the current chain tip, or
	// nil when the chain is empty or the hash is unavailable.
	ChainTipHash() []byte
}

ChainTipHashProvider is an optional extension of SlotClockProvider.

DEPRECATED, and no longer consulted by this package. It existed because the previous SlotClockProvider.ChainTipSlot returned a bare slot, so the tip hash had to be fetched through a second, separately-read method; ChainTip now returns a point carrying both from one snapshot, which is strictly better (the pair cannot straddle two tips), so tipBlockOwnership takes the hash from there.

The interface is retained rather than removed because it is exported API that implementations outside this repository may still satisfy, and satisfying it is harmless. Removing it is a separate API decision.

type ChainTipProvider

type ChainTipProvider interface {
	Tip() ochainsync.Tip
}

ChainTipProvider provides access to the current chain tip.

type ChainTipSigningProvider added in v0.70.13

type ChainTipSigningProvider interface {
	WithTip(func(ochainsync.Tip) error) error
}

ChainTipSigningProvider binds a callback to the chain-tip lock. Production chain implementations use this to keep the parent snapshot stable through header encoding and KES signing. Providers that do not implement it retain the best-effort final tip check for compatibility with embedders.

type ConfirmedTxRemover added in v0.69.0

type ConfirmedTxRemover interface {
	RemoveTxsByHash(hashes []string)
}

ConfirmedTxRemover removes transactions after the block containing them has been adopted locally.

type DefaultBlockBuilder

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

DefaultBlockBuilder implements BlockBuilder using LedgerState components.

func NewDefaultBlockBuilder

func NewDefaultBlockBuilder(
	cfg BlockBuilderConfig,
) (*DefaultBlockBuilder, error)

NewDefaultBlockBuilder creates a new DefaultBlockBuilder.

func (*DefaultBlockBuilder) BuildBlock

func (b *DefaultBlockBuilder) BuildBlock(
	slot uint64,
	kesPeriod uint64,
) (ledger.Block, []byte, error)

BuildBlock creates a new block for the given slot. Returns the block and its CBOR encoding.

func (*DefaultBlockBuilder) BuildBlockWithLeios added in v0.63.0

func (b *DefaultBlockBuilder) BuildBlockWithLeios(
	slot uint64,
	kesPeriod uint64,
	leios LeiosBlockData,
) (ledger.Block, []byte, error)

BuildBlockWithLeios creates a Dijkstra block with Leios prototype announcement or certificate data committed into the block body/header.

type EndorserBlockBroadcaster added in v0.54.0

type EndorserBlockBroadcaster interface {
	BroadcastEndorserBlock(
		slot uint64,
		hash []byte,
		cbor []byte,
		txBodies [][]byte,
	) error
}

EndorserBlockBroadcaster stores a locally-forged endorser block and notifies connected peers via the LeiosNotify protocol. txBodies are the referenced transactions' raw CBOR, in manifest order, so the endorser block can also be served over leios-fetch.

type EpochNonceProvider

type EpochNonceProvider interface {
	// CurrentEpoch returns the current epoch number.
	CurrentEpoch() uint64
	// EpochForSlot returns the epoch containing the given slot.
	EpochForSlot(slot uint64) (uint64, error)
	// EpochNonce returns the nonce for the given epoch.
	EpochNonce(epoch uint64) []byte
}

EpochNonceProvider provides the epoch nonce for VRF proof generation.

type ForgeFenceStore added in v0.70.3

type ForgeFenceStore interface {
	// LoadLastForgedSlot returns the highest recorded slot and whether
	// any fence has been recorded yet.
	LoadLastForgedSlot() (uint64, bool, error)
	// StoreLastForgedSlot durably records slot as used. It must not
	// return until the record survives a crash.
	StoreLastForgedSlot(slot uint64) error
}

ForgeFenceStore persists the highest slot this node has committed to forging. It is the durable half of the duplicate-slot fence: the in-memory chain tip is lost on restart, and a tip that has rolled back no longer proves which slots were already used.

The fence is written before the block header for a slot is signed, so a crash anywhere between signing and adoption still leaves the slot recorded. Refusing a slot the node did not actually use costs one block; signing a second, different block for a slot whose first block may already have reached peers is equivocation.

func NewSyncStateForgeFenceStore added in v0.70.3

func NewSyncStateForgeFenceStore(
	store syncStateStore,
	poolID lcommon.PoolKeyHash,
) ForgeFenceStore

NewSyncStateForgeFenceStore creates a ForgeFenceStore backed by sync state. The fence is namespaced by pool id so a node re-keyed to different credentials is not gated by a fence it never signed under.

type ForgedBlockRecord

type ForgedBlockRecord struct {
	BlockHash []byte
}

ForgedBlockRecord stores the hash of a block we forged for a given slot.

type ForgerConfig

type ForgerConfig struct {
	Mode         Mode
	Logger       *slog.Logger
	SlotDuration time.Duration

	// Production mode configuration. Credentials must have passed
	// ValidateKESPeriod so the forger can enforce the genesis KES lifetime.
	Credentials      *PoolCredentials
	LeaderChecker    LeaderChecker
	BlockBuilder     BlockBuilder
	BlockBroadcaster BlockBroadcaster
	ConfirmedTxs     ConfirmedTxRemover
	BlockForged      BlockForgedObserver
	SlotClock        SlotClockProvider

	// OpCertLedgerView supplies the highest OpCert issue-number counter the
	// ledger has observed on chain for this pool. When non-nil, the forge
	// loop pre-flights the candidate counter against it using the same
	// era-scoped rule block application enforces (see
	// ledger/verify_opcert.go validateOpCertCounter), after leader
	// selection but before Leios work and the forge-slot fence -- a stale
	// or gapped counter is rejected there instead of reaching an
	// `AddLocalBlock` call the chain would discard anyway. Nil disables
	// the check (dev mode, embedders without ledger wiring). Requires
	// EraParams.
	OpCertLedgerView LedgerView
	// EraParams supplies the era-defining protocol parameters in effect for
	// the slot being forged, so OpCertLedgerView's counter check applies
	// the correct era-scoped rule: TPraos (Shelley-Alonzo) accepts any
	// forward counter movement, Praos (Babbage onward) additionally
	// rejects one that skips ahead of the last-seen value by more than
	// one. Required whenever OpCertLedgerView is set.
	EraParams ProtocolParamsProvider

	// ForgeFence persists the last-forged-slot fence so a restart cannot
	// sign a second block for a slot this node already used. Nil
	// disables the durable fence, which leaves only the in-memory chain
	// tip guarding against duplicate slots.
	ForgeFence ForgeFenceStore

	// LeiosProduceChecker enables EB forging when non-nil. Requires
	// LeiosEBBroadcaster and LeiosMempool to also be set.
	LeiosProduceChecker LeiosProduceChecker
	// LeiosEBBroadcaster propagates locally-forged EBs to peers.
	LeiosEBBroadcaster EndorserBlockBroadcaster
	// LeiosMempool provides transactions for EB building. May reuse the
	// same MempoolProvider as the RB builder.
	LeiosMempool MempoolProvider
	// LeiosTxValidator re-validates endorser-block transactions against one
	// coherent ledger snapshot before the EB is broadcast. Production wiring
	// supplies LedgerState; nil preserves compatibility for test embedders.
	LeiosTxValidator TxValidator
	// LeiosCertificateProvider supplies certified EBs for Dijkstra CertRBs.
	LeiosCertificateProvider LeiosCertificateProvider
	// LeiosParentAnnouncementProvider supplies the EB hash announced by the
	// parent RB so CertRB selection cannot certify an unrelated EB.
	LeiosParentAnnouncementProvider LeiosParentAnnouncementProvider

	// ForgeSyncToleranceSlots controls how far the local chain can lag the
	// upstream tip before forging is skipped. Zero uses the default.
	ForgeSyncToleranceSlots uint64
	// ForgePrimaryChainTipToleranceSlots controls how far the ledger-applied
	// tip may lag this node's own primary chain tip before the forger refuses
	// to build on it. Zero selects forgePrimaryChainTipToleranceSlots.
	ForgePrimaryChainTipToleranceSlots uint64
	// ForgeUpstreamStalenessSlots controls how far the newest block this node
	// holds may trail the corroborated upstream target before forging is
	// refused. Zero (the default) DISABLES the bound.
	//
	// It is opt-in rather than defaulted because the two sides of the
	// comparison are not measured at the same stage of the pipeline: the
	// newest block this node holds is a BLOCK, while the upstream target is
	// published when a HEADER is admitted. Between a header's admission and
	// its body being applied the two legitimately differ by the inter-block
	// gap, so a small always-on bound refuses leader slots during ordinary
	// operation -- for exponential gaps with a 20-slot mean, a bound of 5
	// fires for roughly 78% of blocks. Set it well above the expected gap, or
	// leave it off until the admitted header frontier is folded into the
	// comparison.
	ForgeUpstreamStalenessSlots uint64
	// ForgeAppliedTipStalenessSlots controls how many slots older than the
	// current slot the newest block this node holds may be before forging is
	// refused.
	//
	// There is deliberately NO default: zero disables it. A safe value depends
	// on the chain's mean block interval, which the forger cannot see, and
	// getting it wrong is expensive in the direction that matters -- at a
	// 20-slot mean interval a bound of 20 would refuse roughly a third of all
	// leader slots on a perfectly healthy chain, because block arrivals are
	// bursty rather than evenly spaced. ForgeUpstreamStalenessSlots covers the
	// same failure without that hazard by comparing against the network rather
	// than the clock. Operators who know their chain's block rate can set this
	// as a backstop for the one case the upstream comparison cannot see --
	// every peer reporting a stale target -- for which several times the mean
	// block interval is a sane starting point.
	ForgeAppliedTipStalenessSlots uint64
	// ForgeEndorserBlockStalenessSlots controls how far a corroborated Leios
	// endorser block may lead the ledger-applied tip before forging is
	// refused. Zero (the default) DISABLES the bound, and with it the whole
	// endorser-block refusal path.
	//
	// It has its own bound rather than borrowing
	// ForgePrimaryChainTipToleranceSlots, which is documented at its
	// definition as bounding a purely LOCAL block-against-block comparison
	// and defaults to 5. LeiosVerifiedEbSlot is a network-stage value: it
	// advances at leios-notify announcement time, before any header for that
	// slot has to arrive. Sharing one number would tie two unrelated risk
	// budgets together -- widening it to tolerate an endorser-block gap would
	// equally loosen the local coherence check that stops stale-parent
	// forging.
	//
	// Off by default also because the watermark is monotonic and is never
	// lowered on a fork. An endorser block corroborated for a chain this node
	// does not adopt leaves the watermark above the local tip, and an
	// always-on bound would then refuse leader slots for as long as the local
	// chain sits below that slot -- with every local indicator reading
	// healthy. Operators who enable it should set it well above the expected
	// announcement-to-apply lag.
	ForgeEndorserBlockStalenessSlots uint64
	// LeiosVerifiedEbSlot optionally reports the highest slot for which this
	// node has corroborated a Leios endorser block, which is proof a ranking
	// block exists at that slot even if its header has not arrived. Advisory
	// and monotonic; nil disables the signal.
	LeiosVerifiedEbSlot func() uint64
	// ForgeStaleGapThresholdSlots controls when to log an error if the
	// chain tip is far ahead of the slot clock. Zero uses the default.
	ForgeStaleGapThresholdSlots uint64

	// BlockValidator runs its implementation's checks before AddBlock.
	// A failure prevents adoption and diffusion. The node always supplies
	// aggregate reference-script validation and, unless an operator
	// explicitly opts out via ValidateForgedBlock=false (issue #3528: fail
	// closed by default), full VRF/KES header crypto, body-hash, and
	// per-tx ledger rule validation too. Nil disables validation for
	// callers embedding this package directly.
	BlockValidator BlockValidator

	// Prometheus metrics registry (optional)
	PromRegistry prometheus.Registerer
}

ForgerConfig holds configuration for the block forger.

type LeaderChecker

type LeaderChecker interface {
	// ShouldProduceBlock returns true if this pool is the leader for the slot.
	ShouldProduceBlock(slot uint64) bool
	// NextLeaderSlot returns the next slot where this pool is leader.
	NextLeaderSlot(fromSlot uint64) (uint64, bool)
}

LeaderChecker determines if the pool should produce a block for a given slot.

type LedgerValidationResult added in v0.70.11

type LedgerValidationResult struct {
	// Registered is true if the pool registration was found on chain.
	Registered bool
	// VRFMatched is true if Registered and the on-chain VRF key hash matched
	// the loaded VRF verification key. False otherwise, including when the VRF
	// verification key is unavailable (a seed-only VRF skey).
	VRFMatched bool
	// EraUnevaluable is non-nil when the era in effect at the requested slot
	// could not be resolved, so the era-scoped no-gap counter rule was left
	// unenforced and only the staleness rule was applied. It wraps
	// ErrOpCertEraUnevaluable. The cross-check still succeeds; callers should
	// log it where an operator will see it.
	EraUnevaluable error
}

LedgerValidationResult describes the outcome of a startup ledger cross-check.

type LedgerView added in v0.43.0

type LedgerView interface {
	// PoolRegistrationVRFKeyHash returns the VRF key hash recorded on
	// the most recent active pool registration certificate for poolID.
	// found is false when the pool has no on-chain registration yet.
	PoolRegistrationVRFKeyHash(
		poolID [28]byte,
	) (vrfKeyHash [32]byte, found bool, err error)
	// LatestOpCertSequence returns the highest opcert IssueNumber
	// observed on chain for poolID. found is false when on-chain
	// counter tracking is not implemented or this pool has never
	// minted a block.
	LatestOpCertSequence(
		poolID [28]byte,
	) (sequence uint64, found bool, err error)
}

LedgerView is the subset of ledger state the post-startup credential cross-check needs. The forging package depends on it as a small interface so the package itself stays free of a ledger dependency, and tests can drive the logic with a fake.

type LeiosBlockBuilder added in v0.63.0

type LeiosBlockBuilder interface {
	BuildBlockWithLeios(
		slot uint64,
		kesPeriod uint64,
		leios LeiosBlockData,
	) (ledger.Block, []byte, error)
}

LeiosBlockBuilder constructs Dijkstra blocks with Leios prototype header/body extensions. Builders that do not implement it cannot safely announce or certify Leios endorser blocks.

type LeiosBlockData added in v0.63.0

type LeiosBlockData struct {
	Announcement *LeiosEndorserBlockAnnouncement
	Certificate  *lcommon.LeiosEbCertificate
}

LeiosBlockData carries the Leios prototype data a Dijkstra ranking block should commit to. Since prototype-2026w29 a ranking block may certify its parent's endorser block and independently announce a new one.

type LeiosCertificateProvider added in v0.63.0

type LeiosCertificateProvider interface {
	EligibleCertifiedEndorserBlocks() []LeiosCertifiedEndorserBlock
	CertifiedEndorserBlockTxHashes(
		ebHash lcommon.Blake2b256,
		ebSlot uint64,
	) (hashes []string, ok bool)
	MarkEndorserBlockEmbedded(ebHash lcommon.Blake2b256, ebSlot uint64)
}

LeiosCertificateProvider supplies certified EBs and records successful inclusion after the certifying ranking block is adopted.

type LeiosCertifiedEndorserBlock added in v0.63.0

type LeiosCertifiedEndorserBlock struct {
	SlotNo            uint64
	EndorserBlockHash lcommon.Blake2b256
	Certificate       *lcommon.LeiosEbCertificate
	AnnouncingRbHash  lcommon.Blake2b256
}

LeiosCertifiedEndorserBlock is a certified EB ready for inclusion in a Dijkstra ranking block.

type LeiosEndorserBlockAnnouncement added in v0.63.0

type LeiosEndorserBlockAnnouncement struct {
	Hash lcommon.Blake2b256
	Size uint64
}

LeiosEndorserBlockAnnouncement is the header extension payload for an endorser block announced by a Dijkstra ranking block.

type LeiosParentAnnouncementProvider added in v0.63.0

type LeiosParentAnnouncementProvider interface {
	ParentLeiosAnnouncement() (
		lcommon.Blake2b256,
		lcommon.Blake2b256,
		bool,
		error,
	)
}

LeiosParentAnnouncementProvider reports the EB announced by the parent ranking block. CertRBs may only certify that announced EB.

type LeiosProduceChecker added in v0.54.0

type LeiosProduceChecker interface {
	MayProduceEndorserBlock(
		slot uint64,
	) (allowed bool, reason string, err error)
}

LeiosProduceChecker is the forge-loop seam into the Leios pipeline. It reports whether the slot leader may produce an endorser block for the given slot (respects the single-EB-per-slot rule and produce window). A nil checker means Leios EB forging is disabled (relay or pre-Dijkstra era).

type MempoolProvider

type MempoolProvider interface {
	Transactions() []MempoolTransaction
}

MempoolProvider provides access to mempool transactions.

type MempoolTransaction

type MempoolTransaction struct {
	Hash string
	Cbor []byte
	Type uint
}

MempoolTransaction represents a transaction in the mempool.

type Mode

type Mode int

Mode represents the forging mode.

const (
	// ModeDev is a simplified mode where the node produces all blocks on a
	// fixed interval without real VRF/KES. Used for single-node devnets.
	ModeDev Mode = iota

	// ModeProduction uses real VRF leader election and KES signing.
	// Requires loaded pool credentials.
	ModeProduction
)

type OpCert

type OpCert struct {
	KESVKey     []byte // KES verification key (32 bytes)
	IssueNumber uint64 // Certificate sequence number
	KESPeriod   uint64 // KES period when certificate was created
	Signature   []byte // Cold key signature (64 bytes)
	ColdVKey    []byte // Cold verification key (32 bytes)
}

OpCert represents an operational certificate that binds a KES key to a pool.

type PoolCredentials

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

PoolCredentials holds the cryptographic keys required for block production. All keys are loaded using Bursa from standard cardano-cli format files. Fields are unexported to enforce thread-safe access via the mutex.

func NewPoolCredentials

func NewPoolCredentials() *PoolCredentials

NewPoolCredentials creates an empty PoolCredentials instance.

func (*PoolCredentials) ArmKesProtocolLifetime added in v0.70.13

func (pc *PoolCredentials) ArmKesProtocolLifetime(
	genesis *shelley.ShelleyGenesis,
) error

ArmKesProtocolLifetime seeds the protocol-level KES lifetime from the Shelley genesis without judging the operational certificate against the current wall-clock slot. It exists for block producer startup when the ledger's confirmed era history does not yet span the wall clock (genesis re-import, long downtime): in that state the extrapolated current slot uses the newest known era's slot length and cannot reliably place an opcert in time (a fresh mainnet ledger extrapolates Byron's 20s slots over the whole chain, so the current KES period reads far below the real one). The per-slot forge gate still enforces the armed window against the reliable slot, so no block leaves the node outside the operational certificate's lifetime.

func (*PoolCredentials) GetKESPeriod

func (pc *PoolCredentials) GetKESPeriod() uint64

GetKESPeriod returns the current KES period of the loaded key. Returns 0 if the KES key is not loaded.

func (*PoolCredentials) GetKESVKey

func (pc *PoolCredentials) GetKESVKey() []byte

GetKESVKey returns a copy of the KES verification key.

func (*PoolCredentials) GetOpCert

func (pc *PoolCredentials) GetOpCert() *OpCert

GetOpCert returns a copy of the operational certificate. Returns nil if no certificate is loaded.

func (*PoolCredentials) GetPoolID

func (pc *PoolCredentials) GetPoolID() lcommon.PoolId

GetPoolID returns the pool ID (Blake2b-224 of cold vkey).

func (*PoolCredentials) GetVRFSKey

func (pc *PoolCredentials) GetVRFSKey() []byte

GetVRFSKey returns a copy of the VRF secret key (seed).

func (*PoolCredentials) GetVRFVKey

func (pc *PoolCredentials) GetVRFVKey() []byte

GetVRFVKey returns a copy of the VRF verification key.

func (*PoolCredentials) IsLoaded

func (pc *PoolCredentials) IsLoaded() bool

IsLoaded returns true if all credentials have been loaded.

func (*PoolCredentials) KESSign

func (pc *PoolCredentials) KESSign(
	period uint64,
	message []byte,
) ([]byte, error)

KESSign signs a message with the KES key at the specified ABSOLUTE period.

IMPORTANT: Callers must ensure UpdateKESPeriod(period) was called before KESSign to evolve the key to the correct period. The kes.Sign function expects the key to already be at the relative period within the opcert window when an opcert is loaded.

func (*PoolCredentials) LoadFromFiles

func (pc *PoolCredentials) LoadFromFiles(
	vrfSKeyPath string,
	kesSKeyPath string,
	opCertPath string,
) error

LoadFromFiles loads all pool credentials from the specified file paths. Uses Bursa to parse cardano-cli format key files. The full loaded material replaces the prior generation atomically. A failed reload invalidates the prior generation so an active forger cannot continue with stale policy.

func (*PoolCredentials) OpCertExpiryPeriod

func (pc *PoolCredentials) OpCertExpiryPeriod() uint64

OpCertExpiryPeriod returns the protocol KES period at which the OpCert expires. ValidateKESPeriod must first load MaxKESEvolutions from Shelley genesis. It returns zero when no validated protocol lifetime is available.

func (*PoolCredentials) PeriodsRemaining

func (pc *PoolCredentials) PeriodsRemaining(currentPeriod uint64) uint64

PeriodsRemaining returns how many protocol KES periods remain before expiry. It returns zero before the OpCert start, at or after expiry, or when no validated protocol lifetime is available.

func (*PoolCredentials) UpdateKESPeriod

func (pc *PoolCredentials) UpdateKESPeriod(period uint64) error

UpdateKESPeriod evolves the KES key to the specified ABSOLUTE period. The secret key itself tracks the relative period within the opcert window, so we translate chain KES periods by subtracting the opcert start period when an opcert is loaded.

func (*PoolCredentials) VRFProve

func (pc *PoolCredentials) VRFProve(alpha []byte) ([]byte, []byte, error)

VRFProve generates a VRF proof for leader election. alpha should be MkInputVrf(slot, epochNonce).

func (*PoolCredentials) ValidateAgainstLedger added in v0.43.0

func (pc *PoolCredentials) ValidateAgainstLedger(
	view LedgerView,
) (registered, vrfMatched bool, err error)

ValidateAgainstLedger cross-checks the loaded credentials against ledger state once it is available. It is best-effort: a missing pool registration is not fatal because operators commonly stage their keys before submitting the registration certificate.

ValidateAgainstLedger applies the staleness-only rule for callers that do not have protocol parameters. Node startup uses ValidateAgainstLedgerAtSlot so it can apply the era-specific rule before enabling production.

Three return values describe the outcome:

  • registered: true if the pool registration was found on chain.
  • vrfMatched: true if registered AND the on-chain VRF key hash matched our loaded VRF verification key. False otherwise (also false when registered is false or the VRF verification key is unavailable, e.g. for a seed-only VRF skey).
  • err: a non-nil error means the ledger view disagrees with the loaded credentials. Normal networks refuse startup for these; devnet callers may choose to warn on ErrVRFKeyHashMismatch.

func (*PoolCredentials) ValidateAgainstLedgerAtSlot added in v0.70.11

func (pc *PoolCredentials) ValidateAgainstLedgerAtSlot(
	view LedgerView,
	params ProtocolParamsProvider,
	slot uint64,
) (LedgerValidationResult, error)

ValidateAgainstLedgerAtSlot applies the era-specific operational-certificate counter rule for slot, on top of the cross-checks ValidateAgainstLedger performs.

slot must come from the same pipeline stage as the counter baseline the rule is judged against: LedgerView.LatestOpCertSequence reflects only the applied chain, so slot must be an applied-chain slot and not a wall-clock one.

A non-nil error means the ledger view disagrees with the loaded credentials. An era that cannot be resolved is reported in LedgerValidationResult.EraUnevaluable instead, and leaves the staleness rule (candidate below the last observed counter) in force.

func (*PoolCredentials) ValidateKESPeriod added in v0.43.0

func (pc *PoolCredentials) ValidateKESPeriod(
	genesis *shelley.ShelleyGenesis,
	currentSlot uint64,
) error

ValidateKESPeriod checks that the loaded operational certificate's KES period is plausible at currentSlot, given the chain's Shelley genesis. A non-nil result means the node should refuse to start: either the opcert claims a period that hasn't started yet (rotated key staged too early, or wrong network) or the opcert has expired and needs to be rotated.

The protocol-level expiry uses MaxKESEvolutions from genesis rather than the raw 2^depth ceiling, so this matches the chain's view of when an opcert stops being valid. A successful result retains that protocol lifetime for runtime forging checks and operational metrics.

func (*PoolCredentials) ValidateOpCert

func (pc *PoolCredentials) ValidateOpCert() error

ValidateOpCert validates that the operational certificate matches the KES key and that the cold key signature over the certificate body is valid.

type ProtocolParamsProvider

type ProtocolParamsProvider interface {
	GetCurrentPParams() lcommon.ProtocolParameters
	// ProtocolParamsForSlot returns the pparams that should govern a
	// block forged at the given slot. When the slot is in an epoch
	// beyond a scheduled fork that has not yet been applied to the
	// in-memory ledger state, the returned pparams are the
	// post-fork pparams. The forger uses this to produce
	// era-correct blocks at fork boundaries.
	ProtocolParamsForSlot(slot uint64) lcommon.ProtocolParameters
}

ProtocolParamsProvider provides access to protocol parameters.

type SlotBattleEvent

type SlotBattleEvent struct {
	// Slot is the slot number where the battle occurred
	Slot uint64
	// LocalBlockHash is the hash of our locally forged block (if any)
	LocalBlockHash []byte
	// RemoteBlockHash is the hash of the competing block from peers
	RemoteBlockHash []byte
	// Won indicates whether our local block was selected for the chain
	Won bool
}

SlotBattleEvent is emitted when the node detects competing blocks for the same slot, either from receiving an external block while preparing to forge or when detecting a fork at the same slot height.

type SlotClockProvider

type SlotClockProvider interface {
	// CurrentSlot returns the current slot number based on wall-clock time.
	CurrentSlot() (uint64, error)
	// SlotsPerKESPeriod returns the number of slots in a KES period.
	SlotsPerKESPeriod() uint64
	// ChainTip returns the LEDGER-APPLIED tip as a point. It is a point
	// rather than a bare slot because slot alone cannot distinguish an
	// equal-slot fork: chain selection can replace the block at a slot with a
	// competing one at the same slot, and the two tips then differ only by
	// hash. Implementations must return slot and hash from a single snapshot.
	ChainTip() ocommon.Point
	// PrimaryChainTip returns this node's own primary chain tip -- the tip of
	// the primary chain, which the block builder uses as a forged block's
	// parent and which can be ahead of ChainTip while the ledger pipeline is
	// still applying blocks the node has already admitted and selected.
	// Implementations must return slot and hash from a single snapshot.
	PrimaryChainTip() ocommon.Point
	// NextSlotTime returns the wall-clock time when the next slot begins.
	NextSlotTime() (time.Time, error)
	// UpstreamTipSlot returns the latest admitted header slot from upstream
	// peers. Returns 0 if no corroborated target is available.
	UpstreamTipSlot() uint64
	// UpstreamSyncStatus reports whether a live upstream is selected and its
	// corroborated target.
	UpstreamSyncStatus() (targetSlot uint64, active bool)
}

SlotClockProvider provides current slot information from the slot clock.

type SlotTracker

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

SlotTracker is a thread-safe tracker for recently forged block slots and their hashes. It allows chainsync to detect slot battles when an incoming block from a peer occupies a slot for which the local node has already forged a block.

func NewSlotTracker

func NewSlotTracker() *SlotTracker

NewSlotTracker creates a new SlotTracker with the default capacity.

func NewSlotTrackerWithCapacity

func NewSlotTrackerWithCapacity(maxSlots int) *SlotTracker

NewSlotTrackerWithCapacity creates a new SlotTracker with the given maximum capacity.

func (*SlotTracker) Len

func (st *SlotTracker) Len() int

Len returns the number of tracked forged slots.

func (*SlotTracker) RecordForgedBlock

func (st *SlotTracker) RecordForgedBlock(slot uint64, blockHash []byte)

RecordForgedBlock records that the local node forged a block with the given hash at the given slot. If the tracker is at capacity, the oldest entry is evicted.

func (*SlotTracker) WasForgedByUs

func (st *SlotTracker) WasForgedByUs(
	slot uint64,
) (blockHash []byte, ok bool)

WasForgedByUs checks whether the local node forged a block for the given slot. If so, it returns the block hash and true. Otherwise it returns nil, false.

type TxValidationFunc added in v0.70.0

type TxValidationFunc = func(
	tx ledger.Transaction,
	consumedUtxos map[utxoref.Key]struct{},
	createdUtxos map[utxoref.Key]lcommon.Utxo,
) error

type TxValidationSessionProvider added in v0.70.0

type TxValidationSessionProvider interface {
	WithTxValidationSession(func(
		validate TxValidationFunc,
		stillCurrent func() bool,
	) error) error
}

TxValidationSessionProvider pins an ordered validation pass to one ledger publication and repeatable-read transaction. LedgerState implements it.

type TxValidator

type TxValidator interface {
	ValidateTx(tx ledger.Transaction) error
	// ValidateTxWithOverlay re-validates with intra-block UTxO state:
	// consumedUtxos are inputs spent by earlier txs in the same block
	// (double-spend guard), createdUtxos are outputs produced by those
	// txs (enables spending intra-block outputs).
	ValidateTxWithOverlay(
		tx ledger.Transaction,
		consumedUtxos map[utxoref.Key]struct{},
		createdUtxos map[utxoref.Key]lcommon.Utxo,
	) error
}

TxValidator re-validates a transaction against the current ledger state at block assembly time. This catches transactions whose inputs have been consumed since they entered the mempool, protocol parameter changes, or other state mutations that invalidate previously-accepted transactions.

Jump to

Keyboard shortcuts

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