Documentation
¶
Overview ¶
pchain_height_vm.go — the node-layer boundary that delivers the REAL P-CHAIN EPOCH HEIGHT to the consensus chain engine (the b2 wiring; the LAST QUORUM_FINALITY blocker).
THE BUG IT FIXES. The chain engine pins a block's weighted validator set to a P-CHAIN height (engine/chain: pChainHeightOf → epochHeightLocked → the SINGLE height the set-root commitment, the ⅔-by-stake tally, AND the per-voter pubkey resolution are read at). It reads that height by asserting the VM block to a `PChainHeight() uint64` (consensus block.SignedBlock). A bare VM block — every block the node's plugin VMs (C-Chain EVM, dexvm, …) produce — does NOT expose one, so pChainHeightOf returns 0 and the engine resolves the set at P-chain height 0: the GENESIS set. That is SAFE (non-empty, identical on every node, ≤ current) and UNBRICKS finality, but it FREEZES the epoch at genesis: a validator that JOINED after genesis is absent from set@0, so its vote is dropped and its stake is not counted — finality cannot track a DYNAMIC validator set (a departed genesis majority could even collude). This is the frozen-set caveat Gate D must sign away.
THE FIX. pChainHeightVM wraps the chain's BlockBuilder (the VM the engine builds/parses blocks through) and makes the block the engine sees carry the proposer's P-chain epoch height — WITHOUT changing the inner VM's block format, IDs, or ledger state:
- BuildBlock (proposer): build the inner block, then stamp the proposer's live P-chain epoch height H = max(GetCurrentHeight, parentH) (the proposervm selectChildPChainHeight rule — monotone, ≤ current). Return a pChainHeightBlock whose Bytes() are [magic|H|innerBytes] and whose PChainHeight() is H. Every other method (ID, ParentID, Height, Verify, Accept, …) delegates to the inner block — so the block's IDENTITY and the VM's state are the inner VM's, unchanged.
- ParseBlock (follower): split [magic|H|innerBytes], parse the inner bytes through the inner VM, and re-attach H. Bytes WITHOUT the magic (a raw inner block from a pre-wrapper peer, GetAncestors, or genesis) parse with H=0 → the SAFE genesis-set fallback — never worse than today.
DETERMINISM is the whole point and is why H must travel IN the gossiped bytes. The engine gossips only blk.Bytes(); a follower recovers the block solely via ParseBlock(those bytes). The proposer STAMPS H; every follower ADOPTS the identical H from the envelope — it never recomputes H from its own (skewing) current P-chain view. So every honest node derives the SAME epoch height from the SAME signed block, which is the invariant the engine's cert verifier requires (engine/chain HandleIncomingCert cross-checks the cert's set-root against the set-root WE recompute at OUR epoch height for the block: equal H ⟹ equal root ⟹ the cert verifies; a post-genesis validator's vote+stake now count). A build-time-only stamp would give the proposer a real H but leave followers computing a DIFFERENT root from their own height → the cert is dropped → finality STALLS on a dynamic set (strictly worse than the genesis fallback). That is why the height is carried, not recomputed.
This is a consensus-TRANSPORT framing (an 8-byte height + 4-byte magic prefix on the gossiped bytes), NOT a chain/ledger fork: the inner VM's bytes, block IDs, and execution state are byte-identical, so there is no re-genesis — only a coordinated node upgrade (the whole validator set ships together).
quorum.go — the node-layer wiring that makes the consensus engine's α-of-K quorum-cert finality LIVE (HIGH-4). The consensus engine (luxfi/consensus engine/chain) defines the quorum RULE and the vote/cert topology interfaces; THIS file supplies the concrete node implementations the engine needs:
- blsVoteVerifier — verifies a validator's BLS signature over the canonical vote message against the chain's validator set (the engine is scheme- agnostic; the node injects BLS).
- blsVoteSigner — signs THIS node's accept votes with its staking BLS key so its signature can be collected into a cert.
- validatorStakeSource — supplies per-validator stake so finality is a ⅔-by-STAKE supermajority (HIGH-3), not a raw voter count.
- networkGossiper.BroadcastVote / GossipCert — the QuorumGossiper transport: a follower broadcasts its signed vote to ALL validators and any node that collects α distinct signed votes gossips the assembled cert, so finality never hinges on one node's inbound Chits (liveness; no proposer-freeze).
Inbound votes/certs arrive as app-gossip and are demuxed in blockHandler.Gossip (see manager.go) into engine.HandleIncomingVote / HandleIncomingCert.
Index ¶
- Constants
- Variables
- func NewLinearizeOnInitializeVM(vm consensusvertex.LinearizableVMWithEngine, toEngine chan<- vmcore.Message) *linearizeOnInitializeVM
- type ChainConfig
- type ChainDBManager
- func (m *ChainDBManager) Close() error
- func (m *ChainDBManager) GetAllChainIDs() []ids.ID
- func (m *ChainDBManager) GetDatabase(chainID ids.ID, chainAlias string) (database.Database, error)
- func (m *ChainDBManager) GetDatabasePrefix(chainID ids.ID) []byte
- func (m *ChainDBManager) GetGlobalDB() database.Database
- func (m *ChainDBManager) GetVMDatabase(chainID ids.ID, chainAlias string) (database.Database, error)
- type ChainDBManagerConfig
- type ChainInfo
- type ChainParameters
- type ChainRouter
- type ConsensusOverride
- type Engine
- type Manager
- type ManagerConfig
- type NFTAuthorization
- type Nets
- type QbitEvent
- type Registrant
Constants ¶
const (
ChainLabel = "chain"
)
Variables ¶
var ( // corely shared VM DB prefix VMDBPrefix = []byte("vm") // Bootstrapping prefixes for LinearizableVMs VertexDBPrefix = []byte("vertex") VertexBootstrappingDBPrefix = []byte("vertex_bs") TxBootstrappingDBPrefix = []byte("tx_bs") BlockBootstrappingDBPrefix = []byte("interval_block_bs") // Bootstrapping prefixes for ChainVMs ChainBootstrappingDBPrefix = []byte("interval_bs") )
var ErrNoPrimaryNetworkConfig = errors.New("no net config for primary network found")
var ErrNotQuorumGossip = errors.New("chains: not a quorum gossip envelope")
ErrNotQuorumGossip signals a payload is not a quorum envelope (so the caller falls through to the block-gossip path).
var ( // ErrSkipped is returned when a linearizable VM is asked to perform // chain VM operations ErrSkipped = errors.New("skipped") )
Functions ¶
func NewLinearizeOnInitializeVM ¶ added in v0.1.1
func NewLinearizeOnInitializeVM(vm consensusvertex.LinearizableVMWithEngine, toEngine chan<- vmcore.Message) *linearizeOnInitializeVM
Types ¶
type ChainConfig ¶ added in v0.1.1
ChainConfig is configuration settings for the current execution. [Config] is the user-provided config blob for the chain. [Upgrade] is a chain-specific blob for coordinating upgrades.
type ChainDBManager ¶ added in v1.16.56
type ChainDBManager struct {
// contains filtered or unexported fields
}
ChainDBManager manages chain database access using a single global ZapDB. All chains share one ZapDB instance with prefix-based isolation: 1. Single database - easier to manage, backup, and query across chains 2. Prefix isolation - each chain's data is prefixed by its chainID 3. G-Chain compatible - dgraph can index the entire database for GraphQL queries
func NewChainDBManager ¶ added in v1.16.56
func NewChainDBManager(config ChainDBManagerConfig) *ChainDBManager
NewChainDBManager creates a new chain database manager using a single global ZapDB
func (*ChainDBManager) Close ¶ added in v1.16.56
func (m *ChainDBManager) Close() error
Close is a no-op since the global database lifecycle is managed elsewhere. Chain-specific prefixed databases don't need to be closed separately.
func (*ChainDBManager) GetAllChainIDs ¶ added in v1.16.56
func (m *ChainDBManager) GetAllChainIDs() []ids.ID
GetAllChainIDs returns all chain IDs that have databases allocated. Useful for G-Chain to enumerate chains for indexing.
func (*ChainDBManager) GetDatabase ¶ added in v1.16.56
GetDatabase returns a prefixed database for the given chain. Uses prefix-based isolation on the single global ZapDB.
func (*ChainDBManager) GetDatabasePrefix ¶ added in v1.16.56
func (m *ChainDBManager) GetDatabasePrefix(chainID ids.ID) []byte
GetDatabasePrefix returns the prefix used for a chain's data. This is the chainID bytes, which can be used by G-Chain to iterate chain data.
func (*ChainDBManager) GetGlobalDB ¶ added in v1.16.56
func (m *ChainDBManager) GetGlobalDB() database.Database
GetGlobalDB returns the underlying global database. This is useful for G-Chain (dgraph-powered GraphQL VM) to query across all chains.
func (*ChainDBManager) GetVMDatabase ¶ added in v1.16.56
func (m *ChainDBManager) GetVMDatabase(chainID ids.ID, chainAlias string) (database.Database, error)
GetVMDatabase returns a VM-prefixed database for the given chain. Adds a "vm" prefix within the chain's prefix for VM-specific data.
type ChainDBManagerConfig ¶ added in v1.16.56
type ChainDBManagerConfig struct {
// DB is the global shared database (ZapDB)
DB database.Database
Log log.Logger
}
ChainDBManagerConfig holds configuration for the chain database manager
type ChainInfo ¶ added in v1.24.29
type ChainInfo struct {
ID ids.ID `json:"id"`
Name string `json:"name"`
VMID ids.ID `json:"vmID"`
Bootstrapped bool `json:"bootstrapped"`
}
ChainInfo is the public view of a locally running chain. Returned by Manager.GetChains() and exposed via info.GetChains API.
type ChainParameters ¶
type ChainParameters struct {
// The ID of the blockchain being created.
ID ids.ID
// ID of the Net that validates this blockchain.
ChainID ids.ID
// The genesis data of this blockchain's ledger.
GenesisData []byte
// The ID of the vm this blockchain is running.
VMID ids.ID
// The IDs of the feature extensions this blockchain is running.
FxIDs []ids.ID
// Invariant: Only used when [ID] is the P-chain ID.
CustomBeacons validators.Manager
// Name of the chain (used for HTTP routing alias, e.g., /ext/bc/zoo/rpc)
Name string
}
ChainParameters defines the chain being created
type ChainRouter ¶ added in v1.16.56
type ChainRouter interface {
AddChain(ctx context.Context, chainID ids.ID, handler handler.Handler)
}
ChainRouter is the interface for routing messages to chains. This is defined here to avoid circular imports with the node package.
type ConsensusOverride ¶ added in v1.30.48
type ConsensusOverride struct {
SampleSize int // explicit K (--consensus-sample-size); 0 = unset
QuorumSize int // explicit α (--consensus-quorum-size); 0 = unset
}
ConsensusOverride carries an operator's EXPLICIT --consensus-sample-size / --consensus-quorum-size, threaded from node config. A field is honored only when > 0 (the operator actually set the flag); 0 means "use the dynamic live-set value". An explicit value is honored but CLAMPED to the dynamic floor (see applyConsensusOverride) so an operator can RAISE the committee/quorum but never under-set it below the strict-⅔ stake-cert threshold the engine enforces.
type Engine ¶ added in v1.11.14
type Engine interface {
Start(context.Context, bool) error
StopWithError(context.Context, error) error
Context() context.Context
}
Engine represents a consensus engine
type Manager ¶
type Manager interface {
ids.Aliaser
// Queues a chain to be created in the future after chain creator is unblocked.
// This is only called from the P-chain thread to create other chains
// Queued chains are created only after P-chain is bootstrapped.
// This assumes only chains in tracked chains are queued.
QueueChainCreation(ChainParameters)
// Add a registrant [r]. Every time a chain is
// created, [r].RegisterChain([new chain]) is called.
AddRegistrant(Registrant)
// Given an alias, return the ID of the chain associated with that alias
Lookup(string) (ids.ID, error)
// Given an alias, return the ID of the VM associated with that alias
LookupVM(string) (ids.ID, error)
// Returns true iff the chain with the given ID exists and is finished bootstrapping
IsBootstrapped(ids.ID) bool
// Starts the chain creator with the initial platform chain parameters, must
// be called once.
StartChainCreator(platformChain ChainParameters) error
// GetChains returns info about all locally running chains.
GetChains() []ChainInfo
// RetryPendingChains re-queues chains that were waiting for the specified VM.
// This is called when a VM is hot-loaded via admin.loadVMs.
RetryPendingChains(vmID ids.ID) int
// GetPendingChains returns the chain parameters waiting for a VM to be loaded.
GetPendingChains(vmID ids.ID) []ChainParameters
Shutdown()
}
Manager manages the chains running on this node. It can:
- Create a chain
- Add a registrant. When a chain is created, each registrant calls RegisterChain with the new chain as the argument.
- Manage the aliases of chains
var TestManager Manager = testManager{}
TestManager implements Manager but does nothing. Always returns nil error. To be used only in tests
type ManagerConfig ¶ added in v0.1.1
type ManagerConfig struct {
SybilProtectionEnabled bool
// ConsensusOverride carries the operator's EXPLICIT --consensus-sample-size /
// --consensus-quorum-size. Zero fields mean "use the dynamic live-set value";
// a set field is honored but clamped to the dynamic strict-⅔ floor
// (selectConsensusParams / applyConsensusOverride). Sourced from node Config.
ConsensusOverride ConsensusOverride
StakingTLSSigner crypto.Signer
StakingTLSCert *staking.Certificate
StakingBLSKey bls.Signer
TracingEnabled bool
// Must not be used unless [TracingEnabled] is true as this may be nil.
Tracer trace.Tracer
Log log.Logger
LogFactory log.Factory
VMManager vms.Manager // Manage mappings from vm ID --> vm
BlockAcceptorGroup nodeconsensus.AcceptorGroup
TxAcceptorGroup nodeconsensus.AcceptorGroup
VertexAcceptorGroup nodeconsensus.AcceptorGroup
DB database.Database
MsgCreator message.OutboundMsgBuilder // message creator, shared with network
Router ChainRouter // Routes incoming messages to the appropriate chain
Net network.Network // Sends consensus messages to other validators
Validators validators.Manager // Validators validating on this chain
NodeID ids.NodeID // The ID of this node
NetworkID uint32 // ID of the network this node is connected to
PartialSyncPrimaryNetwork bool
Server server.Server // Handles HTTP API calls
AtomicMemory *atomic.Memory
UTXOAssetID ids.ID
SkipBootstrap bool // Skip bootstrapping and start processing immediately
EnableAutomining bool // Enable automining in POA mode
XChainID ids.ID // ID of the X-Chain,
CChainID ids.ID // ID of the C-Chain,
DChainID ids.ID // ID of the D-Chain (DEX),
CriticalChains set.Set[ids.ID] // Chains that can't exit gracefully
// ChainAuthorizations gates per-validator activation of specific chains on
// X-Chain NFT ownership: a validator may track/validate a listed chain
// only if its staking X-address (StakingXAddress) holds the required
// nftfx NFT. A nil/empty map means no chain is NFT-gated, so every chain
// behaves exactly as before. Critical chains are never gated regardless of
// this map. Node wiring (e.g. DChainID -> dex-validator collection) is set
// in node.go; see authorizeChainActivation.
ChainAuthorizations map[ids.ID]NFTAuthorization
// StakingXAddress is this node's X-Chain address derived from its staking
// keys; it is the owner whose UTXOs the NFT-authorization gate inspects.
// Empty when no chain is gated; an empty address with a gated chain causes
// that chain to be opted out (fail closed).
StakingXAddress ids.ShortID
// DexValidator is the operator's opt-in to PARTICIPATE in the D-Chain DEX
// (track/validate it + dial the venue matcher). It is a NECESSARY condition
// for D-Chain activation that composes AND-wise with the NFT gate: the
// D-Chain (DChainID) activates only if DexValidator is true AND the NFT
// requirement (if any) is satisfied. Default false means a node does NOT
// activate the D-Chain even when it is queued by --track-all-chains; the
// activation authority is authorizeChainActivation, not the platformvm
// tracking set. Only the D-Chain consults this flag; every other chain is
// untouched. Sourced from node Config.DexValidator (see node.go).
DexValidator bool
TimeoutManager timeout.Manager // Manages request timeouts when sending messages to other validators
Health health.Registerer
NetConfigs map[ids.ID]nets.Config // ID -> NetConfig
ChainConfigs map[string]ChainConfig // alias -> ChainConfig
// ShutdownNodeFunc allows the chain manager to issue a request to shutdown the node
ShutdownNodeFunc func(exitCode int)
MeterVMEnabled bool // Should each VM be wrapped with a MeterVM
Metrics metrics.MultiGatherer
MeterDBMetrics metrics.MultiGatherer
FrontierPollFrequency time.Duration
ConsensusAppConcurrency int
// Max Time to spend fetching a container and its
// ancestors when responding to a GetAncestors
BootstrapMaxTimeGetAncestors time.Duration
// Max number of containers in an ancestors message sent by this node.
BootstrapAncestorsMaxContainersSent int
// This node will only consider the first [AncestorsMaxContainersReceived]
// containers in an ancestors message it receives.
BootstrapAncestorsMaxContainersReceived int
Upgrades upgrade.Config
// Tracks CPU/disk usage caused by each peer.
ResourceTracker timetracker.ResourceTracker
StateSyncBeacons []ids.NodeID
ChainDataDir string
Nets *Nets
// SecurityProfile is the chain-wide ChainSecurityProfile resolved
// from the genesis pin during node bootstrap (F102). The chain
// manager forwards a ProfileID + ProfileHash pin to every VM
// Initialize call via the VM config bytes so the rpcchainvm plugin
// (coreth C-Chain) inherits the chain-wide posture without
// re-resolving genesis. Closes red-team finding F118.
//
// Nil under classical-compat or pre-locked-profile chains; the
// stamping pass is a no-op in that case.
SecurityProfile *consensusconfig.ChainSecurityProfile
}
type NFTAuthorization ¶ added in v1.30.16
type NFTAuthorization struct {
AssetID ids.ID // X-Chain nftfx asset (the collection)
GroupID uint32 // nftfx group within the collection; 0 = any group
}
NFTAuthorization identifies the X-Chain NFT a validator's staking address must hold to activate (track/validate) a gated chain.
A chain is "gated" when ManagerConfig.ChainAuthorizations has an entry for its blockchain ID. Chains with no entry are ungated and always authorized — so a nil/empty ChainAuthorizations map preserves today's behavior exactly for every chain (P/C/X/Q/D/…).
type Nets ¶ added in v1.16.56
type Nets struct {
// contains filtered or unexported fields
}
Nets holds the currently running chains on this node
func (*Nets) Bootstrapping ¶ added in v1.16.56
Bootstrapping returns the chainIDs of any chains that are still bootstrapping.
type QbitEvent ¶ added in v1.22.81
type QbitEvent struct {
From ids.NodeID // The node that sent the Qbit
BlockID ids.ID // The block being signaled (preferredID)
RequestID uint32 // Request ID for dedup and stale detection
ReceivedAt time.Time // When the Qbit was received
}
QbitEvent is the normalized internal representation of a received Qbit message. This is pure data - no VM calls, no Verify, no Accept derivation. Vote creation happens separately in applyQbit when the block is available.
type Registrant ¶
type Registrant interface {
// Called when a chain is created
// This function is called before the chain starts processing messages
// [vm] should be a vertex.DAGVM or block.ChainVM
RegisterChain(chainName string, rt *runtime.Runtime, vm vm.VM)
}
Registrant can register the existence of a chain