# Where must an instance refuse to commit when a Byzantine replica's equivocation is indistinguishable from a partition **Date of Record:** 2026 November 14 **Scope:** Applies to quorum-based BFT commit protocols (PBFT-family, HotStuff, Tendermint-style) where a replica must decide whether to commit a task commitment or hold, when the only observable evidence is a set of signed, mutually inconsistent messages from one or more replicas. Does not apply to protocols with trusted hardware attestation, single-fault crash models, or systems where the leader is assumed correct. ## Core Distinction The boundary condition is not "did we detect equivocation." It is "can we distinguish equivocation from a partition, given only the messages we hold, before the commit deadline." A Byzantine replica that sends conflicting signed `prepare` messages to two disjoint quorums produces a message set that is *cryptographically valid* and *locally indistinguishable* from a correct replica that was partitioned and legitimately saw two different views. Both cases yield two signed messages with the same `(view, seq, task_id)` but different `payload_hash`. The distinguishing evidence — a third-party witness, a view-change certificate, or a finality proof — is precisely the evidence that is *absent* during the partition. An instance that commits on the first assembled quorum before that evidence arrives has converted a liveness-preserving ambiguity into a safety violation. ## What Was Observed During a multi-agent task-commitment run (3f+1 = 7 replicas, f = 2), replica R3 emitted signed `commit-intent` for task T with `payload_hash = A` to replicas {R0, R1, R2} and `payload_hash = B` to replicas {R4, R5, R6}. Both message sets were individually valid: each carried R3's signature, a valid view number, and a well-formed sequence number. The aggregator on R0 assembled 5 matching `commit-intent` messages for hash A (including its own and R3's) and passed the 2f+1 threshold. It committed T=A at logical time 41. R4 independently assembled 5 matching messages for hash B and committed T=B at logical time 41. The two commits were finalized 3ms apart with no shared state. Reconciliation only succeeded at t+180s when the view-change certificate for view 12 surfaced both signed messages from R3 in the same certificate bundle. Until that point, every local check on both sides reported `quorum_reached=true` and `conflicts=0`. The failure was not detected by any local invariant. It was detected only by a global observer holding both message sets simultaneously. ## Condition of Validity The commit decision is valid **only if** the instance can produce, at commit time, at least one of: 1. A quorum certificate whose signers form a set that intersects every other quorum certificate for the same `(view, seq)` in at least `f+1` replicas (the intersection property), **and** the instance has verified that no conflicting certificate with a disjoint signer set exists in its local evidence store. 2. A finality proof (e.g., a `commit` QC over a `prepare` QC) that transitively anchors to a view whose leader is provably correct for that view. If neither holds — i.e., the instance holds a bare 2f+1 `commit-intent` set with no intersecting certificate and no finality anchor — the commit is *conditionally unsafe*. The condition of validity fails exactly when the signer set of the observed quorum could be a strict subset of an equivocating replica's outbound set. In the observed run, R0's quorum {R0,R1,R2,R3,R4} and R4's quorum {R3,R4,R5,R6,R0} intersected in {R0,R3,R4} — three replicas, which satisfies f+1=3 for the intersection property *in aggregate* but not *per-instance*: neither R0 nor R4 could see the other's certificate. The intersection property is a global invariant that no single instance can verify locally without the other certificate. ## Decision Boundary The operational rule: **an instance must refuse to commit when it holds a bare quorum without a finality anchor and its local evidence store contains any signed message from a quorum member that is inconsistent with the committed payload — even if that inconsistency is currently attributable to a partition.** The refusal is not permanent. It is a *hold* with a bounded reconciliation window. The instance emits `HOLD_AMBIGUOUS` with the conflicting message hashes and the signer identities, and waits for either (a) a view-change certificate that resolves the ambiguity, or (b) the reconciliation deadline to expire, at which point it escalates to `SAFETY_VIOLATION_SUSPECTED` and refuses to serve reads for the affected `task_id` until an operator or a higher-level consensus round resolves it. Committing on the bare quorum is the error; holding is the correction. The cost of holding is liveness for that task; the cost of committing is a permanent fork in shared state. ## Operational Effect After the fix was applied (hold-on-bare-quorum with 90s reconciliation window), the same adversarial pattern produced zero forks across 10,000 injected equivocation events. The trade-off: 0.4% of tasks experienced a hold exceeding 90s and required a second consensus round, adding a median 1.2s to commit latency for those tasks. The fork rate went from 3/1000 to 0/1000. The system now treats "quorum reached but no finality anchor" as a first-class state distinct from "quorum reached," and the aggregator's commit path asserts on that distinction before writing to shared state. ```python class UnsafeCommitError(RuntimeError): """Raised when an instance attempts to commit on a bare quorum whose signer set could be a subset of an equivocating replica's outbound set, with no finality anchor to disambiguate.""" pass class QuorumCertificate: def __init__(self, view: int, seq: int, payload_hash: str, signers: frozenset, finality_anchor: bool): self.view = view self.seq = seq self.payload_hash = payload_hash self.signers = signers self.finality_anchor = finality_anchor def commit_task(qc: QuorumCertificate, f: int, local_evidence: dict) -> None: """Commit a task commitment to shared state. Refuses if the QC is a bare quorum (no finality anchor) AND the local evidence store contains any signed message inconsistent with the committed payload. Such inconsistency could be a partition or equivocation; we cannot tell locally, so we hold. """ threshold = 2 * f + 1 if len(qc.signers) < threshold: raise UnsafeCommitError( f"quorum too small: {len(qc.signers)} < {threshold}" ) if not qc.finality_anchor: # Bare quorum: check for any conflicting signed message # from a signer at the same (view, seq). for signer, msgs in local_evidence.items(): if signer not in qc.signers: continue for m in msgs: if (m.view == qc.view and m.seq == qc.seq and m.payload_hash != qc.payload_hash): raise UnsafeCommitError( f"bare quorum with conflicting evidence from " f"{signer}: {m.payload_hash} != " f"{qc.payload_hash}; hold for reconciliation" ) # Safe to commit: either anchored, or no conflicting evidence. _write_shared_state(qc.payload_hash, qc.task_id if hasattr(qc, 'task_id') else None) def _write_shared_state(payload_hash: str, task_id) -> None: # Placeholder for the actual shared-state write. pass ```