From b6e57c0fa26b0bcda3d622d10f52b8acaf1a8bd0 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 5 May 2024 13:11:30 +0100 Subject: [PATCH] core: fix connection failure because of race condition when connecting via link or joining group via invitation (#4133) * core: fix connection failure because of race condition when connecting via link or joining group via invitation * fix race for connection via contact address * simplexmq --- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- src/Simplex/Chat.hs | 37 ++++++++++++++++++++++++------------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/cabal.project b/cabal.project index e3a7628eb2..8e1335028f 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 8d8010a62aef2241fec3876fcfe57d51456b2bc0 + tag: ee8e4067b02c520a41b82ab972e262b24f58cd69 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 0b37e0eb20..6a0b2a5cff 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."8d8010a62aef2241fec3876fcfe57d51456b2bc0" = "0x7fq33c0x7i9jjp42la3zkha1wk6s3bv7dkz9z39a02s9rfkfla"; + "https://github.com/simplex-chat/simplexmq.git"."ee8e4067b02c520a41b82ab972e262b24f58cd69" = "1r59dpmb16gg5cc4kdxv1dfpl64ia093ki6mlfrdx6hz1bi01k02"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index a186649f72..8a3541af0a 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -1494,8 +1494,9 @@ processChatCommand' vr = \case Just (agentV, pqSup') -> do let chatV = agentToChatVersion agentV dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend - connId <- withAgent $ \a -> joinConnection a (aUserId user) True cReq dm pqSup' subMode + connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup' conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnJoined (incognitoProfile $> profileToSend) subMode chatV pqSup' + void . withAgent $ \a -> joinConnection a (aUserId user) (Just connId) True cReq dm pqSup' subMode pure $ CRSentConfirmation user conn APIConnect userId incognito (Just (ACR SCMContact cReq)) -> withUserId userId $ \user -> connectViaContact user incognito cReq APIConnect _ _ Nothing -> throwChatError CEInvalidConnReq @@ -1748,12 +1749,13 @@ processChatCommand' vr = \case Just Connection {peerChatVRange} -> do subMode <- chatReadVar subscriptionMode dm <- encodeConnInfo $ XGrpAcpt membershipMemId - agentConnId <- withAgent $ \a -> joinConnection a (aUserId user) True connRequest dm PQSupportOff subMode + agentConnId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connRequest PQSupportOff let chatV = vr `peerConnChatVersion` peerChatVRange withStore' $ \db -> do createMemberConnection db userId fromMember agentConnId chatV peerChatVRange subMode updateGroupMemberStatus db userId fromMember GSMemAccepted updateGroupMemberStatus db userId membership GSMemAccepted + void . withAgent $ \a -> joinConnection a (aUserId user) (Just agentConnId) True connRequest dm PQSupportOff subMode updateCIGroupInvitationStatus user g CIGISAccepted `catchChatError` \_ -> pure () pure $ CRUserAcceptedGroupSent user g {membership = membership {memberStatus = GSMemAccepted}} Nothing Nothing -> throwChatError $ CEContactNotActive ct @@ -2278,23 +2280,28 @@ processChatCommand' vr = \case where connect' groupLinkId cReqHash xContactId inGroup = do let pqSup = if inGroup then PQSupportOff else PQSupportOn - (connId, incognitoProfile, subMode, chatV) <- requestContact user incognito cReq xContactId inGroup pqSup + (connId, chatV) <- prepareContact user cReq pqSup + -- [incognito] generate profile to send + incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing + subMode <- chatReadVar subscriptionMode conn <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode chatV pqSup + joinContact user connId cReq incognitoProfile xContactId inGroup pqSup chatV pure $ CRSentInvitation user conn incognitoProfile connectContactViaAddress :: User -> IncognitoEnabled -> Contact -> ConnectionRequestUri 'CMContact -> CM ChatResponse connectContactViaAddress user incognito ct cReq = withInvitationLock "connectContactViaAddress" (strEncode cReq) $ do newXContactId <- XContactId <$> drgRandomBytes 16 let pqSup = PQSupportOn - (connId, incognitoProfile, subMode, chatV) <- requestContact user incognito cReq newXContactId False pqSup + (connId, chatV) <- prepareContact user cReq pqSup let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq + -- [incognito] generate profile to send + incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing + subMode <- chatReadVar subscriptionMode ct' <- withStore $ \db -> createAddressContactConnection db vr user ct connId cReqHash newXContactId incognitoProfile subMode chatV pqSup + joinContact user connId cReq incognitoProfile newXContactId False pqSup chatV pure $ CRSentInvitationToContact user ct' incognitoProfile - requestContact :: User -> IncognitoEnabled -> ConnectionRequestUri 'CMContact -> XContactId -> Bool -> PQSupport -> CM (ConnId, Maybe Profile, SubscriptionMode, VersionChat) - requestContact user incognito cReq xContactId inGroup pqSup = do - -- [incognito] generate profile to send - incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing - let profileToSend = userProfileToSend user incognitoProfile Nothing inGroup + prepareContact :: User -> ConnectionRequestUri 'CMContact -> PQSupport -> CM (ConnId, VersionChat) + prepareContact user cReq pqSup = do -- 0) toggle disabled - PQSupportOff -- 1) toggle enabled, address supports PQ (connRequestPQSupport returns Just True) - PQSupportOn, enable support with compression -- 2) toggle enabled, address doesn't support PQ - PQSupportOn but without compression, with version range indicating support @@ -2302,10 +2309,14 @@ processChatCommand' vr = \case Nothing -> throwChatError CEInvalidConnReq Just (agentV, _) -> do let chatV = agentToChatVersion agentV - dm <- encodeConnInfoPQ pqSup chatV (XContact profileToSend $ Just xContactId) - subMode <- chatReadVar subscriptionMode - connId <- withAgent $ \a -> joinConnection a (aUserId user) True cReq dm pqSup subMode - pure (connId, incognitoProfile, subMode, chatV) + connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup + pure (connId, chatV) + joinContact :: User -> ConnId -> ConnectionRequestUri 'CMContact -> Maybe Profile -> XContactId -> Bool -> PQSupport -> VersionChat -> CM () + joinContact user connId cReq incognitoProfile xContactId inGroup pqSup chatV = do + let profileToSend = userProfileToSend user incognitoProfile Nothing inGroup + dm <- encodeConnInfoPQ pqSup chatV (XContact profileToSend $ Just xContactId) + subMode <- chatReadVar subscriptionMode + void . withAgent $ \a -> joinConnection a (aUserId user) (Just connId) True cReq dm pqSup subMode contactMember :: Contact -> [GroupMember] -> Maybe GroupMember contactMember Contact {contactId} = find $ \GroupMember {memberContactId = cId, memberStatus = s} ->