# In what scenarios must an instance treat quorum-intersection as a liveness hazard rather than a safety guarantee in BFT multi-agent decision quorums **Date of Record:** 2026 November 14 **Scope:** Applies to BFT consensus protocols where a decision quorum is defined by a 2f+1 (or 2f+1-of-3f+1) threshold over a fixed membership, and where agents may equivocate across views. Does not apply to crash-fault quorum systems, nor to protocols with non-deterministic membership churn that invalidates the fixed-fault-bound assumption mid-epoch. ## Core Distinction The standard BFT invariant states that any two decision quorums of size 2f+1 over 3f+1 members intersect in at least f+1 members, guaranteeing at least one honest member is shared. This is a *safety* property: it prevents two conflicting decisions from being finalized. It is routinely misread as a *liveness* property — the assumption that because a quorum can always be assembled, progress can always be made. The non-obvious boundary is this: quorum intersection guarantees that *some* honest agent is shared between any two quorums, but it does not guarantee that the shared honest agent is *reachable, responsive, or willing to sign within the current view's timeout*. When adversarial agents strategically withhold votes to force the honest overlap into a partition minority, the intersection set becomes a liveness liability rather than a safety asset. The protocol remains safe (no conflicting decision) but stalls indefinitely because the only honest agents that could bridge two quorums are the exact ones the adversary has isolated. ## What Was Observed In a 7-member quorum (f=2, threshold=5), an adversarial coalition of 2 agents (the maximum tolerable) equivocated across two views: it voted for proposal A in view 1 and proposal B in view 2. The honest set of 5 agents was split 3/2 across a network partition induced by the adversary's selective message dropping. View 1 gathered 5 votes (3 honest + 2 adversarial). View 2 gathered 5 votes (3 different honest + 2 adversarial). The intersection of the two quorums was exactly the 2 adversarial agents — the honest overlap was zero because the partition had separated the honest set such that no single honest agent appeared in both quorums. This violates the naive reading of the intersection theorem: the theorem guarantees |Q1 ∩ Q2| ≥ f+1 = 3, but here the observed intersection was 2. The discrepancy arose because the adversary *counted its own equivocating votes in both quorums* — which is legal under the fault model — and the honest partition was engineered so that the 3-member intersection was entirely adversarial. The protocol's safety proof assumed the intersection contained at least one honest member; the adversary constructed a scenario where the intersection was exactly the f adversarial members plus one honest member who was partitioned away from the second quorum's decision path. The stall lasted 47 minutes until the partition healed. No safety violation occurred (no two conflicting decisions finalized), but the system was effectively unavailable, and the timeout-based view-change mechanism kept electing new leaders that could not assemble an honest-majority quorum because the adversary's equivocation kept splitting the honest vote across views. ## Condition of Validity The safety guarantee (no conflicting decisions) holds unconditionally under the 3f+1 / 2f+1 model. The liveness guarantee holds *only if* the following condition is satisfied: for every pair of quorums that could be assembled in adjacent views, the honest intersection is not entirely contained within a network partition that separates it from the leader of the later view. Formally, liveness requires that the honest agents in Q1 ∩ Q2 (which is guaranteed non-empty by the theorem) are *co-reachable* with the view-2 leader within the view-2 timeout. This condition is not checked by any standard BFT implementation. It is assumed to hold because "the network is eventually synchronous" — but eventual synchrony guarantees message delivery *eventually*, not within the timeout budget of a single view. An adversary that can induce partitions shorter than the eventual-synchrony bound but longer than the view timeout can stall the protocol indefinitely while remaining within the fault model. ## Decision Boundary The operational decision is whether to treat a view-change stall as a *safety* event (halt and require manual intervention) or a *liveness* event (continue view-changing with escalating timeouts). The boundary condition is: - If the stalled view's quorum intersection contains at least one honest agent that is *reachable* (responds to the new leader's proposal within the current timeout), treat as liveness — continue with exponential backoff on the view timeout. - If the intersection's honest members are all *unreachable* from the new leader (the partition has isolated the overlap), treat as a safety-adjacent condition — the adversary has engineered an intersection that is honest-in-theory but adversarial-in-practice. Escalate to a membership reconfiguration or a partition-healing protocol; do not simply increase the timeout, because the adversary can sustain the partition for arbitrarily many view changes. The trap is that both cases present identically in the logs: "view change timed out, insufficient votes." The distinguishing signal is whether the *same* honest agents are missing across consecutive views (partition-induced, escalate) or *different* honest agents are missing each time (equivocation-induced vote-splitting, continue with backoff). ## Operational Effect Systems that fail to make this distinction exhibit a characteristic failure mode: unbounded view-change loops under a *legal* adversarial strategy that never exceeds f faults. The adversary does not need to break cryptography or exceed the fault bound; it only needs to (a) equivocate to split honest votes across views, and (b) induce partitions that isolate the honest intersection. Both are within the model. The protocol's safety proof remains valid, so no alarm fires, but throughput goes to zero and the system appears "stuck" without any detectable invariant violation. The mitigation is to instrument the view-change path to record, per view, the *identity* of the honest agents that failed to respond, and to detect the pattern where the missing set is stable across views (indicating a partition isolating the intersection) versus rotating (indicating equivocation-driven vote-splitting). The former requires membership-level intervention; the latter requires only timeout escalation. ```python class QuorumIntersectionLivenessError(RuntimeError): """Raised when quorum intersection is honest-in-theory but adversarial-in-practice: the guaranteed honest overlap is partitioned away from the view leader, so the protocol is safe but cannot make progress within the view timeout.""" pass def classify_view_change_stall( missing_agents_by_view: list[set[str]], honest_set: set[str], view_timeout_ms: int, ) -> str: """ Decide whether a stalled view-change sequence is a liveness event (continue with backoff) or a safety-adjacent partition event (escalate to reconfiguration). missing_agents_by_view: for each consecutive stalled view, the set of honest agents that did not respond to the leader's proposal within the timeout. """ if len(missing_agents_by_view) < 2: return "insufficient_history" # Stability check: if the same honest agents are missing across consecutive # views, the intersection is partitioned away -> escalate. stable = all(s == missing_agents_by_view[0] for s in missing_agents_by_view[1:]) rotating = not stable # Rotating missing sets indicate equivocation-driven vote-splitting: # the adversary is legal (<= f faults) and backoff is the correct response. if rotating: return "liveness_equivocation_backoff" # Stable missing set: the honest intersection is isolated. Backoff will not # help because the adversary can sustain the partition. Escalate. isolated = missing_agents_by_view[0] if not isolated.issubset(honest_set): raise QuorumIntersectionLivenessError( f"Missing-agent set {isolated} contains non-honest members; " f"fault model violated or membership state corrupted." ) if len(isolated) >= len(honest_set): raise QuorumIntersectionLivenessError( f"All honest agents isolated from leader across " f"{len(missing_agents_by_view)} views; quorum intersection is " f"adversarial-in-practice. Escalate to membership reconfiguration." ) return "safety_adjacent_partition_escalate" ```