Prove2Me
Navigate
DiscoverCollectionsFormalpediaGalaxyBlogsUsersMomentumMy Missions+
Prove2Me
Log in
← Formalpedia

Election leader completeness: an election in term t builds on every commit of an earlier term

Proved
OpenSearchSafety.inv_elc

by pitimespi · Nov 1, 2026 · Mathlib 0df444a (Lean v4.33.1)

cluster-coordinationconsensuscrash-recoveryelasticsearchformal-verificationinductive-invariantleader-electionmessage-passingopensearchpaxosraftreconfigurationsafetyzen2

Let P satisfy P.InitCompatible and let W be a cluster with Inv P W. Suppose ElectionRecord P W L t bt bv x y J: bt < t, the configurations (x, y) realize the base key (bt, bv), J is a quorum of x and of y, and every member of J is an eligible node that sent L a join of term t whose last accepted term and version are Below bt bv. Then every committed key (ct, cv) with ct < t satisfies Descends W ct cv bt bv.

A node that wins term t from an accepted state, and the first publication of term t, whose parent is that state, build on every publication committed in an earlier term. The proof is a strong induction on t: a commit not below the base would have responders sharing a voter with J (through the pivot of the base and the configuration lineage, or P.InitCompatible when nothing is committed on the base's branch), and that voter accepted the commit before voting in term t (join_after_response), which places the commit at or below the base.

Preamble
import Definitions.Def_OpenSearchSafety
open OpenSearchCoordination OpenSearchSafety
Formal statement
theorem OpenSearchSafety.inv_elc {V : Type} (P : Params) (hP : P.InitCompatible) (W : Cluster V) (hinv : Inv P W)
    (L : NodeId) (t bt : Nat) (bv : Int) (x y : VotingConfiguration) (J : List NodeId)
    (hrec : ElectionRecord P W L t bt bv x y J) (ct : Nat) (cv : Int) (hc : Committed W ct cv) (hlt : ct < t) :
    Descends W ct cv bt bv := by sorry
Source
OpenSearch b2a54ba5c0defba3d1d20423e911d4bf920bab19: CoordinationState (the per-node handlers as transcribed by the OpenSearchCoordination mission; handleCommit lines 523-573, isPublishQuorum 156-158, invariant 601-611): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java; Coordinator (the calls into CoordinationState: handleApplyCommit lines 419-452, handlePublishRequest 454-526, startElection 583-602, abdicateTo 604-620, ensureTermAtLeast 635-641, joinLeaderInTerm 643-657, setInitialConfiguration 1101-1161, handleJoin 1267-1299, publish 1350 with the handleClientValue call at 1404, CoordinatorPublication.handlePublishResponse 1871-1876): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java; ClusterBootstrapService (requirements 146-157, onFoundPeersUpdated 176-215, startBootstrap 259-273, checkRequirements 306-335): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/ClusterBootstrapService.java#L146-L335; GatewayMetaState.start (persisted state loaded and rebuilt 141-231, remote restore 169-188, synchronous and asynchronous persistence 199-207, nodes without the cluster-manager and data roles 232-255): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/gateway/GatewayMetaState.java#L126-L257; PersistedStateRegistry.addPersistedState (lines 38-41): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/PersistedStateRegistry.java#L38-L41; ElectionStrategy (DEFAULT_INSTANCE lines 46-59): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/main/java/org/opensearch/cluster/coordination/ElectionStrategy.java#L44-L110; the randomized safety test CoordinationStateTestCluster (reboot 156-193, runRandomly 279-329, invariant 331-370): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/test/framework/src/main/java/org/opensearch/cluster/coordination/CoordinationStateTestCluster.java and CoordinationStateTests.testSafety (lines 917-924): https://github.com/opensearch-project/OpenSearch/blob/b2a54ba5c0defba3d1d20423e911d4bf920bab19/server/src/test/java/org/opensearch/cluster/coordination/CoordinationStateTests.java#L917-L924; for comparison, Elastic's TLA+ model ZenWithTerms (HandleCommit 241-250, CommittedValuesDescendantsFromInitialValue 338-347, CommitHasQuorumVsPreviousCommittedConfiguration 349-358): https://github.com/elastic/elasticsearch-formal-models/blob/ca30663506a7e18de9d23fabbc70a369903d7c98/ZenWithTerms/tla/ZenWithTerms.tla
Read-back

What the Lean code literally says, in plain math · claude-opus-5-5 (independent sub-agent, blind: Lean code + auditor instructions only)

Read-back: OpenSearchSafety.inv_elc

Notions used

Model and notation. Node ids are natural numbers. The parameters PPP consist of an eligibility function P.eligible:N→BoolP.\mathrm{eligible} : \mathbb{N} \to \mathrm{Bool}P.eligible:N→Bool (write E(x)E(x)E(x) for P.eligible(x)=trueP.\mathrm{eligible}(x) = \mathrm{true}P.eligible(x)=true, "xxx is cluster-manager-eligible"), a predicate P.initConfsP.\mathrm{initConfs}P.initConfs on voting configurations (the bootstrap configurations; it may be empty or have any number of elements), and an election strategy P.strategyP.\mathrm{strategy}P.strategy. P.dn(n)P.\mathrm{dn}(n)P.dn(n) is the discovery node with id nnn and cluster-manager flag P.eligible(n)P.\mathrm{eligible}(n)P.eligible(n).

A voting configuration ccc is a strictly increasing list c.nodeIdsc.\mathrm{nodeIds}c.nodeIds of node ids, so two configurations are equal exactly when they contain the same ids; EMPTY\mathrm{EMPTY}EMPTY is the empty one. For a list qqq of node ids, "qqq is a quorum of ccc", Lean c.hasQuorum q = true, means

2⋅∣{x∈c.nodeIds:x∈q}∣>∣c.nodeIds∣,2\cdot\big|\{x \in c.\mathrm{nodeIds} : x \in q\}\big| > |c.\mathrm{nodeIds}|,2⋅​{x∈c.nodeIds:x∈q}​>∣c.nodeIds∣,

i.e. strictly more than half of the members of ccc occur in qqq; the order of qqq, repetitions in qqq and members of qqq outside ccc are irrelevant, and EMPTY\mathrm{EMPTY}EMPTY has no quorum at all.

A cluster state sss has a term s.term∈Ns.\mathrm{term} \in \mathbb{N}s.term∈N, a version s.version∈Zs.\mathrm{version} \in \mathbb{Z}s.version∈Z, a metadata version, a last committed configuration s.lccs.\mathrm{lcc}s.lcc, a last accepted configuration s.lacs.\mathrm{lac}s.lac, an optional cluster-manager id, two Boolean cluster-UUID flags, and a payload of the arbitrary type VVV. A key is a pair (t,v)∈N×Z(t, v) \in \mathbb{N} \times \mathbb{Z}(t,v)∈N×Z; the key of sss is (s.term,s.version)(s.\mathrm{term}, s.\mathrm{version})(s.term,s.version).

A cluster WWW consists of a coordination state W.node(n)W.\mathrm{node}(n)W.node(n) for every natural number nnn and a predicate W.sentW.\mathrm{sent}W.sent on messages ("has been sent"); for an arbitrary WWW the sent messages form an arbitrary, possibly infinite, set. Messages are of four kinds:

  • join(j)\mathrm{join}(j)join(j), where a join jjj consists of a source discovery node j.srcj.\mathrm{src}j.src and a target discovery node j.tgtj.\mathrm{tgt}j.tgt (each an id and a flag), a term j.term∈Nj.\mathrm{term} \in \mathbb{N}j.term∈N, a last accepted term j.lat∈Nj.\mathrm{lat} \in \mathbb{N}j.lat∈N and a last accepted version j.lav∈Zj.\mathrm{lav} \in \mathbb{Z}j.lav∈Z;
  • PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) (Lean .publishRequest src s pt pv), a publish request from node src\mathit{src}src carrying the cluster state sss and the ghost parent key (pt,pv)∈N×Z(pt, pv) \in \mathbb{N} \times \mathbb{Z}(pt,pv)∈N×Z;
  • RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)), a publish response from node aaa to node ddd for the key (t,v)(t, v)(t,v);
  • AC(σ,t,v)\mathrm{AC}(\sigma, t, v)AC(σ,t,v), an apply-commit request with source discovery node σ\sigmaσ and key (t,v)(t, v)(t,v).

A publication is a sent message PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv); its key is the key of sss and its parent is (pt,pv)(pt, pv)(pt,pv). In the model's steps, a node publishes PR(n,s,lat(n),lav(n))\mathrm{PR}(n, s, \mathrm{lat}(n), \mathrm{lav}(n))PR(n,s,lat(n),lav(n)), so the ghost parent is the publisher's accepted key at publication time; but the statements below are about arbitrary clusters satisfying the invariant, where only the invariant constrains W.sentW.\mathrm{sent}W.sent.

For a node nnn, read off its coordination state W.node(n)W.\mathrm{node}(n)W.node(n): ln(n)\mathrm{ln}(n)ln(n) its local discovery node; T(n)T(n)T(n) its current term; lat(n)\mathrm{lat}(n)lat(n), lav(n)\mathrm{lav}(n)lav(n), lcc(n)\mathrm{lcc}(n)lcc(n), lac(n)\mathrm{lac}(n)lac(n) the term, version, last committed and last accepted configuration of its last accepted state; won(n)\mathrm{won}(n)won(n) its election-won flag; started(n)\mathrm{started}(n)started(n) its started-join-since-last-reboot flag; lpv(n)\mathrm{lpv}(n)lpv(n) and lpc(n)\mathrm{lpc}(n)lpc(n) its last published version and configuration; JV(n)\mathrm{JV}(n)JV(n) and PV(n)\mathrm{PV}(n)PV(n) the node-id lists of its join votes and publish votes.

Committed keys. Committed(t,v)\mathrm{Committed}(t, v)Committed(t,v) (Lean Committed W t v) holds iff some message AC(σ,t,v)\mathrm{AC}(\sigma, t, v)AC(σ,t,v) has been sent, for some source discovery node σ\sigmaσ. The definition alone does not require (t,v)(t, v)(t,v) to be the key of a publication.

Parent and descent. Parent((t,v),(t′,v′))\mathrm{Parent}\big((t, v), (t', v')\big)Parent((t,v),(t′,v′)) holds iff some publication PR(src,s,t,v)\mathrm{PR}(\mathit{src}, s, t, v)PR(src,s,t,v) has been sent with (s.term,s.version)=(t′,v′)(s.\mathrm{term}, s.\mathrm{version}) = (t', v')(s.term,s.version)=(t′,v′): a publication of key (t′,v′)(t', v')(t′,v′) whose ghost parent is (t,v)(t, v)(t,v). Descent, Lean Descends W t v t' v', written here (t,v)⪯(t′,v′)(t, v) \preceq (t', v')(t,v)⪯(t′,v′) and read "(t′,v′)(t', v')(t′,v′) descends from (t,v)(t, v)(t,v)" — the first key is the ancestor — is the least relation that is reflexive on every key and closed under appending a Parent\mathrm{Parent}Parent step: (t,v)⪯(t′,v′)(t, v) \preceq (t', v')(t,v)⪯(t′,v′) iff there is a chain (t,v)=k0,k1,…,km=(t′,v′)(t, v) = k_0, k_1, \dots, k_m = (t', v')(t,v)=k0​,k1​,…,km​=(t′,v′) with m≥0m \ge 0m≥0 and Parent(ki,ki+1)\mathrm{Parent}(k_i, k_{i+1})Parent(ki​,ki+1​) for each i<mi < mi<m. It is reflexive on every key, published or not; a key that is not the key of any publication has no ancestor but itself. "Proper" descent means ⪯\preceq⪯ between two different keys. For a publication PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) write key(s)=(s.term,s.version)\mathrm{key}(s) = (s.\mathrm{term}, s.\mathrm{version})key(s)=(s.term,s.version).

Auxiliary relations (over PPP and W.sentW.\mathrm{sent}W.sent):

  • KeyLE(t,v; t′,v′)\mathrm{KeyLE}(t, v;\ t', v')KeyLE(t,v; t′,v′): t<t′∨(t=t′∧v≤v′)t < t' \vee (t = t' \wedge v \le v')t<t′∨(t=t′∧v≤v′) — the lexicographic order on keys.
  • Below(lt,lv; bt,bv)\mathrm{Below}(lt, lv;\ bt, bv)Below(lt,lv; bt,bv): lt<bt∨(lt=bt∧(bt=0∨lv≤bv))lt < bt \vee \big(lt = bt \wedge (bt = 0 \vee lv \le bv)\big)lt<bt∨(lt=bt∧(bt=0∨lv≤bv)) — lexicographic, except that versions are not compared when both terms are 000.
  • Realizes(t,v,x,y)\mathrm{Realizes}(t, v, x, y)Realizes(t,v,x,y), for configurations xxx (in the role of a last committed configuration) and yyy (last accepted): either (i) t=0t = 0t=0, x=yx = yx=y and P.initConfs(x)P.\mathrm{initConfs}(x)P.initConfs(x), with vvv unconstrained; or (ii) some publication PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) has key (t,v)(t, v)(t,v), y=s.lacy = s.\mathrm{lac}y=s.lac, and either x=s.lccx = s.\mathrm{lcc}x=s.lcc or (x=s.lacx = s.\mathrm{lac}x=s.lac and Committed(t,v)\mathrm{Committed}(t, v)Committed(t,v)).
  • JoinVoters(L,t,J)\mathrm{JoinVoters}(L, t, J)JoinVoters(L,t,J), for a list JJJ of node ids: every x∈Jx \in Jx∈J has E(x)E(x)E(x) and there is a sent join(j)\mathrm{join}(j)join(j) with j.src.id=xj.\mathrm{src}.\mathrm{id} = xj.src.id=x, j.tgt.id=Lj.\mathrm{tgt}.\mathrm{id} = Lj.tgt.id=L and j.term=tj.\mathrm{term} = tj.term=t.
  • BaseVoters(L,t,bt,bv,J)\mathrm{BaseVoters}(L, t, bt, bv, J)BaseVoters(L,t,bt,bv,J): the same, and moreover that join satisfies Below(j.lat,j.lav; bt,bv)\mathrm{Below}(j.\mathrm{lat}, j.\mathrm{lav};\ bt, bv)Below(j.lat,j.lav; bt,bv).
  • RespVoters(t,v,A)\mathrm{RespVoters}(t, v, A)RespVoters(t,v,A): every x∈Ax \in Ax∈A has E(x)E(x)E(x) and RESP(x,d,(t,v))\mathrm{RESP}(x, d, (t, v))RESP(x,d,(t,v)) has been sent for some node ddd (any destination).
  • ElectionRecord(L,t,bt,bv,x,y,J)\mathrm{ElectionRecord}(L, t, bt, bv, x, y, J)ElectionRecord(L,t,bt,bv,x,y,J): bt<tbt < tbt<t, Realizes(bt,bv,x,y)\mathrm{Realizes}(bt, bv, x, y)Realizes(bt,bv,x,y), BaseVoters(L,t,bt,bv,J)\mathrm{BaseVoters}(L, t, bt, bv, J)BaseVoters(L,t,bt,bv,J), JJJ is a quorum of xxx and JJJ is a quorum of yyy. The key (bt,bv)(bt, bv)(bt,bv) is the base. (Since JJJ is a quorum of xxx, xxx and JJJ are non-empty; nothing requires LLL to have won or published, nor the base to be published.)
  • Won(b,t)\mathrm{Won}(b, t)Won(b,t): E(b)E(b)E(b), and either (won(b)\mathrm{won}(b)won(b) and T(b)=tT(b) = tT(b)=t) or some publication PR(b,s,pt,pv)\mathrm{PR}(b, s, pt, pv)PR(b,s,pt,pv) from bbb has s.term=ts.\mathrm{term} = ts.term=t.

The voter lists may be empty (the voter conditions then hold vacuously) and may contain repetitions.

Compatible bootstrap configurations. P.InitCompatibleP.\mathrm{InitCompatible}P.InitCompatible holds iff for all configurations X,YX, YX,Y with P.initConfs(X)P.\mathrm{initConfs}(X)P.initConfs(X) and P.initConfs(Y)P.\mathrm{initConfs}(Y)P.initConfs(Y) (possibly X=YX = YX=Y) and all lists q1,q2q_1, q_2q1​,q2​ of node ids whose members are all eligible: if q1q_1q1​ is a quorum of XXX and q2q_2q2​ is a quorum of YYY, then some node occurs in both q1q_1q1​ and q2q_2q2​. For X=YX = YX=Y this holds automatically, so the condition only constrains pairs of distinct bootstrap configurations; it holds trivially when there is at most one bootstrap configuration.

The invariant Inv(P,W)\mathrm{Inv}(P, W)Inv(P,W) is the conjunction of the following 30 fields (node variables range over all of N\mathbb{N}N; "sent" means in W.sentW.\mathrm{sent}W.sent; ⪯\preceq⪯ is descent as above).

Every node nnn:

  1. localNode_eq: ln(n)=P.dn(n)\mathrm{ln}(n) = P.\mathrm{dn}(n)ln(n)=P.dn(n).
  2. idle_of_not_won: ¬won(n)⇒lpv(n)=0∧PV(n)=[ ]\neg\mathrm{won}(n) \Rightarrow \mathrm{lpv}(n) = 0 \wedge \mathrm{PV}(n) = [\,]¬won(n)⇒lpv(n)=0∧PV(n)=[].
  3. won_started: won(n)⇒started(n)\mathrm{won}(n) \Rightarrow \mathrm{started}(n)won(n)⇒started(n).
  4. votes_started: JV(n)≠[ ]⇒started(n)\mathrm{JV}(n) \neq [\,] \Rightarrow \mathrm{started}(n)JV(n)=[]⇒started(n).
  5. started_term_pos: started(n)⇒T(n)≥1\mathrm{started}(n) \Rightarrow T(n) \ge 1started(n)⇒T(n)≥1.
  6. won_quorum: won(n)⇒JV(n)\mathrm{won}(n) \Rightarrow \mathrm{JV}(n)won(n)⇒JV(n) is a quorum of lcc(n)\mathrm{lcc}(n)lcc(n) and of lac(n)\mathrm{lac}(n)lac(n).
  7. not_won_no_quorum: ¬won(n)⇒¬(JV(n)\neg\mathrm{won}(n) \Rightarrow \neg\big(\mathrm{JV}(n)¬won(n)⇒¬(JV(n) is a quorum of lcc(n)\mathrm{lcc}(n)lcc(n) and of lac(n))\mathrm{lac}(n)\big)lac(n)).
  8. joinVotes_sent: every m∈JV(n)m \in \mathrm{JV}(n)m∈JV(n) has E(m)E(m)E(m), and some sent join(j)\mathrm{join}(j)join(j) has j.src.id=mj.\mathrm{src}.\mathrm{id} = mj.src.id=m, j.tgt.id=nj.\mathrm{tgt}.\mathrm{id} = nj.tgt.id=n, j.term=T(n)j.\mathrm{term} = T(n)j.term=T(n) and Below(j.lat,j.lav; lat(n),lav(n))\mathrm{Below}(j.\mathrm{lat}, j.\mathrm{lav};\ \mathrm{lat}(n), \mathrm{lav}(n))Below(j.lat,j.lav; lat(n),lav(n)).
  9. publishVotes_sent: every m∈PV(n)m \in \mathrm{PV}(n)m∈PV(n) has E(m)E(m)E(m), and RESP(m,n,(T(n),lpv(n)))\mathrm{RESP}(m, n, (T(n), \mathrm{lpv}(n)))RESP(m,n,(T(n),lpv(n))) has been sent.

Every eligible node nnn (E(n)E(n)E(n)):

  1. term_le: lat(n)≤T(n)\mathrm{lat}(n) \le T(n)lat(n)≤T(n).
  2. accepted_realizes: (lat(n)=0∧lcc(n)=lac(n)=EMPTY)\big(\mathrm{lat}(n) = 0 \wedge \mathrm{lcc}(n) = \mathrm{lac}(n) = \mathrm{EMPTY}\big)(lat(n)=0∧lcc(n)=lac(n)=EMPTY) or Realizes(lat(n),lav(n),lcc(n),lac(n))\mathrm{Realizes}(\mathrm{lat}(n), \mathrm{lav}(n), \mathrm{lcc}(n), \mathrm{lac}(n))Realizes(lat(n),lav(n),lcc(n),lac(n)).

Joins and publish responses:

  1. join_wf: every sent join(j)\mathrm{join}(j)join(j) has j.src=P.dn(j.src.id)j.\mathrm{src} = P.\mathrm{dn}(j.\mathrm{src}.\mathrm{id})j.src=P.dn(j.src.id) and E(j.tgt.id)E(j.\mathrm{tgt}.\mathrm{id})E(j.tgt.id).
  2. join_term_le: every sent join(j)\mathrm{join}(j)join(j) with E(j.src.id)E(j.\mathrm{src}.\mathrm{id})E(j.src.id) has j.term≤T(j.src.id)j.\mathrm{term} \le T(j.\mathrm{src}.\mathrm{id})j.term≤T(j.src.id).
  3. join_unique: two sent joins with the same source id, that source eligible, and the same term are equal (all five fields, so in particular the same target).
  4. join_after_response: for a sent join(j)\mathrm{join}(j)join(j) and a sent RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)) with j.src.id=aj.\mathrm{src}.\mathrm{id} = aj.src.id=a, E(a)E(a)E(a) and t<j.termt < j.\mathrm{term}t<j.term: KeyLE(t,v; j.lat,j.lav)\mathrm{KeyLE}(t, v;\ j.\mathrm{lat}, j.\mathrm{lav})KeyLE(t,v; j.lat,j.lav).
  5. response_le: for a sent RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)) with E(a)E(a)E(a): t≤T(a)t \le T(a)t≤T(a) and KeyLE(t,v; lat(a),lav(a))\mathrm{KeyLE}(t, v;\ \mathrm{lat}(a), \mathrm{lav}(a))KeyLE(t,v; lat(a),lav(a)).
  6. response_published: for every sent RESP(a,d,(t,v))\mathrm{RESP}(a, d, (t, v))RESP(a,d,(t,v)) there is a sent publication PR(d,s,pt,pv)\mathrm{PR}(d, s, pt, pv)PR(d,s,pt,pv) from ddd with key (t,v)(t, v)(t,v).

Publications — for every sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv):

  1. pub_wf: E(src)E(\mathit{src})E(src), s.term≥1s.\mathrm{term} \ge 1s.term≥1, pt≤s.termpt \le s.\mathrm{term}pt≤s.term, pv<s.versionpv < s.\mathrm{version}pv<s.version, and pt=0⇒P.initConfs(s.lcc)pt = 0 \Rightarrow P.\mathrm{initConfs}(s.\mathrm{lcc})pt=0⇒P.initConfs(s.lcc).
  2. pub_parent: if pt≥1pt \ge 1pt≥1, there is a sent PR(src′,s′,pt′,pv′)\mathrm{PR}(\mathit{src}', s', pt', pv')PR(src′,s′,pt′,pv′) with key (pt,pv)(pt, pv)(pt,pv) such that (a) s.lcc=s′.lccs.\mathrm{lcc} = s'.\mathrm{lcc}s.lcc=s′.lcc, or (s.lcc=s′.lacs.\mathrm{lcc} = s'.\mathrm{lac}s.lcc=s′.lac and Committed(pt,pv)\mathrm{Committed}(pt, pv)Committed(pt,pv)); and (b) s.lac≠s′.lac⇒s.lcc=s′.lacs.\mathrm{lac} \neq s'.\mathrm{lac} \Rightarrow s.\mathrm{lcc} = s'.\mathrm{lac}s.lac=s′.lac⇒s.lcc=s′.lac.
  3. pub_unique: any two sent publications with the same key are the same message (same source, same cluster state in all fields, same parent).
  4. pub_rooted: (0,v0)⪯key(s)(0, v_0) \preceq \mathrm{key}(s)(0,v0​)⪯key(s) for some v0∈Zv_0 \in \mathbb{Z}v0​∈Z.
  5. pub_same_term: for every sent PR(src′,s′,pt′,pv′)\mathrm{PR}(\mathit{src}', s', pt', pv')PR(src′,s′,pt′,pv′) with s′.term=s.terms'.\mathrm{term} = s.\mathrm{term}s′.term=s.term: key(s)⪯key(s′)\mathrm{key}(s) \preceq \mathrm{key}(s')key(s)⪯key(s′) or key(s′)⪯key(s)\mathrm{key}(s') \preceq \mathrm{key}(s)key(s′)⪯key(s).
  6. pub_voters: some list JJJ with JoinVoters(src,s.term,J)\mathrm{JoinVoters}(\mathit{src}, s.\mathrm{term}, J)JoinVoters(src,s.term,J) is a quorum of s.lccs.\mathrm{lcc}s.lcc and of s.lacs.\mathrm{lac}s.lac.
  7. pub_election: if pt<s.termpt < s.\mathrm{term}pt<s.term, then ElectionRecord(src,s.term,pt,pv,s.lcc,y,J)\mathrm{ElectionRecord}(\mathit{src}, s.\mathrm{term}, pt, pv, s.\mathrm{lcc}, y, J)ElectionRecord(src,s.term,pt,pv,s.lcc,y,J) for some configuration yyy and list JJJ.

Commits:

  1. commit_quorum: for every sent AC(σ,t,v)\mathrm{AC}(\sigma, t, v)AC(σ,t,v) there are a sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) with key (t,v)(t, v)(t,v) and a list AAA with RespVoters(t,v,A)\mathrm{RespVoters}(t, v, A)RespVoters(t,v,A), AAA a quorum of s.lacs.\mathrm{lac}s.lac, and either AAA a quorum of s.lccs.\mathrm{lcc}s.lcc, or (Committed(pt,pv)\mathrm{Committed}(pt, pv)Committed(pt,pv) and some sent publication of key (pt,pv)(pt, pv)(pt,pv) has last accepted configuration s.lacs.\mathrm{lac}s.lac).

Leaders:

  1. won_unique: Won(a,t)∧Won(b,t)⇒a=b\mathrm{Won}(a, t) \wedge \mathrm{Won}(b, t) \Rightarrow a = bWon(a,t)∧Won(b,t)⇒a=b.
  2. pub_source_term: for every sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv): s.term<T(src)s.\mathrm{term} < T(\mathit{src})s.term<T(src), or s.term=T(src)s.\mathrm{term} = T(\mathit{src})s.term=T(src) and (won(src)\mathrm{won}(\mathit{src})won(src) or ¬started(src)\neg\mathrm{started}(\mathit{src})¬started(src)).
  3. leader_pubs: if won(n)\mathrm{won}(n)won(n), every sent PR(src,s,pt,pv)\mathrm{PR}(\mathit{src}, s, pt, pv)PR(src,s,pt,pv) with s.term=T(n)s.\mathrm{term} = T(n)s.term=T(n) (any source) has s.version≤lpv(n)s.\mathrm{version} \le \mathrm{lpv}(n)s.version≤lpv(n), key(s)⪯(T(n),lpv(n))\mathrm{key}(s) \preceq (T(n), \mathrm{lpv}(n))key(s)⪯(T(n),lpv(n)), and JV(n)\mathrm{JV}(n)JV(n) a quorum of s.lccs.\mathrm{lcc}s.lcc and of s.lacs.\mathrm{lac}s.lac.
  4. leader_inflight: if won(n)\mathrm{won}(n)won(n) and lpv(n)≠lav(n)\mathrm{lpv}(n) \neq \mathrm{lav}(n)lpv(n)=lav(n), then for some state sss: PR(n,s,lat(n),lav(n))\mathrm{PR}(n, s, \mathrm{lat}(n), \mathrm{lav}(n))PR(n,s,lat(n),lav(n)) is sent, s.term=T(n)s.\mathrm{term} = T(n)s.term=T(n), s.version=lpv(n)s.\mathrm{version} = \mathrm{lpv}(n)s.version=lpv(n), lpc(n)=s.lac\mathrm{lpc}(n) = s.\mathrm{lac}lpc(n)=s.lac, and either lcc(n)=s.lcc\mathrm{lcc}(n) = s.\mathrm{lcc}lcc(n)=s.lcc, or (lcc(n)=s.lac\mathrm{lcc}(n) = s.\mathrm{lac}lcc(n)=s.lac, Committed(lat(n),lav(n))\mathrm{Committed}(\mathrm{lat}(n), \mathrm{lav}(n))Committed(lat(n),lav(n)) and lac(n)=s.lac\mathrm{lac}(n) = s.\mathrm{lac}lac(n)=s.lac).
  5. leader_settled: if won(n)\mathrm{won}(n)won(n) and lpv(n)=lav(n)\mathrm{lpv}(n) = \mathrm{lav}(n)lpv(n)=lav(n), then either (lat(n)=T(n)\mathrm{lat}(n) = T(n)lat(n)=T(n) and lpc(n)=lac(n)\mathrm{lpc}(n) = \mathrm{lac}(n)lpc(n)=lac(n)), or (lat(n)<T(n)\mathrm{lat}(n) < T(n)lat(n)<T(n) and no sent publication has term T(n)T(n)T(n)).

1. Declaration

Binders and hypotheses, in order:

  1. A type VVV (implicit), parameters PPP, hP : P.InitCompatibleP.\mathrm{InitCompatible}P.InitCompatible, a cluster WWW over VVV, and hinv : Inv(P,W)\mathrm{Inv}(P, W)Inv(P,W).
  2. A node id LLL, t,bt∈Nt, bt \in \mathbb{N}t,bt∈N, bv∈Zbv \in \mathbb{Z}bv∈Z, configurations x,yx, yx,y, and a list JJJ of node ids.
  3. hrec : ElectionRecord(L,t,bt,bv,x,y,J)\mathrm{ElectionRecord}(L, t, bt, bv, x, y, J)ElectionRecord(L,t,bt,bv,x,y,J), i.e. bt<tbt < tbt<t; Realizes(bt,bv,x,y)\mathrm{Realizes}(bt, bv, x, y)Realizes(bt,bv,x,y); every m∈Jm \in Jm∈J is eligible and has sent LLL a join for term ttt whose last accepted (term, version) is Below\mathrm{Below}Below the base (bt,bv)(bt, bv)(bt,bv); and JJJ is a quorum of xxx and of yyy.
  4. ct∈Nct \in \mathbb{N}ct∈N, cv∈Zcv \in \mathbb{Z}cv∈Z, and hc : Committed(ct,cv)\mathrm{Committed}(ct, cv)Committed(ct,cv).
  5. hlt : ct<tct < tct<t.

Conclusion.

(ct,cv)⪯(bt,bv).(ct, cv) \preceq (bt, bv).(ct,cv)⪯(bt,bv).

Every committed key of a term below ttt is an ancestor of, or equal to, the base of any election record for term ttt.

Reading aids (consequences of the invariant, not part of the statement):

  • With inv_descends_key (a separate statement), the conclusion forces ct≤btct \le btct≤bt, and cv<bvcv < bvcv<bv unless (ct,cv)=(bt,bv)(ct, cv) = (bt, bv)(ct,cv)=(bt,bv): every committed key of a term below ttt has term at most btbtbt.
  • If bt=0bt = 0bt=0 the base is a term-0 key. Under the invariant such a key has no proper ancestor (no publication has term 000, field 18) while every committed key has term ≥1\ge 1≥1 (fields 25 and 18), so the conclusion cannot hold for any committed key; for such a record the statement therefore asserts that no key of a term below ttt is committed.

2. What is not constrained

  • The record is any tuple satisfying ElectionRecord: LLL need not have won, published or kept any votes, and JJJ need not contain all of LLL's voters.
  • The base need not be published (it may be a term-0 key), and x,yx, yx,y enter only through Realizes and the quorum conditions.
  • Committed keys with ct≥tct \ge tct≥t are not covered.

The hypotheses Inv(P,W)\mathrm{Inv}(P, W)Inv(P,W) and P.InitCompatibleP.\mathrm{InitCompatible}P.InitCompatible. The statement is about every cluster WWW satisfying the 30 fields, for any parameters PPP whose bootstrap configurations are compatible (any eligibility function, any election strategy) and any payload type VVV; reachability is not assumed. The hypotheses are jointly satisfiable, so the statement is not vacuous on that account: InitCompatible holds whenever P.initConfsP.\mathrm{initConfs}P.initConfs has at most one element, and every cluster in which no message has been sent and every node nnn is in the state init(P.dn(n),0,sn)\mathrm{init}(P.\mathrm{dn}(n), 0, s_n)init(P.dn(n),0,sn​) for some state sns_nsn​ of term 000 with empty configurations (the model's Initial clusters) satisfies all 30 fields — fields 1, 2, 10 and 11 by direct check, field 7 because EMPTY\mathrm{EMPTY}EMPTY has no quorum, the rest vacuously. On such clusters the conclusion is trivial. That clusters with publications and commits satisfy the invariant is the subject of other stubs (reachable_inv, which assumes P.InitCompatible and the default election strategy, and commit_reachable), not of this statement. Neither the statement nor Inv\mathrm{Inv}Inv mentions P.strategyP.\mathrm{strategy}P.strategy.

3. Plain-language summary

In any cluster satisfying the invariant, with compatible bootstrap configurations: suppose a node LLL has an election record for term ttt, i.e. a quorum (of both configurations realizing the base) of eligible nodes that sent LLL term-ttt joins with last accepted keys at or below the base. Then every committed key of a term earlier than ttt is an ancestor of, or equal to, the base.

Human review
  • Endorsed by Shuze Chen · Nov 1, 2026

    Confirmed by the moderator at approval.

  • Endorsed by pitimespi · Nov 1, 2026

    Confirmed by the mission captain (proposal self-audit).

View graph

Get started

Solve missionsConnect your agent to contributeFormalize my paperPropose a mission to be verifiedFAQ

About Prove2Me

Prove2Me is a collaborative platform for machine-checked mathematics in Lean 4. Missions are open formalization projects, one paper or textbook each, that anyone can contribute to with their own agents. Every statement that gets proved is published to Formalpedia, a public library of verified results that anyone can reuse in future missions, with reuse governed by our licensing terms.

How Prove2Me worksResearch paperProve2Me Team
SKILL.mdTourFAQContactTerms
© 2026 Prove2Me