Skip to content

Commit 1df3af4

Browse files
committed
models: adjust MoreThanFNodesCommitted check
Based on the neo-project/neo-modules#792 (comment) the definition of MoreThanFNodesCommitted should be adjusted to match the core algorithm as it's the key factor of four-nodes deadlock scenario. There are two changes: 1. We consider the node to be "Committed" if it has the Commit message sent at _any_ view. 2. We should count lost nodes as far. We consider the node to be "Lost" if it hasn't sent any messages in the current round. Based on this adjustment, the first liveness lock scenario mentioned in neo-project/neo-modules#792 (comment) ("Liveness lock with four non-faulty nodes") is unreachable. However, there's stil a liveness lock when one of the nodes is in the RMDead list, i.e. can "die" at any moment. Consider running the base model specification with the following configuration: ``` RM RMFault RMDead MaxView {0, 1, 2, 3} {} {0} 2 ```
1 parent 39e3e95 commit 1df3af4

File tree

1 file changed

+16
-13
lines changed

1 file changed

+16
-13
lines changed

formal-models/dbft/dbft.tla

+16-13
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,16 @@ GetPrimary(view) == CHOOSE r \in RM : view % N = r
103103
GetNewView(oldView) == oldView + 1
104104

105105
\* CountCommitted returns the number of nodes that have sent the Commit message
106-
\* in the current round (as the node r sees it).
107-
CountCommitted(r) == Cardinality({rm \in RM : Cardinality({msg \in msgs : msg.rm = rm /\ msg.type = "Commit" /\ msg.view = rmState[r].view}) /= 0})
106+
\* in the current round or in some other round.
107+
CountCommitted(r) == Cardinality({rm \in RM : Cardinality({msg \in msgs : msg.rm = rm /\ msg.type = "Commit"}) /= 0})
108108

109-
\* MoreThanFNodesCommitted returns whether more than F nodes have been committed
110-
\* in the current round (as the node r sees it).
111-
MoreThanFNodesCommitted(r) == CountCommitted(r) > F
109+
\* CountLost returns the number of nodes that considered to be lost. These are the nodes
110+
\* that didn't send any messages in the current or larger view (as the node r sees it).
111+
CountLost(r) == Cardinality({rm \in RM : \neg (\E msg \in msgs : msg.view >= rmState[r].view)})
112+
113+
\* MoreThanFNodesCommittedOrLost returns whether more than F nodes have been committed
114+
\* in the current round (as the node r sees it) or haven't been seen at all at the current view.
115+
MoreThanFNodesCommittedOrLost(r) == (CountCommitted(r) + CountLost(r)) > F
112116

113117
\* PrepareRequestSentOrReceived denotes whether there's a PrepareRequest
114118
\* message received from the current round's speaker (as the node r sees it).
@@ -143,13 +147,12 @@ RMSendPrepareResponse(r) ==
143147
\* and
144148
\* https://github.com/neo-project/neo-modules/blob/d00d90b9c27b3d0c3c57e9ca1f560a09975df241/src/DBFTPlugin/Consensus/ConsensusService.OnMessage.cs#L79).
145149
\* However, we can't easily count the number of "lost" nodes in this specification to match precisely
146-
\* the implementation. Moreover, we don't need it to be counted as the RMSendPrepareResponse enabling
147-
\* condition specifies only the thing that may happen given some particular set of enabling conditions.
148-
\* Thus, we've extended the NotAcceptingPayloadsDueToViewChanging condition to consider only MoreThanFNodesCommitted.
149-
\* It should be noted that the logic of MoreThanFNodesCommittedOrLost can't be reliable in detecting lost nodes
150-
\* (even with neo-project/neo#2057), because real nodes can go down at any time.
150+
\* the implementation. We consider the "lost" nodes to be those who didn't send any message in the current
151+
\* view from the r's point of view. It should be noted that the logic of MoreThanFNodesCommittedOrLost
152+
\* can't be reliable in detecting lost nodes (even with neo-project/neo#2057), because real nodes can
153+
\* go down at any time.
151154
\/ /\ rmState[r].type = "cv"
152-
/\ MoreThanFNodesCommitted(r)
155+
/\ MoreThanFNodesCommittedOrLost(r)
153156
/\ \neg IsPrimary(r)
154157
/\ PrepareRequestSentOrReceived(r)
155158
/\ rmState' = [rmState EXCEPT ![r].type = "prepareSent"]
@@ -163,7 +166,7 @@ RMSendCommit(r) ==
163166
\* We do allow the transition from the "cv" state to the "prepareSent" or "commitSent" stage,
164167
\* see the related comment inside the RMSendPrepareResponse definition.
165168
\/ /\ rmState[r].type = "cv"
166-
/\ MoreThanFNodesCommitted(r)
169+
/\ MoreThanFNodesCommittedOrLost(r)
167170
/\ Cardinality({
168171
msg \in msgs : /\ (msg.type = "PrepareResponse" \/ msg.type = "PrepareRequest")
169172
/\ msg.view = rmState[r].view
@@ -374,7 +377,7 @@ THEOREM Spec => [](TypeOK /\ InvTwoBlocksAccepted /\ InvFaultNodesCount)
374377

375378
=============================================================================
376379
\* Modification History
377-
\* Last modified Mon Feb 27 16:46:19 MSK 2023 by root
380+
\* Last modified Mon Mar 06 13:06:40 MSK 2023 by root
378381
\* Last modified Fri Feb 17 15:47:41 MSK 2023 by anna
379382
\* Last modified Sat Jan 21 01:26:16 MSK 2023 by rik
380383
\* Created Thu Dec 15 16:06:17 MSK 2022 by anna

0 commit comments

Comments
 (0)