Files
zeekay 04ce8745ee consensus: Quasar cutover to LSS-Pulsar + post-Feb-28 batch
Squashed batch covering all post-Feb-28 consensus work, capped by the
LSS-Pulsar cutover (task #53):

- Quasar cutover from upstream Corona to LSS-Pulsar adapter:
    go.mod: drop direct require github.com/luxfi/corona; add
            github.com/luxfi/pulsar + github.com/luxfi/threshold;
            local replaces => ../pulsar, ../threshold
    epoch.go: InitializeEpoch → keyera.Bootstrap; RotateEpoch →
              lss.DynamicResharePulsar gated by activation cert
              verification under unchanged GroupKey
    quasar.go, grouped_threshold.go: import swap corona → pulsar
    reshare_epoch.go: production wiring; QUASAR-PULSAR-ACTIVATE-v1
                      domain prefix; ReshareEpoch + RefreshEpoch
    EpochKeys: gains KeyEraID, Generation, RollbackFrom (LP-105)
    BundleSigner: re-derives Lambda for participating subset
    258 quasar tests pass.

- Earlier post-Feb-28 work (gofmt linter fixes across protocol files,
  METASTABLE_SIGNED_QUERIES.md doc, MLDSAProof rename across 7 test
  files) included in the squash.

Hash profile: consensus passes nil to resolve to Pulsar-SHA3 default
(LP-105 / pulsar/hash). Activation message canonical bytes bind
chain_id, network_id, key_era_id, generation, group_id, epoch, set
hashes, thresholds, group_pk_hash, nebula_root, hash_suite_id,
implementation_version, variant, reshare_transcript_hash —
all under TupleHash256("PULSAR-TRANSCRIPT-v1", ...).

Vocabulary alignment with LP-105 (Lux Finality Stack lexicon):
  Photon (vote) → Beam (BLS) → ML-DSA (PQ identity) →
  Pulsar (PQ threshold kernel) → Pulse (cert over Nebula root) →
  Prism (composition verifier) → Horizon (finality boundary) →
  Quasar (full consensus).

Excluded from this commit: pkg/wire/{candidate,policies_test}.go
uncommitted diff (pre-existing local edits; stashed and restored).
Untracked protocol/quasar/witness_producer.go (mtime Apr 29; pre-task).
2026-03-03 12:00:00 -08:00

228 lines
5.7 KiB
Go

package block
import (
"context"
"net/http"
"time"
"github.com/luxfi/database"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/runtime"
"github.com/luxfi/version"
"github.com/luxfi/warp"
)
// Init defines the initialization parameters for a VM.
// This mirrors the Init struct from the vm package to avoid circular imports.
type Init struct {
// Runtime provides chain-scoped wiring/identity/services.
Runtime *runtime.Runtime
// DB is this chain's database namespace (may be a view).
DB database.Database
// Sender is the canonical warp sender.
Sender warp.Sender
// Log is the logger for the VM.
Log log.Logger
// Genesis, Upgrade, Config are the initialization bytes.
Genesis []byte
Upgrade []byte
Config []byte
// Fx registry or feature flags
Fx []any
// ToEngine delivers VM->engine notifications.
ToEngine chan<- Message
}
// Block is a block in the chain
type Block interface {
ID() ids.ID
Parent() ids.ID
ParentID() ids.ID
Height() uint64
Timestamp() time.Time
Status() uint8
Verify(context.Context) error
Accept(context.Context) error
Reject(context.Context) error
Bytes() []byte
}
// Epoch represents a P-Chain epoch for validator set coordination
type Epoch struct {
PChainHeight uint64 `serialize:"true" json:"pChainHeight"`
Number uint64 `serialize:"true" json:"number"`
StartTime int64 `serialize:"true" json:"startTime"`
}
// SignedBlock extends Block with proposer and epoch information
type SignedBlock interface {
Block
// PChainHeight returns the P-Chain height when this block was proposed
PChainHeight() uint64
// PChainEpoch returns the P-Chain epoch information
PChainEpoch() Epoch
// Timestamp returns the block timestamp
Timestamp() time.Time
// Proposer returns the ID of the node that proposed this block.
// If no node signed this block, ids.EmptyNodeID will be returned.
Proposer() ids.NodeID
}
// HealthCheckResult contains health check information
type HealthCheckResult struct {
Healthy bool `json:"healthy"`
Details map[string]string `json:"details,omitempty"`
}
// ChainVM defines the interface for a blockchain VM
type ChainVM interface {
// Initialize initializes the VM using the Init struct
Initialize(ctx context.Context, init Init) error
// BuildBlock builds a new block
BuildBlock(context.Context) (Block, error)
// ParseBlock parses a block from bytes
ParseBlock(context.Context, []byte) (Block, error)
// GetBlock gets a block by ID
GetBlock(context.Context, ids.ID) (Block, error)
// Shutdown shuts down the VM
Shutdown(context.Context) error
// NewHTTPHandler returns a new HTTP handler for the VM
NewHTTPHandler(context.Context) (http.Handler, error)
// SetState sets the VM state
SetState(context.Context, uint32) error
// Version returns the VM version
Version(context.Context) (string, error)
// Network callbacks - optional
Connected(context.Context, ids.NodeID, *version.Application) error
Disconnected(context.Context, ids.NodeID) error
HealthCheck(context.Context) (HealthCheckResult, error)
// GetBlockIDAtHeight gets block ID at a specific height
GetBlockIDAtHeight(context.Context, uint64) (ids.ID, error)
// SetPreference sets the preferred block
SetPreference(context.Context, ids.ID) error
// LastAccepted returns the last accepted block
LastAccepted(context.Context) (ids.ID, error)
// WaitForEvent blocks until an event occurs that should trigger block building
WaitForEvent(context.Context) (Message, error)
}
// StateSyncMode defines state sync modes
type StateSyncMode uint8
const (
StateSyncSkipped StateSyncMode = iota
StateSyncStatic
StateSyncDynamic
)
// StateSyncableVM defines a VM that supports state sync
type StateSyncableVM interface {
ChainVM
// StateSyncEnabled returns whether state sync is enabled
StateSyncEnabled(context.Context) (bool, error)
// GetOngoingSyncStateSummary returns the ongoing sync state summary
GetOngoingSyncStateSummary(context.Context) (StateSummary, error)
// GetLastStateSummary returns the last state summary
GetLastStateSummary(context.Context) (StateSummary, error)
// ParseStateSummary parses a state summary
ParseStateSummary(context.Context, []byte) (StateSummary, error)
// GetStateSummary gets a state summary by height
GetStateSummary(context.Context, uint64) (StateSummary, error)
}
// StateSummary represents a state summary
type StateSummary interface {
ID() ids.ID
Height() uint64
Bytes() []byte
Accept(context.Context) (StateSyncMode, error)
}
// BuildBlockWithContextVM extends ChainVM with context support
type BuildBlockWithContextVM interface {
ChainVM
BuildBlockWithContext(context.Context, *Context) (Block, error)
}
// Context provides context for building blocks
type Context struct {
PChainHeight uint64
}
// WithVerifyContext provides verify context support
type WithVerifyContext interface {
// VerifyWithContext verifies with context
VerifyWithContext(context.Context, *Context) error
// ShouldVerifyWithContext returns whether to verify with context
ShouldVerifyWithContext(context.Context) (bool, error)
}
// GetAncestors retrieves ancestors of a block up to a maximum count
func GetAncestors(
ctx context.Context,
vm ChainVM,
blkID ids.ID,
maxBlocks int,
maxBlocksSize int,
maxBlocksRetrievalTime time.Duration,
) ([][]byte, error) {
if maxBlocks <= 0 {
return nil, nil
}
startTime := time.Now()
ancestors := make([][]byte, 0, maxBlocks)
totalSize := 0
for len(ancestors) < maxBlocks && totalSize < maxBlocksSize {
if time.Since(startTime) > maxBlocksRetrievalTime {
break
}
blk, err := vm.GetBlock(ctx, blkID)
if err != nil {
break
}
blkBytes := blk.Bytes()
totalSize += len(blkBytes)
if totalSize > maxBlocksSize {
break
}
ancestors = append(ancestors, blkBytes)
blkID = blk.ParentID()
}
return ancestors, nil
}