Skip to content

Commit 4b077af

Browse files
authored
MLS: send ext commit before sending ext proposals (#4412)
* MLS: send ext commit before sending ext proposals * add changelog and streamline test
1 parent 527e56d commit 4b077af

File tree

8 files changed

+350
-306
lines changed

8 files changed

+350
-306
lines changed

changelog.d/3-bug-fixes/WPB-15400

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MLS: when recreating external (backend) proposals, these are now propagated to
2+
the clients only after the corresponding external commit has been forwarded to
3+
the clients.

integration/test/Test/MLS/SubConversation.hs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,12 +338,38 @@ testResendingProposals = do
338338
leaveConv subConvId bob2
339339
leaveConv subConvId bob3
340340

341-
subConv <- getMLSConv subConvId
342-
withWebSockets (charlie1 : toList subConv.members) \wss -> do
343-
void $ createExternalCommit subConvId charlie1 Nothing >>= sendAndConsumeCommitBundle
341+
withWebSockets [alice1, alice2, charlie1] \[wsAlice1, wsAlice2, wsCharlie1] -> do
342+
void
343+
$ createExternalCommit subConvId charlie1 Nothing
344+
>>= postMLSCommitBundle charlie1
345+
. mkBundle
346+
>>= getJSON 201
347+
348+
-- increment epoch and add charlie1
349+
modifyMLSState $ \mls ->
350+
mls
351+
{ convs =
352+
Map.adjust
353+
( \conv' ->
354+
conv'
355+
{ epoch = conv'.epoch + 1,
356+
members = conv'.members <> conv'.newMembers,
357+
newMembers = mempty
358+
}
359+
)
360+
subConvId
361+
mls.convs
362+
}
344363

345364
-- consume proposals after backend resends them
346-
for_ wss \ws -> do
365+
for_ [wsAlice1, wsAlice2] $ \ws -> do
366+
commitMsg <- consumeMessage subConvId def (fromJust ws.client) Nothing ws
367+
commitMsg %. "message.content.sender" `shouldMatch` "NewMemberCommit"
368+
replicateM 3 do
369+
msg <- consumeMessage subConvId def (fromJust ws.client) Nothing ws
370+
msg %. "message.content.sender.External" `shouldMatchInt` 0
371+
void $ do
372+
let ws = wsCharlie1
347373
replicateM 3 do
348374
msg <- consumeMessage subConvId def (fromJust ws.client) Nothing ws
349375
msg %. "message.content.sender.External" `shouldMatchInt` 0

services/galley/src/Galley/API/MLS/Commit/ExternalCommit.hs

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ where
2323

2424
import Control.Comonad
2525
import Control.Lens (forOf_)
26+
import Control.Monad.Codensity
2627
import Data.Map qualified as Map
2728
import Data.Qualified
2829
import Data.Set qualified as Set
@@ -141,52 +142,56 @@ processExternalCommit ::
141142
Epoch ->
142143
ExternalCommitAction ->
143144
Maybe UpdatePath ->
144-
Sem r ()
145+
Codensity (Sem r) ()
145146
processExternalCommit senderIdentity lConvOrSub ciphersuite ciphersuiteUpdate epoch action updatePath = do
146147
let convOrSub = tUnqualified lConvOrSub
147148

148149
-- only members can join a subconversation
149150
forOf_ _SubConv convOrSub $ \(mlsConv, _) ->
150151
unless (isClientMember senderIdentity (mcMembers mlsConv)) $
151-
throwS @'MLSSubConvClientNotInParent
152+
lift $
153+
throwS @'MLSSubConvClientNotInParent
152154

153155
-- extract leaf node from update path and validate it
154156
leafNode <-
155157
(.leaf)
156-
<$> note
157-
(mlsProtocolError "External commits need an update path")
158-
updatePath
158+
<$> lift
159+
( note
160+
(mlsProtocolError "External commits need an update path")
161+
updatePath
162+
)
159163
let groupId = cnvmlsGroupId convOrSub.mlsMeta
160164
let extra = LeafNodeTBSExtraCommit groupId action.add
161165
case validateLeafNode ciphersuite (Just senderIdentity) extra leafNode.value of
162166
Left errMsg ->
163-
throw $
167+
lift . throw $
164168
mlsProtocolError ("Tried to add invalid LeafNode: " <> errMsg)
165169
Right _ -> pure ()
166170

167-
withCommitLock (fmap (.id) lConvOrSub) groupId epoch $ do
168-
executeExternalCommitAction lConvOrSub senderIdentity action
171+
withCommitLock (fmap (.id) lConvOrSub) groupId epoch
169172

170-
-- increment epoch number
171-
lConvOrSub' <- for lConvOrSub incrementEpoch
173+
lift $ executeExternalCommitAction lConvOrSub senderIdentity action
172174

173-
-- fetch backend remove proposals of the previous epoch
174-
indices0 <- getPendingBackendRemoveProposals groupId epoch
175+
-- increment epoch number
176+
lConvOrSub' <- for lConvOrSub $ lift . incrementEpoch
175177

176-
-- skip proposals for clients already removed by the external commit
177-
let indices = maybe id Set.delete action.remove indices0
178+
-- fetch backend remove proposals of the previous epoch
179+
indices0 <- lift $ getPendingBackendRemoveProposals groupId epoch
178180

179-
-- set cipher suite
180-
when ciphersuiteUpdate $ case convOrSub.id of
181-
Conv cid -> setConversationCipherSuite cid ciphersuite
182-
SubConv cid sub -> setSubConversationCipherSuite cid sub ciphersuite
181+
-- skip proposals for clients already removed by the external commit
182+
let indices = maybe id Set.delete action.remove indices0
183183

184-
-- requeue backend remove proposals for the current epoch
185-
createAndSendRemoveProposals
186-
lConvOrSub'
187-
indices
188-
(cidQualifiedUser senderIdentity)
189-
(tUnqualified lConvOrSub').members
184+
-- set cipher suite
185+
lift $ when ciphersuiteUpdate $ case convOrSub.id of
186+
Conv cid -> setConversationCipherSuite cid ciphersuite
187+
SubConv cid sub -> setSubConversationCipherSuite cid sub ciphersuite
188+
189+
-- requeue backend remove proposals for the current epoch
190+
createAndSendRemoveProposals
191+
lConvOrSub'
192+
(toList indices)
193+
(cidQualifiedUser senderIdentity)
194+
(tUnqualified lConvOrSub').members
190195

191196
executeExternalCommitAction ::
192197
forall r.

0 commit comments

Comments
 (0)