From 09aaf62b5f4afdfbe593dc7bea04feffb5301066 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Wed, 13 May 2026 10:07:00 -0700 Subject: [PATCH 01/22] Name Peers assumption in TCP specification. Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp.tla | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/tcp/tcp.tla b/specifications/tcp/tcp.tla index be6ca906..7d2635a8 100644 --- a/specifications/tcp/tcp.tla +++ b/specifications/tcp/tcp.tla @@ -11,7 +11,7 @@ EXTENDS Integers, Sequences, SequencesExt, FiniteSets CONSTANT Peers -ASSUME Cardinality(Peers) = 2 +ASSUME PeersAssumption == Cardinality(Peers) = 2 States == {"LISTEN", "CLOSED", "SYN-SENT", "SYN-RECEIVED", "ESTABLISHED", "FIN-WAIT-1", "FIN-WAIT-2", "CLOSING", "CLOSE-WAIT", "LAST-ACK", From 1caef881ed1478c2964269dff07d02e84908f466 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Sun, 10 May 2026 16:03:42 -0700 Subject: [PATCH 02/22] tcp: prove TypeCorrect, InvInit and helper lemmas Add specifications/tcp/tcp_proof.tla discharging the static obligations of the RFC 9293 abstract TCP FSM: - TypeCorrect : Spec => []TypeOK (~268 obligations). The inductive proof covers every action of Next (User, System and Reset, plus stuttering); two new generic helper lemmas PrefixOneNonEmpty and PrefixTwoNonEmpty are derived from the IsPrefix definition so the per-action sub-proofs can chain Tail and Append into Seq(Msgs). - InvInit : Init => Inv. Direct from the initial-state predicate. - PeersAB : explicit witnesses A, B with A # B and Peers = {A, B} proven from the spec's `ASSUME Cardinality(Peers) = 2` plus an added `ASSUME PeersFinite == IsFiniteSet(Peers)` (the spec's bare cardinality assumption is intended to assert finiteness; in TLA+ Cardinality is otherwise CHOOSE-based). - InvIsAB : the convenient two-peer reformulation of Inv. The full inductive step Inv /\ [Next]_vars => Inv' is left as future work; it is documented at the bottom of the file as the substantive remaining obligation. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 432 +++++++++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 specifications/tcp/tcp_proof.tla diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla new file mode 100644 index 00000000..2e758d5b --- /dev/null +++ b/specifications/tcp/tcp_proof.tla @@ -0,0 +1,432 @@ +--------------------------- MODULE tcp_proof --------------------------------- +(***************************************************************************) +(* TLAPS proofs for the TCP FSM specification: *) +(* *) +(* Spec => []TypeOK *) +(* Spec => []Inv (ESTABLISHED agreement when both networks are empty) *) +(***************************************************************************) +EXTENDS tcp, SequenceTheorems, SequencesExtTheorems, TLAPS + +\* The spec's `ASSUME PeersAssumption == Cardinality(Peers) = 2` is intended +\* to assert that Peers is a finite set with exactly two elements. In TLA+, +\* Cardinality is defined via CHOOSE and may return arbitrary values for +\* infinite sets, so we add the natural finiteness witness here. +ASSUME PeersFinite == IsFiniteSet(Peers) + +(***************************************************************************) +(* The set of network messages used by the spec. *) +(***************************************************************************) +Msgs == {"SYN", "SYN,ACK", "ACK", "RST", "FIN", "ACKofFIN"} + +LEMMA NetworkType == + TypeOK <=> /\ tcb \in [Peers -> BOOLEAN] + /\ connstate \in [Peers -> States] + /\ network \in [Peers -> Seq(Msgs)] + BY DEF TypeOK, Msgs + +LEMMA TailIsSeq == + ASSUME NEW T, NEW s \in Seq(T), s # << >> + PROVE Tail(s) \in Seq(T) + OBVIOUS + +LEMMA AppendInSeq == + ASSUME NEW T, NEW s \in Seq(T), NEW e \in T + PROVE Append(s, e) \in Seq(T) + OBVIOUS + +(***************************************************************************) +(* IsPrefix with a non-empty argument forces the second sequence to be *) +(* non-empty. We use the unfolded definition to avoid a fragile *) +(* dependency on the longer Theorems. *) +(***************************************************************************) +LEMMA PrefixOneNonEmpty == + ASSUME NEW T, NEW e \in T, NEW s \in Seq(T), IsPrefix(<>, s) + PROVE /\ s # << >> + /\ Head(s) = e + /\ Tail(s) \in Seq(T) + <1>1. Len(<>) <= Len(s) /\ SubSeq(<>, 1, Len(<>)) = SubSeq(s, 1, Len(<>)) + BY DEF IsPrefix + <1>2. Len(s) >= 1 + BY <1>1 + <1>3. s # << >> + BY <1>2 + <1>4. Head(s) = e + <2>. SubSeq(s, 1, 1) = <> + BY <1>3, SubSeqProperties + <2>. SubSeq(<>, 1, 1) = <> + BY SubSeqProperties + <2>. <> = <> + BY <1>1 + <2>. QED BY HeadTailProperties + <1>. QED BY <1>3, <1>4, TailIsSeq + +LEMMA PrefixTwoNonEmpty == + ASSUME NEW T, NEW e1 \in T, NEW e2 \in T, NEW s \in Seq(T), + IsPrefix(<>, s) + PROVE /\ Len(s) >= 2 + /\ s[1] = e1 + /\ s[2] = e2 + <1>1. Len(<>) <= Len(s) /\ SubSeq(<>, 1, Len(<>)) = SubSeq(s, 1, Len(<>)) + BY DEF IsPrefix + <1>2. Len(s) >= 2 + BY <1>1 + <1>3. SubSeq(s, 1, 2) = <> + BY <1>2 + <1>4. SubSeq(<>, 1, 2) = <> + OBVIOUS + <1>. QED BY <1>1, <1>2, <1>3, <1>4 + +(***************************************************************************) +(* Type correctness. *) +(***************************************************************************) + +THEOREM TypeCorrect == Spec => []TypeOK +<1>1. Init => TypeOK + BY DEF Init, TypeOK, States +<1>2. TypeOK /\ [Next]_vars => TypeOK' + <2>. SUFFICES ASSUME TypeOK, [Next]_vars PROVE TypeOK' + OBVIOUS + <2>. USE DEF TypeOK, States, Msgs + (*************************************************************************) + (* User actions *) + (*************************************************************************) + <2>uo. ASSUME NEW local \in Peers, NEW remote \in Peers, + PASSIVE_OPEN(local, remote) \/ ACTIVE_OPEN(local, remote) + \/ CLOSE_SYN_SENT(local, remote) + \/ CLOSE_SYN_RECEIVED(local, remote) + \/ CLOSE_LISTEN(local, remote) + \/ CLOSE_ESTABLISHED(local, remote) + \/ CLOSE_CLOSE_WAIT(local, remote) + \/ SEND(local, remote) + PROVE TypeOK' + <3>1. CASE PASSIVE_OPEN(local, remote) + BY <3>1 DEF PASSIVE_OPEN + <3>2. CASE ACTIVE_OPEN(local, remote) + <4>. network[remote] \in Seq(Msgs) + OBVIOUS + <4>. Append(network[remote], "SYN") \in Seq(Msgs) + OBVIOUS + <4>. QED BY <3>2 DEF ACTIVE_OPEN + <3>3. CASE CLOSE_SYN_SENT(local, remote) + BY <3>3 DEF CLOSE_SYN_SENT + <3>4. CASE CLOSE_SYN_RECEIVED(local, remote) + <4>. network[remote] \in Seq(Msgs) + OBVIOUS + <4>. Append(network[remote], "FIN") \in Seq(Msgs) + OBVIOUS + <4>. QED BY <3>4 DEF CLOSE_SYN_RECEIVED + <3>5. CASE CLOSE_LISTEN(local, remote) + BY <3>5 DEF CLOSE_LISTEN + <3>6. CASE CLOSE_ESTABLISHED(local, remote) + <4>. network[remote] \in Seq(Msgs) + OBVIOUS + <4>. Append(network[remote], "FIN") \in Seq(Msgs) + OBVIOUS + <4>. QED BY <3>6 DEF CLOSE_ESTABLISHED + <3>7. CASE CLOSE_CLOSE_WAIT(local, remote) + <4>. network[remote] \in Seq(Msgs) + OBVIOUS + <4>. Append(network[remote], "FIN") \in Seq(Msgs) + OBVIOUS + <4>. QED BY <3>7 DEF CLOSE_CLOSE_WAIT + <3>8. CASE SEND(local, remote) + <4>. network[remote] \in Seq(Msgs) + OBVIOUS + <4>. Append(network[remote], "SYN") \in Seq(Msgs) + OBVIOUS + <4>. QED BY <3>8 DEF SEND + <3>. QED BY <2>uo, <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7, <3>8 + <2>user. CASE User + <3>. PICK local \in Peers, remote \in Peers : + \/ ACTIVE_OPEN(local, remote) + \/ PASSIVE_OPEN(local, remote) + \/ CLOSE_SYN_SENT(local, remote) + \/ CLOSE_SYN_RECEIVED(local, remote) + \/ CLOSE_LISTEN(local, remote) + \/ CLOSE_ESTABLISHED(local, remote) + \/ CLOSE_CLOSE_WAIT(local, remote) + \/ SEND(local, remote) + BY <2>user DEF User + <3>. QED BY <2>uo + (*************************************************************************) + (* System actions *) + (*************************************************************************) + <2>sys. ASSUME NEW local \in Peers, NEW remote \in Peers, + \/ SynSent(local, remote) + \/ SynReceived(local, remote) + \/ Listen(local, remote) + \/ Established(local, remote) + \/ FinWait1(local, remote) + \/ FinWait2(local, remote) + \/ Closing(local, remote) + \/ LastAck(local, remote) + \/ TimeWait(local, remote) + \/ Note2(local, remote) + PROVE TypeOK' + <3>1. CASE SynSent(local, remote) + <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + OBVIOUS + <4>1. CASE /\ IsPrefix(<<"SYN">>, network[local]) + /\ network' = [ network EXCEPT ![remote] = Append(@, "SYN,ACK"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "SYN-RECEIVED"] + <5>1. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <5>2. Append(network[remote], "SYN,ACK") \in Seq(Msgs) + BY <4>0 + <5>. QED BY <3>1, <4>1, <5>1, <5>2 DEF SynSent + <4>2. CASE /\ IsPrefix(<<"SYN,ACK">>, network[local]) + /\ network' = [ network EXCEPT ![remote] = Append(@, "ACK"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "ESTABLISHED"] + <5>1. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>2, PrefixOneNonEmpty + <5>2. Append(network[remote], "ACK") \in Seq(Msgs) + BY <4>0 + <5>. QED BY <3>1, <4>2, <5>1, <5>2 DEF SynSent + <4>. QED BY <3>1, <4>1, <4>2 DEF SynSent + <3>2. CASE SynReceived(local, remote) + <4>0. network[local] \in Seq(Msgs) + OBVIOUS + <4>1. CASE /\ IsPrefix(<<"RST">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "LISTEN"] + <5>1. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <5>. QED BY <3>2, <4>1, <5>1 DEF SynReceived + <4>2. CASE /\ IsPrefix(<<"ACK">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "ESTABLISHED"] + <5>1. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>2, PrefixOneNonEmpty + <5>. QED BY <3>2, <4>2, <5>1 DEF SynReceived + <4>. QED BY <3>2, <4>1, <4>2 DEF SynReceived + <3>3. CASE Listen(local, remote) + <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + OBVIOUS + <4>1. IsPrefix(<<"SYN">>, network[local]) + BY <3>3 DEF Listen + <4>2. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <4>3. Append(network[remote], "SYN,ACK") \in Seq(Msgs) + BY <4>0 + <4>. QED BY <3>3, <4>2, <4>3 DEF Listen + <3>4. CASE Established(local, remote) + <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + OBVIOUS + <4>1. IsPrefix(<<"FIN">>, network[local]) + BY <3>4 DEF Established + <4>2. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <4>3. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <4>0 + <4>. QED BY <3>4, <4>2, <4>3 DEF Established + <3>5. CASE FinWait1(local, remote) + <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + OBVIOUS + <4>1. CASE /\ IsPrefix(<<"FIN">>, network[local]) + /\ network' = [network EXCEPT ![remote] = Append(@, "ACKofFIN"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "CLOSING"] + <5>1. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <5>2. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <4>0 + <5>. QED BY <3>5, <4>1, <5>1, <5>2 DEF FinWait1 + <4>2. CASE /\ IsPrefix(<<"ACKofFIN">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "FIN-WAIT-2"] + <5>1. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>2, PrefixOneNonEmpty + <5>. QED BY <3>5, <4>2, <5>1 DEF FinWait1 + <4>. QED BY <3>5, <4>1, <4>2 DEF FinWait1 + <3>6. CASE FinWait2(local, remote) + <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + OBVIOUS + <4>1. IsPrefix(<<"FIN">>, network[local]) + BY <3>6 DEF FinWait2 + <4>2. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <4>3. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <4>0 + <4>. QED BY <3>6, <4>2, <4>3 DEF FinWait2 + <3>7. CASE Closing(local, remote) + <4>0. network[local] \in Seq(Msgs) + OBVIOUS + <4>1. IsPrefix(<<"ACKofFIN">>, network[local]) + BY <3>7 DEF Closing + <4>2. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <4>. QED BY <3>7, <4>2 DEF Closing + <3>8. CASE LastAck(local, remote) + <4>0. network[local] \in Seq(Msgs) + OBVIOUS + <4>1. IsPrefix(<<"ACKofFIN">>, network[local]) + BY <3>8 DEF LastAck + <4>2. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <4>1, PrefixOneNonEmpty + <4>. QED BY <3>8, <4>2 DEF LastAck + <3>9. CASE TimeWait(local, remote) + BY <3>9 DEF TimeWait + <3>10. CASE Note2(local, remote) + <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + OBVIOUS + <4>1. IsPrefix(<<"FIN", "ACKofFIN">>, network[local]) + BY <3>10 DEF Note2 + <4>2. Len(network[local]) >= 2 + BY <4>0, <4>1, PrefixTwoNonEmpty + <4>3. SubSeq(network[local], 3, Len(network[local])) \in Seq(Msgs) + BY <4>0, <4>2, SubSeqProperties + <4>4. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <4>0 + <4>. QED BY <3>10, <4>3, <4>4 DEF Note2 + <3>. QED BY <2>sys, <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7, <3>8, + <3>9, <3>10 + <2>system. CASE System + <3>. PICK local \in Peers, remote \in Peers : + \/ SynSent(local, remote) + \/ SynReceived(local, remote) + \/ Listen(local, remote) + \/ Established(local, remote) + \/ FinWait1(local, remote) + \/ FinWait2(local, remote) + \/ Closing(local, remote) + \/ LastAck(local, remote) + \/ TimeWait(local, remote) + \/ Note2(local, remote) + BY <2>system DEF System + <3>. QED BY <2>sys + <2>reset. CASE Reset + <3>. PICK local \in Peers, remote \in Peers : Note3(local, remote) + BY <2>reset DEF Reset + <3>. USE DEF Note3 + <3>1. CASE /\ tcb[local] + /\ network' = [network EXCEPT ![remote] = Append(@, "RST")] + /\ connstate' = [connstate EXCEPT ![local] = "TIME-WAIT"] + <4>. network[remote] \in Seq(Msgs) + OBVIOUS + <4>. Append(network[remote], "RST") \in Seq(Msgs) + OBVIOUS + <4>. QED BY <3>1 + <3>2. CASE /\ IsPrefix(<<"RST">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ \/ connstate' = [connstate EXCEPT ![local] = "LISTEN"] + \/ connstate' = [connstate EXCEPT ![local] = "CLOSED"] + <4>0. network[local] \in Seq(Msgs) + OBVIOUS + <4>1. Tail(network[local]) \in Seq(Msgs) + BY <4>0, <3>2, PrefixOneNonEmpty + <4>. QED BY <3>2, <4>1 + <3>. QED BY <3>1, <3>2 + <2>stutter. CASE UNCHANGED vars + BY <2>stutter DEF vars + <2>. QED BY <2>user, <2>system, <2>reset, <2>stutter DEF Next +<1>. QED BY <1>1, <1>2, PTL DEF Spec + +(***************************************************************************) +(* Pick the two distinct peers using the cardinality-2 assumption. *) +(***************************************************************************) +A == CHOOSE p \in Peers : TRUE +B == CHOOSE p \in Peers : p # A + +LEMMA PeersAB == + /\ A \in Peers + /\ B \in Peers + /\ A # B + /\ Peers = {A, B} + <1>1. Peers # {} + <2>. SUFFICES ASSUME Peers = {} PROVE FALSE + OBVIOUS + <2>1. Cardinality({}) = 0 + BY FS_EmptySet + <2>. QED BY <2>1, PeersAssumption, PeersFinite + <1>2. A \in Peers + BY <1>1 DEF A + <1>3. \E y \in Peers : y # A + <2>. SUFFICES ASSUME \A y \in Peers : y = A PROVE FALSE + OBVIOUS + <2>1. Peers = {A} + BY <1>2 + <2>2. Cardinality({A}) = 1 + BY FS_Singleton + <2>. QED BY <2>1, <2>2, PeersAssumption, PeersFinite + <1>4. B \in Peers /\ B # A + BY <1>3 DEF B + <1>5. Peers = {A, B} + <2>. SUFFICES ASSUME NEW z \in Peers, z # A, z # B PROVE FALSE + BY <1>2, <1>4 + <2>a. IsFiniteSet({A}) /\ Cardinality({A}) = 1 + BY FS_Singleton + <2>b. {A} \cup {B} = {A, B} /\ B \notin {A} + BY <1>4 + <2>c. IsFiniteSet({A, B}) /\ Cardinality({A, B}) = 2 + <3>1. IsFiniteSet({A} \cup {B}) /\ + Cardinality({A} \cup {B}) = (IF B \in {A} THEN 1 ELSE 1 + 1) + BY <2>a, FS_AddElement + <3>. QED BY <2>b, <3>1 + <2>d. {A, B} \cup {z} = {A, B, z} /\ z \notin {A, B} + OBVIOUS + <2>e. IsFiniteSet({A, B, z}) /\ Cardinality({A, B, z}) = 3 + <3>1. IsFiniteSet({A, B} \cup {z}) /\ + Cardinality({A, B} \cup {z}) = (IF z \in {A, B} THEN 2 ELSE 2 + 1) + BY <2>c, FS_AddElement + <3>. QED BY <2>d, <3>1 + <2>1. {A, B, z} \subseteq Peers + BY <1>2, <1>4 + <2>3. Cardinality({A, B, z}) <= Cardinality(Peers) + BY <2>1, <2>e, FS_Subset, PeersFinite + <2>. QED BY <2>3, <2>e, PeersAssumption + <1>. QED BY <1>2, <1>4, <1>5 + +(***************************************************************************) +(* Inv reformulated explicitly in terms of the two peers. This is *) +(* convenient for case analysis since {p \in Peers : network[p] = <<>>} *) +(* is one of {}, {A}, {B}, {A, B}. *) +(***************************************************************************) +LEMMA InvIsAB == + Inv <=> ((network[A] = <<>> /\ network[B] = <<>>) + => (connstate[A] = "ESTABLISHED" <=> connstate[B] = "ESTABLISHED")) + BY PeersAB DEF Inv + +(***************************************************************************) +(* Initial state satisfies Inv. *) +(***************************************************************************) +THEOREM InvInit == Init => Inv + <1>. SUFFICES ASSUME Init PROVE Inv + OBVIOUS + <1>1. \A p \in Peers : connstate[p] = "CLOSED" /\ network[p] = <<>> + BY DEF Init + <1>. QED BY <1>1 DEF Inv + +(***************************************************************************) +(* *) +(* Status summary: *) +(* *) +(* * TypeCorrect : Spec => []TypeOK fully discharged *) +(* * InvInit : Init => Inv fully discharged *) +(* * InvIsAB : two-peer reformulation fully discharged *) +(* * PeersAB : two-element Peers witnesses fully discharged *) +(* *) +(* The inductive step *) +(* *) +(* Inv /\ [Next]_vars => Inv' *) +(* *) +(* requires a substantial strengthening of Inv that captures the *) +(* message-history correlations of the TCP FSM. The required auxiliary *) +(* invariants relate the head of each peer's incoming queue to the *) +(* possible states of the sender. In particular, there are six trigger *) +(* "consume-only" actions whose post-state may leave both networks *) +(* empty -- SynReceived RST, SynReceived ACK, FW1 ACKofFIN, Closing, *) +(* LastAck, and Note3 RST -- and for each of them the system can only *) +(* land in an EST-agreement-respecting state because of detailed *) +(* invariants of the form *) +(* *) +(* network[p] = <> /\ network[q] = <<>> => connstate[q] \in T(X). *) +(* *) +(* These invariants are themselves preserved only by accounting for *) +(* further protocol-specific facts about how each TCP message can ever *) +(* be enqueued. The full discharge of the inductive step for Inv is *) +(* therefore left as future work; the static obligations above are *) +(* fully proved in TLAPS. *) +(***************************************************************************) +============================================================================ From 865981fa9cfb6fefc9638fcccf0ca73149b819a9 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Sun, 10 May 2026 16:04:21 -0700 Subject: [PATCH 03/22] tcp: discover an inductive strengthening of Inv with Apalache Add specifications/tcp/IndInv_apa.tla together with two cfg files: - IndInv_apa_init.cfg drives the Init => IndInv check (apalache-mc check --length=0 --init=Init --inv=IndInv) - IndInv_apa.cfg drives the inductive-step check (apalache-mc check --length=1 --init=IndInit --inv=IndInv) After ~26 counterexample-driven iterations both obligations pass (~5s and ~70s respectively) for the bounded model with MaxLen=4 and Cardinality(Peers)=2, so Spec => []IndInv => []Inv holds for executions in which every queue stays bounded by MaxLen. The strengthened invariant is IndInv == TypeOKBounded /\ BoundedNetwork /\ Inv /\ Aux_singleton_RST \* (i) /\ Aux_singleton_ACK \* (ii) /\ Aux_singleton_ACKofFIN \* (iii) /\ Aux_EST_evidence \* (iv) /\ Aux_LastMsg \* (v) /\ Aux_RST_at_end \* (vi) (i)-(iii) the three "singleton" trigger conditions that pin down connstate[q] for each consume-only action whose post- state can leave both networks empty (SynReceived RST/ ACK, FW1/Closing/LastAck on ACKofFIN, Note3 RST recv). (iv) p in EST forces some TCP-message witness in either queue (or q in some PostEst state) -- otherwise p has no trace of the handshake. (v) for q in {SR, FW1, CW, LA, CLOSING, SS} the LAST element of n[p] equals the message q appended on entry to that state. Reflects "while q stays in X, q does not append to n[p]". (vi) LastMsg(p)=RST forces q to be in {TIME-WAIT, CLOSED, LISTEN} -- after Note3 send q can only transit through those without re-touching n[p]. The file documents the iteration history. Several intermediate candidates I tried (Aux_RST_head, Aux_q_PostEst, Aux_FIN_handshake_predecessor with a "p in PostEst" disjunct, Aux_SR_q_EST_has_ACK) were OVER-RESTRICTIVE: they were violated by reachable post-states where Inv is vacuously true on a singleton empty-queue case. The successful invariant is built from the singleton triggers + a careful evidence clause for EST + per-state LastMsg + the Note3-RST anchor. The tcp.tla spec is unchanged; all bookkeeping (Apalache type annotations, the symbolic IndInit havoc, the bounded NextBounded transition) lives in the IndInv_apa module via INSTANCE tcp. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/IndInv_apa.cfg | 8 + specifications/tcp/IndInv_apa.tla | 220 +++++++++++++++++++++++++ specifications/tcp/IndInv_apa_init.cfg | 8 + 3 files changed, 236 insertions(+) create mode 100644 specifications/tcp/IndInv_apa.cfg create mode 100644 specifications/tcp/IndInv_apa.tla create mode 100644 specifications/tcp/IndInv_apa_init.cfg diff --git a/specifications/tcp/IndInv_apa.cfg b/specifications/tcp/IndInv_apa.cfg new file mode 100644 index 00000000..1d6676a6 --- /dev/null +++ b/specifications/tcp/IndInv_apa.cfg @@ -0,0 +1,8 @@ +CONSTANT + Peers <- PeersVal + MaxLen <- MaxLenVal + +INIT IndInit +NEXT NextBounded + +INVARIANT IndInv diff --git a/specifications/tcp/IndInv_apa.tla b/specifications/tcp/IndInv_apa.tla new file mode 100644 index 00000000..7d248286 --- /dev/null +++ b/specifications/tcp/IndInv_apa.tla @@ -0,0 +1,220 @@ +------------------------------ MODULE IndInv_apa ------------------------------ +(***************************************************************************) +(* Apalache-based search for an inductive strengthening of tcp.tla's Inv. *) +(* *) +(* *** SUCCESS *** *) +(* *) +(* After ~26 counterexample-driven iterations, the candidate IndInv *) +(* defined below is INDUCTIVE for the bounded model (MaxLen=4): *) +(* *) +(* apalache-mc check --config=IndInv_apa_init.cfg --length=0 *) +(* checks Init => IndInv --> EXITCODE: OK (~5s) *) +(* *) +(* apalache-mc check --config=IndInv_apa.cfg --length=1 *) +(* checks IndInv /\ [Next]_vars => IndInv' --> EXITCODE: OK *) +(* (~70s) *) +(* *) +(* Since Inv is a conjunct of IndInv, the standard inductive argument *) +(* yields: *) +(* *) +(* Spec => []IndInv => []Inv *) +(* *) +(* for executions in which every queue stays bounded by MaxLen=4. *) +(* *) +(* The final IndInv has 7 auxiliary clauses (in addition to TypeOK and *) +(* Inv), each of which proved necessary to discharge a counterexample: *) +(* *) +(* 1. Aux_singleton_RST (iter 1) *) +(* n[p]=<> /\ n[q]=<<>> => connstate[q] /= EST *) +(* The original SynReceived(RST) trigger. *) +(* *) +(* 2. Aux_singleton_ACK (iter 2) *) +(* n[p]=<> /\ n[q]=<<>> /\ connstate[p]=SR *) +(* => connstate[q]=EST *) +(* The SynReceived(ACK) trigger. *) +(* *) +(* 3. Aux_singleton_ACKofFIN (iter 3) *) +(* n[p]=<> /\ n[q]=<<>> /\ connstate[p] in {FW1,CLOSING,LA}*) +(* => connstate[q] /= EST *) +(* The FW1/Closing/LastAck triggers. *) +(* *) +(* 4. Aux_EST_evidence (iter 22, 25, 26) *) +(* connstate[p]=EST => q in PostEst OR a TCP-message in either *) +(* queue (handshake or teardown) is present. *) +(* Forbids "p reached EST with no trace anywhere". *) +(* *) +(* 5. Aux_LastMsg (iter 23) *) +(* For q in {SR, FW1, CW, LA, CLOSING, SS}, the last element of *) +(* n[p] equals the message q appended on entry to that state. *) +(* Captures "while q stays in X, no further appends from q". *) +(* *) +(* 6. Aux_RST_at_end (iter 24) *) +(* LastMsg(p)=RST => connstate[q] in {TIME-WAIT, CLOSED, LISTEN}. *) +(* Captures "after Note3 send q is in TW; q can transit only *) +(* through CLOSED/LISTEN without re-touching n[p]". *) +(* *) +(* Iteration history (~26 iterations). Several "structural" candidates *) +(* I tried (Aux_RST_head, Aux_q_PostEst, Aux_FIN_handshake_predecessor *) +(* with a "p in PostEst" disjunct, Aux_SR_q_EST_has_ACK) turned out to *) +(* be OVER-RESTRICTIVE: they were violated by reachable post-states *) +(* where Inv was vacuously true on a singleton. The successful *) +(* invariant is built from the singleton triggers + a careful evidence *) +(* clause for EST + LastMsg per state + the Note3-RST anchor. *) +(***************************************************************************) +EXTENDS Integers, Sequences, FiniteSets, Apalache + +CONSTANT + \* @type: Set(PEER); + Peers, + \* @type: Int; + MaxLen + +VARIABLE + \* @type: PEER -> Bool; + tcb, + \* @type: PEER -> Str; + connstate, + \* @type: PEER -> Seq(Str); + network + +INSTANCE tcp + +PeersVal == { "p1_OF_PEER", "p2_OF_PEER" } +MaxLenVal == 4 + +Msgs == {"SYN", "SYN,ACK", "ACK", "RST", "FIN", "ACKofFIN"} + +(***************************************************************************) +(* Apalache-friendly "havoc" for the inductive-invariant initial state. *) +(***************************************************************************) +\* @type: () => PEER -> Bool; +GenTcb == [p \in Peers |-> Gen(1)] +\* @type: () => PEER -> Str; +GenConnstate == [p \in Peers |-> Gen(1)] +\* @type: () => PEER -> Seq(Str); +GenNetwork == [p \in Peers |-> Gen(MaxLen)] + +(***************************************************************************) +(* Type-correctness clause that uses a constant index range so Apalache *) +(* can enumerate. *) +(***************************************************************************) +TypeOKBounded == + /\ tcb \in [Peers -> BOOLEAN] + /\ connstate \in [Peers -> States] + /\ \A p \in Peers : + \A i \in 1..MaxLen : + (i <= Len(network[p])) => network[p][i] \in Msgs + +BoundedNetwork == + \A p \in Peers : Len(network[p]) \in 0..MaxLen + +(***************************************************************************) +(* Helper. *) +(***************************************************************************) +HasMsg(m, p) == + \E i \in 1..MaxLen : i <= Len(network[p]) /\ network[p][i] = m + +PostEstStrict == {"ESTABLISHED", "FIN-WAIT-1", "FIN-WAIT-2", "CLOSING", + "CLOSE-WAIT", "LAST-ACK", "TIME-WAIT"} +PostEst == PostEstStrict \cup {"CLOSED"} + +(***************************************************************************) +(* Singleton-form Aux invariants -- one for each "consume-only" action *) +(* that can leave the system in the both-empty configuration. *) +(***************************************************************************) +Aux_singleton_RST == + \A p, q \in Peers : + (p # q /\ network[p] = <<"RST">> /\ network[q] = <<>>) + => connstate[q] # "ESTABLISHED" + +Aux_singleton_ACK == + \A p, q \in Peers : + (p # q /\ network[p] = <<"ACK">> /\ network[q] = <<>> + /\ connstate[p] = "SYN-RECEIVED") + => connstate[q] = "ESTABLISHED" + +Aux_singleton_ACKofFIN == + \A p, q \in Peers : + (p # q /\ network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + /\ connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"}) + => connstate[q] # "ESTABLISHED" + +(***************************************************************************) +(* iter 22: if p is in ESTABLISHED, then either q is also in PostEst or *) +(* there is handshake-completion evidence (ACK or SYN,ACK) in q's queue *) +(* or teardown evidence in p's queue. *) +(***************************************************************************) +Aux_EST_evidence == + \A p, q \in Peers : + (p # q /\ connstate[p] = "ESTABLISHED") + => \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + +(***************************************************************************) +(* iter 23: while q is in a state X whose entry transition appends a *) +(* known message m to n[p], the LAST element of n[p] (if non-empty) *) +(* equals m. Apalache used n[p1]=<<"ACK">> with p2=SR -- but p2's last *) +(* append (Listen/SynSent SYN entering SR) writes SYN,ACK, not ACK. *) +(* For SR, FW1, CW, LA, CLOSING the entry transition has a fixed *) +(* append pattern; for the others (LISTEN, EST, FW2, TW, CLOSED, SS) *) +(* the entry may be no-append, so we cannot constrain. *) +(***************************************************************************) +LastMsg(p) == network[p][Len(network[p])] + +Aux_LastMsg == + \A p, q \in Peers : + (p # q /\ network[p] # <<>>) => + /\ connstate[q] = "SYN-RECEIVED" => LastMsg(p) = "SYN,ACK" + /\ connstate[q] = "FIN-WAIT-1" => LastMsg(p) \in {"FIN", "RST"} + /\ connstate[q] = "CLOSE-WAIT" => LastMsg(p) = "ACKofFIN" + /\ connstate[q] = "LAST-ACK" => LastMsg(p) = "FIN" + /\ connstate[q] = "CLOSING" => LastMsg(p) = "ACKofFIN" + /\ connstate[q] = "SYN-SENT" => LastMsg(p) = "SYN" + +(***************************************************************************) +(* iter 24: when the LAST element of n[p] is RST, q just sent RST via *) +(* Note3 and is therefore in {TIME-WAIT, CLOSED, LISTEN} (no further *) +(* q-action could have appended to n[p] without changing its last *) +(* element). *) +(***************************************************************************) +Aux_RST_at_end == + \A p, q \in Peers : + (p # q /\ network[p] # <<>> /\ LastMsg(p) = "RST") + => connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + +(***************************************************************************) +(* Candidate inductive invariant (singleton-form only). *) +(***************************************************************************) +IndInv == + /\ TypeOKBounded + /\ BoundedNetwork + /\ Inv + /\ Aux_singleton_RST + /\ Aux_singleton_ACK + /\ Aux_singleton_ACKofFIN + /\ Aux_EST_evidence + /\ Aux_LastMsg + /\ Aux_RST_at_end + +(***************************************************************************) +(* Symbolic-init form of IndInv. *) +(***************************************************************************) +IndInit == + /\ tcb = GenTcb + /\ connstate = GenConnstate + /\ network = GenNetwork + /\ IndInv + +(***************************************************************************) +(* Bounded-step transition that keeps the queue length within MaxLen. *) +(***************************************************************************) +NextBounded == + /\ Next + /\ \A p \in Peers : Len(network'[p]) \in 0..MaxLen + +============================================================================== diff --git a/specifications/tcp/IndInv_apa_init.cfg b/specifications/tcp/IndInv_apa_init.cfg new file mode 100644 index 00000000..1ea5d6c4 --- /dev/null +++ b/specifications/tcp/IndInv_apa_init.cfg @@ -0,0 +1,8 @@ +CONSTANT + Peers <- PeersVal + MaxLen <- MaxLenVal + +INIT Init +NEXT NextBounded + +INVARIANT IndInv From bd4f5253e2d49029ac33653cdd27b94931780aee Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Sun, 10 May 2026 16:48:20 -0700 Subject: [PATCH 04/22] tcp_proof: start TLAPS proof of IndInv (PASSIVE_OPEN case) Translate the Apalache-discovered inductive strengthening into TLA+ definitions (HasMsg, LastMsg, PostEst, Aux_singleton_RST, Aux_singleton_ACK, Aux_singleton_ACKofFIN, Aux_EST_evidence, Aux_LastMsg, Aux_RST_at_end, IndInv) and discharge the static half: - IndInvInit : Init => IndInv (clause-by-clause). - IndInvStutter : IndInv /\ UNCHANGED vars => IndInv' (trivial). Refactor the existing TypeCorrect proof to expose TypeOKInductive as a re-usable lemma so the IndInv inductive step does not have to re-derive type preservation. Begin the action-based per-clause analysis for IndInvIsInductive in a sub-lemma IndInvUser: dispatch on the eight user actions, and fully discharge the PASSIVE_OPEN case (no network change, local -> LISTEN). The interesting sub-step is Aux_EST_evidence with q=local: when local transitions CLOSED -> LISTEN the "q in PostEst" disjunct is lost, but Inv (which excludes both networks empty when EST disagrees) forces a HasMsg disjunct via TypeOK and the Msgs alphabet. The remaining 7 user actions, the System and Reset cases, and the top-level IndInvIsInductive QED are all marked OMITTED as work-in-progress placeholders to be filled in incrementally. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 679 ++++++++++++++++++++++--------- 1 file changed, 497 insertions(+), 182 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 2e758d5b..97c1d700 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -80,17 +80,14 @@ LEMMA PrefixTwoNonEmpty == (* Type correctness. *) (***************************************************************************) -THEOREM TypeCorrect == Spec => []TypeOK -<1>1. Init => TypeOK - BY DEF Init, TypeOK, States -<1>2. TypeOK /\ [Next]_vars => TypeOK' - <2>. SUFFICES ASSUME TypeOK, [Next]_vars PROVE TypeOK' +LEMMA TypeOKInductive == TypeOK /\ [Next]_vars => TypeOK' + <1>. SUFFICES ASSUME TypeOK, [Next]_vars PROVE TypeOK' OBVIOUS - <2>. USE DEF TypeOK, States, Msgs + <1>. USE DEF TypeOK, States, Msgs (*************************************************************************) (* User actions *) (*************************************************************************) - <2>uo. ASSUME NEW local \in Peers, NEW remote \in Peers, + <1>uo. ASSUME NEW local \in Peers, NEW remote \in Peers, PASSIVE_OPEN(local, remote) \/ ACTIVE_OPEN(local, remote) \/ CLOSE_SYN_SENT(local, remote) \/ CLOSE_SYN_RECEIVED(local, remote) @@ -99,45 +96,45 @@ THEOREM TypeCorrect == Spec => []TypeOK \/ CLOSE_CLOSE_WAIT(local, remote) \/ SEND(local, remote) PROVE TypeOK' - <3>1. CASE PASSIVE_OPEN(local, remote) - BY <3>1 DEF PASSIVE_OPEN - <3>2. CASE ACTIVE_OPEN(local, remote) - <4>. network[remote] \in Seq(Msgs) + <2>1. CASE PASSIVE_OPEN(local, remote) + BY <2>1 DEF PASSIVE_OPEN + <2>2. CASE ACTIVE_OPEN(local, remote) + <3>. network[remote] \in Seq(Msgs) OBVIOUS - <4>. Append(network[remote], "SYN") \in Seq(Msgs) + <3>. Append(network[remote], "SYN") \in Seq(Msgs) OBVIOUS - <4>. QED BY <3>2 DEF ACTIVE_OPEN - <3>3. CASE CLOSE_SYN_SENT(local, remote) - BY <3>3 DEF CLOSE_SYN_SENT - <3>4. CASE CLOSE_SYN_RECEIVED(local, remote) - <4>. network[remote] \in Seq(Msgs) + <3>. QED BY <2>2 DEF ACTIVE_OPEN + <2>3. CASE CLOSE_SYN_SENT(local, remote) + BY <2>3 DEF CLOSE_SYN_SENT + <2>4. CASE CLOSE_SYN_RECEIVED(local, remote) + <3>. network[remote] \in Seq(Msgs) OBVIOUS - <4>. Append(network[remote], "FIN") \in Seq(Msgs) + <3>. Append(network[remote], "FIN") \in Seq(Msgs) OBVIOUS - <4>. QED BY <3>4 DEF CLOSE_SYN_RECEIVED - <3>5. CASE CLOSE_LISTEN(local, remote) - BY <3>5 DEF CLOSE_LISTEN - <3>6. CASE CLOSE_ESTABLISHED(local, remote) - <4>. network[remote] \in Seq(Msgs) + <3>. QED BY <2>4 DEF CLOSE_SYN_RECEIVED + <2>5. CASE CLOSE_LISTEN(local, remote) + BY <2>5 DEF CLOSE_LISTEN + <2>6. CASE CLOSE_ESTABLISHED(local, remote) + <3>. network[remote] \in Seq(Msgs) OBVIOUS - <4>. Append(network[remote], "FIN") \in Seq(Msgs) + <3>. Append(network[remote], "FIN") \in Seq(Msgs) OBVIOUS - <4>. QED BY <3>6 DEF CLOSE_ESTABLISHED - <3>7. CASE CLOSE_CLOSE_WAIT(local, remote) - <4>. network[remote] \in Seq(Msgs) + <3>. QED BY <2>6 DEF CLOSE_ESTABLISHED + <2>7. CASE CLOSE_CLOSE_WAIT(local, remote) + <3>. network[remote] \in Seq(Msgs) OBVIOUS - <4>. Append(network[remote], "FIN") \in Seq(Msgs) + <3>. Append(network[remote], "FIN") \in Seq(Msgs) OBVIOUS - <4>. QED BY <3>7 DEF CLOSE_CLOSE_WAIT - <3>8. CASE SEND(local, remote) - <4>. network[remote] \in Seq(Msgs) + <3>. QED BY <2>7 DEF CLOSE_CLOSE_WAIT + <2>8. CASE SEND(local, remote) + <3>. network[remote] \in Seq(Msgs) OBVIOUS - <4>. Append(network[remote], "SYN") \in Seq(Msgs) + <3>. Append(network[remote], "SYN") \in Seq(Msgs) OBVIOUS - <4>. QED BY <3>8 DEF SEND - <3>. QED BY <2>uo, <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7, <3>8 - <2>user. CASE User - <3>. PICK local \in Peers, remote \in Peers : + <3>. QED BY <2>8 DEF SEND + <2>. QED BY <1>uo, <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7, <2>8 + <1>user. CASE User + <2>. PICK local \in Peers, remote \in Peers : \/ ACTIVE_OPEN(local, remote) \/ PASSIVE_OPEN(local, remote) \/ CLOSE_SYN_SENT(local, remote) @@ -146,12 +143,12 @@ THEOREM TypeCorrect == Spec => []TypeOK \/ CLOSE_ESTABLISHED(local, remote) \/ CLOSE_CLOSE_WAIT(local, remote) \/ SEND(local, remote) - BY <2>user DEF User - <3>. QED BY <2>uo + BY <1>user DEF User + <2>. QED BY <1>uo (*************************************************************************) (* System actions *) (*************************************************************************) - <2>sys. ASSUME NEW local \in Peers, NEW remote \in Peers, + <1>sys. ASSUME NEW local \in Peers, NEW remote \in Peers, \/ SynSent(local, remote) \/ SynReceived(local, remote) \/ Listen(local, remote) @@ -163,127 +160,127 @@ THEOREM TypeCorrect == Spec => []TypeOK \/ TimeWait(local, remote) \/ Note2(local, remote) PROVE TypeOK' - <3>1. CASE SynSent(local, remote) - <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + <2>1. CASE SynSent(local, remote) + <3>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) OBVIOUS - <4>1. CASE /\ IsPrefix(<<"SYN">>, network[local]) + <3>1. CASE /\ IsPrefix(<<"SYN">>, network[local]) /\ network' = [ network EXCEPT ![remote] = Append(@, "SYN,ACK"), ![local] = Tail(network[local])] /\ connstate' = [connstate EXCEPT ![local] = "SYN-RECEIVED"] - <5>1. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <5>2. Append(network[remote], "SYN,ACK") \in Seq(Msgs) - BY <4>0 - <5>. QED BY <3>1, <4>1, <5>1, <5>2 DEF SynSent - <4>2. CASE /\ IsPrefix(<<"SYN,ACK">>, network[local]) + <4>1. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <4>2. Append(network[remote], "SYN,ACK") \in Seq(Msgs) + BY <3>0 + <4>. QED BY <2>1, <3>1, <4>1, <4>2 DEF SynSent + <3>2. CASE /\ IsPrefix(<<"SYN,ACK">>, network[local]) /\ network' = [ network EXCEPT ![remote] = Append(@, "ACK"), ![local] = Tail(network[local])] /\ connstate' = [connstate EXCEPT ![local] = "ESTABLISHED"] - <5>1. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>2, PrefixOneNonEmpty - <5>2. Append(network[remote], "ACK") \in Seq(Msgs) - BY <4>0 - <5>. QED BY <3>1, <4>2, <5>1, <5>2 DEF SynSent - <4>. QED BY <3>1, <4>1, <4>2 DEF SynSent - <3>2. CASE SynReceived(local, remote) - <4>0. network[local] \in Seq(Msgs) + <4>1. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>2, PrefixOneNonEmpty + <4>2. Append(network[remote], "ACK") \in Seq(Msgs) + BY <3>0 + <4>. QED BY <2>1, <3>2, <4>1, <4>2 DEF SynSent + <3>. QED BY <2>1, <3>1, <3>2 DEF SynSent + <2>2. CASE SynReceived(local, remote) + <3>0. network[local] \in Seq(Msgs) OBVIOUS - <4>1. CASE /\ IsPrefix(<<"RST">>, network[local]) + <3>1. CASE /\ IsPrefix(<<"RST">>, network[local]) /\ network' = [network EXCEPT ![local] = Tail(network[local])] /\ connstate' = [connstate EXCEPT ![local] = "LISTEN"] - <5>1. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <5>. QED BY <3>2, <4>1, <5>1 DEF SynReceived - <4>2. CASE /\ IsPrefix(<<"ACK">>, network[local]) + <4>1. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <4>. QED BY <2>2, <3>1, <4>1 DEF SynReceived + <3>2. CASE /\ IsPrefix(<<"ACK">>, network[local]) /\ network' = [network EXCEPT ![local] = Tail(network[local])] /\ connstate' = [connstate EXCEPT ![local] = "ESTABLISHED"] - <5>1. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>2, PrefixOneNonEmpty - <5>. QED BY <3>2, <4>2, <5>1 DEF SynReceived - <4>. QED BY <3>2, <4>1, <4>2 DEF SynReceived - <3>3. CASE Listen(local, remote) - <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + <4>1. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>2, PrefixOneNonEmpty + <4>. QED BY <2>2, <3>2, <4>1 DEF SynReceived + <3>. QED BY <2>2, <3>1, <3>2 DEF SynReceived + <2>3. CASE Listen(local, remote) + <3>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) OBVIOUS - <4>1. IsPrefix(<<"SYN">>, network[local]) - BY <3>3 DEF Listen - <4>2. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <4>3. Append(network[remote], "SYN,ACK") \in Seq(Msgs) - BY <4>0 - <4>. QED BY <3>3, <4>2, <4>3 DEF Listen - <3>4. CASE Established(local, remote) - <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + <3>1. IsPrefix(<<"SYN">>, network[local]) + BY <2>3 DEF Listen + <3>2. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <3>3. Append(network[remote], "SYN,ACK") \in Seq(Msgs) + BY <3>0 + <3>. QED BY <2>3, <3>2, <3>3 DEF Listen + <2>4. CASE Established(local, remote) + <3>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) OBVIOUS - <4>1. IsPrefix(<<"FIN">>, network[local]) - BY <3>4 DEF Established - <4>2. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <4>3. Append(network[remote], "ACKofFIN") \in Seq(Msgs) - BY <4>0 - <4>. QED BY <3>4, <4>2, <4>3 DEF Established - <3>5. CASE FinWait1(local, remote) - <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + <3>1. IsPrefix(<<"FIN">>, network[local]) + BY <2>4 DEF Established + <3>2. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <3>3. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <3>0 + <3>. QED BY <2>4, <3>2, <3>3 DEF Established + <2>5. CASE FinWait1(local, remote) + <3>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) OBVIOUS - <4>1. CASE /\ IsPrefix(<<"FIN">>, network[local]) + <3>1. CASE /\ IsPrefix(<<"FIN">>, network[local]) /\ network' = [network EXCEPT ![remote] = Append(@, "ACKofFIN"), ![local] = Tail(network[local])] /\ connstate' = [connstate EXCEPT ![local] = "CLOSING"] - <5>1. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <5>2. Append(network[remote], "ACKofFIN") \in Seq(Msgs) - BY <4>0 - <5>. QED BY <3>5, <4>1, <5>1, <5>2 DEF FinWait1 - <4>2. CASE /\ IsPrefix(<<"ACKofFIN">>, network[local]) + <4>1. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <4>2. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <3>0 + <4>. QED BY <2>5, <3>1, <4>1, <4>2 DEF FinWait1 + <3>2. CASE /\ IsPrefix(<<"ACKofFIN">>, network[local]) /\ network' = [network EXCEPT ![local] = Tail(network[local])] /\ connstate' = [connstate EXCEPT ![local] = "FIN-WAIT-2"] - <5>1. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>2, PrefixOneNonEmpty - <5>. QED BY <3>5, <4>2, <5>1 DEF FinWait1 - <4>. QED BY <3>5, <4>1, <4>2 DEF FinWait1 - <3>6. CASE FinWait2(local, remote) - <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + <4>1. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>2, PrefixOneNonEmpty + <4>. QED BY <2>5, <3>2, <4>1 DEF FinWait1 + <3>. QED BY <2>5, <3>1, <3>2 DEF FinWait1 + <2>6. CASE FinWait2(local, remote) + <3>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) OBVIOUS - <4>1. IsPrefix(<<"FIN">>, network[local]) - BY <3>6 DEF FinWait2 - <4>2. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <4>3. Append(network[remote], "ACKofFIN") \in Seq(Msgs) - BY <4>0 - <4>. QED BY <3>6, <4>2, <4>3 DEF FinWait2 - <3>7. CASE Closing(local, remote) - <4>0. network[local] \in Seq(Msgs) + <3>1. IsPrefix(<<"FIN">>, network[local]) + BY <2>6 DEF FinWait2 + <3>2. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <3>3. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <3>0 + <3>. QED BY <2>6, <3>2, <3>3 DEF FinWait2 + <2>7. CASE Closing(local, remote) + <3>0. network[local] \in Seq(Msgs) OBVIOUS - <4>1. IsPrefix(<<"ACKofFIN">>, network[local]) - BY <3>7 DEF Closing - <4>2. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <4>. QED BY <3>7, <4>2 DEF Closing - <3>8. CASE LastAck(local, remote) - <4>0. network[local] \in Seq(Msgs) + <3>1. IsPrefix(<<"ACKofFIN">>, network[local]) + BY <2>7 DEF Closing + <3>2. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <3>. QED BY <2>7, <3>2 DEF Closing + <2>8. CASE LastAck(local, remote) + <3>0. network[local] \in Seq(Msgs) OBVIOUS - <4>1. IsPrefix(<<"ACKofFIN">>, network[local]) - BY <3>8 DEF LastAck - <4>2. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <4>1, PrefixOneNonEmpty - <4>. QED BY <3>8, <4>2 DEF LastAck - <3>9. CASE TimeWait(local, remote) - BY <3>9 DEF TimeWait - <3>10. CASE Note2(local, remote) - <4>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) + <3>1. IsPrefix(<<"ACKofFIN">>, network[local]) + BY <2>8 DEF LastAck + <3>2. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <3>1, PrefixOneNonEmpty + <3>. QED BY <2>8, <3>2 DEF LastAck + <2>9. CASE TimeWait(local, remote) + BY <2>9 DEF TimeWait + <2>10. CASE Note2(local, remote) + <3>0. network[local] \in Seq(Msgs) /\ network[remote] \in Seq(Msgs) OBVIOUS - <4>1. IsPrefix(<<"FIN", "ACKofFIN">>, network[local]) - BY <3>10 DEF Note2 - <4>2. Len(network[local]) >= 2 - BY <4>0, <4>1, PrefixTwoNonEmpty - <4>3. SubSeq(network[local], 3, Len(network[local])) \in Seq(Msgs) - BY <4>0, <4>2, SubSeqProperties - <4>4. Append(network[remote], "ACKofFIN") \in Seq(Msgs) - BY <4>0 - <4>. QED BY <3>10, <4>3, <4>4 DEF Note2 - <3>. QED BY <2>sys, <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7, <3>8, - <3>9, <3>10 - <2>system. CASE System - <3>. PICK local \in Peers, remote \in Peers : + <3>1. IsPrefix(<<"FIN", "ACKofFIN">>, network[local]) + BY <2>10 DEF Note2 + <3>2. Len(network[local]) >= 2 + BY <3>0, <3>1, PrefixTwoNonEmpty + <3>3. SubSeq(network[local], 3, Len(network[local])) \in Seq(Msgs) + BY <3>0, <3>2, SubSeqProperties + <3>4. Append(network[remote], "ACKofFIN") \in Seq(Msgs) + BY <3>0 + <3>. QED BY <2>10, <3>3, <3>4 DEF Note2 + <2>. QED BY <1>sys, <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7, <2>8, + <2>9, <2>10 + <1>system. CASE System + <2>. PICK local \in Peers, remote \in Peers : \/ SynSent(local, remote) \/ SynReceived(local, remote) \/ Listen(local, remote) @@ -294,34 +291,38 @@ THEOREM TypeCorrect == Spec => []TypeOK \/ LastAck(local, remote) \/ TimeWait(local, remote) \/ Note2(local, remote) - BY <2>system DEF System - <3>. QED BY <2>sys - <2>reset. CASE Reset - <3>. PICK local \in Peers, remote \in Peers : Note3(local, remote) - BY <2>reset DEF Reset - <3>. USE DEF Note3 - <3>1. CASE /\ tcb[local] + BY <1>system DEF System + <2>. QED BY <1>sys + <1>reset. CASE Reset + <2>. PICK local \in Peers, remote \in Peers : Note3(local, remote) + BY <1>reset DEF Reset + <2>. USE DEF Note3 + <2>1. CASE /\ tcb[local] /\ network' = [network EXCEPT ![remote] = Append(@, "RST")] /\ connstate' = [connstate EXCEPT ![local] = "TIME-WAIT"] - <4>. network[remote] \in Seq(Msgs) + <3>. network[remote] \in Seq(Msgs) OBVIOUS - <4>. Append(network[remote], "RST") \in Seq(Msgs) + <3>. Append(network[remote], "RST") \in Seq(Msgs) OBVIOUS - <4>. QED BY <3>1 - <3>2. CASE /\ IsPrefix(<<"RST">>, network[local]) + <3>. QED BY <2>1 + <2>2. CASE /\ IsPrefix(<<"RST">>, network[local]) /\ network' = [network EXCEPT ![local] = Tail(network[local])] /\ \/ connstate' = [connstate EXCEPT ![local] = "LISTEN"] \/ connstate' = [connstate EXCEPT ![local] = "CLOSED"] - <4>0. network[local] \in Seq(Msgs) + <3>0. network[local] \in Seq(Msgs) OBVIOUS - <4>1. Tail(network[local]) \in Seq(Msgs) - BY <4>0, <3>2, PrefixOneNonEmpty - <4>. QED BY <3>2, <4>1 - <3>. QED BY <3>1, <3>2 - <2>stutter. CASE UNCHANGED vars - BY <2>stutter DEF vars - <2>. QED BY <2>user, <2>system, <2>reset, <2>stutter DEF Next -<1>. QED BY <1>1, <1>2, PTL DEF Spec + <3>1. Tail(network[local]) \in Seq(Msgs) + BY <3>0, <2>2, PrefixOneNonEmpty + <3>. QED BY <2>2, <3>1 + <2>. QED BY <2>1, <2>2 + <1>stutter. CASE UNCHANGED vars + BY <1>stutter DEF vars + <1>. QED BY <1>user, <1>system, <1>reset, <1>stutter DEF Next + +THEOREM TypeCorrect == Spec => []TypeOK + <1>1. Init => TypeOK + BY DEF Init, TypeOK, States + <1>. QED BY <1>1, TypeOKInductive, PTL DEF Spec (***************************************************************************) (* Pick the two distinct peers using the cardinality-2 assumption. *) @@ -399,34 +400,348 @@ THEOREM InvInit == Init => Inv <1>. QED BY <1>1 DEF Inv (***************************************************************************) +(* Inductive strengthening for the proof of Spec => []Inv. *) (* *) -(* Status summary: *) -(* *) -(* * TypeCorrect : Spec => []TypeOK fully discharged *) -(* * InvInit : Init => Inv fully discharged *) -(* * InvIsAB : two-peer reformulation fully discharged *) -(* * PeersAB : two-element Peers witnesses fully discharged *) -(* *) -(* The inductive step *) -(* *) -(* Inv /\ [Next]_vars => Inv' *) -(* *) -(* requires a substantial strengthening of Inv that captures the *) -(* message-history correlations of the TCP FSM. The required auxiliary *) -(* invariants relate the head of each peer's incoming queue to the *) -(* possible states of the sender. In particular, there are six trigger *) -(* "consume-only" actions whose post-state may leave both networks *) -(* empty -- SynReceived RST, SynReceived ACK, FW1 ACKofFIN, Closing, *) -(* LastAck, and Note3 RST -- and for each of them the system can only *) -(* land in an EST-agreement-respecting state because of detailed *) -(* invariants of the form *) +(* The seven Aux clauses below were discovered using Apalache's *) +(* inductive-invariant search (see specifications/tcp/IndInv_apa.tla, *) +(* the cfg files IndInv_apa.cfg and IndInv_apa_init.cfg, and the *) +(* commit message of the corresponding commit for the iteration log). *) +(***************************************************************************) +HasMsg(m, p) == + \E i \in 1..Len(network[p]) : network[p][i] = m + +LastMsg(p) == network[p][Len(network[p])] + +PostEstStrict == {"ESTABLISHED", "FIN-WAIT-1", "FIN-WAIT-2", "CLOSING", + "CLOSE-WAIT", "LAST-ACK", "TIME-WAIT"} +PostEst == PostEstStrict \cup {"CLOSED"} + +Aux_singleton_RST == + \A p, q \in Peers : + (p # q /\ network[p] = <<"RST">> /\ network[q] = <<>>) + => connstate[q] # "ESTABLISHED" + +Aux_singleton_ACK == + \A p, q \in Peers : + (p # q /\ network[p] = <<"ACK">> /\ network[q] = <<>> + /\ connstate[p] = "SYN-RECEIVED") + => connstate[q] = "ESTABLISHED" + +Aux_singleton_ACKofFIN == + \A p, q \in Peers : + (p # q /\ network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + /\ connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"}) + => connstate[q] # "ESTABLISHED" + +Aux_EST_evidence == + \A p, q \in Peers : + (p # q /\ connstate[p] = "ESTABLISHED") + => \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + +Aux_LastMsg == + \A p, q \in Peers : + (p # q /\ network[p] # <<>>) => + /\ connstate[q] = "SYN-RECEIVED" => LastMsg(p) = "SYN,ACK" + /\ connstate[q] = "FIN-WAIT-1" => LastMsg(p) \in {"FIN", "RST"} + /\ connstate[q] = "CLOSE-WAIT" => LastMsg(p) = "ACKofFIN" + /\ connstate[q] = "LAST-ACK" => LastMsg(p) = "FIN" + /\ connstate[q] = "CLOSING" => LastMsg(p) = "ACKofFIN" + /\ connstate[q] = "SYN-SENT" => LastMsg(p) = "SYN" + +Aux_RST_at_end == + \A p, q \in Peers : + (p # q /\ network[p] # <<>> /\ LastMsg(p) = "RST") + => connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + +IndInv == + /\ TypeOK + /\ Inv + /\ Aux_singleton_RST + /\ Aux_singleton_ACK + /\ Aux_singleton_ACKofFIN + /\ Aux_EST_evidence + /\ Aux_LastMsg + /\ Aux_RST_at_end + +(***************************************************************************) +(* Initial state. *) +(***************************************************************************) +THEOREM IndInvInit == Init => IndInv + <1>. SUFFICES ASSUME Init PROVE IndInv + OBVIOUS + <1>. USE DEF Init + <1>1. \A p \in Peers : connstate[p] = "CLOSED" /\ network[p] = <<>> /\ tcb[p] = FALSE + OBVIOUS + <1>2. TypeOK + BY <1>1 DEF TypeOK, States + <1>3. Inv + BY <1>1 DEF Inv + <1>4. Aux_singleton_RST /\ Aux_singleton_ACK /\ Aux_singleton_ACKofFIN + BY <1>1 DEF Aux_singleton_RST, Aux_singleton_ACK, Aux_singleton_ACKofFIN + <1>5. Aux_EST_evidence + BY <1>1 DEF Aux_EST_evidence + <1>6. Aux_LastMsg + BY <1>1 DEF Aux_LastMsg + <1>7. Aux_RST_at_end + BY <1>1 DEF Aux_RST_at_end + <1>. QED BY <1>2, <1>3, <1>4, <1>5, <1>6, <1>7 DEF IndInv + +(***************************************************************************) +(* Inductive step for IndInv. *) (* *) -(* network[p] = <> /\ network[q] = <<>> => connstate[q] \in T(X). *) +(* The proof has two parts: a stutter case (trivial) and a per-action *) +(* analysis covering every TCP transition. *) +(***************************************************************************) + +\* The "stutter" case is trivial: vars unchanged ⇒ every clause is the +\* same primed and unprimed. +LEMMA IndInvStutter == IndInv /\ UNCHANGED vars => IndInv' + <1>. SUFFICES ASSUME IndInv, UNCHANGED vars PROVE IndInv' + OBVIOUS + <1>. USE DEF vars + <1>1. tcb' = tcb /\ connstate' = connstate /\ network' = network + OBVIOUS + <1>. QED + BY <1>1 + DEF IndInv, TypeOK, Inv, + Aux_singleton_RST, Aux_singleton_ACK, Aux_singleton_ACKofFIN, + Aux_EST_evidence, Aux_LastMsg, Aux_RST_at_end, + HasMsg, LastMsg + +(***************************************************************************) +(* The non-stutter case is split into the three top-level disjuncts of *) +(* Next. Each sub-lemma takes IndInv, the action, and TypeOK' (already *) +(* discharged via TypeOKInductive) and proves the remaining clauses. *) +(***************************************************************************) + +\* User actions: PASSIVE_OPEN, CLOSE_SYN_SENT, CLOSE_LISTEN do NOT change +\* network[_]. ACTIVE_OPEN, SEND append "SYN" to n[remote]. +\* CLOSE_SYN_RECEIVED, CLOSE_ESTABLISHED, CLOSE_CLOSE_WAIT append "FIN". +\* In every case connstate[local] changes; connstate[r] for r # local is +\* unchanged. None of these actions transition local INTO ESTABLISHED. + +LEMMA IndInvUser == + ASSUME IndInv, TypeOK', + NEW local \in Peers, NEW remote \in Peers, + \/ PASSIVE_OPEN(local, remote) + \/ ACTIVE_OPEN(local, remote) + \/ CLOSE_SYN_SENT(local, remote) + \/ CLOSE_SYN_RECEIVED(local, remote) + \/ CLOSE_LISTEN(local, remote) + \/ CLOSE_ESTABLISHED(local, remote) + \/ CLOSE_CLOSE_WAIT(local, remote) + \/ SEND(local, remote) + PROVE IndInv' + <1>. USE PeersAB DEF IndInv + <1>1. CASE PASSIVE_OPEN(local, remote) + <2>. USE <1>1 DEF PASSIVE_OPEN + <2>. /\ network' = network + /\ connstate' = [connstate EXCEPT ![local] = "LISTEN"] + /\ connstate'[local] = "LISTEN" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : network'[r] = network[r] + BY DEF TypeOK + <2>1. Inv' + BY DEF Inv + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, + network'[p] = <<"RST">>, + network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. network[p] = <<"RST">> /\ network[q] = <<>> + OBVIOUS + <3>2. connstate[q] # "ESTABLISHED" + BY <3>1 DEF Aux_singleton_RST + <3>. QED + BY <3>2 + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, + network'[p] = <<"ACK">>, + network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. network[p] = <<"ACK">> /\ network[q] = <<>> + OBVIOUS + <3>2. connstate[p] = "SYN-RECEIVED" + \* p # local since action makes local LISTEN, not SR. + BY DEF TypeOK + <3>3. connstate[q] = "ESTABLISHED" + BY <3>1, <3>2 DEF Aux_singleton_ACK + <3>4. q # local + \* PASSIVE_OPEN required local=CLOSED, but q=EST. + BY <3>3 DEF TypeOK + <3>. QED + BY <3>3, <3>4 + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, + network'[p] = <<"ACKofFIN">>, + network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + OBVIOUS + <3>2. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY DEF TypeOK + <3>. QED + BY <3>1, <3>2 DEF Aux_singleton_ACKofFIN + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, + connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>3. \A m \in Msgs, r \in Peers : HasMsg(m, r) <=> HasMsg(m, r)' + BY DEF HasMsg + <3>5. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>5 DEF TypeOK + <4>2. \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + BY <3>1 DEF Aux_EST_evidence + <4>. QED + BY <3>3, <4>1, <4>2 DEF PostEst, PostEstStrict + <3>6. CASE q = local + \* connstate[local] was CLOSED; CLOSED in PostEst. After PASSIVE_OPEN + \* connstate[local] = LISTEN, NOT in PostEst. We instead derive a + \* HasMsg disjunct from Inv: with p=EST and q=CLOSED, Inv forces at + \* least one network to be non-empty (otherwise EST agreement fails). + \* Any element of a non-empty network is in Msgs (TypeOK), giving the + \* corresponding HasMsg disjunct. + <4>1. connstate[q] = "CLOSED" + BY <3>6 + <4>2. ~ (network[p] = <<>> /\ network[q] = <<>>) + <5>1. {r \in Peers : network[r] = <<>>} \subseteq Peers + OBVIOUS + <5>2. p \in Peers /\ q \in Peers /\ p # q + OBVIOUS + <5>. SUFFICES ASSUME network[p] = <<>>, network[q] = <<>> + PROVE FALSE + OBVIOUS + <5>3. p \in {r \in Peers : network[r] = <<>>} + /\ q \in {r \in Peers : network[r] = <<>>} + OBVIOUS + <5>4. connstate[p] = "ESTABLISHED" <=> connstate[q] = "ESTABLISHED" + BY <5>3 DEF Inv + <5>. QED + BY <3>1, <4>1, <5>4 + <4>3. network[p] # <<>> \/ network[q] # <<>> + BY <4>2 + <4>4. network[p] \in Seq(Msgs) /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>5. \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + <5>1. CASE network[p] # <<>> + <6>1. Len(network[p]) >= 1 /\ network[p][1] \in Msgs + BY <4>4, <5>1 + <6>2. \E i \in 1..Len(network[p]) : network[p][i] = network[p][1] + BY <6>1 + <6>. QED + BY <6>1, <6>2 DEF HasMsg, Msgs + <5>2. CASE network[q] # <<>> + <6>1. Len(network[q]) >= 1 /\ network[q][1] \in Msgs + BY <4>4, <5>2 + <6>2. \E i \in 1..Len(network[q]) : network[q][i] = network[q][1] + BY <6>1 + <6>. QED + BY <6>1, <6>2 DEF HasMsg, Msgs + <5>. QED + BY <4>3, <5>1, <5>2 + <4>. QED + BY <3>3, <4>5 DEF Msgs + <3>. QED + BY <3>5, <3>6 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. network[p] # <<>> /\ network[p] = network'[p] /\ LastMsg(p)' = LastMsg(p) + BY DEF LastMsg + <3>2. q # local => + /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p) = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p) \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p) = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p) = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p) = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p) = "SYN" + BY <3>1 DEF Aux_LastMsg + <3>3. q = local => connstate'[q] = "LISTEN" + OBVIOUS + <3>. QED BY <3>1, <3>2, <3>3 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. network[p] # <<>> /\ LastMsg(p) = "RST" + BY DEF LastMsg + <3>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <3>1 DEF Aux_RST_at_end + <3>. QED BY <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + <1>2. CASE PASSIVE_OPEN(local, remote) \/ ACTIVE_OPEN(local, remote) + \/ CLOSE_SYN_SENT(local, remote) + \/ CLOSE_SYN_RECEIVED(local, remote) + \/ CLOSE_LISTEN(local, remote) + \/ CLOSE_ESTABLISHED(local, remote) + \/ CLOSE_CLOSE_WAIT(local, remote) + \/ SEND(local, remote) + \* TODO: discharge the remaining 7 user-action sub-cases. + OMITTED + <1>. QED BY <1>1, <1>2 + +THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' + <1>. SUFFICES ASSUME IndInv, [Next]_vars PROVE IndInv' + OBVIOUS + <1>. USE DEF IndInv + <1>0. TypeOK' + BY TypeOKInductive + <1>stutter. CASE UNCHANGED vars + BY <1>stutter, IndInvStutter + <1>next. CASE Next + \* Per-action analysis -- to be filled in below. + OMITTED + <1>. QED BY <1>stutter, <1>next + +(***************************************************************************) +(* Once IndInvIsInductive is fully discharged, the main theorem follows: *) (* *) -(* These invariants are themselves preserved only by accounting for *) -(* further protocol-specific facts about how each TCP message can ever *) -(* be enqueued. The full discharge of the inductive step for Inv is *) -(* therefore left as future work; the static obligations above are *) -(* fully proved in TLAPS. *) +(* THEOREM SpecImpliesInv == Spec => []Inv *) +(* <1>. QED BY IndInvInit, IndInvIsInductive, PTL DEF Spec, IndInv *) (***************************************************************************) ============================================================================ From cb683555476a063a8cebee0864c5498a29bd9c8d Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Sun, 10 May 2026 17:20:14 -0700 Subject: [PATCH 05/22] tcp_proof: discharge IndInv preservation for 5 user actions Extend IndInvUser with the inductive-step proof for: - CLOSE_SYN_SENT (no network change, SS -> CLOSED) - CLOSE_LISTEN (no network change, LISTEN -> CLOSED) - ACTIVE_OPEN (append "SYN" to n[remote], CLOSED -> SS) - SEND (append "SYN" to n[remote], LISTEN -> SS) The two no-network actions follow the PASSIVE_OPEN template: - the singleton-aux's are preserved by IH except possibly when q = local, in which case the post connstate is non-EST and contradicts the IH's required q = EST; - Aux_EST_evidence uses Inv to derive a HasMsg disjunct when the "q in PostEst" disjunct is lost (only an issue for PASSIVE_OPEN); - Aux_LastMsg and Aux_RST_at_end propagate via "connstate'[q] = connstate[q] when q # local". The two append-SYN actions exploit the 2-peer cardinality (PeersAB) to force {p, q} = {local, remote} and derive contradictions in the singleton-aux cases where the appended SYN is forced to equal RST, ACK, or ACKofFIN. Aux_EST_evidence uses HasMsg("SYN", remote) as the witness when q = local (post SS is not in PostEst). Three CLOSE_* cases (CLOSE_SYN_RECEIVED, CLOSE_ESTABLISHED, CLOSE_CLOSE_WAIT), all System actions, both Reset cases and the top-level QED of IndInvIsInductive remain OMITTED placeholders. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 664 ++++++++++++++++++++++++++++++- 1 file changed, 657 insertions(+), 7 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 97c1d700..893a7a64 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -714,16 +714,666 @@ LEMMA IndInvUser == <3>. QED BY <3>2 <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv - <1>2. CASE PASSIVE_OPEN(local, remote) \/ ACTIVE_OPEN(local, remote) - \/ CLOSE_SYN_SENT(local, remote) - \/ CLOSE_SYN_RECEIVED(local, remote) - \/ CLOSE_LISTEN(local, remote) + (*************************************************************************) + (* The next two user actions also leave network unchanged and merely *) + (* transition connstate[local] to CLOSED. We re-use the same per-aux *) + (* structure as PASSIVE_OPEN above; the only differences are the pre- *) + (* state of local and the post-state value. *) + (*************************************************************************) + <1>2. CASE CLOSE_SYN_SENT(local, remote) + <2>. USE <1>2 DEF CLOSE_SYN_SENT + <2>. /\ network' = network + /\ connstate' = [connstate EXCEPT ![local] = "CLOSED"] + /\ connstate'[local] = "CLOSED" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : network'[r] = network[r] + /\ connstate[local] = "SYN-SENT" + BY DEF TypeOK + <2>1. Inv' + BY DEF Inv + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. network[p] = <<"RST">> /\ network[q] = <<>> + OBVIOUS + <3>2. connstate[q] # "ESTABLISHED" + BY <3>1 DEF Aux_singleton_RST + <3>. QED BY <3>2 DEF TypeOK + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. connstate[p] = "SYN-RECEIVED" + BY DEF TypeOK + <3>2. connstate[q] = "ESTABLISHED" + BY <3>1 DEF Aux_singleton_ACK + <3>3. q # local + BY <3>2 DEF TypeOK + <3>. QED BY <3>2, <3>3 DEF TypeOK + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY DEF TypeOK + <3>2. connstate[q] # "ESTABLISHED" + BY <3>1 DEF Aux_singleton_ACKofFIN + <3>. QED BY <3>2 DEF TypeOK + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. \A m \in Msgs, r \in Peers : HasMsg(m, r) <=> HasMsg(m, r)' + BY DEF HasMsg + <3>3. CASE q = local + BY <3>3 DEF PostEst, PostEstStrict + <3>4. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>. QED + BY <3>1, <3>2, <4>1 DEF Aux_EST_evidence, Msgs, PostEst, PostEstStrict + <3>. QED BY <3>3, <3>4 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. network[p] # <<>> /\ LastMsg(p)' = LastMsg(p) + BY DEF LastMsg + <3>2. q # local => connstate'[q] = connstate[q] + BY DEF TypeOK + <3>3. q = local => connstate'[q] = "CLOSED" + OBVIOUS + <3>. QED + BY <3>1, <3>2, <3>3 DEF Aux_LastMsg + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. network[p] # <<>> /\ LastMsg(p) = "RST" + BY DEF LastMsg + <3>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <3>1 DEF Aux_RST_at_end + <3>3. q # local => connstate'[q] = connstate[q] + BY DEF TypeOK + <3>4. q = local => connstate'[q] = "CLOSED" + OBVIOUS + <3>. QED BY <3>2, <3>3, <3>4 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + <1>3. CASE CLOSE_LISTEN(local, remote) + <2>. USE <1>3 DEF CLOSE_LISTEN + <2>. /\ network' = network + /\ connstate' = [connstate EXCEPT ![local] = "CLOSED"] + /\ connstate'[local] = "CLOSED" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : network'[r] = network[r] + /\ connstate[local] = "LISTEN" + BY DEF TypeOK + <2>1. Inv' + BY DEF Inv + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. network[p] = <<"RST">> /\ network[q] = <<>> + OBVIOUS + <3>2. connstate[q] # "ESTABLISHED" + BY <3>1 DEF Aux_singleton_RST + <3>. QED BY <3>2 DEF TypeOK + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. connstate[p] = "SYN-RECEIVED" + BY DEF TypeOK + <3>2. connstate[q] = "ESTABLISHED" + BY <3>1 DEF Aux_singleton_ACK + <3>3. q # local + BY <3>2 DEF TypeOK + <3>. QED BY <3>2, <3>3 + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY DEF TypeOK + <3>. QED BY <3>1 DEF Aux_singleton_ACKofFIN + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. \A m \in Msgs, r \in Peers : HasMsg(m, r) <=> HasMsg(m, r)' + BY DEF HasMsg + <3>3. CASE q = local + BY <3>3 DEF PostEst, PostEstStrict + <3>4. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>. QED + BY <3>1, <3>2, <4>1 DEF Aux_EST_evidence, Msgs, PostEst, PostEstStrict + <3>. QED BY <3>3, <3>4 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. network[p] # <<>> /\ LastMsg(p)' = LastMsg(p) + BY DEF LastMsg + <3>2. q # local => connstate'[q] = connstate[q] + BY DEF TypeOK + <3>3. q = local => connstate'[q] = "CLOSED" + OBVIOUS + <3>. QED BY <3>1, <3>2, <3>3 DEF Aux_LastMsg + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. network[p] # <<>> /\ LastMsg(p) = "RST" + BY DEF LastMsg + <3>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <3>1 DEF Aux_RST_at_end + <3>3. q # local => connstate'[q] = connstate[q] + BY DEF TypeOK + <3>4. q = local => connstate'[q] = "CLOSED" + OBVIOUS + <3>. QED BY <3>2, <3>3, <3>4 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + (*************************************************************************) + (* The remaining five user actions all append one message to n[remote] *) + (* and transition local to a non-EST state. We bundle them via a *) + (* helper sub-lemma parameterised on the appended message and the *) + (* target state. *) + (*************************************************************************) + <1>4. CASE ACTIVE_OPEN(local, remote) + <2>. USE <1>4 DEF ACTIVE_OPEN + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "SYN")] + /\ connstate' = [connstate EXCEPT ![local] = "SYN-SENT"] + /\ connstate'[local] = "SYN-SENT" + /\ network'[remote] = Append(network[remote], "SYN") + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # remote => network'[r] = network[r] + /\ connstate[local] = "CLOSED" + BY DEF TypeOK + <2>. local # remote + OBVIOUS + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>1. Inv' + <3>. \A p \in Peers : network'[remote] # <<>> + BY DEF TypeOK, Msgs + <3>. {p \in Peers : network'[p] = <<>>} \subseteq {p \in Peers : p # remote} + OBVIOUS + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ l # remote /\ r # remote + /\ network[l] = <<>> /\ network[r] = <<>> + OBVIOUS + <3>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <3>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ (l # local => connstate'[l] = connstate[l]) + /\ (r # local => connstate'[r] = connstate[r]) + /\ (l = local => connstate'[l] = "SYN-SENT") + /\ (r = local => connstate'[r] = "SYN-SENT") + OBVIOUS + <3>. QED + OBVIOUS + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. q # remote /\ network[q] = <<>> + OBVIOUS + <3>2. p # remote + \* If p = remote, network'[p] = Append(network[p], "SYN") which has + \* "SYN" as the last element, not "RST" alone. + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>1. network'[p] = Append(network[p], "SYN") + OBVIOUS + <4>2. Append(network[p], "SYN") = <<"RST">> + BY <4>1 + <4>3. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED + BY <4>2, <4>3 + <3>3. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED + BY <3>3 + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>1. Append(network[p], "SYN") = <<"ACK">> + OBVIOUS + <4>2. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED BY <4>1, <4>2 + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>3. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED + BY <3>3 + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>1. Append(network[p], "SYN") = <<"ACKofFIN">> + OBVIOUS + <4>2. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED BY <4>1, <4>2 + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>3. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED + BY <3>3 + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + \* Either q = local (just opened), or q is the other peer. + \* In the q = local case the new SYN sits in n'[remote] = n'[p]; + \* HasMsg("SYN", p)' holds because p = remote. + \* In the q # local case, connstate'[q] = connstate[q] and the + \* HasMsg disjuncts are unchanged. + <3>2. CASE q = local + <4>. p = remote + BY <3>1, <3>2, PeersAB + <4>1. network'[p] = Append(network[p], "SYN") + OBVIOUS + <4>2. network[p] \in Seq(Msgs) /\ Len(network[p]) \in Nat + BY DEF TypeOK, Msgs + <4>3. Len(network'[p]) = Len(network[p]) + 1 + /\ network'[p][Len(network'[p])] = "SYN" + BY <4>1, <4>2 + <4>4. \E i \in 1..Len(network'[p]) : network'[p][i] = "SYN" + BY <4>3 + <4>. QED + BY <4>4 DEF HasMsg + <3>3. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>3 DEF TypeOK + <4>2. \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + BY <3>1 DEF Aux_EST_evidence + <4>3. \A m \in Msgs, r \in Peers : HasMsg(m, r) => HasMsg(m, r)' + \* Append-only: every pre witness is also a post witness (with the + \* same index, since indices below Len(network[r]) are unaffected). + <5>. SUFFICES ASSUME NEW m \in Msgs, NEW r \in Peers, HasMsg(m, r) + PROVE HasMsg(m, r)' + OBVIOUS + <5>1. \E i \in 1..Len(network[r]) : network[r][i] = m + BY DEF HasMsg + <5>2. CASE r = remote + <6>1. network[r] \in Seq(Msgs) /\ network[r] = network[remote] + BY <5>2 DEF TypeOK, Msgs + <6>2. network'[r] = Append(network[r], "SYN") + BY <5>2 + <6>3. Len(network'[r]) = Len(network[r]) + 1 + BY <6>1, <6>2 + <6>4. \A i \in 1..Len(network[r]) : network'[r][i] = network[r][i] + BY <6>1, <6>2 + <6>. QED + BY <5>1, <6>3, <6>4 DEF HasMsg + <5>3. CASE r # remote + <6>1. network'[r] = network[r] + BY <5>3 + <6>. QED + BY <5>1, <6>1 DEF HasMsg + <5>. QED BY <5>2, <5>3 + <4>. QED + BY <4>1, <4>2, <4>3 DEF Msgs, PostEst, PostEstStrict + <3>. QED + BY <3>2, <3>3 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + <4>. q # remote /\ q = local + BY <3>1, PeersAB + <4>1. connstate'[q] = "SYN-SENT" + OBVIOUS + <4>2. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>3. network'[p] = Append(network[p], "SYN") + BY <3>1 + <4>4. LastMsg(p)' = "SYN" + BY <4>2, <4>3 DEF LastMsg + <4>. QED + BY <4>1, <4>4 + <3>2. CASE p # remote + <4>1. network'[p] = network[p] + BY <3>2 + <4>2. LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <4>1 DEF LastMsg + <4>3. q # local + \* If q = local then connstate'[q] = SYN-SENT, and Aux_LastMsg + \* requires LastMsg(p) = SYN; but p # remote in this branch + \* implies p = local, contradicting p # q. + BY <3>2, PeersAB + <4>4. connstate'[q] = connstate[q] + BY <4>3 DEF TypeOK + <4>. QED + BY <4>2, <4>4 DEF Aux_LastMsg + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + \* LastMsg(p)' = "SYN" # "RST" -- vacuous. + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. network'[p] = Append(network[p], "SYN") /\ LastMsg(p)' = "SYN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>1 DEF Aux_RST_at_end + <4>3. q # local + \* If q = local then connstate'[q] = SYN-SENT \notin + \* {TW, CLOSED, LISTEN}; but pre connstate[q] = CLOSED is in + \* the set so this is consistent only when q != local. We + \* take the easier route: p # remote implies p = local in + \* the 2-peer model, so q # local follows from p # q. + BY <3>2, PeersAB + <4>4. connstate'[q] = connstate[q] + BY <4>3 DEF TypeOK + <4>. QED + BY <4>2, <4>4 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + (*************************************************************************) + (* SEND mirrors ACTIVE_OPEN (LISTEN -> SS, append "SYN"). *) + (*************************************************************************) + <1>5. CASE SEND(local, remote) + <2>. USE <1>5 DEF SEND + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "SYN")] + /\ connstate' = [connstate EXCEPT ![local] = "SYN-SENT"] + /\ connstate'[local] = "SYN-SENT" + /\ network'[remote] = Append(network[remote], "SYN") + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # remote => network'[r] = network[r] + /\ connstate[local] = "LISTEN" + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ l # remote /\ r # remote + /\ network[l] = <<>> /\ network[r] = <<>> + BY DEF TypeOK, Msgs + <3>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <3>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <3>. QED OBVIOUS + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "SYN") = <<"RST">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "SYN") = <<"ACK">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "SYN") = <<"ACKofFIN">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. CASE q = local + <4>. p = remote + BY <3>1, <3>2, PeersAB + <4>1. network[p] \in Seq(Msgs) /\ Len(network[p]) \in Nat + BY DEF TypeOK, Msgs + <4>2. network'[p] = Append(network[p], "SYN") + /\ Len(network'[p]) = Len(network[p]) + 1 + /\ network'[p][Len(network'[p])] = "SYN" + BY <4>1 + <4>. QED + BY <4>2 DEF HasMsg + <3>3. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>3 DEF TypeOK + <4>2. \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + BY <3>1 DEF Aux_EST_evidence + <4>3. \A m \in Msgs, r \in Peers : HasMsg(m, r) => HasMsg(m, r)' + <5>. SUFFICES ASSUME NEW m \in Msgs, NEW r \in Peers, HasMsg(m, r) + PROVE HasMsg(m, r)' + OBVIOUS + <5>1. \E i \in 1..Len(network[r]) : network[r][i] = m + BY DEF HasMsg + <5>2. CASE r = remote + <6>1. network[r] \in Seq(Msgs) + BY <5>2 DEF TypeOK, Msgs + <6>2. network'[r] = Append(network[r], "SYN") + BY <5>2 + <6>3. Len(network'[r]) = Len(network[r]) + 1 + BY <6>1, <6>2 + <6>4. \A i \in 1..Len(network[r]) : network'[r][i] = network[r][i] + BY <6>1, <6>2 + <6>. QED + BY <5>1, <6>3, <6>4 DEF HasMsg + <5>3. CASE r # remote + BY <5>1, <5>3 DEF HasMsg + <5>. QED BY <5>2, <5>3 + <4>. QED + BY <4>1, <4>2, <4>3 DEF Msgs, PostEst, PostEstStrict + <3>. QED BY <3>2, <3>3 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + <4>. q # remote /\ q = local + BY <3>1, PeersAB + <4>. connstate'[q] = "SYN-SENT" + OBVIOUS + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. network'[p] = Append(network[p], "SYN") /\ LastMsg(p)' = "SYN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. q # local + BY <3>2, PeersAB + <4>3. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <4>. QED + BY <4>1, <4>3 DEF Aux_LastMsg + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. LastMsg(p)' = "SYN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>1 DEF Aux_RST_at_end + <4>3. q # local + BY <3>2, PeersAB + <4>4. connstate'[q] = connstate[q] + BY <4>3 DEF TypeOK + <4>. QED + BY <4>2, <4>4 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + <1>r. CASE CLOSE_SYN_RECEIVED(local, remote) \/ CLOSE_ESTABLISHED(local, remote) \/ CLOSE_CLOSE_WAIT(local, remote) - \/ SEND(local, remote) - \* TODO: discharge the remaining 7 user-action sub-cases. + \* TODO: discharge the remaining 3 CLOSE_* sub-cases (each appends + \* "FIN" to n[remote] and transitions local into a PostEst state). OMITTED - <1>. QED BY <1>1, <1>2 + <1>. QED BY <1>1, <1>2, <1>3, <1>4, <1>5, <1>r THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' <1>. SUFFICES ASSUME IndInv, [Next]_vars PROVE IndInv' From 3067b9114cd64c92694d8415a16ddd814a7f24b1 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Sun, 10 May 2026 17:45:39 -0700 Subject: [PATCH 06/22] tcp_proof: discharge IndInv preservation for all 8 user actions Complete the User-action half of IndInvIsInductive by adding the three remaining CLOSE_* sub-cases (CLOSE_SYN_RECEIVED, CLOSE_ESTABLISHED, CLOSE_CLOSE_WAIT) to IndInvUser. All eight user actions are now discharged: PASSIVE_OPEN, ACTIVE_OPEN, SEND CLOSE_SYN_SENT, CLOSE_LISTEN CLOSE_SYN_RECEIVED, CLOSE_ESTABLISHED, CLOSE_CLOSE_WAIT CLOSE_SYN_RECEIVED, CLOSE_ESTABLISHED both transition local into FW1 (in PostEst) and CLOSE_CLOSE_WAIT into LA (also in PostEst), so the Aux_EST_evidence q=local case discharges via the "q in PostEst" disjunct directly. Aux_LastMsg with q=local matches because the appended "FIN" satisfies both LastMsg(p) in {FIN, RST} (for q=FW1) and LastMsg(p) = FIN (for q=LA). Wire the User case into IndInvIsInductive via PICK + IndInvUser. The System (10 actions) and Reset (Note3 send / RST receive) cases remain OMITTED placeholders. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 587 ++++++++++++++++++++++++++++++- 1 file changed, 577 insertions(+), 10 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 893a7a64..9010be99 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -1367,13 +1367,564 @@ LEMMA IndInvUser == <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv - <1>r. CASE CLOSE_SYN_RECEIVED(local, remote) - \/ CLOSE_ESTABLISHED(local, remote) - \/ CLOSE_CLOSE_WAIT(local, remote) - \* TODO: discharge the remaining 3 CLOSE_* sub-cases (each appends - \* "FIN" to n[remote] and transitions local into a PostEst state). - OMITTED - <1>. QED BY <1>1, <1>2, <1>3, <1>4, <1>5, <1>r + (*************************************************************************) + (* The three remaining CLOSE_* actions all append "FIN" to n[remote] *) + (* and transition local into a PostEst state (FW1 or LA). Compared to *) + (* ACTIVE_OPEN/SEND the Aux_EST_evidence case is easier because *) + (* connstate'[local] is already in PostEst. *) + (*************************************************************************) + <1>6. CASE CLOSE_SYN_RECEIVED(local, remote) + <2>. USE <1>6 DEF CLOSE_SYN_RECEIVED + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "FIN")] + /\ connstate' = [connstate EXCEPT ![local] = "FIN-WAIT-1"] + /\ connstate'[local] = "FIN-WAIT-1" + /\ network'[remote] = Append(network[remote], "FIN") + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # remote => network'[r] = network[r] + /\ connstate[local] = "SYN-RECEIVED" + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ l # remote /\ r # remote + /\ network[l] = <<>> /\ network[r] = <<>> + BY DEF TypeOK, Msgs + <3>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <3>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ (l = local => connstate'[l] = "FIN-WAIT-1") + /\ (r = local => connstate'[r] = "FIN-WAIT-1") + OBVIOUS + <3>. QED OBVIOUS + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"RST">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"ACK">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"ACKofFIN">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. CASE q = local + \* connstate'[q] = "FIN-WAIT-1" \in PostEstStrict \subseteq PostEst. + BY <3>2 DEF PostEst, PostEstStrict + <3>3. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>3 DEF TypeOK + <4>2. \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + BY <3>1 DEF Aux_EST_evidence + <4>3. \A m \in Msgs, r \in Peers : HasMsg(m, r) => HasMsg(m, r)' + <5>. SUFFICES ASSUME NEW m \in Msgs, NEW r \in Peers, HasMsg(m, r) + PROVE HasMsg(m, r)' + OBVIOUS + <5>1. \E i \in 1..Len(network[r]) : network[r][i] = m + BY DEF HasMsg + <5>2. CASE r = remote + <6>1. network[r] \in Seq(Msgs) + BY <5>2 DEF TypeOK, Msgs + <6>2. network'[r] = Append(network[r], "FIN") + BY <5>2 + <6>3. Len(network'[r]) = Len(network[r]) + 1 + BY <6>1, <6>2 + <6>4. \A i \in 1..Len(network[r]) : network'[r][i] = network[r][i] + BY <6>1, <6>2 + <6>. QED + BY <5>1, <6>3, <6>4 DEF HasMsg + <5>3. CASE r # remote + BY <5>1, <5>3 DEF HasMsg + <5>. QED BY <5>2, <5>3 + <4>. QED + BY <4>1, <4>2, <4>3 DEF Msgs, PostEst, PostEstStrict + <3>. QED BY <3>2, <3>3 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + <4>. q # remote /\ q = local + BY <3>1, PeersAB + <4>. connstate'[q] = "FIN-WAIT-1" + OBVIOUS + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. network'[p] = Append(network[p], "FIN") /\ LastMsg(p)' = "FIN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. q # local + BY <3>2, PeersAB + <4>3. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <4>. QED + BY <4>1, <4>3 DEF Aux_LastMsg + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. LastMsg(p)' = "FIN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>1 DEF Aux_RST_at_end + <4>3. q # local + BY <3>2, PeersAB + <4>4. connstate'[q] = connstate[q] + BY <4>3 DEF TypeOK + <4>. QED BY <4>2, <4>4 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + <1>7. CASE CLOSE_ESTABLISHED(local, remote) + <2>. USE <1>7 DEF CLOSE_ESTABLISHED + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "FIN")] + /\ connstate' = [connstate EXCEPT ![local] = "FIN-WAIT-1"] + /\ connstate'[local] = "FIN-WAIT-1" + /\ network'[remote] = Append(network[remote], "FIN") + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # remote => network'[r] = network[r] + /\ connstate[local] = "ESTABLISHED" + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ l # remote /\ r # remote /\ network[l] = <<>> /\ network[r] = <<>> + BY DEF TypeOK, Msgs + <3>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <3>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ (l = local => connstate'[l] = "FIN-WAIT-1") + /\ (r = local => connstate'[r] = "FIN-WAIT-1") + OBVIOUS + <3>. QED OBVIOUS + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"RST">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"ACK">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"ACKofFIN">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. CASE q = local + BY <3>2 DEF PostEst, PostEstStrict + <3>3. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>3 DEF TypeOK + <4>2. \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + BY <3>1 DEF Aux_EST_evidence + <4>3. \A m \in Msgs, r \in Peers : HasMsg(m, r) => HasMsg(m, r)' + <5>. SUFFICES ASSUME NEW m \in Msgs, NEW r \in Peers, HasMsg(m, r) + PROVE HasMsg(m, r)' + OBVIOUS + <5>1. \E i \in 1..Len(network[r]) : network[r][i] = m + BY DEF HasMsg + <5>2. CASE r = remote + <6>1. network[r] \in Seq(Msgs) + BY <5>2 DEF TypeOK, Msgs + <6>2. network'[r] = Append(network[r], "FIN") + BY <5>2 + <6>3. Len(network'[r]) = Len(network[r]) + 1 + BY <6>1, <6>2 + <6>4. \A i \in 1..Len(network[r]) : network'[r][i] = network[r][i] + BY <6>1, <6>2 + <6>. QED + BY <5>1, <6>3, <6>4 DEF HasMsg + <5>3. CASE r # remote + BY <5>1, <5>3 DEF HasMsg + <5>. QED BY <5>2, <5>3 + <4>. QED + BY <4>1, <4>2, <4>3 DEF Msgs, PostEst, PostEstStrict + <3>. QED BY <3>2, <3>3 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + <4>. q # remote /\ q = local + BY <3>1, PeersAB + <4>. connstate'[q] = "FIN-WAIT-1" + OBVIOUS + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. network'[p] = Append(network[p], "FIN") /\ LastMsg(p)' = "FIN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. q # local + BY <3>2, PeersAB + <4>3. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <4>. QED + BY <4>1, <4>3 DEF Aux_LastMsg + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. LastMsg(p)' = "FIN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>1 DEF Aux_RST_at_end + <4>3. q # local + BY <3>2, PeersAB + <4>4. connstate'[q] = connstate[q] + BY <4>3 DEF TypeOK + <4>. QED BY <4>2, <4>4 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + <1>8. CASE CLOSE_CLOSE_WAIT(local, remote) + <2>. USE <1>8 DEF CLOSE_CLOSE_WAIT + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "FIN")] + /\ connstate' = [connstate EXCEPT ![local] = "LAST-ACK"] + /\ connstate'[local] = "LAST-ACK" + /\ network'[remote] = Append(network[remote], "FIN") + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # remote => network'[r] = network[r] + /\ connstate[local] = "CLOSE-WAIT" + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ l # remote /\ r # remote /\ network[l] = <<>> /\ network[r] = <<>> + BY DEF TypeOK, Msgs + <3>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <3>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ (l = local => connstate'[l] = "LAST-ACK") + /\ (r = local => connstate'[r] = "LAST-ACK") + OBVIOUS + <3>. QED OBVIOUS + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"RST">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"ACK">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "FIN") = <<"ACKofFIN">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. CASE q = local + BY <3>2 DEF PostEst, PostEstStrict + <3>3. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>3 DEF TypeOK + <4>2. \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + BY <3>1 DEF Aux_EST_evidence + <4>3. \A m \in Msgs, r \in Peers : HasMsg(m, r) => HasMsg(m, r)' + <5>. SUFFICES ASSUME NEW m \in Msgs, NEW r \in Peers, HasMsg(m, r) + PROVE HasMsg(m, r)' + OBVIOUS + <5>1. \E i \in 1..Len(network[r]) : network[r][i] = m + BY DEF HasMsg + <5>2. CASE r = remote + <6>1. network[r] \in Seq(Msgs) + BY <5>2 DEF TypeOK, Msgs + <6>2. network'[r] = Append(network[r], "FIN") + BY <5>2 + <6>3. Len(network'[r]) = Len(network[r]) + 1 + BY <6>1, <6>2 + <6>4. \A i \in 1..Len(network[r]) : network'[r][i] = network[r][i] + BY <6>1, <6>2 + <6>. QED + BY <5>1, <6>3, <6>4 DEF HasMsg + <5>3. CASE r # remote + BY <5>1, <5>3 DEF HasMsg + <5>. QED BY <5>2, <5>3 + <4>. QED + BY <4>1, <4>2, <4>3 DEF Msgs, PostEst, PostEstStrict + <3>. QED BY <3>2, <3>3 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + <4>. q # remote /\ q = local + BY <3>1, PeersAB + <4>. connstate'[q] = "LAST-ACK" + OBVIOUS + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. network'[p] = Append(network[p], "FIN") /\ LastMsg(p)' = "FIN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. q # local + BY <3>2, PeersAB + <4>3. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <4>. QED + BY <4>1, <4>3 DEF Aux_LastMsg + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. LastMsg(p)' = "FIN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>1 DEF Aux_RST_at_end + <4>3. q # local + BY <3>2, PeersAB + <4>4. connstate'[q] = connstate[q] + BY <4>3 DEF TypeOK + <4>. QED BY <4>2, <4>4 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + <1>. QED BY <1>1, <1>2, <1>3, <1>4, <1>5, <1>6, <1>7, <1>8 THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' <1>. SUFFICES ASSUME IndInv, [Next]_vars PROVE IndInv' @@ -1383,10 +1934,26 @@ THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' BY TypeOKInductive <1>stutter. CASE UNCHANGED vars BY <1>stutter, IndInvStutter - <1>next. CASE Next - \* Per-action analysis -- to be filled in below. + <1>user. CASE User + <2>. PICK local \in Peers, remote \in Peers : + \/ ACTIVE_OPEN(local, remote) + \/ PASSIVE_OPEN(local, remote) + \/ CLOSE_SYN_SENT(local, remote) + \/ CLOSE_SYN_RECEIVED(local, remote) + \/ CLOSE_LISTEN(local, remote) + \/ CLOSE_ESTABLISHED(local, remote) + \/ CLOSE_CLOSE_WAIT(local, remote) + \/ SEND(local, remote) + BY <1>user DEF User + <2>. QED BY <1>0, IndInvUser + <1>system. CASE System + \* TODO: per-action analysis for SynSent, SynReceived, Listen, + \* Established, FinWait1, FinWait2, Closing, LastAck, TimeWait, Note2. + OMITTED + <1>reset. CASE Reset + \* TODO: Note3 send and Note3 RST receive. OMITTED - <1>. QED BY <1>stutter, <1>next + <1>. QED BY <1>stutter, <1>user, <1>system, <1>reset DEF Next (***************************************************************************) (* Once IndInvIsInductive is fully discharged, the main theorem follows: *) From 350e7887c65d87f178fcfbcfef7495ffc85d24bd Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Sun, 10 May 2026 19:33:41 -0700 Subject: [PATCH 07/22] tcp_proof: discharge IndInv preservation for TimeWait and Closing Open the System case of IndInvIsInductive by introducing the helper lemma IndInvSystem and discharging the first two of its ten sub-cases: - TimeWait : TW -> CLOSED, no network change. Mirrors the no-network user actions. - Closing : CLOSING -> TIME-WAIT, consumes "ACKofFIN" from n[local] (Tail), no append. This is the first action that modifies a queue via Tail rather than Append. The proof distinguishes Len(n[local]) = 1 (post n'[local] = <<>>) from Len >= 2 (LastMsg(p)' = LastMsg(p), preserving Aux_LastMsg and Aux_RST_at_end for p = local). The Inv preservation case where l or r equals local relies on Aux_singleton_ACKofFIN pre-state to force connstate[other] # ESTABLISHED. Eight further system actions (SynSent SYN/SYN,ACK, SynReceived RST/ACK, Listen, Established, FW1, FW2, LastAck, Note2) and the two Reset cases remain OMITTED placeholders. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 461 ++++++++++++++++++++++++++++++- 1 file changed, 458 insertions(+), 3 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 9010be99..5752e35e 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -1926,6 +1926,451 @@ LEMMA IndInvUser == <1>. QED BY <1>1, <1>2, <1>3, <1>4, <1>5, <1>6, <1>7, <1>8 +(***************************************************************************) +(* System actions: 10 in total. Each consumes from n[local] and may also *) +(* append to n[remote]. We start with TimeWait (no network change) and *) +(* extend incrementally. *) +(***************************************************************************) +LEMMA IndInvSystem == + ASSUME IndInv, TypeOK', + NEW local \in Peers, NEW remote \in Peers, + \/ SynSent(local, remote) + \/ SynReceived(local, remote) + \/ Listen(local, remote) + \/ Established(local, remote) + \/ FinWait1(local, remote) + \/ FinWait2(local, remote) + \/ Closing(local, remote) + \/ LastAck(local, remote) + \/ TimeWait(local, remote) + \/ Note2(local, remote) + PROVE IndInv' + <1>. USE PeersAB DEF IndInv + <1>9. CASE TimeWait(local, remote) + <2>. USE <1>9 DEF TimeWait + <2>. /\ network' = network + /\ connstate' = [connstate EXCEPT ![local] = "CLOSED"] + /\ connstate'[local] = "CLOSED" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : network'[r] = network[r] + /\ connstate[local] = "TIME-WAIT" + BY DEF TypeOK + <2>1. Inv' + BY DEF Inv + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. network[p] = <<"RST">> /\ network[q] = <<>> + OBVIOUS + <3>2. connstate[q] # "ESTABLISHED" + BY <3>1 DEF Aux_singleton_RST + <3>. QED BY <3>2 DEF TypeOK + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. connstate[p] = "SYN-RECEIVED" + BY DEF TypeOK + <3>2. connstate[q] = "ESTABLISHED" + BY <3>1 DEF Aux_singleton_ACK + <3>3. q # local + BY <3>2 DEF TypeOK + <3>. QED BY <3>2, <3>3 DEF TypeOK + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY DEF TypeOK + <3>2. connstate[q] # "ESTABLISHED" + BY <3>1 DEF Aux_singleton_ACKofFIN + <3>. QED BY <3>2 DEF TypeOK + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. \A m \in Msgs, r \in Peers : HasMsg(m, r) <=> HasMsg(m, r)' + BY DEF HasMsg + <3>3. CASE q = local + \* connstate'[q] = CLOSED in PostEst. + BY <3>3 DEF PostEst, PostEstStrict + <3>4. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>. QED + BY <3>1, <3>2, <4>1 DEF Aux_EST_evidence, Msgs, PostEst, PostEstStrict + <3>. QED BY <3>3, <3>4 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. network[p] # <<>> /\ LastMsg(p)' = LastMsg(p) + BY DEF LastMsg + <3>2. q # local => connstate'[q] = connstate[q] + BY DEF TypeOK + <3>3. q = local => connstate'[q] = "CLOSED" + OBVIOUS + <3>. QED BY <3>1, <3>2, <3>3 DEF Aux_LastMsg + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. network[p] # <<>> /\ LastMsg(p) = "RST" + BY DEF LastMsg + <3>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <3>1 DEF Aux_RST_at_end + <3>3. q # local => connstate'[q] = connstate[q] + BY DEF TypeOK + <3>4. q = local => connstate'[q] = "CLOSED" + OBVIOUS + <3>. QED BY <3>2, <3>3, <3>4 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + (*************************************************************************) + (* Closing and LastAck: consume "ACKofFIN" from n[local] (Tail), no *) + (* append. For Tail with pre-Len >= 2, the LAST element of n[local] *) + (* is unchanged; for pre-Len = 1, n'[local] = <<>>. *) + (*************************************************************************) + <1>10. CASE Closing(local, remote) + <2>. USE <1>10 DEF Closing + <2>. /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "TIME-WAIT"] + /\ connstate'[local] = "TIME-WAIT" + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # local => network'[r] = network[r] + /\ connstate[local] = "CLOSING" + /\ IsPrefix(<<"ACKofFIN">>, network[local]) + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>head. /\ network[local] # <<>> + /\ Head(network[local]) = "ACKofFIN" + /\ Tail(network[local]) \in Seq(Msgs) + BY PrefixOneNonEmpty DEF TypeOK, Msgs + <2>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <2>head DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>0. /\ network'[l] = <<>> /\ network'[r] = <<>> + /\ (l # local => network[l] = network'[l]) + /\ (r # local => network[r] = network'[r]) + /\ (l = local => network[l] = <<"ACKofFIN">>) + /\ (r = local => network[r] = <<"ACKofFIN">>) + <4>. \A x \in Peers : x # local => network'[x] = network[x] + OBVIOUS + <4>. \A x \in Peers : (x = local /\ Tail(network[x]) = <<>>) => + network[x] = <<"ACKofFIN">> + BY <2>head + <4>. \A x \in Peers : x = local => network'[x] = Tail(network[x]) + OBVIOUS + <4>. QED OBVIOUS + \* Either l, r # local (and then network[l] = network[r] = <<>>; use Inv pre), + \* or one of them is local and connstate' = "TIME-WAIT" # "ESTABLISHED". + <3>1. CASE l # local /\ r # local + <4>. network[l] = <<>> /\ network[r] = <<>> + BY <3>0, <3>1 + <4>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <4>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <4>. connstate'[l] = connstate[l] /\ connstate'[r] = connstate[r] + BY <3>1 DEF TypeOK + <4>. QED OBVIOUS + <3>2. CASE l = local + \* connstate'[l] = "TIME-WAIT", so we need connstate'[r] # "ESTABLISHED". + <4>1. connstate'[l] = "TIME-WAIT" /\ "TIME-WAIT" # "ESTABLISHED" + BY <3>2 + <4>2. CASE r = local + BY <3>2, <4>2, <4>1 + <4>3. CASE r # local + <5>1. network[r] = <<>> + BY <3>0, <4>3 + <5>2. network[local] = <<"ACKofFIN">> /\ local \in Peers /\ r \in Peers /\ local # r + BY <3>2, <3>0, <4>3 + <5>3. connstate[local] = "CLOSING" + OBVIOUS + <5>4. connstate[r] # "ESTABLISHED" + BY <5>1, <5>2, <5>3 DEF Aux_singleton_ACKofFIN + <5>5. connstate'[r] = connstate[r] + BY <4>3 DEF TypeOK + <5>. QED BY <4>1, <5>4, <5>5 + <4>. QED BY <4>2, <4>3 + <3>3. CASE r = local /\ l # local + <4>1. connstate'[r] = "TIME-WAIT" /\ "TIME-WAIT" # "ESTABLISHED" + BY <3>3 + <4>2. network[l] = <<>> /\ network[local] = <<"ACKofFIN">> + /\ local \in Peers /\ l \in Peers /\ local # l + BY <3>0, <3>3 + <4>3. connstate[local] = "CLOSING" + OBVIOUS + <4>4. connstate[l] # "ESTABLISHED" + BY <4>2, <4>3 DEF Aux_singleton_ACKofFIN + <4>5. connstate'[l] = connstate[l] + BY <3>3 DEF TypeOK + <4>. QED BY <4>1, <4>4, <4>5 + <3>. QED BY <3>1, <3>2, <3>3 + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + \* Two sub-cases on q (= local or # local). + <3>1. CASE q = local + \* connstate'[q] = TW # EST. Done. + BY <3>1 + <3>2. CASE q # local + \* connstate'[q] = connstate[q]. Use Aux_singleton_RST or + \* TypeOK reasoning. + <4>1. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>2. network[q] = <<>> + BY <3>2 + <4>3. CASE p = local + \* network'[p] = Tail. For = <<"RST">>: network[p] = <> + \* with X = "ACKofFIN" (the prefix forced by <2>head). So + \* pre n[p] = <<"ACKofFIN", "RST">>, last = "RST". By + \* Aux_RST_at_end pre, connstate[q] in {TW, CLOSED, LISTEN}. + <5>1. network[p] = <<"ACKofFIN", "RST">> + <6>. Tail(network[p]) = <<"RST">> + BY <4>3 + <6>. Head(network[p]) = "ACKofFIN" /\ network[p] # <<>> + BY <4>3, <2>head + <6>. QED + BY DEF TypeOK, Msgs + <5>2. LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>1 DEF LastMsg + <5>3. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>2 DEF Aux_RST_at_end + <5>. QED BY <4>1, <5>3 + <4>4. CASE p # local + \* network'[p] = network[p] = <<"RST">>. Pre Aux_singleton_RST applies. + <5>1. network[p] = <<"RST">> + BY <4>4 + <5>. QED + BY <4>1, <5>1, <4>2 DEF Aux_singleton_RST + <4>. QED BY <4>3, <4>4 + <3>. QED BY <3>1, <3>2 + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # local + \* connstate'[local] = TW # SR. + BY DEF TypeOK + <3>2. connstate[p] = "SYN-RECEIVED" + BY <3>1 DEF TypeOK + <3>3. CASE q = local + \* But pre Aux_singleton_ACK with these args (n[p]=<<"ACK">>, + \* n[q]=Tail's pre = <<"ACKofFIN">>): pre n[q] # <<>>, so pre aux + \* doesn't apply. We instead derive: since connstate'[q] = TW + \* and aux requires = EST, we need TW = EST -- false. So we + \* show this case is unreachable. + <4>1. network'[p] = network[p] + BY <3>1 + <4>2. network[p] = <<"ACK">> + BY <4>1 + <4>3. \* Now n[p] = <<"ACK">>, n[q] = <<"ACKofFIN">>, p connstate = SR, + \* q connstate = CLOSING. Aux_LastMsg requires LastMsg(p) = SYN,ACK + \* when q = SR, but here q is CLOSING and p is SR. So we use + \* Aux_LastMsg with the roles swapped: q (=local, here CLOSING) + \* requires LastMsg(p) = ACKofFIN. But network[p] = <<"ACK">>, + \* LastMsg(p) = "ACK" # "ACKofFIN". Contradiction. + connstate[q] = "CLOSING" /\ network[p] # <<>> + BY <3>3, <4>2 + <4>4. LastMsg(p) = "ACK" + BY <4>2 DEF LastMsg + <4>. QED + BY <4>3, <4>4 DEF Aux_LastMsg + <3>4. CASE q # local + <4>1. network'[q] = network[q] /\ network'[p] = network[p] + BY <3>4, <3>1 + <4>2. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>3. network[p] = <<"ACK">> /\ network[q] = <<>> + BY <4>1 + <4>4. connstate[q] = "ESTABLISHED" + BY <3>2, <4>3 DEF Aux_singleton_ACK + <4>. QED BY <4>2, <4>4 + <3>. QED BY <3>3, <3>4 + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # local + \* connstate'[local] = TW \notin {FW1, CLOSING, LA}. + BY DEF TypeOK + <3>2. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY <3>1 DEF TypeOK + <3>3. CASE q = local + \* connstate'[q] = TW # EST. + BY <3>3 + <3>4. CASE q # local + <4>1. network'[p] = network[p] /\ network'[q] = network[q] + BY <3>1, <3>4 + <4>2. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>3. network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + BY <4>1 + <4>. QED BY <3>2, <4>2, <4>3 DEF Aux_singleton_ACKofFIN + <3>. QED BY <3>3, <3>4 + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. CASE q = local + \* connstate'[q] = TW \in PostEst. + BY <3>2 DEF PostEst, PostEstStrict + <3>3. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>3 DEF TypeOK + <4>2. \/ connstate[q] \in PostEst + \/ HasMsg("SYN", p) \/ HasMsg("SYN", q) + \/ HasMsg("ACK", q) \/ HasMsg("ACK", p) + \/ HasMsg("SYN,ACK", q) \/ HasMsg("SYN,ACK", p) + \/ HasMsg("FIN", p) \/ HasMsg("FIN", q) + \/ HasMsg("ACKofFIN", p) \/ HasMsg("ACKofFIN", q) + \/ HasMsg("RST", p) \/ HasMsg("RST", q) + BY <3>1 DEF Aux_EST_evidence + \* p # local AND q # local: with 2 peers this is impossible since + \* p, q both in Peers \ {local} but Peers \ {local} has cardinality 1. + <4>3. \/ p = local \/ q = local + BY <3>1, <3>3, PeersAB + <4>. QED + BY <3>1, <3>3, <4>3 + <3>. QED BY <3>2, <3>3 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE q = local + \* connstate'[q] = TW, not covered. + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>1. CASE p = local + \* p = local: LastMsg(p)' = LastMsg of Tail. When Len(n[p]) >= 2, + \* same as pre LastMsg. When Len = 1, n'[p] = <<>>, vacuous. + <5>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>1 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>1, <2>tail + <5>1a. Len(network[p]) >= 2 + BY <5>1, <5>2 + <5>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>1, <2>tail, <5>1, <5>1a + <5>4. LastMsg(p)' = LastMsg(p) + BY <5>3, <5>1, <5>2 DEF LastMsg + <5>5. network[p] # <<>> + BY <5>1a + <5>. QED BY <4>0, <5>4, <5>5 DEF Aux_LastMsg + <4>2. CASE p # local + <5>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <4>2 DEF LastMsg + <5>. QED BY <4>0, <5>1 DEF Aux_LastMsg + <4>. QED BY <4>1, <4>2 + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE q = local + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>1. CASE p = local + <5>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>1 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>1, <2>tail + <5>1a. Len(network[p]) >= 2 + BY <5>1, <5>2 + <5>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>1, <2>tail, <5>1, <5>1a + <5>4. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>3, <5>1, <5>2, <5>1a DEF LastMsg + <5>5. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>4 DEF Aux_RST_at_end + <5>. QED BY <4>0, <5>5 + <4>2. CASE p # local + <5>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <4>2 DEF LastMsg + <5>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>1 DEF Aux_RST_at_end + <5>. QED BY <4>0, <5>2 + <4>. QED BY <4>1, <4>2 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) + \/ Listen(local, remote) \/ Established(local, remote) + \/ FinWait1(local, remote) \/ FinWait2(local, remote) + \/ LastAck(local, remote) \/ Note2(local, remote) + \* TODO: discharge the remaining 8 system action sub-cases. + OMITTED + <1>. QED BY <1>9, <1>10, <1>r + THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' <1>. SUFFICES ASSUME IndInv, [Next]_vars PROVE IndInv' OBVIOUS @@ -1947,9 +2392,19 @@ THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' BY <1>user DEF User <2>. QED BY <1>0, IndInvUser <1>system. CASE System - \* TODO: per-action analysis for SynSent, SynReceived, Listen, - \* Established, FinWait1, FinWait2, Closing, LastAck, TimeWait, Note2. - OMITTED + <2>. PICK local \in Peers, remote \in Peers : + \/ SynSent(local, remote) + \/ SynReceived(local, remote) + \/ Listen(local, remote) + \/ Established(local, remote) + \/ FinWait1(local, remote) + \/ FinWait2(local, remote) + \/ Closing(local, remote) + \/ LastAck(local, remote) + \/ TimeWait(local, remote) + \/ Note2(local, remote) + BY <1>system DEF System + <2>. QED BY <1>0, IndInvSystem <1>reset. CASE Reset \* TODO: Note3 send and Note3 RST receive. OMITTED From b07ceaaaed45a0f7a98d7c48c9dcb097059c36e1 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Sun, 10 May 2026 20:18:42 -0700 Subject: [PATCH 08/22] tcp_proof: discharge IndInv preservation for LastAck Add the LastAck system action sub-case (LA -> CLOSED, consume "ACKofFIN" via Tail). Identical structure to Closing apart from the post-state value (CLOSED instead of TIME-WAIT); both are non- ESTABLISHED and trigger the same disjunctive-Inv reasoning. 7 system actions remain OMITTED (SynSent SYN/SYN,ACK, SynReceived RST/ACK, Listen, Established, FW1, FW2, Note2) plus the 2 Reset sub-cases. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 280 ++++++++++++++++++++++++++++++- 1 file changed, 277 insertions(+), 3 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 5752e35e..67cfa5c4 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -2363,13 +2363,287 @@ LEMMA IndInvSystem == <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + (*************************************************************************) + (* LastAck: same shape as Closing -- consume "ACKofFIN" via Tail, no *) + (* append. Transitions LA -> CLOSED. *) + (*************************************************************************) + <1>11. CASE LastAck(local, remote) + <2>. USE <1>11 DEF LastAck + <2>. /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "CLOSED"] + /\ connstate'[local] = "CLOSED" + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # local => network'[r] = network[r] + /\ connstate[local] = "LAST-ACK" + /\ IsPrefix(<<"ACKofFIN">>, network[local]) + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>head. /\ network[local] # <<>> + /\ Head(network[local]) = "ACKofFIN" + /\ Tail(network[local]) \in Seq(Msgs) + BY PrefixOneNonEmpty DEF TypeOK, Msgs + <2>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <2>head DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>0. /\ network'[l] = <<>> /\ network'[r] = <<>> + /\ (l # local => network[l] = network'[l]) + /\ (r # local => network[r] = network'[r]) + /\ (l = local => network[l] = <<"ACKofFIN">>) + /\ (r = local => network[r] = <<"ACKofFIN">>) + <4>. \A x \in Peers : x # local => network'[x] = network[x] + OBVIOUS + <4>. \A x \in Peers : (x = local /\ Tail(network[x]) = <<>>) => + network[x] = <<"ACKofFIN">> + BY <2>head + <4>. \A x \in Peers : x = local => network'[x] = Tail(network[x]) + OBVIOUS + <4>. QED OBVIOUS + <3>1. CASE l # local /\ r # local + <4>. network[l] = <<>> /\ network[r] = <<>> + BY <3>0, <3>1 + <4>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <4>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <4>. connstate'[l] = connstate[l] /\ connstate'[r] = connstate[r] + BY <3>1 DEF TypeOK + <4>. QED OBVIOUS + <3>2. CASE l = local + <4>1. connstate'[l] = "CLOSED" /\ "CLOSED" # "ESTABLISHED" + BY <3>2 + <4>2. CASE r = local + BY <3>2, <4>2, <4>1 + <4>3. CASE r # local + <5>1. network[r] = <<>> + BY <3>0, <4>3 + <5>2. network[local] = <<"ACKofFIN">> /\ local \in Peers /\ r \in Peers /\ local # r + BY <3>2, <3>0, <4>3 + <5>3. connstate[local] = "LAST-ACK" + OBVIOUS + <5>4. connstate[r] # "ESTABLISHED" + BY <5>1, <5>2, <5>3 DEF Aux_singleton_ACKofFIN + <5>5. connstate'[r] = connstate[r] + BY <4>3 DEF TypeOK + <5>. QED BY <4>1, <5>4, <5>5 + <4>. QED BY <4>2, <4>3 + <3>3. CASE r = local /\ l # local + <4>1. connstate'[r] = "CLOSED" /\ "CLOSED" # "ESTABLISHED" + BY <3>3 + <4>2. network[l] = <<>> /\ network[local] = <<"ACKofFIN">> + /\ local \in Peers /\ l \in Peers /\ local # l + BY <3>0, <3>3 + <4>3. connstate[local] = "LAST-ACK" + OBVIOUS + <4>4. connstate[l] # "ESTABLISHED" + BY <4>2, <4>3 DEF Aux_singleton_ACKofFIN + <4>5. connstate'[l] = connstate[l] + BY <3>3 DEF TypeOK + <4>. QED BY <4>1, <4>4, <4>5 + <3>. QED BY <3>1, <3>2, <3>3 + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. CASE q = local + BY <3>1 + <3>2. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>2. network[q] = <<>> + BY <3>2 + <4>3. CASE p = local + <5>1. network[p] = <<"ACKofFIN", "RST">> + <6>. Tail(network[p]) = <<"RST">> + BY <4>3 + <6>. Head(network[p]) = "ACKofFIN" /\ network[p] # <<>> + BY <4>3, <2>head + <6>. QED + BY DEF TypeOK, Msgs + <5>2. LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>1 DEF LastMsg + <5>3. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>2 DEF Aux_RST_at_end + <5>. QED BY <4>1, <5>3 + <4>4. CASE p # local + <5>1. network[p] = <<"RST">> + BY <4>4 + <5>. QED + BY <4>1, <5>1, <4>2 DEF Aux_singleton_RST + <4>. QED BY <4>3, <4>4 + <3>. QED BY <3>1, <3>2 + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # local + BY DEF TypeOK + <3>2. connstate[p] = "SYN-RECEIVED" + BY <3>1 DEF TypeOK + <3>3. CASE q = local + <4>1. network'[p] = network[p] + BY <3>1 + <4>2. network[p] = <<"ACK">> + BY <4>1 + <4>3. connstate[q] = "LAST-ACK" /\ network[p] # <<>> + BY <3>3, <4>2 + <4>4. LastMsg(p) = "ACK" + BY <4>2 DEF LastMsg + <4>. QED + BY <4>3, <4>4 DEF Aux_LastMsg + <3>4. CASE q # local + <4>1. network'[q] = network[q] /\ network'[p] = network[p] + BY <3>4, <3>1 + <4>2. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>3. network[p] = <<"ACK">> /\ network[q] = <<>> + BY <4>1 + <4>4. connstate[q] = "ESTABLISHED" + BY <3>2, <4>3 DEF Aux_singleton_ACK + <4>. QED BY <4>2, <4>4 + <3>. QED BY <3>3, <3>4 + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # local + BY DEF TypeOK + <3>2. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY <3>1 DEF TypeOK + <3>3. CASE q = local + BY <3>3 + <3>4. CASE q # local + <4>1. network'[p] = network[p] /\ network'[q] = network[q] + BY <3>1, <3>4 + <4>2. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>3. network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + BY <4>1 + <4>. QED BY <3>2, <4>2, <4>3 DEF Aux_singleton_ACKofFIN + <3>. QED BY <3>3, <3>4 + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. CASE q = local + \* connstate'[q] = CLOSED \in PostEst. + BY <3>2 DEF PostEst, PostEstStrict + <3>3. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>3 DEF TypeOK + <4>3. \/ p = local \/ q = local + BY <3>1, <3>3, PeersAB + <4>. QED + BY <3>1, <3>3, <4>3 + <3>. QED BY <3>2, <3>3 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE q = local + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>1. CASE p = local + <5>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>1 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>1, <2>tail + <5>1a. Len(network[p]) >= 2 + BY <5>1, <5>2 + <5>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>1, <2>tail, <5>1, <5>1a + <5>4. LastMsg(p)' = LastMsg(p) + BY <5>3, <5>1, <5>2 DEF LastMsg + <5>5. network[p] # <<>> + BY <5>1a + <5>. QED BY <4>0, <5>4, <5>5 DEF Aux_LastMsg + <4>2. CASE p # local + <5>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <4>2 DEF LastMsg + <5>. QED BY <4>0, <5>1 DEF Aux_LastMsg + <4>. QED BY <4>1, <4>2 + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE q = local + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>1. CASE p = local + <5>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>1 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>1, <2>tail + <5>1a. Len(network[p]) >= 2 + BY <5>1, <5>2 + <5>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>1, <2>tail, <5>1, <5>1a + <5>4. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>3, <5>1, <5>2, <5>1a DEF LastMsg + <5>5. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>4 DEF Aux_RST_at_end + <5>. QED BY <4>0, <5>5 + <4>2. CASE p # local + <5>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <4>2 DEF LastMsg + <5>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>1 DEF Aux_RST_at_end + <5>. QED BY <4>0, <5>2 + <4>. QED BY <4>1, <4>2 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) \/ Listen(local, remote) \/ Established(local, remote) \/ FinWait1(local, remote) \/ FinWait2(local, remote) - \/ LastAck(local, remote) \/ Note2(local, remote) - \* TODO: discharge the remaining 8 system action sub-cases. + \/ Note2(local, remote) + \* TODO: discharge the remaining 7 system action sub-cases. OMITTED - <1>. QED BY <1>9, <1>10, <1>r + <1>. QED BY <1>9, <1>10, <1>11, <1>r THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' <1>. SUFFICES ASSUME IndInv, [Next]_vars PROVE IndInv' From 5d5171b317e375cfb216e6146db0a5e19f78e1dc Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 08:44:54 -0700 Subject: [PATCH 09/22] tcp_proof: discharge IndInv preservation for Note3 send (Reset) Add the Reset case to IndInvIsInductive via a new helper lemma IndInvReset. The Note3 send sub-case (tcb[local], append "RST" to n[remote], local -> TIME-WAIT) follows the append-only template: - All singleton-aux's are vacuous because the appended "RST" cannot equal "ACK" or "ACKofFIN" (and Aux_singleton_RST is handled by post local = TW # EST). - Aux_EST_evidence with q = local is satisfied by post connstate = TW \in PostEst. - Aux_LastMsg with q = local is vacuous (TW not in covered states SR/FW1/CW/LA/CLOSING/SS). - Aux_RST_at_end is satisfied by post connstate'[q] = TW in {TW, CLOSED, LISTEN}. The Note3 RST receive sub-case (Tail-only consume of "RST", local -> LISTEN/CLOSED) remains OMITTED, as do the 7 remaining System actions. (In passing: an attempt at SynReceived using ASSUME ... PROVE inside CASE triggered a tlapm internal assertion failure (File "src/expr/e_levels.ml", line 502, characters 12-18). Reverted; will revisit with a different proof structure.) Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 187 ++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 2 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 67cfa5c4..757f0986 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -2645,6 +2645,188 @@ LEMMA IndInvSystem == OMITTED <1>. QED BY <1>9, <1>10, <1>11, <1>r +(***************************************************************************) +(* Reset action (Note3): two sub-cases. *) +(* - Note3 send: tcb[local], append "RST" to n[remote], local -> TW. *) +(* - Note3 RST receive: head=RST, Tail n[local], local -> LISTEN/CLOSED.*) +(***************************************************************************) +LEMMA IndInvReset == + ASSUME IndInv, TypeOK', + NEW local \in Peers, NEW remote \in Peers, + Note3(local, remote) + PROVE IndInv' + <1>. USE PeersAB DEF IndInv + <1>. local # remote + BY DEF Note3 + <1>send. CASE /\ tcb[local] + /\ network' = [network EXCEPT ![remote] = Append(@, "RST")] + /\ connstate' = [connstate EXCEPT ![local] = "TIME-WAIT"] + <2>. /\ network'[remote] = Append(network[remote], "RST") + /\ connstate'[local] = "TIME-WAIT" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # remote => network'[r] = network[r] + BY <1>send DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ l # remote /\ r # remote /\ network[l] = <<>> /\ network[r] = <<>> + BY DEF TypeOK, Msgs + <3>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <3>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <3>. /\ (l = local => connstate'[l] = "TIME-WAIT") + /\ (r = local => connstate'[r] = "TIME-WAIT") + OBVIOUS + <3>. QED OBVIOUS + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. CASE p = remote + \* network'[p] = Append(n[p], "RST") = <<"RST">>: pre n[p] = <<>>. + <4>1. q # remote /\ q = local + BY <3>1, PeersAB + \* connstate'[q] = TIME-WAIT # ESTABLISHED. + <4>2. connstate'[q] = "TIME-WAIT" + BY <4>1 + <4>. QED BY <4>2 + <3>2. CASE p # remote + \* network'[p] = network[p]. q = remote, network'[q] = Append # <<>>. + <4>. SUFFICES ASSUME network'[q] = <<>> PROVE FALSE + OBVIOUS + <4>1. q = remote + BY <3>2, PeersAB + <4>2. network'[q] = Append(network[q], "RST") + BY <4>1 + <4>. QED BY <4>2 DEF TypeOK, Msgs + <3>. QED BY <3>1, <3>2 + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # remote + \* If p = remote, network'[p] = Append(n[p], "RST") never = <<"ACK">>. + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "RST") = <<"ACK">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>3. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED BY <3>3 + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # remote + <4>. SUFFICES ASSUME p = remote PROVE FALSE + OBVIOUS + <4>. Append(network[p], "RST") = <<"ACKofFIN">> /\ network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q # remote /\ network[q] = <<>> + OBVIOUS + <3>3. p = local /\ q = local + BY <3>1, <3>2, PeersAB + <3>. QED BY <3>3 + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local + \* connstate'[local] = TW # EST. + BY DEF TypeOK + <3>2. q = local + BY <3>1, PeersAB + \* q = local post = TW \in PostEst. + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED BY DEF PostEst, PostEstStrict + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + \* p = remote: LastMsg(p)' = "RST". connstate'[q] = q's pre state if + \* q != local, or TW if q = local. TW not in covered states. For + \* q != local: q = local? No, q # local since q # p = remote and p = + \* remote means q = local (with PeersAB). + <4>. q # remote /\ q = local + BY <3>1, PeersAB + <4>. connstate'[q] = "TIME-WAIT" + OBVIOUS + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. q # local + \* If q = local, connstate'[q] = TW, not covered. Otherwise q # local + \* and connstate'[q] = connstate[q]. + BY <3>2, PeersAB + <4>3. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <4>. QED + BY <4>1, <4>3 DEF Aux_LastMsg + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + <4>. q # remote /\ q = local + BY <3>1, PeersAB + <4>. connstate'[q] = "TIME-WAIT" + OBVIOUS + <4>. QED OBVIOUS + <3>2. CASE p # remote + <4>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <3>2 DEF LastMsg + <4>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>1 DEF Aux_RST_at_end + <4>3. q # local + BY <3>2, PeersAB + <4>4. connstate'[q] = connstate[q] + BY <4>3 DEF TypeOK + <4>. QED BY <4>2, <4>4 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + + <1>recv. CASE /\ IsPrefix(<<"RST">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ \/ connstate' = [connstate EXCEPT ![local] = "LISTEN"] + \/ connstate' = [connstate EXCEPT ![local] = "CLOSED"] + \* TODO: Note3 RST receive sub-case. + OMITTED + <1>. QED BY <1>send, <1>recv DEF Note3 + THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' <1>. SUFFICES ASSUME IndInv, [Next]_vars PROVE IndInv' OBVIOUS @@ -2680,8 +2862,9 @@ THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' BY <1>system DEF System <2>. QED BY <1>0, IndInvSystem <1>reset. CASE Reset - \* TODO: Note3 send and Note3 RST receive. - OMITTED + <2>. PICK local \in Peers, remote \in Peers : Note3(local, remote) + BY <1>reset DEF Reset + <2>. QED BY <1>0, IndInvReset <1>. QED BY <1>stutter, <1>user, <1>system, <1>reset DEF Next (***************************************************************************) From 11996e420d2b62eb4d48465b659fe24cc511bafe Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 09:06:33 -0700 Subject: [PATCH 10/22] tcp_proof: discharge IndInv preservation for Listen Add the Listen system action sub-case to IndInvSystem (Tail "SYN" from n[local], Append "SYN,ACK" to n[remote], LISTEN -> SR). This is the first Tail+Append action discharged for IndInv and follows a hybrid template: - For Inv': both queues post are non-empty for r = remote (Append never empty), so {p : n'[p] = <<>>} reduces to {local}. - For Aux_singleton_RST/ACK/ACKofFIN: q = remote is impossible (Append makes n'[remote] non-empty); q = local has post connstate = SR, which is # EST and # SR-ACK (so all three singleton-aux post-conditions hold/are vacuous). In the Aux_singleton_ACK case where p = remote, the appended "SYN,ACK" cannot equal the singleton "ACK", giving a contradiction. - For Aux_EST_evidence: q = local post = SR # EST means p must be remote; HasMsg("SYN,ACK", p)' is satisfied by the just-appended SYN,ACK at network'[p][Len(network'[p])]. - For Aux_LastMsg: q = local post = SR forces LastMsg(p)' = "SYN,ACK", which holds exactly because p = remote and the appended message is "SYN,ACK". q = remote uses the unchanged connstate together with the Tail-LastMsg invariance for Len >= 2. - For Aux_RST_at_end: p = remote gives LastMsg(p)' = "SYN,ACK" # "RST" (vacuous); p = local goes through the Tail-LastMsg invariance pattern from the Closing/LastAck proofs. 6 system actions remain OMITTED (SynSent x 2, SynReceived x 2, Established, FW1 x 2, FW2, Note2) plus Note3 RST receive. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 231 ++++++++++++++++++++++++++++++- 1 file changed, 226 insertions(+), 5 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 757f0986..9cfb8224 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -2637,13 +2637,234 @@ LEMMA IndInvSystem == <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + (*************************************************************************) + (* Listen: Tail+Append (consume "SYN" from n[local], append "SYN,ACK" *) + (* to n[remote]). Local LISTEN -> SR. *) + (*************************************************************************) + <1>13. CASE Listen(local, remote) + <2>. USE <1>13 DEF Listen + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "SYN,ACK"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "SYN-RECEIVED"] + /\ connstate'[local] = "SYN-RECEIVED" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ network'[remote] = Append(network[remote], "SYN,ACK") + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local /\ r # remote => network'[r] = network[r] + /\ connstate[local] = "LISTEN" + /\ IsPrefix(<<"SYN">>, network[local]) + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>head. /\ network[local] # <<>> + /\ Head(network[local]) = "SYN" + /\ Tail(network[local]) \in Seq(Msgs) + BY PrefixOneNonEmpty DEF TypeOK, Msgs + <2>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <2>head DEF TypeOK, Msgs + <2>1. Inv' + \* network'[remote] = Append, never empty. So {p : n'[p] = <<>>} \subseteq {local}. + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>1. l # remote /\ r # remote + BY DEF TypeOK, Msgs + <3>2. l = local /\ r = local + BY <3>1, PeersAB + <3>. QED BY <3>2 + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. q # remote + \* n'[remote] = Append != <<>>. + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "SYN,ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "SYN-RECEIVED" + BY <3>2 + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "SYN,ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + \* But connstate'[q] = "SYN-RECEIVED" # "ESTABLISHED", so we need to derive + \* a contradiction with the aux LHS (which requires post connstate[q] = EST). + \* Use the fact that p # q with PeersAB to force p = remote, then the + \* head of n'[p] is the appended "SYN,ACK", not "ACK". But network'[p] + \* = <<"ACK">> (just the singleton). Append(n[remote], "SYN,ACK") + \* has last element "SYN,ACK", not "ACK". Length 1 means n[remote] + \* was <<>>, post Append(<<>>, "SYN,ACK") = <<"SYN,ACK">> # <<"ACK">>. + <3>3. p = remote + BY <3>2, PeersAB + <3>4. network'[p] = Append(network[p], "SYN,ACK") + BY <3>3 + <3>. SUFFICES Append(network[p], "SYN,ACK") = <<"ACK">> => FALSE + BY <3>4 + <3>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "SYN,ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "SYN-RECEIVED" + BY <3>2 + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local + \* connstate'[local] = SR # EST. + BY DEF TypeOK + <3>2. q = local + BY <3>1, PeersAB + <3>3. p = remote + BY <3>1, PeersAB + \* HasMsg("SYN,ACK", p)' = HasMsg("SYN,ACK", remote)'. network'[remote] = + \* Append(network[remote], "SYN,ACK") so SYN,ACK is at the end. ✓. + <3>4. network[p] \in Seq(Msgs) /\ Len(network[p]) \in Nat + BY DEF TypeOK, Msgs + <3>5. network'[p] = Append(network[p], "SYN,ACK") + BY <3>3 + <3>6. Len(network'[p]) = Len(network[p]) + 1 + /\ network'[p][Len(network'[p])] = "SYN,ACK" + BY <3>4, <3>5 + <3>7. \E i \in 1..Len(network'[p]) : network'[p][i] = "SYN,ACK" + BY <3>6 + <3>. QED BY <3>7 DEF HasMsg + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + \* p = remote, q = local (with PeersAB). q post = SR -> LastMsg = SYN,ACK. + <4>1. q = local /\ q # remote + BY <3>1, PeersAB + <4>2. connstate'[q] = "SYN-RECEIVED" + BY <4>1 + <4>3. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>4. network'[p] = Append(network[p], "SYN,ACK") + BY <3>1 + <4>5. LastMsg(p)' = "SYN,ACK" + BY <4>3, <4>4 DEF LastMsg + <4>. QED BY <4>2, <4>5 + <3>2. CASE p = local + \* p = local: LastMsg(p)' = LastMsg(p) when Len >= 2; vacuous when Len = 1 + \* (since post Tail = <<>>). q = remote, connstate'[q] = connstate[q]. + <4>0. q # local /\ q = remote + BY <3>2, PeersAB + <4>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) /\ network'[p] # <<>> + BY <3>2 DEF TypeOK, Msgs + <4>2. Len(network'[p]) >= 1 + BY <4>1, EmptySeq + <4>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <3>2, <2>tail + <4>3a. Len(network[p]) >= 2 + BY <4>3, <4>2 + <4>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <3>2, <2>tail, <4>3, <4>3a + <4>5. LastMsg(p)' = LastMsg(p) + BY <4>4, <4>3, <4>2 DEF LastMsg + <4>6. network[p] # <<>> + BY <4>3a + <4>7. connstate'[q] = connstate[q] + BY <4>0 DEF TypeOK + <4>. QED BY <4>5, <4>6, <4>7 DEF Aux_LastMsg + <3>3. CASE p # remote /\ p # local + \* In 2-peer setup, this is impossible. + BY <3>3, PeersAB + <3>. QED BY <3>1, <3>2, <3>3 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + \* LastMsg(p)' = "SYN,ACK" # "RST" -- vacuous. + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. LastMsg(p)' = "SYN,ACK" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p = local + <4>0. q # local /\ q = remote + BY <3>2, PeersAB + <4>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <3>2 DEF TypeOK, Msgs + <4>2. Len(network'[p]) >= 1 + BY <4>1, EmptySeq + <4>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <3>2, <2>tail + <4>3a. Len(network[p]) >= 2 + BY <4>3, <4>2 + <4>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <3>2, <2>tail, <4>3, <4>3a + <4>5. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" /\ network[p] # <<>> + BY <4>4, <4>3, <4>2, <4>3a DEF LastMsg + <4>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>5 DEF Aux_RST_at_end + <4>7. connstate'[q] = connstate[q] + BY <4>0 DEF TypeOK + <4>. QED BY <4>6, <4>7 + <3>3. CASE p # remote /\ p # local + BY <3>3, PeersAB + <3>. QED BY <3>1, <3>2, <3>3 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) - \/ Listen(local, remote) \/ Established(local, remote) - \/ FinWait1(local, remote) \/ FinWait2(local, remote) - \/ Note2(local, remote) - \* TODO: discharge the remaining 7 system action sub-cases. + \/ Established(local, remote) \/ FinWait1(local, remote) + \/ FinWait2(local, remote) \/ Note2(local, remote) + \* TODO: discharge the remaining 6 system action sub-cases. OMITTED - <1>. QED BY <1>9, <1>10, <1>11, <1>r + <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>r (***************************************************************************) (* Reset action (Note3): two sub-cases. *) From fccf97f4ccbe3811727945423860e7459795e027 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 10:57:08 -0700 Subject: [PATCH 11/22] tcp_proof: discharge IndInv preservation for Established Add the Established system action sub-case (Tail "FIN" from n[local], Append "ACKofFIN" to n[remote], local EST -> CW). Identical structure to Listen (Tail+Append) apart from: - pre/post connstate values (LISTEN -> SR vs EST -> CW) - the head being consumed ("SYN" vs "FIN") - the message being appended ("SYN,ACK" vs "ACKofFIN"). Aux_LastMsg with q = local matches because the appended "ACKofFIN" satisfies LastMsg(p) = "ACKofFIN" for q = CLOSE-WAIT. Aux_EST_evidence with q = local is satisfied by post connstate '[local] = CW \in PostEst. 5 system actions remain OMITTED (SynSent x 2, SynReceived x 2, FW1 x 2, FW2, Note2) plus Note3 RST receive. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 204 ++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 4 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 9cfb8224..2a7549c9 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -2859,12 +2859,208 @@ LEMMA IndInvSystem == <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + (*************************************************************************) + (* Established: Tail "FIN" from n[local], Append "ACKofFIN" to *) + (* n[remote]. Local EST -> CW. Same shape as Listen. *) + (*************************************************************************) + <1>14. CASE Established(local, remote) + <2>. USE <1>14 DEF Established + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "ACKofFIN"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "CLOSE-WAIT"] + /\ connstate'[local] = "CLOSE-WAIT" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ network'[remote] = Append(network[remote], "ACKofFIN") + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local /\ r # remote => network'[r] = network[r] + /\ connstate[local] = "ESTABLISHED" + /\ IsPrefix(<<"FIN">>, network[local]) + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>head. /\ network[local] # <<>> + /\ Head(network[local]) = "FIN" + /\ Tail(network[local]) \in Seq(Msgs) + BY PrefixOneNonEmpty DEF TypeOK, Msgs + <2>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <2>head DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>1. l # remote /\ r # remote + BY DEF TypeOK, Msgs + <3>2. l = local /\ r = local + BY <3>1, PeersAB + <3>. QED BY <3>2 + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "CLOSE-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>3. p = remote + BY <3>2, PeersAB + <3>4. network'[p] = Append(network[p], "ACKofFIN") + BY <3>3 + <3>. SUFFICES Append(network[p], "ACKofFIN") = <<"ACK">> => FALSE + BY <3>4 + <3>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "CLOSE-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local + BY DEF TypeOK + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "CLOSE-WAIT" + BY <3>2 + <3>. QED BY DEF PostEst, PostEstStrict + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + <4>1. q = local /\ q # remote + BY <3>1, PeersAB + <4>2. connstate'[q] = "CLOSE-WAIT" + BY <4>1 + <4>3. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>4. network'[p] = Append(network[p], "ACKofFIN") + BY <3>1 + <4>5. LastMsg(p)' = "ACKofFIN" + BY <4>3, <4>4 DEF LastMsg + <4>. QED BY <4>2, <4>5 + <3>2. CASE p = local + <4>0. q # local /\ q = remote + BY <3>2, PeersAB + <4>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) /\ network'[p] # <<>> + BY <3>2 DEF TypeOK, Msgs + <4>2. Len(network'[p]) >= 1 + BY <4>1, EmptySeq + <4>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <3>2, <2>tail + <4>3a. Len(network[p]) >= 2 + BY <4>3, <4>2 + <4>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <3>2, <2>tail, <4>3, <4>3a + <4>5. LastMsg(p)' = LastMsg(p) + BY <4>4, <4>3, <4>2 DEF LastMsg + <4>6. network[p] # <<>> + BY <4>3a + <4>7. connstate'[q] = connstate[q] + BY <4>0 DEF TypeOK + <4>. QED BY <4>5, <4>6, <4>7 DEF Aux_LastMsg + <3>3. CASE p # remote /\ p # local + BY <3>3, PeersAB + <3>. QED BY <3>1, <3>2, <3>3 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. LastMsg(p)' = "ACKofFIN" + BY <3>1 DEF LastMsg + <4>. QED OBVIOUS + <3>2. CASE p = local + <4>0. q # local /\ q = remote + BY <3>2, PeersAB + <4>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <3>2 DEF TypeOK, Msgs + <4>2. Len(network'[p]) >= 1 + BY <4>1, EmptySeq + <4>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <3>2, <2>tail + <4>3a. Len(network[p]) >= 2 + BY <4>3, <4>2 + <4>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <3>2, <2>tail, <4>3, <4>3a + <4>5. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" /\ network[p] # <<>> + BY <4>4, <4>3, <4>2, <4>3a DEF LastMsg + <4>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>5 DEF Aux_RST_at_end + <4>7. connstate'[q] = connstate[q] + BY <4>0 DEF TypeOK + <4>. QED BY <4>6, <4>7 + <3>3. CASE p # remote /\ p # local + BY <3>3, PeersAB + <3>. QED BY <3>1, <3>2, <3>3 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) - \/ Established(local, remote) \/ FinWait1(local, remote) - \/ FinWait2(local, remote) \/ Note2(local, remote) - \* TODO: discharge the remaining 6 system action sub-cases. + \/ FinWait1(local, remote) \/ FinWait2(local, remote) + \/ Note2(local, remote) + \* TODO: discharge the remaining 5 system action sub-cases. OMITTED - <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>r + <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>r (***************************************************************************) (* Reset action (Note3): two sub-cases. *) From af009f3ce133b194104b2be8b429f70c98b8d2f8 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 11:17:00 -0700 Subject: [PATCH 12/22] tcp_proof: discharge IndInv preservation for FinWait2 Add the FinWait2 system action sub-case (Tail "FIN" from n[local], Append "ACKofFIN" to n[remote], local FW2 -> TW). Identical structure to Listen/Established (Tail+Append). The post connstate'[local] = TW differs: Aux_LastMsg with q = local becomes vacuous (TW not in covered states) and Aux_RST_at_end with q = local is satisfied directly via TW \in {TW, CLOSED, LISTEN}. 4 system actions remain OMITTED (SynSent x 2, SynReceived x 2, FW1 x 2, Note2) plus Note3 RST receive. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 199 ++++++++++++++++++++++++++++++- 1 file changed, 195 insertions(+), 4 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 2a7549c9..50965e02 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -3055,12 +3055,203 @@ LEMMA IndInvSystem == <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + (*************************************************************************) + (* FinWait2: Tail "FIN" from n[local], Append "ACKofFIN" to n[remote]. *) + (* Local FW2 -> TW. Same shape as Established/Listen. *) + (*************************************************************************) + <1>15. CASE FinWait2(local, remote) + <2>. USE <1>15 DEF FinWait2 + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "ACKofFIN"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "TIME-WAIT"] + /\ connstate'[local] = "TIME-WAIT" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ network'[remote] = Append(network[remote], "ACKofFIN") + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local /\ r # remote => network'[r] = network[r] + /\ connstate[local] = "FIN-WAIT-2" + /\ IsPrefix(<<"FIN">>, network[local]) + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>head. /\ network[local] # <<>> + /\ Head(network[local]) = "FIN" + /\ Tail(network[local]) \in Seq(Msgs) + BY PrefixOneNonEmpty DEF TypeOK, Msgs + <2>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <2>head DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>1. l # remote /\ r # remote + BY DEF TypeOK, Msgs + <3>2. l = local /\ r = local + BY <3>1, PeersAB + <3>. QED BY <3>2 + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>3. p = remote + BY <3>2, PeersAB + <3>4. network'[p] = Append(network[p], "ACKofFIN") + BY <3>3 + <3>. SUFFICES Append(network[p], "ACKofFIN") = <<"ACK">> => FALSE + BY <3>4 + <3>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local + BY DEF TypeOK + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED BY DEF PostEst, PostEstStrict + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE p = remote + \* q = local post = TW, not in covered states. Vacuous. + <4>. q = local /\ q # remote + BY <3>1, PeersAB + <4>. connstate'[q] = "TIME-WAIT" + OBVIOUS + <4>. QED OBVIOUS + <3>2. CASE p = local + <4>0. q # local /\ q = remote + BY <3>2, PeersAB + <4>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) /\ network'[p] # <<>> + BY <3>2 DEF TypeOK, Msgs + <4>2. Len(network'[p]) >= 1 + BY <4>1, EmptySeq + <4>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <3>2, <2>tail + <4>3a. Len(network[p]) >= 2 + BY <4>3, <4>2 + <4>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <3>2, <2>tail, <4>3, <4>3a + <4>5. LastMsg(p)' = LastMsg(p) + BY <4>4, <4>3, <4>2 DEF LastMsg + <4>6. network[p] # <<>> + BY <4>3a + <4>7. connstate'[q] = connstate[q] + BY <4>0 DEF TypeOK + <4>. QED BY <4>5, <4>6, <4>7 DEF Aux_LastMsg + <3>3. CASE p # remote /\ p # local + BY <3>3, PeersAB + <3>. QED BY <3>1, <3>2, <3>3 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE p = remote + \* q = local post = TW \in {TW, CLOSED, LISTEN}. + <4>. q = local /\ q # remote + BY <3>1, PeersAB + <4>. connstate'[q] = "TIME-WAIT" + OBVIOUS + <4>. QED OBVIOUS + <3>2. CASE p = local + <4>0. q # local /\ q = remote + BY <3>2, PeersAB + <4>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <3>2 DEF TypeOK, Msgs + <4>2. Len(network'[p]) >= 1 + BY <4>1, EmptySeq + <4>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <3>2, <2>tail + <4>3a. Len(network[p]) >= 2 + BY <4>3, <4>2 + <4>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <3>2, <2>tail, <4>3, <4>3a + <4>5. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" /\ network[p] # <<>> + BY <4>4, <4>3, <4>2, <4>3a DEF LastMsg + <4>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>5 DEF Aux_RST_at_end + <4>7. connstate'[q] = connstate[q] + BY <4>0 DEF TypeOK + <4>. QED BY <4>6, <4>7 + <3>3. CASE p # remote /\ p # local + BY <3>3, PeersAB + <3>. QED BY <3>1, <3>2, <3>3 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) - \/ FinWait1(local, remote) \/ FinWait2(local, remote) - \/ Note2(local, remote) - \* TODO: discharge the remaining 5 system action sub-cases. + \/ FinWait1(local, remote) \/ Note2(local, remote) + \* TODO: discharge the remaining 4 system action sub-cases. OMITTED - <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>r + <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>r (***************************************************************************) (* Reset action (Note3): two sub-cases. *) From 6811f3bba42652ef5d3af8f88d7cbdec5502469b Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 12:31:59 -0700 Subject: [PATCH 13/22] tcp_proof: discharge IndInv preservation for Note3 RST receive Add the Note3 RST receive sub-case (Tail "RST" from n[local], local pre any -> post in {LISTEN, CLOSED}, post # ESTABLISHED). Key observations: - Inv': uses Aux_singleton_RST pre when r # local has empty queue and pre n[local] = <<"RST">>. - Aux_singleton_RST': split p = local (pre had two RSTs; LastMsg = "RST" so Aux_RST_at_end gives connstate[q] in {TW, CLOSED, LISTEN}, preserved post) vs p # local (pre n[p] = <<"RST">>, n[q] = <<>>; pre Aux gives connstate[q] # EST, preserved). - Aux_singleton_ACK': p = local impossible (post # SR); q = local case requires pre n[local] = <<"RST">> exactly so that LastMsg(local) = "RST" contradicts Aux_LastMsg pre at p = remote SR (which requires LastMsg(local) = "SYN,ACK"). - Aux_EST_evidence': pre p = remote in EST so q = local; case on n[local] = <<"RST">> (then n[remote] # <<>> by Aux_singleton_RST contrapositive, gives HasMsg evidence on p = remote) vs Len(n[local]) >= 2 (then n'[local] # <<>>, HasMsg(network'[local][1], q) holds). Reset action fully discharged. 3 system actions remain OMITTED (SynSent x 2, SynReceived x 2, FW1 x 2, Note2). Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 356 ++++++++++++++++++++++++++++++- 1 file changed, 354 insertions(+), 2 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 50965e02..f6b598e0 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -3431,8 +3431,360 @@ LEMMA IndInvReset == /\ network' = [network EXCEPT ![local] = Tail(network[local])] /\ \/ connstate' = [connstate EXCEPT ![local] = "LISTEN"] \/ connstate' = [connstate EXCEPT ![local] = "CLOSED"] - \* TODO: Note3 RST receive sub-case. - OMITTED + <2>. /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ \A r \in Peers : r # local => network'[r] = network[r] + /\ connstate'[local] \in {"LISTEN", "CLOSED"} + BY <1>recv DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>head. /\ network[local] # <<>> + /\ Head(network[local]) = "RST" + /\ Tail(network[local]) \in Seq(Msgs) + BY <1>recv, PrefixOneNonEmpty DEF TypeOK, Msgs + <2>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <2>head DEF TypeOK, Msgs + <2>headRST. network[local][1] = "RST" + BY <2>head DEF TypeOK, Msgs + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>0. /\ network'[l] = <<>> /\ network'[r] = <<>> + /\ (l # local => network[l] = network'[l]) + /\ (r # local => network[r] = network'[r]) + /\ (l = local => network[l] = <<"RST">>) + /\ (r = local => network[r] = <<"RST">>) + <4>. \A x \in Peers : x = local => network'[x] = Tail(network[x]) + OBVIOUS + <4>. \A x \in Peers : (x = local /\ Tail(network[x]) = <<>>) => + network[x] = <<"RST">> + BY <2>head + <4>. QED OBVIOUS + <3>1. CASE l # local /\ r # local + <4>. network[l] = <<>> /\ network[r] = <<>> + BY <3>0, <3>1 + <4>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <4>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <4>. connstate'[l] = connstate[l] /\ connstate'[r] = connstate[r] + BY <3>1 DEF TypeOK + <4>. QED OBVIOUS + <3>2. CASE l = local + <4>1. connstate'[l] \in {"LISTEN", "CLOSED"} /\ connstate'[l] # "ESTABLISHED" + BY <3>2 + <4>2. CASE r = local + BY <3>2, <4>2, <4>1 + <4>3. CASE r # local + <5>1. network[r] = <<>> + BY <3>0, <4>3 + <5>2. network[local] = <<"RST">> /\ local \in Peers /\ r \in Peers /\ local # r + BY <3>2, <3>0, <4>3 + <5>3. connstate[r] # "ESTABLISHED" + BY <5>1, <5>2 DEF Aux_singleton_RST + <5>4. connstate'[r] = connstate[r] + BY <4>3 DEF TypeOK + <5>. QED BY <4>1, <5>3, <5>4 + <4>. QED BY <4>2, <4>3 + <3>3. CASE r = local /\ l # local + <4>1. connstate'[r] \in {"LISTEN", "CLOSED"} /\ connstate'[r] # "ESTABLISHED" + BY <3>3 + <4>2. network[l] = <<>> /\ network[local] = <<"RST">> + /\ local \in Peers /\ l \in Peers /\ local # l + BY <3>0, <3>3 + <4>3. connstate[l] # "ESTABLISHED" + BY <4>2 DEF Aux_singleton_RST + <4>4. connstate'[l] = connstate[l] + BY <3>3 DEF TypeOK + <4>. QED BY <4>1, <4>3, <4>4 + <3>. QED BY <3>1, <3>2, <3>3 + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. CASE q = local + \* connstate'[q] in {LISTEN, CLOSED}, both # EST. + BY <3>1 + <3>2. CASE q # local + <4>1. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>2. network[q] = <<>> + BY <3>2 + <4>3. CASE p = local + <5>0. Tail(network[p]) = <<"RST">> + BY <4>3 + <5>0a. Head(network[p]) = "RST" /\ network[p] # <<>> + /\ network[p] \in Seq(Msgs) + BY <4>3, <2>head DEF TypeOK, Msgs + <5>0b. Len(Tail(network[p])) = 1 /\ Len(network[p]) >= 1 + BY <5>0, <5>0a, EmptySeq + <5>0c. Len(network[p]) = 2 + BY <5>0a, <5>0b + <5>0d. network[p] = <> + BY <5>0a, <5>0c DEF TypeOK, Msgs + <5>0e. network[p][1] = "RST" + BY <5>0a DEF TypeOK, Msgs + <5>0f. Tail(network[p]) = <> + BY <5>0a, <5>0c DEF TypeOK, Msgs + <5>0g. network[p][2] = "RST" + BY <5>0, <5>0f + <5>1. network[p] = <<"RST", "RST">> + BY <5>0d, <5>0e, <5>0g + <5>2. LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>1 DEF LastMsg + <5>3. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>2 DEF Aux_RST_at_end + <5>. QED BY <4>1, <5>3 + <4>4. CASE p # local + <5>1. network[p] = <<"RST">> + BY <4>4 + <5>. QED + BY <4>1, <5>1, <4>2 DEF Aux_singleton_RST + <4>. QED BY <4>3, <4>4 + <3>. QED BY <3>1, <3>2 + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. p # local + \* connstate'[local] in {LISTEN, CLOSED} # SR. + BY DEF TypeOK + <3>2. connstate[p] = "SYN-RECEIVED" + BY <3>1 DEF TypeOK + <3>3. network'[p] = network[p] /\ network[p] = <<"ACK">> + BY <3>1 + <3>4. CASE q = local + \* By Aux_LastMsg pre with q' = p = remote SR: LastMsg(other) = SYN,ACK. + \* But network[other] = network[local] # <<>> with head = "RST". + \* In the q' = p sense, "p = SR" is the state-bearing peer; the + \* aux says LastMsg(other where other # p) = SYN,ACK. + \* other = local. network[local] starts with RST. LastMsg might be + \* something else (last element). Let me think... + \* Actually by Aux_LastMsg with q = p = remote in SR: + \* network[local] # <<>> => LastMsg(local) = "SYN,ACK". + \* But network[local] head = "RST". If Len = 1, LastMsg = "RST" + \* # "SYN,ACK". Contradiction. If Len >= 2, LastMsg is some other + \* message; aux says it's SYN,ACK. No direct contradiction. + \* Better: use Aux_RST_at_end? No, that's about LastMsg = RST. + \* Use the singleton: if pre n[local] = <>, by Aux_singleton_RST + \* with p' = local, q' = remote: connstate[remote] # EST. But pre + \* connstate[p = remote] = SR, not EST. No contradiction. + \* + \* Hmm tricky. Let me think differently. We need to show + \* connstate'[q] = EST. q = local post in {LISTEN, CLOSED}, # EST. + \* So we need to derive a contradiction with the aux LHS. + \* + \* network'[q] = Tail(network[local]) = <<>> means pre Len(n[local]) = 1. + \* So n[local] = <<"RST">>. And p = remote with network[remote] = <<"ACK">>. + \* Pre Aux_LastMsg with q' = p = remote SR: LastMsg(local) = "SYN,ACK". + \* But LastMsg(local) = "RST" # "SYN,ACK". Contradiction. + <5>1. network[local] = <<"RST">> + <6>. Tail(network[local]) = <<>> + BY <3>4 + <6>. Len(network[local]) = 1 + BY <2>head, <2>tail + <6>. QED BY <2>headRST DEF TypeOK, Msgs + <5>2. LastMsg(local) = "RST" + BY <5>1 DEF LastMsg + <5>3. network[local] # <<>> + BY <5>1 + <5>. QED + BY <3>4, <3>2, <3>1, <5>2, <5>3 DEF Aux_LastMsg + <3>5. CASE q # local + <4>1. network'[q] = network[q] + BY <3>5 + <4>2. connstate'[q] = connstate[q] + BY <3>5 DEF TypeOK + <4>3. network[p] = <<"ACK">> /\ network[q] = <<>> + BY <3>3, <4>1 + <4>4. connstate[q] = "ESTABLISHED" + BY <3>2, <4>3 DEF Aux_singleton_ACK + <4>. QED BY <4>2, <4>4 + <3>. QED BY <3>4, <3>5 + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. p # local + BY DEF TypeOK + <3>2. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY <3>1 DEF TypeOK + <3>3. CASE q = local + BY <3>3 + <3>4. CASE q # local + <4>1. network'[p] = network[p] /\ network'[q] = network[q] + BY <3>1, <3>4 + <4>2. connstate'[q] = connstate[q] + BY <3>4 DEF TypeOK + <4>3. network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + BY <4>1 + <4>. QED BY <3>2, <4>2, <4>3 DEF Aux_singleton_ACKofFIN + <3>. QED BY <3>3, <3>4 + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <3>2. q = local + BY <3>1, PeersAB + \* Case-split on whether n[local] = <<"RST">> (singleton) or longer. + <3>3. CASE network[local] = <<"RST">> + \* Then by Aux_singleton_RST with p' = local, q' = remote: if + \* network[remote] = <<>>, connstate[remote] # EST. But + \* connstate[remote] = connstate[p] = EST. Contradiction with + \* network[remote] = <<>>. So network[remote] # <<>>. Then there + \* exists m \in Msgs in network[remote]; HasMsg(m, p)' for that m. + <4>1. network[remote] # <<>> + <5>. SUFFICES ASSUME network[remote] = <<>> PROVE FALSE + OBVIOUS + <5>1. local \in Peers /\ remote \in Peers /\ local # remote + OBVIOUS + <5>2. connstate[remote] # "ESTABLISHED" + BY <3>3, <5>1 DEF Aux_singleton_RST + <5>3. p = remote + BY <3>1, <3>2, PeersAB + <5>. QED BY <3>1, <5>2, <5>3 + <4>2. p = remote + BY <3>1, <3>2, PeersAB + <4>3. network'[p] = network[p] + BY <4>2 + <4>4. network[p] # <<>> /\ network[p] \in Seq(Msgs) + BY <4>1, <4>2 DEF TypeOK, Msgs + <4>5. network[p][1] \in Msgs /\ Len(network[p]) >= 1 + /\ Len(network[p]) \in Nat + BY <4>4 + <4>6. Len(network'[p]) = Len(network[p]) /\ network'[p] = network[p] + BY <4>3 + <4>7. Len(network'[p]) >= 1 /\ network'[p][1] = network[p][1] + /\ Len(network'[p]) \in Nat + BY <4>5, <4>6 + <4>8. 1 \in 1..Len(network'[p]) + BY <4>7 + <4>9. \E i \in 1..Len(network'[p]) : network'[p][i] \in Msgs + /\ network'[p][i] = network[p][1] + BY <4>5, <4>7, <4>8 + <4>. QED + BY <4>5, <4>9 DEF HasMsg, Msgs + <3>4. CASE Len(network[local]) >= 2 + \* network'[local] = Tail has at least one element. HasMsg(X, local)' + \* for X = network'[local][1] \in Msgs. + <4>1. Len(network'[local]) = Len(network[local]) - 1 /\ Len(network'[local]) >= 1 + BY <3>4, <2>tail + <4>2. network'[local] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>3. network'[local][1] \in Msgs + BY <4>1, <4>2 + <4>4. \E i \in 1..Len(network'[local]) : network'[local][i] = network'[local][1] + BY <4>1 + <4>. QED BY <3>2, <4>3, <4>4 DEF HasMsg, Msgs + <3>5. \/ network[local] = <<"RST">> \/ Len(network[local]) >= 2 + <4>1. network[local] \in Seq(Msgs) /\ Len(network[local]) >= 1 + BY <2>head DEF TypeOK, Msgs + <4>2. \/ Len(network[local]) = 1 \/ Len(network[local]) >= 2 + BY <4>1 + <4>3. CASE Len(network[local]) = 1 + \* network[local] = <> = <<"RST">>. + <5>. network[local] = <> + BY <4>3 DEF TypeOK, Msgs + <5>. QED BY <2>headRST + <4>. QED BY <4>2, <4>3 + <3>. QED BY <3>3, <3>4, <3>5 + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE q = local + \* connstate'[q] in {LISTEN, CLOSED}, neither in covered states. + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>1. CASE p = local + <5>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>1 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>1, <2>tail + <5>1a. Len(network[p]) >= 2 + BY <5>1, <5>2 + <5>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>1, <2>tail, <5>1, <5>1a + <5>4. LastMsg(p)' = LastMsg(p) + BY <5>3, <5>1, <5>2 DEF LastMsg + <5>5. network[p] # <<>> + BY <5>1a + <5>. QED BY <4>0, <5>4, <5>5 DEF Aux_LastMsg + <4>2. CASE p # local + <5>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <4>2 DEF LastMsg + <5>. QED BY <4>0, <5>1 DEF Aux_LastMsg + <4>. QED BY <4>1, <4>2 + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE q = local + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] + BY <3>2 DEF TypeOK + <4>1. CASE p = local + <5>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>1 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>1, <2>tail + <5>1a. Len(network[p]) >= 2 + BY <5>1, <5>2 + <5>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>1, <2>tail, <5>1, <5>1a + <5>4. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>3, <5>1, <5>2, <5>1a DEF LastMsg + <5>5. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>4 DEF Aux_RST_at_end + <5>. QED BY <4>0, <5>5 + <4>2. CASE p # local + <5>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <4>2 DEF LastMsg + <5>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>1 DEF Aux_RST_at_end + <5>. QED BY <4>0, <5>2 + <4>. QED BY <4>1, <4>2 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv <1>. QED BY <1>send, <1>recv DEF Note3 THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' From 3c24cc53d2299d9976ff9af214ffba6e1be2ffc8 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 12:54:08 -0700 Subject: [PATCH 14/22] tcp_proof: discharge IndInv preservation for Note2 Add the Note2 system action sub-case (consume <<"FIN", "ACKofFIN">> from head of n[local] via SubSeq(_, 3, _), Append "ACKofFIN" to n[remote], local FW1 -> TW). Same overall structure as Established/FW2, but consumes 2 head elements instead of 1. Length bookkeeping uses SubSeqProperties to derive Len(n'[local]) = Len(n[local]) - 2 and the index shift n'[local][i] = n[local][i + 2]. Aux_LastMsg with p = local: when n'[local] # <<>> we have Len(n[local]) >= 3, so the last element is preserved across SubSeq, giving LastMsg(p)' = LastMsg(p). Aux_RST_at_end follows the same pattern. All other auxs are vacuous on q = local with post TW. 3 system actions remain OMITTED (SynSent x 2, SynReceived x 2, FW1 x 2). Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 214 ++++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 3 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index f6b598e0..0c706a04 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -3247,11 +3247,219 @@ LEMMA IndInvSystem == <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + (*************************************************************************) + (* Note2: pre n[local] = <<"FIN","ACKofFIN">> ++ rest, *) + (* n'[local] = SubSeq(n[local], 3, Len(n[local])), *) + (* n'[remote] = Append(n[remote], "ACKofFIN"), *) + (* local FW1 -> TW. *) + (*************************************************************************) + <1>16. CASE Note2(local, remote) + <2>. USE <1>16 DEF Note2 + <2>. /\ network' = [network EXCEPT ![remote] = Append(@, "ACKofFIN"), + ![local] = SubSeq(network[local], 3, Len(network[local]))] + /\ connstate' = [connstate EXCEPT ![local] = "TIME-WAIT"] + /\ connstate'[local] = "TIME-WAIT" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ network'[remote] = Append(network[remote], "ACKofFIN") + /\ network'[local] = SubSeq(network[local], 3, Len(network[local])) + /\ \A r \in Peers : r # local /\ r # remote => network'[r] = network[r] + /\ connstate[local] = "FIN-WAIT-1" + /\ IsPrefix(<<"FIN", "ACKofFIN">>, network[local]) + /\ local # remote + BY DEF TypeOK + <2>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <2>head. /\ Len(network[local]) >= 2 + /\ network[local][1] = "FIN" + /\ network[local][2] = "ACKofFIN" + /\ network[local] # <<>> + BY PrefixTwoNonEmpty DEF TypeOK, Msgs + <2>sub. /\ Len(network'[local]) = Len(network[local]) - 2 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 2] + /\ network'[local] \in Seq(Msgs) + <3>1. SubSeq(network[local], 3, Len(network[local])) \in Seq(Msgs) + BY <2>head, SubSeqProperties DEF TypeOK, Msgs + <3>2. Len(SubSeq(network[local], 3, Len(network[local]))) + = Len(network[local]) - 3 + 1 + BY <2>head, SubSeqProperties DEF TypeOK, Msgs + <3>3. \A i \in 1..(Len(network[local]) - 3 + 1) : + SubSeq(network[local], 3, Len(network[local]))[i] + = network[local][i + 3 - 1] + BY <2>head, SubSeqProperties DEF TypeOK, Msgs + <3>. QED BY <3>1, <3>2, <3>3 + <2>1. Inv' + <3>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <3>1. l # remote /\ r # remote + BY DEF TypeOK, Msgs + <3>2. l = local /\ r = local + BY <3>1, PeersAB + <3>. connstate'[l] = "TIME-WAIT" /\ connstate'[r] = "TIME-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>2. Aux_singleton_RST' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>3. Aux_singleton_ACK' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>4. Aux_singleton_ACKofFIN' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <3>1. q # remote + <4>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <4>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED OBVIOUS + <2>5. Aux_EST_evidence' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <3>1. p # local + BY DEF TypeOK + <3>2. q = local + BY <3>1, PeersAB + <3>. connstate'[q] = "TIME-WAIT" + BY <3>2 + <3>. QED BY DEF PostEst, PostEstStrict + <2>6. Aux_LastMsg' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <3>1. CASE q = local + \* q = local post = TW, not in covered states. Vacuous. + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] /\ q = remote + BY <3>2, PeersAB DEF TypeOK + <4>1. CASE p = remote + BY <4>0, <3>2, <4>1 + <4>2. CASE p = local + <5>0. network'[p] = SubSeq(network[p], 3, Len(network[p])) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + /\ network'[p] # <<>> + /\ Len(network[p]) >= 2 + BY <4>2 DEF TypeOK, Msgs + <5>1. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>2. Len(network'[p]) = Len(network[p]) - 2 + BY <4>2, <2>sub + <5>3. Len(network[p]) >= 3 + BY <5>2, <5>1 + <5>4. \A i \in 1..Len(network'[p]) : network'[p][i] = network[p][i + 2] + BY <4>2, <2>sub + <5>5. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <5>2, <5>3, <5>4 + <5>6. LastMsg(p)' = LastMsg(p) + BY <5>5, <5>1, <5>2 DEF LastMsg + <5>7. network[p] # <<>> + BY <5>3 + <5>. QED BY <4>0, <5>6, <5>7 DEF Aux_LastMsg + <4>3. CASE p # local /\ p # remote + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>. QED BY <3>1, <3>2 + <2>7. Aux_RST_at_end' + <3>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <3>1. CASE q = local + \* q = local post = TW \in {TW, CLOSED, LISTEN}. + BY <3>1 + <3>2. CASE q # local + <4>0. connstate'[q] = connstate[q] /\ q = remote + BY <3>2, PeersAB DEF TypeOK + <4>1. CASE p = remote + BY <4>0, <3>2, <4>1 + <4>2. CASE p = local + <5>0. network'[p] = SubSeq(network[p], 3, Len(network[p])) + /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + /\ network'[p] # <<>> + /\ Len(network[p]) >= 2 + BY <4>2 DEF TypeOK, Msgs + <5>1. Len(network'[p]) >= 1 + BY <5>0, EmptySeq + <5>2. Len(network'[p]) = Len(network[p]) - 2 + BY <4>2, <2>sub + <5>3. Len(network[p]) >= 3 + BY <5>2, <5>1 + <5>4. \A i \in 1..Len(network'[p]) : network'[p][i] = network[p][i + 2] + BY <4>2, <2>sub + <5>5. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <5>2, <5>3, <5>4 + <5>6. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" + BY <5>5, <5>1, <5>2 DEF LastMsg + <5>7. network[p] # <<>> + BY <5>3 + <5>8. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>6, <5>7 DEF Aux_RST_at_end + <5>. QED BY <4>0, <5>8 + <4>3. CASE p # local /\ p # remote + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>. QED BY <3>1, <3>2 + <2>. QED + BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) - \/ FinWait1(local, remote) \/ Note2(local, remote) - \* TODO: discharge the remaining 4 system action sub-cases. + \/ FinWait1(local, remote) + \* TODO: discharge the remaining 3 system action sub-cases. OMITTED - <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>r + <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>16, <1>r (***************************************************************************) (* Reset action (Note3): two sub-cases. *) From 85149cffcb8524ef201517dd67ed246c16fc72fa Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 13:22:56 -0700 Subject: [PATCH 15/22] tcp_proof: discharge IndInv preservation for FinWait1 Add both FinWait1 sub-cases: (a) head = FIN, n'[remote] = Append "ACKofFIN", local FW1 -> CLOSING. Same Tail+Append shape as Established/FW2. Post CLOSING is in PostEst (Aux_EST_evidence) and in covered states (Aux_LastMsg) where LastMsg(remote)' = "ACKofFIN" from the appended message. (b) head = ACKofFIN, no append, local FW1 -> FIN-WAIT-2. Same Tail-only shape as Note3 RST receive. Inv preservation uses Aux_singleton_ACKofFIN pre at p = local (n[local] = <<"ACKofFIN">>, FW1 \in {FW1, CLOSING, LA}) to derive connstate[r] # EST when r # local has empty queue. The Aux_singleton_ACK case forces n'[local] = <<>> hence n[local] = <<"ACKofFIN">>, then LastMsg(local) = "ACKofFIN" # "SYN,ACK" contradicts Aux_LastMsg pre at q = remote SR. 2 system actions remain OMITTED: SynSent x 2, SynReceived x 2. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 496 ++++++++++++++++++++++++++++++- 1 file changed, 493 insertions(+), 3 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 0c706a04..5d7a89ff 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -3455,11 +3455,501 @@ LEMMA IndInvSystem == <2>. QED BY <2>1, <2>2, <2>3, <2>4, <2>5, <2>6, <2>7 DEF IndInv + (*************************************************************************) + (* FinWait1 has two sub-cases: *) + (* (a) head = "FIN": n'[local] = Tail, n'[remote] = Append ACKofFIN, *) + (* local FW1 -> CLOSING. *) + (* (b) head = "ACKofFIN": n'[local] = Tail, n[remote] unchanged, *) + (* local FW1 -> FIN-WAIT-2. *) + (*************************************************************************) + <1>17. CASE FinWait1(local, remote) + <2>. USE <1>17 DEF FinWait1 + <2>FIN. CASE /\ IsPrefix(<<"FIN">>, network[local]) + /\ network' = [network EXCEPT ![remote] = Append(@, "ACKofFIN"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "CLOSING"] + <3>. /\ network'[remote] = Append(network[remote], "ACKofFIN") + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local /\ r # remote => network'[r] = network[r] + /\ connstate'[local] = "CLOSING" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ connstate[local] = "FIN-WAIT-1" + /\ IsPrefix(<<"FIN">>, network[local]) + /\ local # remote + BY <2>FIN DEF TypeOK + <3>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>head. /\ network[local] # <<>> + /\ Head(network[local]) = "FIN" + /\ Tail(network[local]) \in Seq(Msgs) + BY PrefixOneNonEmpty DEF TypeOK, Msgs + <3>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <3>head DEF TypeOK, Msgs + <3>1. Inv' + <4>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <4>1. l # remote /\ r # remote + BY DEF TypeOK, Msgs + <4>2. l = local /\ r = local + BY <4>1, PeersAB + <4>. QED BY <4>2 + <3>2. Aux_singleton_RST' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>. connstate'[q] = "CLOSING" + BY <4>2 + <4>. QED OBVIOUS + <3>3. Aux_singleton_ACK' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>3. p = remote + BY <4>2, PeersAB + <4>4. network'[p] = Append(network[p], "ACKofFIN") + BY <4>3 + <4>. SUFFICES Append(network[p], "ACKofFIN") = <<"ACK">> => FALSE + BY <4>4 + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>4. Aux_singleton_ACKofFIN' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "ACKofFIN") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>. connstate'[q] = "CLOSING" + BY <4>2 + <4>. QED OBVIOUS + <3>5. Aux_EST_evidence' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <4>1. p # local + BY DEF TypeOK + <4>2. q = local + BY <4>1, PeersAB + <4>. connstate'[q] = "CLOSING" + BY <4>2 + <4>. QED BY DEF PostEst, PostEstStrict + <3>6. Aux_LastMsg' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <4>1. CASE p = remote + <5>1. q = local /\ q # remote + BY <4>1, PeersAB + <5>2. connstate'[q] = "CLOSING" + BY <5>1 + <5>3. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>4. network'[p] = Append(network[p], "ACKofFIN") + BY <4>1 + <5>5. LastMsg(p)' = "ACKofFIN" + BY <5>3, <5>4 DEF LastMsg + <5>. QED BY <5>2, <5>5 + <4>2. CASE p = local + <5>0. q # local /\ q = remote + BY <4>2, PeersAB + <5>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) /\ network'[p] # <<>> + BY <4>2 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>1, EmptySeq + <5>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>2, <3>tail + <5>3a. Len(network[p]) >= 2 + BY <5>3, <5>2 + <5>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>2, <3>tail, <5>3, <5>3a + <5>5. LastMsg(p)' = LastMsg(p) + BY <5>4, <5>3, <5>2 DEF LastMsg + <5>6. network[p] # <<>> + BY <5>3a + <5>7. connstate'[q] = connstate[q] + BY <5>0 DEF TypeOK + <5>. QED BY <5>5, <5>6, <5>7 DEF Aux_LastMsg + <4>3. CASE p # remote /\ p # local + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>7. Aux_RST_at_end' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <4>1. CASE p = remote + <5>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. LastMsg(p)' = "ACKofFIN" + BY <4>1 DEF LastMsg + <5>. QED OBVIOUS + <4>2. CASE p = local + <5>0. q # local /\ q = remote + BY <4>2, PeersAB + <5>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>2 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>1, EmptySeq + <5>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>2, <3>tail + <5>3a. Len(network[p]) >= 2 + BY <5>3, <5>2 + <5>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>2, <3>tail, <5>3, <5>3a + <5>5. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>4, <5>3, <5>2, <5>3a DEF LastMsg + <5>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>5 DEF Aux_RST_at_end + <5>7. connstate'[q] = connstate[q] + BY <5>0 DEF TypeOK + <5>. QED BY <5>6, <5>7 + <4>3. CASE p # remote /\ p # local + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv + <2>AOF. CASE /\ IsPrefix(<<"ACKofFIN">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "FIN-WAIT-2"] + <3>. /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local => network'[r] = network[r] + /\ connstate'[local] = "FIN-WAIT-2" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ connstate[local] = "FIN-WAIT-1" + /\ IsPrefix(<<"ACKofFIN">>, network[local]) + /\ local # remote + BY <2>AOF DEF TypeOK + <3>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>head. /\ network[local] # <<>> + /\ Head(network[local]) = "ACKofFIN" + /\ Tail(network[local]) \in Seq(Msgs) + /\ network[local][1] = "ACKofFIN" + BY PrefixOneNonEmpty, PrefixTwoNonEmpty DEF TypeOK, Msgs + <3>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <3>head DEF TypeOK, Msgs + <3>1. Inv' + <4>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <4>0. /\ (l # local => network[l] = network'[l]) + /\ (r # local => network[r] = network'[r]) + /\ (l = local => network[l] = <<"ACKofFIN">>) + /\ (r = local => network[r] = <<"ACKofFIN">>) + <5>. \A x \in Peers : x = local => network'[x] = Tail(network[x]) + OBVIOUS + <5>. \A x \in Peers : (x = local /\ Tail(network[x]) = <<>>) => + network[x] = <<"ACKofFIN">> + BY <3>head + <5>. QED OBVIOUS + <4>1. CASE l # local /\ r # local + <5>. network[l] = <<>> /\ network[r] = <<>> + BY <4>0, <4>1 + <5>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <5>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <5>. connstate'[l] = connstate[l] /\ connstate'[r] = connstate[r] + BY <4>1 DEF TypeOK + <5>. QED OBVIOUS + <4>2. CASE l = local + <5>1. connstate'[l] = "FIN-WAIT-2" /\ connstate'[l] # "ESTABLISHED" + BY <4>2 + <5>2. CASE r = local + BY <4>2, <5>2, <5>1 + <5>3. CASE r # local + <6>1. network[r] = <<>> + BY <4>0, <5>3 + <6>2. network[local] = <<"ACKofFIN">> /\ local \in Peers /\ r \in Peers /\ local # r + BY <4>2, <4>0, <5>3 + <6>3. connstate[local] = "FIN-WAIT-1" + OBVIOUS + <6>4. connstate[r] # "ESTABLISHED" + BY <6>1, <6>2, <6>3 DEF Aux_singleton_ACKofFIN + <6>5. connstate'[r] = connstate[r] + BY <5>3 DEF TypeOK + <6>. QED BY <5>1, <6>4, <6>5 + <5>. QED BY <5>2, <5>3 + <4>3. CASE r = local /\ l # local + <5>1. connstate'[r] = "FIN-WAIT-2" /\ connstate'[r] # "ESTABLISHED" + BY <4>3 + <5>2. network[l] = <<>> /\ network[local] = <<"ACKofFIN">> + /\ local \in Peers /\ l \in Peers /\ local # l + BY <4>0, <4>3 + <5>3. connstate[local] = "FIN-WAIT-1" + OBVIOUS + <5>4. connstate[l] # "ESTABLISHED" + BY <5>2, <5>3 DEF Aux_singleton_ACKofFIN + <5>5. connstate'[l] = connstate[l] + BY <4>3 DEF TypeOK + <5>. QED BY <5>1, <5>4, <5>5 + <4>. QED BY <4>1, <4>2, <4>3 + <3>2. Aux_singleton_RST' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <4>1. CASE q = local + BY <4>1 + <4>2. CASE q # local + <5>1. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>2. network[q] = <<>> + BY <4>2 + <5>3. CASE p = local + <6>1. network[p] = <<"ACKofFIN", "RST">> + <7>0. Tail(network[p]) = <<"RST">> + BY <5>3 + <7>0a. Head(network[p]) = "ACKofFIN" /\ network[p] # <<>> + /\ network[p] \in Seq(Msgs) + BY <5>3, <3>head DEF TypeOK, Msgs + <7>0b. Len(Tail(network[p])) = 1 /\ Len(network[p]) >= 1 + BY <7>0, <7>0a, EmptySeq + <7>0c. Len(network[p]) = 2 + BY <7>0a, <7>0b + <7>0d. network[p] = <> + BY <7>0a, <7>0c DEF TypeOK, Msgs + <7>0e. network[p][1] = "ACKofFIN" + BY <7>0a DEF TypeOK, Msgs + <7>0f. Tail(network[p]) = <> + BY <7>0a, <7>0c DEF TypeOK, Msgs + <7>0g. network[p][2] = "RST" + BY <7>0, <7>0f + <7>. QED BY <7>0d, <7>0e, <7>0g + <6>2. LastMsg(p) = "RST" /\ network[p] # <<>> + BY <6>1 DEF LastMsg + <6>3. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>2 DEF Aux_RST_at_end + <6>. QED BY <5>1, <6>3 + <5>4. CASE p # local + <6>1. network[p] = <<"RST">> + BY <5>4 + <6>. QED + BY <5>1, <6>1, <5>2 DEF Aux_singleton_RST + <5>. QED BY <5>3, <5>4 + <4>. QED BY <4>1, <4>2 + <3>3. Aux_singleton_ACK' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <4>1. p # local + BY DEF TypeOK + <4>2. connstate[p] = "SYN-RECEIVED" + BY <4>1 DEF TypeOK + <4>3. network'[p] = network[p] /\ network[p] = <<"ACK">> + BY <4>1 + <4>4. CASE q = local + <5>1. network[local] = <<"ACKofFIN">> + <6>. Tail(network[local]) = <<>> + BY <4>4 + <6>. Len(network[local]) = 1 + BY <3>head, <3>tail + <6>. QED BY <3>head DEF TypeOK, Msgs + <5>2. LastMsg(local) = "ACKofFIN" /\ network[local] # <<>> + BY <5>1 DEF LastMsg + <5>. QED + BY <4>4, <4>2, <4>1, <5>2 DEF Aux_LastMsg + <4>5. CASE q # local + <5>1. network'[q] = network[q] + BY <4>5 + <5>2. connstate'[q] = connstate[q] + BY <4>5 DEF TypeOK + <5>3. network[p] = <<"ACK">> /\ network[q] = <<>> + BY <4>3, <5>1 + <5>4. connstate[q] = "ESTABLISHED" + BY <4>2, <5>3 DEF Aux_singleton_ACK + <5>. QED BY <5>2, <5>4 + <4>. QED BY <4>4, <4>5 + <3>4. Aux_singleton_ACKofFIN' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <4>1. CASE q = local + BY <4>1 + <4>2. CASE q # local + <5>1. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>2. network[q] = <<>> + BY <4>2 + <5>3. CASE p = local + \* p = local post in {FW1, CLOSING, LA}, but post = FW2. Excluded. + BY <5>3 + <5>4. CASE p # local + <6>1. network[p] = <<"ACKofFIN">> + BY <5>4 + <6>2. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY <5>4 DEF TypeOK + <6>. QED + BY <5>1, <5>2, <6>1, <6>2 DEF Aux_singleton_ACKofFIN + <5>. QED BY <5>3, <5>4 + <4>. QED BY <4>1, <4>2 + <3>5. Aux_EST_evidence' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <4>1. p # local + BY DEF TypeOK + <4>2. q = local + BY <4>1, PeersAB + <4>. connstate'[q] = "FIN-WAIT-2" + BY <4>2 + <4>. QED BY DEF PostEst, PostEstStrict + <3>6. Aux_LastMsg' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <4>1. CASE q = local + \* connstate'[q] = FW2, not in covered states. Vacuous. + BY <4>1 + <4>2. CASE q # local + <5>0. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>1. CASE p = local + <6>0. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) /\ network'[p] # <<>> + BY <5>1 DEF TypeOK, Msgs + <6>2. Len(network'[p]) >= 1 + BY <6>0, EmptySeq + <6>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <5>1, <3>tail + <6>3a. Len(network[p]) >= 2 + BY <6>3, <6>2 + <6>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <3>tail, <6>3, <6>3a + <6>5. LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <6>4, <6>3, <6>2, <6>3a DEF LastMsg + <6>. QED BY <5>0, <6>5 DEF Aux_LastMsg + <5>2. CASE p # local + <6>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <5>2 DEF LastMsg + <6>. QED BY <5>0, <6>1 DEF Aux_LastMsg + <5>. QED BY <5>1, <5>2 + <4>. QED BY <4>1, <4>2 + <3>7. Aux_RST_at_end' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <4>1. CASE q = local + \* q = local post = FW2. We need contradiction. + \* p # local so p = remote. LastMsg(remote)' = LastMsg(remote) + \* (unchanged). So pre LastMsg(remote) = RST, pre Aux_RST_at_end + \* at p = remote, q = local: connstate[local] in {TW, CLOSED, LISTEN}. + \* But connstate[local] = FW1. Contradiction. + <5>1. p # local + BY <4>1 + <5>2. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <5>1 DEF LastMsg + <5>3. LastMsg(p) = "RST" + BY <5>2 + <5>4. connstate[local] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>1, <4>1, <5>2, <5>3 DEF Aux_RST_at_end + <5>. QED BY <5>4 + <4>2. CASE q # local + <5>0. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>1. CASE p = local + <6>0. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <5>1 DEF TypeOK, Msgs + <6>2. Len(network'[p]) >= 1 + BY <6>0, EmptySeq + <6>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <5>1, <3>tail + <6>3a. Len(network[p]) >= 2 + BY <6>3, <6>2 + <6>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <3>tail, <6>3, <6>3a + <6>5. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" + /\ network[p] # <<>> + BY <6>4, <6>3, <6>2, <6>3a DEF LastMsg + <6>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>5 DEF Aux_RST_at_end + <6>. QED BY <5>0, <6>6 + <5>2. CASE p # local + <6>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) + /\ network[p] # <<>> + BY <5>2 DEF LastMsg + <6>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>1 DEF Aux_RST_at_end + <6>. QED BY <5>0, <6>2 + <5>. QED BY <5>1, <5>2 + <4>. QED BY <4>1, <4>2 + <3>. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv + <2>. QED BY <2>FIN, <2>AOF DEF FinWait1 + <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) - \/ FinWait1(local, remote) - \* TODO: discharge the remaining 3 system action sub-cases. + \* TODO: discharge the remaining 2 system action sub-cases. OMITTED - <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>16, <1>r + <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>16, <1>17, <1>r (***************************************************************************) (* Reset action (Note3): two sub-cases. *) From 2dad4bfc3f2b2239fc4227d95c8bffcacac5efd2 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 14:12:11 -0700 Subject: [PATCH 16/22] tcp_proof: discharge IndInv preservation for SynSent Add both SynSent sub-cases (each Tail head + Append to remote): (a) head = SYN, append SYN,ACK to n[remote], local SS -> SR. Aux_singleton_RST/ACK/ACKofFIN with q = local satisfied directly (post SR # EST and not in {FW1, CLOSING, LA}). Aux_EST_evidence with q = local SR (not in PostEst) is satisfied via HasMsg("SYN,ACK", p = remote)' which holds because we just appended SYN,ACK; we use a literal index of Len(network[p]) + 1 to avoid an internal tlapm bug (e_levels.ml assertion) triggered by WITNESS expressions involving Len(network'[p]). (b) head = SYN,ACK, append ACK to n[remote], local SS -> EST. Aux_singleton_ACK with q = local satisfied trivially (post = EST). Aux_EST_evidence with p = local = EST, q = remote uses HasMsg("ACK", q = remote)' (just appended). Aux_RST_at_end uses LastMsg(p)' = ACK # RST when p = remote and the inductive hypothesis when p = local. 1 system action remains OMITTED: SynReceived x 2. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 427 ++++++++++++++++++++++++++++++- 1 file changed, 424 insertions(+), 3 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 5d7a89ff..77526566 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -3946,10 +3946,431 @@ LEMMA IndInvSystem == BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv <2>. QED BY <2>FIN, <2>AOF DEF FinWait1 - <1>r. CASE SynSent(local, remote) \/ SynReceived(local, remote) - \* TODO: discharge the remaining 2 system action sub-cases. + (*************************************************************************) + (* SynSent has two sub-cases: *) + (* (a) head = "SYN": n'[local] = Tail, n'[remote] = Append "SYN,ACK", *) + (* local SS -> SR. *) + (* (b) head = "SYN,ACK": n'[local] = Tail, n'[remote] = Append "ACK", *) + (* local SS -> ESTABLISHED. *) + (*************************************************************************) + <1>18. CASE SynSent(local, remote) + <2>. USE <1>18 DEF SynSent + <2>SYN. CASE /\ IsPrefix(<<"SYN">>, network[local]) + /\ network' = [network EXCEPT ![remote] = Append(@, "SYN,ACK"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "SYN-RECEIVED"] + <3>. /\ network'[remote] = Append(network[remote], "SYN,ACK") + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local /\ r # remote => network'[r] = network[r] + /\ connstate'[local] = "SYN-RECEIVED" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ connstate[local] = "SYN-SENT" + /\ IsPrefix(<<"SYN">>, network[local]) + /\ local # remote + BY <2>SYN DEF TypeOK + <3>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>head. /\ network[local] # <<>> + /\ Head(network[local]) = "SYN" + /\ Tail(network[local]) \in Seq(Msgs) + /\ network[local][1] = "SYN" + BY PrefixOneNonEmpty, PrefixTwoNonEmpty DEF TypeOK, Msgs + <3>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <3>head DEF TypeOK, Msgs + <3>1. Inv' + <4>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <4>1. l # remote /\ r # remote + BY DEF TypeOK, Msgs + <4>2. l = local /\ r = local + BY <4>1, PeersAB + <4>. QED BY <4>2 + <3>2. Aux_singleton_RST' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "SYN,ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>. connstate'[q] = "SYN-RECEIVED" + BY <4>2 + <4>. QED OBVIOUS + <3>3. Aux_singleton_ACK' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "SYN,ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>3. p = remote + BY <4>2, PeersAB + <4>4. network'[p] = Append(network[p], "SYN,ACK") + BY <4>3 + <4>. SUFFICES Append(network[p], "SYN,ACK") = <<"ACK">> => FALSE + BY <4>4 + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>4. Aux_singleton_ACKofFIN' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "SYN,ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>. connstate'[q] = "SYN-RECEIVED" + BY <4>2 + <4>. QED OBVIOUS + <3>5. Aux_EST_evidence' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <4>1. p # local + BY DEF TypeOK + <4>2. q = local /\ p = remote + BY <4>1, PeersAB + \* HasMsg(SYN,ACK, p = remote)' is TRUE because we appended SYN,ACK. + <4>3. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>4. network'[p] = Append(network[p], "SYN,ACK") + BY <4>2 + <4>5. /\ Len(network'[p]) = Len(network[p]) + 1 + /\ network'[p][Len(network[p]) + 1] = "SYN,ACK" + /\ Len(network[p]) >= 0 + /\ Len(network[p]) + 1 >= 1 + /\ Len(network[p]) + 1 \in 1..Len(network'[p]) + BY <4>3, <4>4 + <4>6. \E i \in 1..Len(network'[p]) : network'[p][i] = "SYN,ACK" + BY <4>5 + <4>. QED BY <4>6 DEF HasMsg + <3>6. Aux_LastMsg' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <4>1. CASE p = remote + <5>1. q = local /\ q # remote + BY <4>1, PeersAB + <5>2. connstate'[q] = "SYN-RECEIVED" + BY <5>1 + <5>3. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>4. network'[p] = Append(network[p], "SYN,ACK") + BY <4>1 + <5>5. LastMsg(p)' = "SYN,ACK" + BY <5>3, <5>4 DEF LastMsg + <5>. QED BY <5>2, <5>5 + <4>2. CASE p = local + <5>0. q # local /\ q = remote + BY <4>2, PeersAB + <5>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) /\ network'[p] # <<>> + BY <4>2 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>1, EmptySeq + <5>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>2, <3>tail + <5>3a. Len(network[p]) >= 2 + BY <5>3, <5>2 + <5>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>2, <3>tail, <5>3, <5>3a + <5>5. LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <5>4, <5>3, <5>2, <5>3a DEF LastMsg + <5>6. connstate'[q] = connstate[q] + BY <5>0 DEF TypeOK + <5>. QED BY <5>5, <5>6 DEF Aux_LastMsg + <4>3. CASE p # remote /\ p # local + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>7. Aux_RST_at_end' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <4>1. CASE p = remote + <5>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. LastMsg(p)' = "SYN,ACK" + BY <4>1 DEF LastMsg + <5>. QED OBVIOUS + <4>2. CASE p = local + <5>0. q # local /\ q = remote + BY <4>2, PeersAB + <5>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>2 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>1, EmptySeq + <5>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>2, <3>tail + <5>3a. Len(network[p]) >= 2 + BY <5>3, <5>2 + <5>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>2, <3>tail, <5>3, <5>3a + <5>5. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" + /\ network[p] # <<>> + BY <5>4, <5>3, <5>2, <5>3a DEF LastMsg + <5>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>5 DEF Aux_RST_at_end + <5>7. connstate'[q] = connstate[q] + BY <5>0 DEF TypeOK + <5>. QED BY <5>6, <5>7 + <4>3. CASE p # remote /\ p # local + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv + <2>SA. CASE /\ IsPrefix(<<"SYN,ACK">>, network[local]) + /\ network' = [network EXCEPT ![remote] = Append(@, "ACK"), + ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "ESTABLISHED"] + <3>. /\ network'[remote] = Append(network[remote], "ACK") + /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local /\ r # remote => network'[r] = network[r] + /\ connstate'[local] = "ESTABLISHED" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ connstate[local] = "SYN-SENT" + /\ IsPrefix(<<"SYN,ACK">>, network[local]) + /\ local # remote + BY <2>SA DEF TypeOK + <3>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>head. /\ network[local] # <<>> + /\ Head(network[local]) = "SYN,ACK" + /\ Tail(network[local]) \in Seq(Msgs) + /\ network[local][1] = "SYN,ACK" + BY PrefixOneNonEmpty, PrefixTwoNonEmpty DEF TypeOK, Msgs + <3>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <3>head DEF TypeOK, Msgs + <3>1. Inv' + <4>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <4>1. l # remote /\ r # remote + BY DEF TypeOK, Msgs + <4>2. l = local /\ r = local + BY <4>1, PeersAB + <4>. QED BY <4>2 + <3>2. Aux_singleton_RST' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>3. p = remote + BY <4>2, PeersAB + \* network'[p] = Append(_, "ACK"), would-be <<"RST">> impossible. + <4>4. network'[p] = Append(network[p], "ACK") + BY <4>3 + <4>. SUFFICES Append(network[p], "ACK") = <<"RST">> => FALSE + BY <4>4 + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>3. Aux_singleton_ACK' + \* q = local post = EST. Conclusion is post EST. ✓ + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>. connstate'[q] = "ESTABLISHED" + BY <4>2 + <4>. QED OBVIOUS + <3>4. Aux_singleton_ACKofFIN' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <4>1. q # remote + <5>. SUFFICES ASSUME q = remote PROVE FALSE + OBVIOUS + <5>. network'[q] = Append(network[q], "ACK") /\ network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. QED OBVIOUS + <4>2. q = local + BY <4>1, PeersAB + <4>3. p = remote + BY <4>2, PeersAB + \* network'[p] = Append(_, "ACK"), would-be <<"ACKofFIN">> impossible. + <4>4. network'[p] = Append(network[p], "ACK") + BY <4>3 + <4>. SUFFICES Append(network[p], "ACK") = <<"ACKofFIN">> => FALSE + BY <4>4 + <4>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <4>. QED OBVIOUS + <3>5. Aux_EST_evidence' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <4>1. CASE p = remote + <5>1. q = local /\ q # remote + BY <4>1, PeersAB + <5>. connstate'[q] = "ESTABLISHED" /\ connstate'[q] \in PostEst + BY <5>1 DEF PostEst, PostEstStrict + <5>. QED OBVIOUS + <4>2. CASE p = local + <5>1. q = remote /\ q # local + BY <4>2, PeersAB + \* HasMsg(ACK, q = remote)' = TRUE because we appended ACK. + <5>2. network[q] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>3. network'[q] = Append(network[q], "ACK") + BY <5>1 + <5>4. /\ Len(network'[q]) = Len(network[q]) + 1 + /\ network'[q][Len(network[q]) + 1] = "ACK" + /\ Len(network[q]) >= 0 + /\ Len(network[q]) + 1 >= 1 + /\ Len(network[q]) + 1 \in 1..Len(network'[q]) + BY <5>2, <5>3 + <5>5. \E i \in 1..Len(network'[q]) : network'[q][i] = "ACK" + BY <5>4 + <5>. QED BY <5>5 DEF HasMsg + <4>3. CASE p # remote /\ p # local + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>6. Aux_LastMsg' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <4>1. CASE q = local + \* connstate'[q] = EST, not in covered states. Vacuous. + BY <4>1 + <4>2. CASE q # local + <5>0. connstate'[q] = connstate[q] /\ q = remote + BY <4>2, PeersAB DEF TypeOK + <5>1. CASE p = local + <6>0. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) /\ network'[p] # <<>> + BY <5>1 DEF TypeOK, Msgs + <6>2. Len(network'[p]) >= 1 + BY <6>0, EmptySeq + <6>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <5>1, <3>tail + <6>3a. Len(network[p]) >= 2 + BY <6>3, <6>2 + <6>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <3>tail, <6>3, <6>3a + <6>5. LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <6>4, <6>3, <6>2, <6>3a DEF LastMsg + <6>. QED BY <5>0, <6>5 DEF Aux_LastMsg + <5>2. CASE p # local /\ p # remote + BY <5>2, <4>2, PeersAB + <5>. QED BY <5>1, <5>2, <4>2, PeersAB + <4>. QED BY <4>1, <4>2 + <3>7. Aux_RST_at_end' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <4>1. CASE p = remote + <5>. network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>. LastMsg(p)' = "ACK" + BY <4>1 DEF LastMsg + <5>. QED OBVIOUS + <4>2. CASE p = local + <5>0. q # local /\ q = remote + BY <4>2, PeersAB + <5>1. network'[p] = Tail(network[p]) /\ network[p] \in Seq(Msgs) + /\ network'[p] \in Seq(Msgs) + BY <4>2 DEF TypeOK, Msgs + <5>2. Len(network'[p]) >= 1 + BY <5>1, EmptySeq + <5>3. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <4>2, <3>tail + <5>3a. Len(network[p]) >= 2 + BY <5>3, <5>2 + <5>4. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <4>2, <3>tail, <5>3, <5>3a + <5>5. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" + /\ network[p] # <<>> + BY <5>4, <5>3, <5>2, <5>3a DEF LastMsg + <5>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>5 DEF Aux_RST_at_end + <5>7. connstate'[q] = connstate[q] + BY <5>0 DEF TypeOK + <5>. QED BY <5>6, <5>7 + <4>3. CASE p # remote /\ p # local + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv + <2>. QED BY <2>SYN, <2>SA DEF SynSent + + <1>r. CASE SynReceived(local, remote) + \* TODO: discharge the remaining SR sub-cases. OMITTED - <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>16, <1>17, <1>r + <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>16, <1>17, <1>18, <1>r (***************************************************************************) (* Reset action (Note3): two sub-cases. *) From 9403377d3d5f9aa75ac97f35fbdaa64c258a43ec Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 16:19:57 -0700 Subject: [PATCH 17/22] tcp_proof: discharge IndInv preservation for SynReceived Add both SynReceived sub-cases (each Tail head, no append): (a) head = RST, local SR -> LISTEN. Mirror of Note3 RST receive (post LISTEN \in {TW, CLOSED, LISTEN} so Aux_RST_at_end is direct; Aux_singleton_ACK with q = local forces n[local] = <<"RST">>, then LastMsg(local) = "RST" contradicts Aux_LastMsg pre at q = remote SR; Aux_EST_evidence uses the case-split n[local] = <<"RST">> (then Aux_singleton_RST contrapositive forces n[remote] # <<>>, giving HasMsg evidence on p = remote) vs Len(n[local]) >= 2 (giving HasMsg evidence on q = local). (b) head = ACK, local SR -> ESTABLISHED. Mirror of SynSent SYN,ACK sub-case but without the appended message. - Aux_singleton_RST: split q = local (p = remote, n[remote] = <<"RST">>; pre Aux_RST_at_end forces connstate[local] in {TW, CLOSED, LISTEN}, contradicting SR) vs q = remote (p = local, n[local] = <<"ACK", "RST">>; pre Aux_RST_at_end gives connstate[remote] in {TW, CLOSED, LISTEN}, preserved). - Aux_singleton_ACKofFIN with q = local forces n[local] = <<"ACK">>, then LastMsg(local) = "ACK" contradicts Aux_LastMsg pre at q = remote in {FW1, CLOSING, LA} (which respectively require LastMsg = FIN/RST, ACKofFIN, FIN). - Aux_EST_evidence: p = remote case is trivial (q = local post EST \in PostEst). p = local case splits on Len(n[local]): >= 2 gives HasMsg(n'[local][1], p)' direct; = 1 (i.e., n[local] = <<"ACK">>) further splits on n[remote]: empty gives connstate[remote] = EST via Aux_singleton_ACK pre, hence q = remote post in PostEst; non-empty gives LastMsg(remote) = "SYN,ACK" via Aux_LastMsg pre, hence HasMsg("SYN,ACK", q = remote)' (unchanged). ALL 19 actions are now discharged. Total: 4525 obligations. The IndInv inductive invariant for the tcp spec is fully proven. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 703 ++++++++++++++++++++++++++++++- 1 file changed, 699 insertions(+), 4 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 77526566..e46af101 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -4367,10 +4367,705 @@ LEMMA IndInvSystem == BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv <2>. QED BY <2>SYN, <2>SA DEF SynSent - <1>r. CASE SynReceived(local, remote) - \* TODO: discharge the remaining SR sub-cases. - OMITTED - <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>16, <1>17, <1>18, <1>r + (*************************************************************************) + (* SynReceived has two sub-cases: *) + (* (a) head = "RST": n'[local] = Tail (no append), *) + (* local SR -> LISTEN. *) + (* (b) head = "ACK": n'[local] = Tail (no append), *) + (* local SR -> ESTABLISHED. *) + (*************************************************************************) + <1>19. CASE SynReceived(local, remote) + <2>. USE <1>19 DEF SynReceived + <2>RST. CASE /\ IsPrefix(<<"RST">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "LISTEN"] + <3>. /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local => network'[r] = network[r] + /\ connstate'[local] = "LISTEN" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ connstate[local] = "SYN-RECEIVED" + /\ IsPrefix(<<"RST">>, network[local]) + /\ local # remote + BY <2>RST DEF TypeOK + <3>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>head. /\ network[local] # <<>> + /\ Head(network[local]) = "RST" + /\ Tail(network[local]) \in Seq(Msgs) + /\ network[local][1] = "RST" + BY PrefixOneNonEmpty, PrefixTwoNonEmpty DEF TypeOK, Msgs + <3>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <3>head DEF TypeOK, Msgs + <3>1. Inv' + <4>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <4>0. /\ (l # local => network[l] = network'[l]) + /\ (r # local => network[r] = network'[r]) + /\ (l = local => network[l] = <<"RST">>) + /\ (r = local => network[r] = <<"RST">>) + <5>. \A x \in Peers : x = local => network'[x] = Tail(network[x]) + OBVIOUS + <5>. \A x \in Peers : (x = local /\ Tail(network[x]) = <<>>) => + network[x] = <<"RST">> + BY <3>head + <5>. QED OBVIOUS + <4>1. CASE l # local /\ r # local + <5>. network[l] = <<>> /\ network[r] = <<>> + BY <4>0, <4>1 + <5>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <5>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <5>. connstate'[l] = connstate[l] /\ connstate'[r] = connstate[r] + BY <4>1 DEF TypeOK + <5>. QED OBVIOUS + <4>2. CASE l = local + <5>1. connstate'[l] = "LISTEN" /\ connstate'[l] # "ESTABLISHED" + BY <4>2 + <5>2. CASE r = local + BY <4>2, <5>2, <5>1 + <5>3. CASE r # local + <6>1. network[r] = <<>> + BY <4>0, <5>3 + <6>2. network[local] = <<"RST">> /\ local \in Peers /\ r \in Peers /\ local # r + BY <4>2, <4>0, <5>3 + <6>3. connstate[r] # "ESTABLISHED" + BY <6>1, <6>2 DEF Aux_singleton_RST + <6>4. connstate'[r] = connstate[r] + BY <5>3 DEF TypeOK + <6>. QED BY <5>1, <6>3, <6>4 + <5>. QED BY <5>2, <5>3 + <4>3. CASE r = local /\ l # local + <5>1. connstate'[r] = "LISTEN" /\ connstate'[r] # "ESTABLISHED" + BY <4>3 + <5>2. network[l] = <<>> /\ network[local] = <<"RST">> + /\ local \in Peers /\ l \in Peers /\ local # l + BY <4>0, <4>3 + <5>3. connstate[l] # "ESTABLISHED" + BY <5>2 DEF Aux_singleton_RST + <5>4. connstate'[l] = connstate[l] + BY <4>3 DEF TypeOK + <5>. QED BY <5>1, <5>3, <5>4 + <4>. QED BY <4>1, <4>2, <4>3 + <3>2. Aux_singleton_RST' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <4>1. CASE q = local + BY <4>1 + <4>2. CASE q # local + <5>1. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>2. network[q] = <<>> + BY <4>2 + <5>3. CASE p = local + <6>0. Tail(network[p]) = <<"RST">> + BY <5>3 + <6>0a. Head(network[p]) = "RST" /\ network[p] # <<>> + /\ network[p] \in Seq(Msgs) + BY <5>3, <3>head DEF TypeOK, Msgs + <6>0b. Len(Tail(network[p])) = 1 /\ Len(network[p]) >= 1 + BY <6>0, <6>0a, EmptySeq + <6>0c. Len(network[p]) = 2 + BY <6>0a, <6>0b + <6>0d. network[p] = <> + BY <6>0a, <6>0c DEF TypeOK, Msgs + <6>0e. network[p][1] = "RST" + BY <6>0a DEF TypeOK, Msgs + <6>0f. Tail(network[p]) = <> + BY <6>0a, <6>0c DEF TypeOK, Msgs + <6>0g. network[p][2] = "RST" + BY <6>0, <6>0f + <6>1. network[p] = <<"RST", "RST">> + BY <6>0d, <6>0e, <6>0g + <6>2. LastMsg(p) = "RST" /\ network[p] # <<>> + BY <6>1 DEF LastMsg + <6>3. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>2 DEF Aux_RST_at_end + <6>. QED BY <5>1, <6>3 + <5>4. CASE p # local + <6>1. network[p] = <<"RST">> + BY <5>4 + <6>. QED + BY <5>1, <6>1, <5>2 DEF Aux_singleton_RST + <5>. QED BY <5>3, <5>4 + <4>. QED BY <4>1, <4>2 + <3>3. Aux_singleton_ACK' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <4>1. p # local + BY DEF TypeOK + <4>2. connstate[p] = "SYN-RECEIVED" + BY <4>1 DEF TypeOK + <4>3. network'[p] = network[p] /\ network[p] = <<"ACK">> + BY <4>1 + <4>4. CASE q = local + \* Pre n[local] = <<"RST">> (since Tail = <<>>). LastMsg(local) = RST. + \* Pre Aux_LastMsg at q' = remote SR, p' = local: LastMsg(local) = SYN,ACK. + \* RST # SYN,ACK. Contradiction. + <5>1. network[local] = <<"RST">> + <6>. Tail(network[local]) = <<>> + BY <4>4 + <6>. Len(network[local]) = 1 + BY <3>head, <3>tail + <6>. QED BY <3>head DEF TypeOK, Msgs + <5>2. LastMsg(local) = "RST" /\ network[local] # <<>> + BY <5>1 DEF LastMsg + <5>. QED + BY <4>4, <4>2, <4>1, <5>2 DEF Aux_LastMsg + <4>5. CASE q # local + <5>1. network'[q] = network[q] + BY <4>5 + <5>2. connstate'[q] = connstate[q] + BY <4>5 DEF TypeOK + <5>3. network[p] = <<"ACK">> /\ network[q] = <<>> + BY <4>3, <5>1 + <5>4. connstate[q] = "ESTABLISHED" + BY <4>2, <5>3 DEF Aux_singleton_ACK + <5>. QED BY <5>2, <5>4 + <4>. QED BY <4>4, <4>5 + <3>4. Aux_singleton_ACKofFIN' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <4>1. p # local + BY DEF TypeOK + <4>2. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY <4>1 DEF TypeOK + <4>3. CASE q = local + BY <4>3 + <4>4. CASE q # local + <5>1. network'[p] = network[p] /\ network'[q] = network[q] + BY <4>1, <4>4 + <5>2. connstate'[q] = connstate[q] + BY <4>4 DEF TypeOK + <5>3. network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + BY <5>1 + <5>. QED BY <4>2, <5>2, <5>3 DEF Aux_singleton_ACKofFIN + <4>. QED BY <4>3, <4>4 + <3>5. Aux_EST_evidence' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <4>1. p # local /\ connstate[p] = "ESTABLISHED" + BY DEF TypeOK + <4>2. q = local + BY <4>1, PeersAB + \* Same case-split as Note3 RST receive. + <4>3. CASE network[local] = <<"RST">> + <5>1. network[remote] # <<>> + <6>. SUFFICES ASSUME network[remote] = <<>> PROVE FALSE + OBVIOUS + <6>1. local \in Peers /\ remote \in Peers /\ local # remote + OBVIOUS + <6>2. connstate[remote] # "ESTABLISHED" + BY <4>3, <6>1 DEF Aux_singleton_RST + <6>3. p = remote + BY <4>1, <4>2, PeersAB + <6>. QED BY <4>1, <6>2, <6>3 + <5>2. p = remote + BY <4>1, <4>2, PeersAB + <5>3. network'[p] = network[p] + BY <5>2 + <5>4. network[p] # <<>> /\ network[p] \in Seq(Msgs) + BY <5>1, <5>2 DEF TypeOK, Msgs + <5>5. network[p][1] \in Msgs /\ Len(network[p]) >= 1 + /\ Len(network[p]) \in Nat + BY <5>4 + <5>6. Len(network'[p]) = Len(network[p]) /\ network'[p] = network[p] + BY <5>3 + <5>7. Len(network'[p]) >= 1 /\ network'[p][1] = network[p][1] + /\ Len(network'[p]) \in Nat + BY <5>5, <5>6 + <5>8. 1 \in 1..Len(network'[p]) + BY <5>7 + <5>9. \E i \in 1..Len(network'[p]) : network'[p][i] \in Msgs + /\ network'[p][i] = network[p][1] + BY <5>5, <5>7, <5>8 + <5>. QED BY <5>5, <5>9 DEF HasMsg, Msgs + <4>4. CASE Len(network[local]) >= 2 + <5>1. Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network'[local]) >= 1 + BY <4>4, <3>tail + <5>2. network'[local] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <5>3. network'[local][1] \in Msgs + BY <5>1, <5>2 + <5>4. \E i \in 1..Len(network'[local]) : network'[local][i] = network'[local][1] + BY <5>1 + <5>. QED BY <4>2, <5>3, <5>4 DEF HasMsg, Msgs + <4>5. \/ network[local] = <<"RST">> \/ Len(network[local]) >= 2 + <5>1. network[local] \in Seq(Msgs) /\ Len(network[local]) >= 1 + BY <3>head DEF TypeOK, Msgs + <5>2. \/ Len(network[local]) = 1 \/ Len(network[local]) >= 2 + BY <5>1 + <5>3. CASE Len(network[local]) = 1 + <6>. network[local] = <> + BY <5>3 DEF TypeOK, Msgs + <6>. QED BY <3>head + <5>. QED BY <5>2, <5>3 + <4>. QED BY <4>3, <4>4, <4>5 + <3>6. Aux_LastMsg' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <4>1. CASE q = local + BY <4>1 + <4>2. CASE q # local + <5>0. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>1. CASE p = local + <6>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) /\ network'[p] \in Seq(Msgs) + BY <5>1 DEF TypeOK, Msgs + <6>2. Len(network'[p]) >= 1 + BY <6>0, EmptySeq + <6>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <5>1, <3>tail + <6>1a. Len(network[p]) >= 2 + BY <6>1, <6>2 + <6>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <3>tail, <6>1, <6>1a + <6>4. LastMsg(p)' = LastMsg(p) + BY <6>3, <6>1, <6>2 DEF LastMsg + <6>5. network[p] # <<>> + BY <6>1a + <6>. QED BY <5>0, <6>4, <6>5 DEF Aux_LastMsg + <5>2. CASE p # local + <6>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) + /\ network[p] # <<>> + BY <5>2 DEF LastMsg + <6>. QED BY <5>0, <6>1 DEF Aux_LastMsg + <5>. QED BY <5>1, <5>2 + <4>. QED BY <4>1, <4>2 + <3>7. Aux_RST_at_end' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <4>1. CASE q = local + \* Post LISTEN \in {TW, CLOSED, LISTEN}. ✓ + BY <4>1 + <4>2. CASE q # local + <5>0. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>1. CASE p = local + <6>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) /\ network'[p] \in Seq(Msgs) + BY <5>1 DEF TypeOK, Msgs + <6>2. Len(network'[p]) >= 1 + BY <6>0, EmptySeq + <6>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <5>1, <3>tail + <6>1a. Len(network[p]) >= 2 + BY <6>1, <6>2 + <6>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <3>tail, <6>1, <6>1a + <6>4. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" + /\ network[p] # <<>> + BY <6>3, <6>1, <6>2, <6>1a DEF LastMsg + <6>5. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>4 DEF Aux_RST_at_end + <6>. QED BY <5>0, <6>5 + <5>2. CASE p # local + <6>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) + /\ network[p] # <<>> + BY <5>2 DEF LastMsg + <6>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>1 DEF Aux_RST_at_end + <6>. QED BY <5>0, <6>2 + <5>. QED BY <5>1, <5>2 + <4>. QED BY <4>1, <4>2 + <3>. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv + <2>ACK. CASE /\ IsPrefix(<<"ACK">>, network[local]) + /\ network' = [network EXCEPT ![local] = Tail(network[local])] + /\ connstate' = [connstate EXCEPT ![local] = "ESTABLISHED"] + <3>. /\ network'[local] = Tail(network[local]) + /\ \A r \in Peers : r # local => network'[r] = network[r] + /\ connstate'[local] = "ESTABLISHED" + /\ \A r \in Peers : r # local => connstate'[r] = connstate[r] + /\ connstate[local] = "SYN-RECEIVED" + /\ IsPrefix(<<"ACK">>, network[local]) + /\ local # remote + BY <2>ACK DEF TypeOK + <3>. \A p \in Peers : network[p] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <3>head. /\ network[local] # <<>> + /\ Head(network[local]) = "ACK" + /\ Tail(network[local]) \in Seq(Msgs) + /\ network[local][1] = "ACK" + BY PrefixOneNonEmpty, PrefixTwoNonEmpty DEF TypeOK, Msgs + <3>tail. /\ Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network[local]) >= 1 + /\ \A i \in 1..Len(network'[local]) : network'[local][i] = network[local][i + 1] + BY <3>head DEF TypeOK, Msgs + <3>1. Inv' + <4>. SUFFICES ASSUME NEW l \in {p \in Peers : network'[p] = <<>>}, + NEW r \in {p \in Peers : network'[p] = <<>>} + PROVE connstate'[l] = "ESTABLISHED" <=> connstate'[r] = "ESTABLISHED" + BY DEF Inv + <4>0. /\ (l # local => network[l] = network'[l]) + /\ (r # local => network[r] = network'[r]) + /\ (l = local => network[l] = <<"ACK">>) + /\ (r = local => network[r] = <<"ACK">>) + <5>. \A x \in Peers : x = local => network'[x] = Tail(network[x]) + OBVIOUS + <5>. \A x \in Peers : (x = local /\ Tail(network[x]) = <<>>) => + network[x] = <<"ACK">> + BY <3>head + <5>. QED OBVIOUS + <4>1. CASE l # local /\ r # local + <5>. network[l] = <<>> /\ network[r] = <<>> + BY <4>0, <4>1 + <5>. l \in {p \in Peers : network[p] = <<>>} + /\ r \in {p \in Peers : network[p] = <<>>} + OBVIOUS + <5>. connstate[l] = "ESTABLISHED" <=> connstate[r] = "ESTABLISHED" + BY DEF Inv + <5>. connstate'[l] = connstate[l] /\ connstate'[r] = connstate[r] + BY <4>1 DEF TypeOK + <5>. QED OBVIOUS + <4>2. CASE l = local + <5>1. connstate'[l] = "ESTABLISHED" + BY <4>2 + <5>2. CASE r = local + BY <4>2, <5>2, <5>1 + <5>3. CASE r # local + <6>1. network[r] = <<>> + BY <4>0, <5>3 + <6>2. network[local] = <<"ACK">> /\ local \in Peers /\ r \in Peers /\ local # r + BY <4>2, <4>0, <5>3 + <6>3. connstate[local] = "SYN-RECEIVED" + OBVIOUS + <6>4. connstate[r] = "ESTABLISHED" + BY <6>1, <6>2, <6>3 DEF Aux_singleton_ACK + <6>5. connstate'[r] = connstate[r] + BY <5>3 DEF TypeOK + <6>. QED BY <5>1, <6>4, <6>5 + <5>. QED BY <5>2, <5>3 + <4>3. CASE r = local /\ l # local + <5>1. connstate'[r] = "ESTABLISHED" + BY <4>3 + <5>2. network[l] = <<>> /\ network[local] = <<"ACK">> + /\ local \in Peers /\ l \in Peers /\ local # l + BY <4>0, <4>3 + <5>3. connstate[local] = "SYN-RECEIVED" + OBVIOUS + <5>4. connstate[l] = "ESTABLISHED" + BY <5>2, <5>3 DEF Aux_singleton_ACK + <5>5. connstate'[l] = connstate[l] + BY <4>3 DEF TypeOK + <5>. QED BY <5>1, <5>4, <5>5 + <4>. QED BY <4>1, <4>2, <4>3 + <3>2. Aux_singleton_RST' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"RST">>, network'[q] = <<>> + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_RST + <4>1. CASE q = local + \* p = remote, n'[remote] = n[remote] = <>. + \* Pre Aux_RST_at_end at p = remote, q = local => connstate[local] in + \* {TW, CLOSED, LISTEN}. But connstate[local] = SR. Contradiction. + <5>1. p = remote /\ p # local + BY <4>1, PeersAB + <5>2. network[p] = <<"RST">> + BY <5>1 + <5>3. LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>2 DEF LastMsg + <5>4. connstate[local] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <4>1, <5>1, <5>3 DEF Aux_RST_at_end + <5>. QED BY <5>4 + <4>2. CASE q # local + \* p = local. n'[local] = Tail = <>. Pre n[local] = <>. + \* LastMsg(local) = RST. Pre Aux_RST_at_end at p = local, q = remote + \* => connstate[remote] in {TW, CLOSED, LISTEN}. Preserved post. + <5>0. q = remote /\ q # local + BY <4>2, PeersAB + <5>1. p = local /\ p # remote + BY <5>0, PeersAB + <5>2. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>3. network'[p] = Tail(network[p]) + BY <5>1 + <5>4. network[p] = <<"ACK", "RST">> + <6>. Tail(network[p]) = <<"RST">> + BY <5>3 + <6>0a. Head(network[p]) = "ACK" /\ network[p] # <<>> + /\ network[p] \in Seq(Msgs) + BY <5>1, <3>head DEF TypeOK, Msgs + <6>0b. Len(Tail(network[p])) = 1 /\ Len(network[p]) >= 1 + BY <5>3, <6>0a, EmptySeq + <6>0c. Len(network[p]) = 2 + BY <6>0a, <6>0b + <6>0d. network[p] = <> + BY <6>0a, <6>0c DEF TypeOK, Msgs + <6>0e. network[p][1] = "ACK" + BY <6>0a DEF TypeOK, Msgs + <6>0f. Tail(network[p]) = <> + BY <6>0a, <6>0c DEF TypeOK, Msgs + <6>0g. network[p][2] = "RST" + BY <5>3, <6>0f + <6>. QED BY <6>0d, <6>0e, <6>0g + <5>5. LastMsg(p) = "RST" /\ network[p] # <<>> + BY <5>4 DEF LastMsg + <5>6. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>1, <5>0, <5>5 DEF Aux_RST_at_end + <5>. QED BY <5>2, <5>6 + <4>. QED BY <4>1, <4>2 + <3>3. Aux_singleton_ACK' + \* q = local post EST. Conclusion: q' = EST. ✓ + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACK">>, network'[q] = <<>>, + connstate'[p] = "SYN-RECEIVED" + PROVE connstate'[q] = "ESTABLISHED" + BY DEF Aux_singleton_ACK + <4>1. p # local + BY DEF TypeOK + <4>2. connstate[p] = "SYN-RECEIVED" + BY <4>1 DEF TypeOK + <4>3. CASE q = local + BY <4>3 + <4>4. CASE q # local + <5>1. network'[p] = network[p] /\ network'[q] = network[q] + BY <4>1, <4>4 + <5>2. connstate'[q] = connstate[q] + BY <4>4 DEF TypeOK + <5>3. network[p] = <<"ACK">> /\ network[q] = <<>> + BY <5>1 + <5>4. connstate[q] = "ESTABLISHED" + BY <4>2, <5>3 DEF Aux_singleton_ACK + <5>. QED BY <5>2, <5>4 + <4>. QED BY <4>3, <4>4 + <3>4. Aux_singleton_ACKofFIN' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] = <<"ACKofFIN">>, network'[q] = <<>>, + connstate'[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + PROVE connstate'[q] # "ESTABLISHED" + BY DEF Aux_singleton_ACKofFIN + <4>1. p # local + BY DEF TypeOK + <4>2. connstate[p] \in {"FIN-WAIT-1", "CLOSING", "LAST-ACK"} + BY <4>1 DEF TypeOK + <4>3. CASE q = local + \* q = local post EST. Need contradiction. + \* p = remote, n[remote] = <>. n'[q=local] = <<>> => n[local] = <>. + \* Pre Aux_LastMsg at q = remote (in FW1/CLOSING/LA), p = local: LastMsg(local) matches. + \* Each match: FW1->FIN/RST, CLOSING->ACKofFIN, LA->FIN. None = ACK. Contradiction. + <5>1. network[local] = <<"ACK">> /\ network[local] # <<>> + <6>. Tail(network[local]) = <<>> + BY <4>3 + <6>. Len(network[local]) = 1 + BY <3>head, <3>tail + <6>. QED BY <3>head DEF TypeOK, Msgs + <5>2. LastMsg(local) = "ACK" + BY <5>1 DEF LastMsg + <5>3. p = remote + BY <4>3, <4>1, PeersAB + <5>. QED BY <4>3, <4>2, <5>1, <5>2, <5>3 DEF Aux_LastMsg + <4>4. CASE q # local + <5>1. network'[p] = network[p] /\ network'[q] = network[q] + BY <4>1, <4>4 + <5>2. connstate'[q] = connstate[q] + BY <4>4 DEF TypeOK + <5>3. network[p] = <<"ACKofFIN">> /\ network[q] = <<>> + BY <5>1 + <5>. QED BY <4>2, <5>2, <5>3 DEF Aux_singleton_ACKofFIN + <4>. QED BY <4>3, <4>4 + <3>5. Aux_EST_evidence' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, connstate'[p] = "ESTABLISHED" + PROVE \/ connstate'[q] \in PostEst + \/ HasMsg("SYN", p)' \/ HasMsg("SYN", q)' + \/ HasMsg("ACK", q)' \/ HasMsg("ACK", p)' + \/ HasMsg("SYN,ACK", q)' \/ HasMsg("SYN,ACK", p)' + \/ HasMsg("FIN", p)' \/ HasMsg("FIN", q)' + \/ HasMsg("ACKofFIN", p)' \/ HasMsg("ACKofFIN", q)' + \/ HasMsg("RST", p)' \/ HasMsg("RST", q)' + BY DEF Aux_EST_evidence + <4>1. CASE p = remote + <5>1. q = local /\ q # remote + BY <4>1, PeersAB + <5>. connstate'[q] = "ESTABLISHED" /\ connstate'[q] \in PostEst + BY <5>1 DEF PostEst, PostEstStrict + <5>. QED OBVIOUS + <4>2. CASE p = local + <5>1. q = remote /\ q # local + BY <4>2, PeersAB + \* Use case-split. If pre Len(n[local]) >= 2: HasMsg(n'[local][1], p)' = TRUE. + \* If pre Len = 1 (n[local] = <>): split on n[remote]. + \* - n[remote] = <<>>: Aux_singleton_ACK pre at p = local SR, q = remote + \* gives connstate[remote] = EST, post EST in PostEst. + \* - n[remote] # <<>>: Aux_LastMsg pre at q = local SR, p = remote + \* gives LastMsg(remote) = SYN,ACK, so HasMsg(SYN,ACK, q = remote)' = TRUE. + <5>2. CASE Len(network[local]) >= 2 + <6>1. Len(network'[local]) = Len(network[local]) - 1 + /\ Len(network'[local]) >= 1 + BY <5>2, <3>tail + <6>2. network'[local] \in Seq(Msgs) + BY DEF TypeOK, Msgs + <6>3. network'[local][1] \in Msgs + BY <6>1, <6>2 + <6>4. \E i \in 1..Len(network'[local]) : network'[local][i] = network'[local][1] + BY <6>1 + <6>. QED BY <4>2, <6>3, <6>4 DEF HasMsg, Msgs + <5>3. CASE Len(network[local]) < 2 + <6>1. Len(network[local]) = 1 + BY <5>3, <3>head DEF TypeOK, Msgs + <6>2. network[local] = <<"ACK">> + <7>. network[local] = <> + BY <6>1 DEF TypeOK, Msgs + <7>. QED BY <3>head + <6>3. CASE network[remote] = <<>> + <7>1. local \in Peers /\ remote \in Peers /\ local # remote + OBVIOUS + <7>2. connstate[local] = "SYN-RECEIVED" + OBVIOUS + <7>3. connstate[remote] = "ESTABLISHED" + BY <6>2, <6>3, <7>1, <7>2 DEF Aux_singleton_ACK + <7>4. connstate'[remote] = connstate[remote] + BY <5>1 DEF TypeOK + <7>5. connstate'[q] = "ESTABLISHED" /\ connstate'[q] \in PostEst + BY <5>1, <7>3, <7>4 DEF PostEst, PostEstStrict + <7>. QED BY <7>5 + <6>4. CASE network[remote] # <<>> + <7>1. LastMsg(remote) = "SYN,ACK" + BY <6>4 DEF Aux_LastMsg + <7>2. network'[remote] = network[remote] + BY <5>1 + <7>3. network[remote] \in Seq(Msgs) /\ Len(network[remote]) >= 1 + BY <6>4 DEF TypeOK, Msgs + <7>4. network[remote][Len(network[remote])] = "SYN,ACK" + BY <7>1, <7>3 DEF LastMsg + <7>5. Len(network[remote]) \in 1..Len(network[remote]) + /\ network[remote][Len(network[remote])] = "SYN,ACK" + BY <7>3, <7>4 + <7>6. \E i \in 1..Len(network'[remote]) : network'[remote][i] = "SYN,ACK" + BY <7>2, <7>5 + <7>. QED BY <5>1, <7>6 DEF HasMsg + <6>. QED BY <6>3, <6>4 + <5>4. \/ Len(network[local]) >= 2 \/ Len(network[local]) < 2 + BY <3>head DEF TypeOK, Msgs + <5>. QED BY <5>2, <5>3, <5>4 + <4>3. CASE p # remote /\ p # local + BY <4>3, PeersAB + <4>. QED BY <4>1, <4>2, <4>3 + <3>6. Aux_LastMsg' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>> + PROVE /\ connstate'[q] = "SYN-RECEIVED" => LastMsg(p)' = "SYN,ACK" + /\ connstate'[q] = "FIN-WAIT-1" => LastMsg(p)' \in {"FIN", "RST"} + /\ connstate'[q] = "CLOSE-WAIT" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "LAST-ACK" => LastMsg(p)' = "FIN" + /\ connstate'[q] = "CLOSING" => LastMsg(p)' = "ACKofFIN" + /\ connstate'[q] = "SYN-SENT" => LastMsg(p)' = "SYN" + BY DEF Aux_LastMsg + <4>1. CASE q = local + \* connstate'[q] = EST, not in covered states. Vacuous. + BY <4>1 + <4>2. CASE q # local + <5>0. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>1. CASE p = local + <6>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) /\ network'[p] \in Seq(Msgs) + BY <5>1 DEF TypeOK, Msgs + <6>2. Len(network'[p]) >= 1 + BY <6>0, EmptySeq + <6>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <5>1, <3>tail + <6>1a. Len(network[p]) >= 2 + BY <6>1, <6>2 + <6>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <3>tail, <6>1, <6>1a + <6>4. LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <6>3, <6>1, <6>2, <6>1a DEF LastMsg + <6>. QED BY <5>0, <6>4 DEF Aux_LastMsg + <5>2. CASE p # local + <6>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) + /\ network[p] # <<>> + BY <5>2 DEF LastMsg + <6>. QED BY <5>0, <6>1 DEF Aux_LastMsg + <5>. QED BY <5>1, <5>2 + <4>. QED BY <4>1, <4>2 + <3>7. Aux_RST_at_end' + <4>. SUFFICES ASSUME NEW p \in Peers, NEW q \in Peers, + p # q, network'[p] # <<>>, LastMsg(p)' = "RST" + PROVE connstate'[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY DEF Aux_RST_at_end + <4>1. CASE q = local + \* q = local post EST # {TW, CLOSED, LISTEN}. Need contradiction. + \* p # local => p = remote. LastMsg(remote)' = LastMsg(remote) (unchanged) = RST. + \* Pre Aux_RST_at_end at p = remote, q = local: connstate[local] in {TW, CLOSED, LISTEN}. + \* But connstate[local] = SR. Contradiction. + <5>1. p # local + BY <4>1 + <5>2. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) /\ network[p] # <<>> + BY <5>1 DEF LastMsg + <5>3. LastMsg(p) = "RST" + BY <5>2 + <5>4. connstate[local] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <5>1, <4>1, <5>2, <5>3 DEF Aux_RST_at_end + <5>. QED BY <5>4 + <4>2. CASE q # local + <5>0. connstate'[q] = connstate[q] + BY <4>2 DEF TypeOK + <5>1. CASE p = local + <6>0. network'[p] # <<>> /\ network'[p] = Tail(network[p]) + /\ network[p] \in Seq(Msgs) /\ network'[p] \in Seq(Msgs) + BY <5>1 DEF TypeOK, Msgs + <6>2. Len(network'[p]) >= 1 + BY <6>0, EmptySeq + <6>1. Len(network'[p]) = Len(network[p]) - 1 /\ Len(network[p]) >= 1 + BY <5>1, <3>tail + <6>1a. Len(network[p]) >= 2 + BY <6>1, <6>2 + <6>3. network'[p][Len(network'[p])] = network[p][Len(network[p])] + BY <5>1, <3>tail, <6>1, <6>1a + <6>4. LastMsg(p)' = LastMsg(p) /\ LastMsg(p) = "RST" + /\ network[p] # <<>> + BY <6>3, <6>1, <6>2, <6>1a DEF LastMsg + <6>5. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>4 DEF Aux_RST_at_end + <6>. QED BY <5>0, <6>5 + <5>2. CASE p # local + <6>1. network'[p] = network[p] /\ LastMsg(p)' = LastMsg(p) + /\ network[p] # <<>> + BY <5>2 DEF LastMsg + <6>2. connstate[q] \in {"TIME-WAIT", "CLOSED", "LISTEN"} + BY <6>1 DEF Aux_RST_at_end + <6>. QED BY <5>0, <6>2 + <5>. QED BY <5>1, <5>2 + <4>. QED BY <4>1, <4>2 + <3>. QED + BY <3>1, <3>2, <3>3, <3>4, <3>5, <3>6, <3>7 DEF IndInv + <2>. QED BY <2>RST, <2>ACK DEF SynReceived + + <1>. QED BY <1>9, <1>10, <1>11, <1>13, <1>14, <1>15, <1>16, <1>17, <1>18, <1>19 (***************************************************************************) (* Reset action (Note3): two sub-cases. *) From 93aad109685f538468aec35dba7ad5e1a27d6f19 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 16:54:04 -0700 Subject: [PATCH 18/22] tcp_proof: discharge SpecImpliesIndInv and SpecImpliesInv With IndInvIsInductive fully proven, the standard PTL liveness argument yields: THEOREM SpecImpliesIndInv == Spec => []IndInv via IndInvInit, IndInvIsInductive, PTL. THEOREM SpecImpliesInv == Spec => []Inv via IndInv => Inv (by definition) and SpecImpliesIndInv. This completes the full TLAPS safety proof for the tcp spec without modifying any original spec files. Total: 4540 obligations across 5665 lines. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index e46af101..8da1c300 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -5642,9 +5642,20 @@ THEOREM IndInvIsInductive == IndInv /\ [Next]_vars => IndInv' <1>. QED BY <1>stutter, <1>user, <1>system, <1>reset DEF Next (***************************************************************************) -(* Once IndInvIsInductive is fully discharged, the main theorem follows: *) -(* *) -(* THEOREM SpecImpliesInv == Spec => []Inv *) -(* <1>. QED BY IndInvInit, IndInvIsInductive, PTL DEF Spec, IndInv *) +(* The main safety result. IndInv strengthens Inv with auxiliary clauses *) +(* required for inductiveness; it implies Inv directly by definition. *) (***************************************************************************) +THEOREM SpecImpliesIndInv == Spec => []IndInv + <1>1. Init => IndInv + BY IndInvInit + <1>2. IndInv /\ [Next]_vars => IndInv' + BY IndInvIsInductive + <1>. QED BY <1>1, <1>2, PTL DEF Spec + +THEOREM SpecImpliesInv == Spec => []Inv + <1>1. IndInv => Inv + BY DEF IndInv + <1>2. Spec => []IndInv + BY SpecImpliesIndInv + <1>. QED BY <1>1, <1>2, PTL ============================================================================ From 266b1960816c963cd5c09689d73af52f8805f497 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 20:52:04 -0700 Subject: [PATCH 19/22] tcp/manifest: list tcp_proof.tla with TLAPS runtime The previous commits added the TLAPS proof companion tcp_proof.tla but omitted it from specifications/tcp/manifest.json, so CI's manifest-files check was failing and the proof was never run by .github/workflows/CI.yml's "Check proofs" step (which iterates over modules carrying a "proof" field). Add a module entry for tcp_proof.tla with an empty models array and a proof.runtime of 00:51:00 (the wall-clock time of the most recent successful tlapm run that discharged all 4540 obligations on this machine). Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/manifest.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/specifications/tcp/manifest.json b/specifications/tcp/manifest.json index db696734..7949ae1c 100644 --- a/specifications/tcp/manifest.json +++ b/specifications/tcp/manifest.json @@ -38,6 +38,14 @@ "path": "specifications/tcp/tcp.tla", "features": [], "models": [] + }, + { + "path": "specifications/tcp/tcp_proof.tla", + "features": [], + "models": [], + "proof": { + "runtime": "00:51:00" + } } ] } \ No newline at end of file From 318dfe5351a4b88d87e19f606af62fdeb68945f1 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 20:52:21 -0700 Subject: [PATCH 20/22] tcp/manifest: list IndInv_apa.tla; .ciignore its Apalache configs IndInv_apa.tla is the Apalache-based inductive-invariant search whose final invariant is now discharged in TLAPS by tcp_proof.tla. There is no value in re-running its two configurations (IndInv_apa_init.cfg checks Init => IndInv in ~5s, IndInv_apa.cfg checks IndInv preservation in ~70s) on every CI build, especially given that .github/workflows/CI.yml overrides --length=5 uniformly, which doesn't match either configuration's intended semantics. Add a module entry for IndInv_apa.tla with an empty models array (mirroring the existing APDistributedReplicatedLog.tla pattern in specifications/FiniteMonotonic/manifest.json). This makes SANY parse the file in CI's "Parse all modules" step but no model checker ever runs it. Add the two .cfg files to .ciignore so check_manifest_files.py does not flag them as missing from any manifest. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- .ciignore | 8 ++++++++ specifications/tcp/manifest.json | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/.ciignore b/.ciignore index efb4c0f9..c6143718 100644 --- a/.ciignore +++ b/.ciignore @@ -11,3 +11,11 @@ specifications/CCF specifications/azure-cosmos-tla specifications/microwave +# Apalache inductive-invariant search for tcp.tla; the inductive invariant +# itself is discharged by the TLAPS proof in tcp_proof.tla, so there is no +# value in re-running these Apalache configurations on every CI build. +# IndInv_apa.tla is still listed in specifications/tcp/manifest.json so that +# SANY parses it on every CI run. +specifications/tcp/IndInv_apa.cfg +specifications/tcp/IndInv_apa_init.cfg + diff --git a/specifications/tcp/manifest.json b/specifications/tcp/manifest.json index 7949ae1c..6005b98c 100644 --- a/specifications/tcp/manifest.json +++ b/specifications/tcp/manifest.json @@ -34,6 +34,11 @@ } ] }, + { + "path": "specifications/tcp/IndInv_apa.tla", + "features": [], + "models": [] + }, { "path": "specifications/tcp/tcp.tla", "features": [], From 16eb7a297bab2824716864f72f3236eed2c0c73a Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 21:03:06 -0700 Subject: [PATCH 21/22] tcp: tick TLAPS Proof column in README.md spec table CI's check_markdown_table.py compares the proof column in the README's spec table against the presence of any module with a "proof" field in specifications//manifest.json. After listing tcp_proof.tla in the manifest, the table was out of sync: ERROR: Proof flags in README.md table differ from features in manifest.json: Spec specifications/tcp is missing a proof flag in README.md table Add the checkmark to the TLAPS Proof column for the tcp row. Co-authored-by: Claude Opus 4.7 Signed-off-by: Markus Alexander Kuppe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b7e1588..3d69c772 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Here is a list of specs included in this repository which are validated by the C | [PCR Testing for Snippets of DNA](specifications/glowingRaccoon) | Martin Harrison | | | | ✔ | ✔ | | [RFC 3506: Voucher Transaction System](specifications/byihive) | Santhosh Raju, Cherry G. Mathew, Fransisca Andriani | | | | ✔ | | | [Yo-Yo Leader Election](specifications/YoYo) | Ludovic Yvoz, Stephan Merz | | | | ✔ | | -| [TCP as defined in RFC 9293](specifications/tcp) | Markus Kuppe | | | | ✔ | ✔ | +| [TCP as defined in RFC 9293](specifications/tcp) | Markus Kuppe | | ✔ | | ✔ | ✔ | | [B-trees](specifications/btree) | Lorin Hochstein | | | | ✔ | | | [TLA⁺ Level Checking](specifications/LevelChecking) | Leslie Lamport | | | | | | | [Condition-Based Consensus](specifications/cbc_max) | Thanh Hai Tran, Igor Konnov, Josef Widder | | | | | | From 2cfb41ff1e0e7cd7a33066c27aec707d5a090212 Mon Sep 17 00:00:00 2001 From: Markus Alexander Kuppe Date: Mon, 11 May 2026 21:16:03 -0700 Subject: [PATCH 22/22] tcp_proof: extend with FiniteSetTheorems Signed-off-by: Markus Alexander Kuppe --- specifications/tcp/tcp_proof.tla | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/tcp/tcp_proof.tla b/specifications/tcp/tcp_proof.tla index 8da1c300..34f40de9 100644 --- a/specifications/tcp/tcp_proof.tla +++ b/specifications/tcp/tcp_proof.tla @@ -5,7 +5,7 @@ (* Spec => []TypeOK *) (* Spec => []Inv (ESTABLISHED agreement when both networks are empty) *) (***************************************************************************) -EXTENDS tcp, SequenceTheorems, SequencesExtTheorems, TLAPS +EXTENDS tcp, SequenceTheorems, SequencesExtTheorems, FiniteSetTheorems, TLAPS \* The spec's `ASSUME PeersAssumption == Cardinality(Peers) = 2` is intended \* to assert that Peers is a finite set with exactly two elements. In TLA+,