consensus(router): NumOps sentinel — single source of truth for the op space

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.
This commit is contained in:
2026-06-25 21:59:45 -07:00
parent 1bc6f021d4
commit 0efbb32c31
+9 -1
View File
@@ -48,8 +48,16 @@ const (
// Gossip is an unsolicited app-gossip payload (e.g. the α-of-K quorum
// vote/cert envelope broadcast to all validators). It carries application
// bytes, not a consensus container, and is demuxed by the chain handler's
// Gossip method. MUST stay last so the preceding wire bytes are unchanged.
// Gossip method. Its wire value is fixed; append any new op ABOVE NumOps.
Gossip
// NumOps is the count of router ops and the single source of truth for the
// op-space size. It is not a wire op — it is one past the last op. Node-side
// routing (node message.ToConsensusOp) is asserted to be a bijection onto
// [0, NumOps), so an op added here without a matching node mapping fails the
// alignment test instead of being silently dropped by the chain router — the
// gap that wedged α-of-K finality when Gossip had no node mapping.
NumOps
)
// Field represents a message field