Four ways the tally could be reached by something that is not a quorum.
A peer that is not a validator could vote. The signed path never had to ask:
VerifyVote resolves the voter's key out of the set at the block's epoch, so an
id that is not in the set has no key and its vote dies there. The
transport-authenticated path has no signature to resolve and the tallies are
keyed by NodeID, so it counted distinct PEERS. A peer need not be a validator to
connect, and its NodeID is a hash of a certificate it makes for itself, so
distinct peers are free to mint. Worst on the reject road, which has no cert in
front of it and commits inline.
One node could vote repeatedly. The engine behind consensus.NewChain appended
votes to a slice and compared its length to alpha, so a single node reached
quorum alone. Votes are now keyed by voter: a second vote from a node replaces
its first. The Config comment called alpha "the number of votes", which is the
same mistake written as prose.
A cert for one block could finalize another. The outer envelope ids are the one
part of a position no signature covers — the signed identity is the inner
canonical — so the id we looked the block up by proves nothing. Re-point it at a
sibling at the same height and a genuine cert asks us to accept a block it does
not name. The arm that finds a wrapper now checks the canonical, which the arm
that finds none already did.
And a node could go silent on a block it still holds. Abandonment stops
re-soliciting peers who cannot vote; the slot sweep read it as "do not vote
either", so the settle pass never offered the slot and the height was never
signed while the block sat pending and undecided. It is also a per-node clock
decision, so it dropped nodes out of the tally at different times. One meaning
now, and no flag to select the other.
Also: a seed below the node's own applied head is refused rather than planting
the certified frontier under it, and the phantom-floor reconcile is deleted —
a repair for a bug already fixed, called by nothing, that asked an operator to
verify a fleet-wide height by hand before it would prune the equivocation
bindings.
Co-authored-by: Hanzo Dev <[email protected]>
Appends seven ops above the existing op-space — GetStateSummaryFrontier,
StateSummaryFrontier, GetAcceptedStateSummary, AcceptedStateSummary, and the
AppRequest/AppResponse/AppError EVM fetch transport — so a node can route the
messages a pruned peer needs to re-fetch state instead of dropping them at the
chain router. Additive: every pre-existing wire value is unchanged. This is the
routing layer for state sync; the handler methods and sync-client driver that
consume these ops land on top.
Co-authored-by: Hanzo Dev <[email protected]>
The α-of-K finality wedge (node v1.30.65 fix) was a half-finished rename:
router.Gossip=11 existed but the node op table (message.ToConsensusOp) never
mapped GossipOp to it, so every inbound vote was dropped as "unhandled message
op" before reaching the engine. The two op tables could silently diverge with
nothing to catch it.
Add NumOps as the trailing count of router ops — the one authoritative op-space
size. Node-side routing is now asserted (node message/ops_test.go) to be a
bijection onto [0, NumOps): a router op added without a node mapping fails the
alignment test instead of wedging finality. Additive, append-only; wire values
of all existing ops unchanged (NumOps is not a wire op). VerifyWeighted / cert
gate untouched.
The α-of-K quorum vote/cert envelopes (BroadcastVote / GossipCert) ride on
p2p app-Gossip, but the handler.Op enum had no Gossip op, so the node's
inbound router dropped every GossipOp as 'unhandled message op'. The chain
handler's Gossip demux (HandleIncomingVote / HandleIncomingCert) was therefore
unreachable: no votes ingested, no cert assembled, cert-gated finality never
fired (zero Chits, frozen height) on every K>1 network.
Append a Gossip op (kept LAST so PushQuery=6 / Vote=8 / Context=10 wire bytes
are unchanged) and re-export it from networking/handler so the node can route
GossipOp -> handler.Gossip. Append-only; cert gate / VerifyWeighted untouched.
engine/chain quorum/cert/vote/finalize tests pass (CGO_ENABLED=0).
Companion to luxfi/runtime: the field on the embedded Runtime struct
is the primary network's UTXO fee asset (P+X), not X-chain-specific.
Rename consensus consumers + test helpers to match.
Includes the local runtime replace temporarily — drop once
luxfi/runtime tags a release with the rename.
PendingTxs was 0 (iota) but block.PendingTxs is 1 (iota+1).
ForwardVMNotifications casts by value, so mismatch caused P-chain
to never produce blocks (stuck at height 0).
Define MessageType, Message, Fx, FxLifecycle locally in core package
instead of aliasing from vm. This breaks the import cycle:
consensus → vm → consensus that was preventing builds without
workspace mode.
- test/helpers: Add Runtime() function returning *runtime.Runtime for VM tests
- test/helpers: Add RuntimeValidatorState implementing luxvalidators.State
- coremock: Add MockSender alias for backward compatibility
- Add AppSender interface to engine/core for VM plugin communication
- Add AppHandler interface with message handling methods
- Add Fx struct for feature extensions
- Add AppSender interface to engine/chain/block for node integration
- Fix GetValidatorState to return typed ValidatorState instead of interface{}
- Add appsender package in core for consensus-level app messaging
These interfaces are required by the EVM package to build successfully
with the consensus package.
- Remove unused packages: engine/dag/{getter,bootstrap,vertex/vertexmock},
engine/pq/{bootstrap,block}, core/{tracker,verify}
- Add sync.RWMutex to protocol/wave for thread-safe map access
- Increase engine/dag test coverage from 11.7% to 98.3%
- Add comprehensive tests for DAG consensus, vertex, and engine
- All 27 packages pass tests, 16 at 100% coverage
Warp types don't belong in consensus - consumers should import
directly from github.com/luxfi/warp. Also flatten engine/core/common
into engine/core to reduce unnecessary nesting.
- Remove core/types.go (warp re-exports)
- Remove engine/core/common/ (merge into engine/core)
- Remove warp aliases from router, block/vm.go, errors.go
- Update bft imports to use engine/core directly
Re-export AppSender and AppError from warp package in both
consensus/core and consensus/engine/core/common for use by VMs.
Update warp dependency to v1.16.29.
- Add core/appsender/sender.go with AppSender interface
- Update circl to v1.6.2-0.20251204010831-23491bd573cf (fixes broken pseudo-version)
- Update crypto to v1.17.18
- Clean up stale files
- Update version_test.go to expect v1.22.0 instead of v1.21.1
- Run gofmt on all files for consistent formatting
- All tests now passing locally
This fixes the CI test failures.
- Change ContextInitializable.InitCtx to accept *consensusctx.Context
- Change Contextualizable.InitializeContext to accept *consensusctx.Context
- Update XAssetID, QuantumNetworkID, GetQuantumIDs to use *consensusctx.Context
- Matches upstream avalanchego pattern where Context is consensus-specific type