Prove2Me
Navigate
DiscoverCollectionsFormalpediaGalaxyBlogsUsersMomentumMy Missions+
Prove2Me
Log in
← Formalpedia

Committed keys are totally ordered by descent

Proved
OpenSearchSafety.inv_committed_comparable

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

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

For P with P.InitCompatible, every cluster W with Inv P W and committed keys (t₁, v₁) and (t₂, v₂) (Committed W t₁ v₁, Committed W t₂ v₂): Descends W t₁ v₁ t₂ v₂ ∨ Descends W t₂ v₂ t₁ v₁.

Preamble
import Definitions.Def_OpenSearchSafety
open OpenSearchCoordination OpenSearchSafety
Formal statement
theorem OpenSearchSafety.inv_committed_comparable {V : Type} (P : Params) (hP : P.InitCompatible) (W : Cluster V)
    (hinv : Inv P W) (t₁ t₂ : Nat) (v₁ v₂ : Int) (h₁ : Committed W t₁ v₁) (h₂ : Committed W t₂ v₂) :
    Descends W t₁ v₁ t₂ v₂ ∨ Descends W t₂ v₂ t₁ v₁ := 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_committed_comparable

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. t1,t2∈Nt_1, t_2 \in \mathbb{N}t1​,t2​∈N, v1,v2∈Zv_1, v_2 \in \mathbb{Z}v1​,v2​∈Z, h₁ : Committed(t1,v1)\mathrm{Committed}(t_1, v_1)Committed(t1​,v1​) and h₂ : Committed(t2,v2)\mathrm{Committed}(t_2, v_2)Committed(t2​,v2​).

Conclusion.

(t1,v1)⪯(t2,v2)  ∨  (t2,v2)⪯(t1,v1).(t_1, v_1) \preceq (t_2, v_2) \;\vee\; (t_2, v_2) \preceq (t_1, v_1).(t1​,v1​)⪯(t2​,v2​)∨(t2​,v2​)⪯(t1​,v1​).

Any two committed keys lie on one line of descent: the committed keys are totally ordered by descent.

2. What is not constrained

  • The statement is about keys only. That a key determines a single publication, hence a single published state, is field 20 of the hypothesis, not part of the conclusion.
  • Nothing is said about uncommitted keys or about which of the two descends from the other.

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, of any two committed keys one descends from the other.

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