Bump consensus v1.35.37 -> v1.36.1 + rip node-side view-change plumbing

v1.36 upstreamed round-scoped view-change INTO the engine (internal prevote/POL
in attestation/reconcile/cert), dropping the external hooks config.Parameters.
ViewChange + Runtime.HandleIncomingPrevote. Ripped the node's now-redundant
external plumbing: the ViewChange enable block (LUX_CONSENSUS_VIEW_CHANGE gate),
the quorumKindPrevote gossip routing + HandleIncomingPrevote call, the dead
BroadcastPrevote gossiper method, and the quorumKindPrevote envelope kind. The
engine owns view-change natively now (fail-secure halt on 2a-n>f unchanged).

Whole node builds EXIT 0 on v1.36.1; luxd binary compiles (55M).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
zeekay
2026-07-10 21:07:38 -07:00
co-authored by Hanzo Dev
parent 87a57925f3
commit 9626ca6c2f
4 changed files with 14 additions and 55 deletions
+6 -41
View File
@@ -1199,25 +1199,12 @@ func (m *manager) buildChain(chainParams ChainParameters, sb nets.Net) (*chainIn
return nil, fmt.Errorf("refusing to start multi-node chain %s with non-BFT consensus params: %w", chainParams.ID, err)
}
}
// Round-scoped view-change (restores liveness under competing siblings + a zero-margin
// quorum — the 415→416 freeze). OPT-IN per deployment via LUX_CONSENSUS_VIEW_CHANGE=true
// so devnet/testnet can enable it without a mainnet default (mainnet is owner-gated). Only
// meaningful on a multi-validator (K>1) chain; K==1 has no competing proposers. The engine
// itself fail-secure HALTS the view-change if the committee fails the 2α−n>f bound, so
// enabling it can never weaken safety — at worst it halts (never forks).
if consensusParams.K > 1 && strings.EqualFold(os.Getenv("LUX_CONSENSUS_VIEW_CHANGE"), "true") {
consensusParams.ViewChange = true
// NOTE: presetK/presetAlpha are the sample preset (MainnetParams K=21/α=15),
// NOT the finality committee. The α-of-K cert and the view-change POL/precommit are
// sized to the LIVE validator set at runtime via effectiveCommittee/bftCommittee
// (e.g. 5 validators → K=5/α=4); the engine logs the effective (K,α) whenever it
// re-clamps ("committee re-clamped … newK/newAlpha"). Do not read presetK as the quorum.
m.Log.Info("round-scoped view-change ENABLED for chain",
log.Stringer("chainID", chainParams.ID),
log.Int("presetK", consensusParams.K),
log.Int("presetAlpha", consensusParams.AlphaConfidence),
log.String("note", "finality committee sized to the live validator set at runtime (see engine committee-clamp log for effective K/alpha)"))
}
// Round-scoped view-change (restores liveness under competing siblings + a
// zero-margin quorum — the 415→416 freeze) is now owned INTERNALLY by the
// consensus engine (v1.36+): the engine drives the prevote/POL tally + the
// lock/unlock re-convergence natively and fail-secure HALTS if the committee
// fails the 2α−n>f bound. The node no longer sets a ViewChange param or routes
// prevotes — that external plumbing was upstreamed into the engine.
_, innerIsDAGNative := vmTyped.(interface {
Linearize(context.Context, ids.ID, chan<- vm.Message) error
})
@@ -3873,10 +3860,6 @@ func (b *blockHandler) Gossip(ctx context.Context, nodeID ids.NodeID, msg []byte
b.engine.HandleIncomingVote(blockID, payload)
case quorumKindCert:
b.engine.HandleIncomingCert(payload)
case quorumKindPrevote:
// Round-scoped view-change prevote: the engine decodes+verifies
// (height,round,canonical,sig) from the payload and tallies it toward a POL.
b.engine.HandleIncomingPrevote(payload)
}
return nil
}
@@ -4191,24 +4174,6 @@ func (g *networkGossiper) BroadcastVote(chainID ids.ID, networkID ids.ID, blockI
return g.net.Gossip(msg, nil, g.networkID, -1, 0, 0).Len()
}
// BroadcastPrevote sends this node's signed ROUND-SCOPED view-change prevote (the
// non-binding preference signal) for `canonical` at (height, round) to ALL validators,
// framed in a quorum envelope (kind 3) and decoded by blockHandler.Gossip into
// engine.HandleIncomingPrevote. Prevotes never finalize anything — they drive the POL +
// the lock/unlock rule that lets a competing-sibling split RE-CONVERGE (liveness under a
// down proposer + zero-margin quorum). Only emitted when the chain runs params.ViewChange.
func (g *networkGossiper) BroadcastPrevote(chainID ids.ID, networkID ids.ID, height uint64, round uint32, canonical ids.ID, voteBytes []byte) int {
if g.net == nil || g.msgCreator == nil {
return 0
}
envelope := encodeQuorumGossip(quorumKindPrevote, canonical, voteBytes)
msg, err := g.msgCreator.Gossip(chainID, envelope)
if err != nil {
return 0
}
return g.net.Gossip(msg, nil, g.networkID, -1, 0, 0).Len()
}
// GossipCert broadcasts an assembled α-of-K finality cert to ALL validators so
// followers finalize blockID on a verifiable proof (HandleIncomingCert), not a
// fast-follow guess. Best effort: the gossiping node's own finality is already
+5 -9
View File
@@ -374,17 +374,13 @@ func hashValidatorSet(set map[ids.NodeID]*validators.GetValidatorOutput) ids.ID
//
// kind 1 = signed vote (payload = engine encodeSignedVote: nodeID+sig)
// kind 2 = finality cert (payload = engine cert MarshalBinary)
// kind 3 = round-scoped view-change PREVOTE (payload = engine encodeSignedPrevote:
// nodeID+height+round+canonical+sig, domain "LUX/chain/prevote/v1"). The
// envelope blockID field carries the canonical for routing but is advisory —
// HandleIncomingPrevote decodes the authoritative (height,round,canonical) from
// the payload and verifies the signature over the reconstructed message.
// (round-scoped view-change prevotes are engine-INTERNAL since consensus v1.36 —
// the node no longer frames or routes a prevote kind)
var quorumGossipMagic = [4]byte{'L', 'X', 'Q', 0x01}
const (
quorumKindVote byte = 1
quorumKindCert byte = 2
quorumKindPrevote byte = 3
quorumKindVote byte = 1
quorumKindCert byte = 2
)
// ErrNotQuorumGossip signals a payload is not a quorum envelope (so the caller
@@ -410,7 +406,7 @@ func decodeQuorumGossip(data []byte) (kind byte, blockID ids.ID, payload []byte,
kind = data[4]
copy(blockID[:], data[5:5+32])
payload = data[5+32:]
if kind != quorumKindVote && kind != quorumKindCert && kind != quorumKindPrevote {
if kind != quorumKindVote && kind != quorumKindCert {
return 0, ids.Empty, nil, ErrNotQuorumGossip
}
return kind, blockID, payload, nil
+1 -1
View File
@@ -26,7 +26,7 @@ require (
github.com/huin/goupnp v1.3.0
github.com/jackpal/gateway v1.1.1
github.com/jackpal/go-nat-pmp v1.0.2
github.com/luxfi/consensus v1.35.37
github.com/luxfi/consensus v1.36.1
github.com/luxfi/crypto v1.19.26
github.com/luxfi/database v1.20.4
github.com/luxfi/ids v1.3.1
+2 -4
View File
@@ -315,8 +315,8 @@ github.com/luxfi/compress v0.0.5 h1:4tEUHw5MK1bu5UOjfYCt4OKMiH7yykIgmGPRA/BfJTM=
github.com/luxfi/compress v0.0.5/go.mod h1:Cc1yxD2pfzrvpO32W2GDwLKff+CylHEvzZh2Ko8RSIU=
github.com/luxfi/concurrent v0.0.3 h1:eJyv1fhaC0jMLMw6+QS774cUmp7GK+ouMgvLCqnC7cc=
github.com/luxfi/concurrent v0.0.3/go.mod h1:Aj/FR5NpM0cB2P4Nt3+tz9+dV6V+LUW4HuMgSjwq5hw=
github.com/luxfi/consensus v1.35.37 h1:y76OfTaNH13S7X5d4kDvnxdLM16wtSekDlBhKlPStL8=
github.com/luxfi/consensus v1.35.37/go.mod h1:hmsGz3CcTrKxvw7/YSmfu8qAtzgyL5zQ3ajpUNcub/4=
github.com/luxfi/consensus v1.36.1 h1:aHCUTgu0SaJXRouisQuFo1CkkdpHX0XftpM9GU07LzU=
github.com/luxfi/consensus v1.36.1/go.mod h1:hmsGz3CcTrKxvw7/YSmfu8qAtzgyL5zQ3ajpUNcub/4=
github.com/luxfi/constants v1.6.1 h1:4AfBh1YxDgnQjWPLqLpjiBaLAjPBw5naTTzRWWM19ms=
github.com/luxfi/constants v1.6.1/go.mod h1:Pu5jWHdnUtQRbWC43yTUjU/pbIIKMDOd2a2yroSfo48=
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
@@ -427,8 +427,6 @@ github.com/luxfi/vm v1.2.5 h1:L1etY/gh68f9tns1BtyDUpZcBVqc3Ng1mqU3n38GyLo=
github.com/luxfi/vm v1.2.5/go.mod h1:TCCg4lDcQFCjxaxfXnxPIrpRSVAyyf2ucT4A4w654Hg=
github.com/luxfi/warp v1.24.0 h1:jrcJNlbOiZsAEopJMy9bSaCwI5NDZ8qgp/6sNoXqepg=
github.com/luxfi/warp v1.24.0/go.mod h1:bKvTi24JHlANsl7qkWZAVr/DsMfvwy42f+Cc9x4+Sq8=
github.com/luxfi/zap v0.8.11 h1:NIiZp/YyS1TQHfU/PHnMXynzPOKCfZOQssn5ytCdYXg=
github.com/luxfi/zap v0.8.11/go.mod h1:JfqII8VtVQYLLTX6obU1DP9sjGqf9L24vfug5ifh0b8=
github.com/luxfi/zapcodec v1.0.1 h1:pRxLxCOi6uihQMg8A8riDjNjefU2cXZxfRVZ+obeuL8=
github.com/luxfi/zapcodec v1.0.1/go.mod h1:txrRt2JK4O76ssTxlXIwoNVsgzyZVL0ES4mlXqGNogs=
github.com/luxfi/zapdb v1.10.1 h1:XV3k4UTTKKxUMgbfC7woPXgUEIJd3P5nj2lGTQ88xeE=