Revert "core: relay key and member id as immutable relay link data (#6711)"

This reverts commit d665b1f5d8.
This commit is contained in:
spaced4ndy
2026-03-28 14:03:09 +04:00
parent d665b1f5d8
commit 30386178ec
29 changed files with 141 additions and 302 deletions
-1
View File
@@ -858,7 +858,6 @@ data ChatEvent
| CEvtReceivedGroupInvitation {user :: User, groupInfo :: GroupInfo, contact :: Contact, fromMemberRole :: GroupMemberRole, memberRole :: GroupMemberRole}
| CEvtUserJoinedGroup {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember}
| CEvtGroupLinkDataUpdated {user :: User, groupInfo :: GroupInfo, groupLink :: GroupLink, groupRelays :: [GroupRelay], relaysChanged :: Bool}
| CEvtGroupRelayUpdated {user :: User, groupInfo :: GroupInfo, member :: GroupMember, groupRelay :: GroupRelay}
| CEvtJoinedGroupMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember} -- there is the same command response
| CEvtJoinedGroupMemberConnecting {user :: User, groupInfo :: GroupInfo, hostMember :: GroupMember, member :: GroupMember}
| CEvtMemberAcceptedByOther {user :: User, groupInfo :: GroupInfo, acceptingMember :: GroupMember, member :: GroupMember}
+16 -15
View File
@@ -1841,7 +1841,7 @@ processChatCommand vr nm = \case
let userData = contactShortLinkData (userProfileDirect user incognitoProfile Nothing True) Nothing
userLinkData = UserInvLinkData userData
-- TODO [certs rcv]
(connId, (_, ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) Nothing True False SCMInvitation (Just userLinkData) Nothing IKPQOn subMode
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation (Just userLinkData) Nothing IKPQOn subMode
ccLink' <- shortenCreatedLink ccLink
-- TODO PQ pass minVersion from the current range
conn <- withFastStore' $ \db -> createDirectConnection db user connId ccLink' Nothing ConnNew incognitoProfile subMode initialChatVersion PQSupportOn
@@ -1883,7 +1883,7 @@ processChatCommand vr nm = \case
| short = Just $ UserInvLinkData $ contactShortLinkData (userProfileDirect newUser Nothing Nothing True) Nothing
| otherwise = Nothing
-- TODO [certs rcv]
(agConnId, (_, ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId newUser) Nothing True False SCMInvitation userLinkData_ Nothing IKPQOn subMode
(agConnId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId newUser) True False SCMInvitation userLinkData_ Nothing IKPQOn subMode
ccLink' <- shortenCreatedLink ccLink
conn' <- withFastStore' $ \db -> do
deleteConnectionRecord db user connId
@@ -2067,19 +2067,18 @@ processChatCommand vr nm = \case
_ -> False
connectToRelay gInfo' relayLink = do
gVar <- asks random
-- TODO [relays] member: set relay profile before/during connection
-- TODO - on fetching relay link data? (-> relay should add profile to relay link)
-- TODO - or update upon connection, as in regular prepared groups
-- TODO (current logic mimics insertHost_ from createPreparedGroup)
-- Save relayLink to re-use relay member record on retry (check by relayLink)
relayMember <- withFastStore $ \db -> getCreateRelayForMember db vr gVar user gInfo' relayLink
r <- tryAllErrors $ do
(fd@FixedLinkData {rootKey = relayKey, linkEntityId}, cData) <- getShortLinkConnReq nm user relayLink
relayLinkData_ <- liftIO $ decodeLinkUserData cData
case (relayLinkData_, linkEntityId) of
(Just RelayShortLinkData {relayProfile = p}, Just entityId) ->
withFastStore $ \db -> updateRelayMemberData db user relayMember (MemberId entityId) (MemberKey relayKey) p
_ -> throwChatError $ CEException "relay link: no relay link data or entity id"
(fd, _cData) <- getShortLinkConnReq nm user relayLink
let cReq = linkConnReq fd
relayLinkToConnect = CCLink cReq (Just relayLink)
void $ connectViaContact user (Just $ PCEGroup gInfo' relayMember) incognito relayLinkToConnect Nothing Nothing
-- Re-read member to get updated activeConn and updated data (from updateRelayMemberData)
-- Re-read member to get updated activeConn
relayMember' <- withFastStore $ \db -> getGroupMember db vr user groupId (groupMemberId' relayMember)
pure (relayLink, relayMember', r)
retryRelayConnectionAsync gInfo' relayLink relayMember@GroupMember {activeConn} = do
@@ -2087,7 +2086,7 @@ processChatCommand vr nm = \case
deleteAgentConnectionAsync $ aConnId conn
withStore' $ \db -> deleteConnectionRecord db user (dbConnId conn)
subMode <- chatReadVar subscriptionMode
newConnIds <- getAgentConnShortLinkAsync user CFGetRelayDataJoin Nothing relayLink
newConnIds <- getAgentConnShortLinkAsync user relayLink
withStore' $ \db -> createRelayMemberConnectionAsync db user gInfo' relayMember relayLink newConnIds subMode
GroupInfo {preparedGroup = Just PreparedGroup {connLinkToConnect, welcomeSharedMsgId, requestSharedMsgId}} -> do
hostMember <- withFastStore $ \db -> getHostMember db vr user groupId
@@ -2162,11 +2161,13 @@ processChatCommand vr nm = \case
Left e -> throwError $ ChatErrorStore e
Right _ -> throwError $ ChatErrorStore SEDuplicateContactLink
subMode <- chatReadVar subscriptionMode
-- TODO [relays] relay: add relay profile, identity, key to link data?
-- TODO [relays] relay: address creation
-- TODO - add relay key, identity to link data
-- TODO - validate short link is created (returned by agent)
let userData = contactShortLinkData (userProfileDirect user Nothing Nothing True) Nothing
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
-- TODO [certs rcv]
(connId, (_, ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) Nothing True True SCMContact (Just userLinkData) Nothing IKPQOn subMode
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) Nothing IKPQOn subMode
ccLink' <- shortenCreatedLink ccLink
let ccLink'' = if isTrue userChatRelay then createdRelayLink ccLink' else ccLink'
withFastStore $ \db -> createUserContactLink db user connId ccLink'' subMode
@@ -2432,7 +2433,7 @@ processChatCommand vr nm = \case
gVar <- asks random
subMode <- chatReadVar subscriptionMode
-- TODO [certs rcv]
(agentConnId, (_, CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) Nothing True False SCMInvitation Nothing Nothing IKPQOff subMode
(agentConnId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff subMode
member <- withFastStore $ \db -> createNewContactMember db gVar user gInfo contact memRole agentConnId cReq subMode
sendInvitation member cReq
pure $ CRSentGroupInvitation user gInfo contact member
@@ -2856,7 +2857,7 @@ processChatCommand vr nm = \case
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
crClientData = encodeJSON $ CRDataGroup groupLinkId
-- TODO [certs rcv]
(connId, (_, ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) Nothing True True SCMContact (Just userLinkData) (Just crClientData) IKPQOff subMode
(connId, (ccLink, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True True SCMContact (Just userLinkData) (Just crClientData) IKPQOff subMode
ccLink' <- createdGroupLink <$> shortenCreatedLink ccLink
gVar <- asks random
gLink <- withFastStore $ \db -> createGroupLink db gVar user gInfo connId ccLink' groupLinkId mRole subMode
@@ -2897,7 +2898,7 @@ processChatCommand vr nm = \case
subMode <- chatReadVar subscriptionMode
-- TODO PQ should negotitate contact connection with PQSupportOn?
-- TODO [certs rcv]
(connId, (_, CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) Nothing True False SCMInvitation Nothing Nothing IKPQOff subMode
(connId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True False SCMInvitation Nothing Nothing IKPQOff subMode
-- [incognito] reuse membership incognito profile
ct <- withFastStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode
void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing (Just epochStart)
+9 -8
View File
@@ -1032,7 +1032,7 @@ acceptBusinessJoinRequestAsync
-- TODO [short links] get updated business chat group and member? (currently not used)
pure (gInfo, clientMember)
acceptRelayJoinRequestAsync :: User -> Int64 -> GroupInfo -> GroupMember -> InvitationId -> VersionRangeChat -> ShortLinkContact -> CM (GroupInfo, GroupMember)
acceptRelayJoinRequestAsync :: User -> Int64 -> GroupInfo -> GroupMember -> InvitationId -> VersionRangeChat -> ShortLinkContact -> MemberKey -> CM (GroupInfo, GroupMember)
acceptRelayJoinRequestAsync
user
uclId
@@ -1040,8 +1040,9 @@ acceptRelayJoinRequestAsync
_ownerMember@GroupMember {groupMemberId}
cReqInvId
cReqChatVRange
relayLink = do
let msg = XGrpRelayAcpt relayLink
relayLink
memberKey = do
let msg = XGrpRelayAcpt relayLink memberKey
subMode <- chatReadVar subscriptionMode
vr <- chatVersionRange
let chatV = vr `peerConnChatVersion` cReqChatVRange
@@ -1322,7 +1323,7 @@ updatePublicGroupData user gInfo
pure gInfo'
| otherwise = pure gInfo
-- TODO [relays] owner: set owners on updating link data (multi-owner)
-- TODO [relays] owner: set owners on updating link data
groupLinkData :: GroupInfo -> GroupLink -> [GroupRelay] -> (UserConnLinkData 'CMContact, CRClientData)
groupLinkData gInfo@GroupInfo {groupProfile, groupSummary = GroupSummary {publicMemberCount}} GroupLink {groupLinkId} groupRelays =
let direct = not $ useRelays' gInfo
@@ -2447,11 +2448,11 @@ setAgentConnShortLinkAsync user conn@Connection {connId} userLinkData crClientDa
cmdId <- withStore' $ \db -> createCommand db user (Just connId) CFSetShortLink
withAgent $ \a -> setConnShortLinkAsync a (aCorrId cmdId) (aConnId conn) userLinkData crClientData_
getAgentConnShortLinkAsync :: User -> CommandFunction -> Maybe Connection -> ShortLinkContact -> CM (CommandId, ConnId)
getAgentConnShortLinkAsync user cmdFunc conn_ shortLink = do
getAgentConnShortLinkAsync :: User -> ShortLinkContact -> CM (CommandId, ConnId)
getAgentConnShortLinkAsync user shortLink = do
shortLink' <- restoreShortLink' shortLink
cmdId <- withStore' $ \db -> createCommand db user (dbConnId <$> conn_) cmdFunc
connId <- withAgent $ \a -> getConnShortLinkAsync a (aUserId user) (aCorrId cmdId) (aConnId <$> conn_) shortLink'
cmdId <- withStore' $ \db -> createCommand db user Nothing CFGetShortLink
connId <- withAgent $ \a -> getConnShortLinkAsync a (aUserId user) (aCorrId cmdId) shortLink'
pure (cmdId, connId)
agentXFTPDeleteRcvFile :: RcvFileId -> FileTransferId -> CM ()
+62 -76
View File
@@ -735,10 +735,13 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
-- [async agent commands] no continuation needed, but command should be asynchronous for stability
allowAgentConnectionAsync user conn' confId XOk
| otherwise -> messageError "x.grp.acpt: memberId is different from expected"
XGrpRelayAcpt relayLink
XGrpRelayAcpt relayLink memberKey
| memberRole' membership == GROwner && isRelay m -> do
withStore' $ \db -> setRelayLinkConfId db m confId relayLink
void $ getAgentConnShortLinkAsync user CFGetRelayDataAccept (Just conn') relayLink
withStore $ \db -> do
relay <- getGroupRelayByGMId db (groupMemberId' m)
liftIO $ updateGroupMemberStatus db userId m GSMemAccepted
void $ liftIO $ setRelayLinkAccepted db relay relayLink memberKey
allowAgentConnectionAsync user conn' confId XOk
| otherwise -> messageError "x.grp.relay.acpt: only owner can add relay"
_ -> messageError "CONF from invited member must have x.grp.acpt"
GCHostMember ->
@@ -863,9 +866,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
when (groupFeatureAllowed SGFHistory gInfo'' && not memberIsCustomer) $ sendHistory user gInfo'' m'
where
sendXGrpLinkMem gInfo'' = do
let incognitoProfile = ExistingIncognito <$> incognitoMembershipProfile gInfo''
let GroupInfo {membership = membership'} = gInfo''
incognitoProfile = ExistingIncognito <$> incognitoMembershipProfile gInfo''
profileToSend = userProfileInGroup user gInfo (fromIncognitoProfile <$> incognitoProfile)
void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend) groupId
memberKey = MemberKey <$> memberPubKey membership'
void $ sendDirectMemberMessage conn (XGrpLinkMem profileToSend memberKey) groupId
_ -> do
unless (memberPending m) $ withStore' $ \db -> updateGroupMemberStatus db userId m GSMemConnected
notifyMemberConnected gInfo m Nothing
@@ -963,7 +968,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
XFileCancel sharedMsgId -> xFileCancelGroup gInfo' (Just m'') sharedMsgId
XFileAcptInv sharedMsgId fileConnReq_ fName -> Nothing <$ xFileAcptInvGroup gInfo' m'' sharedMsgId fileConnReq_ fName
XInfo p -> fmap ctx <$> xInfoMember gInfo' m'' p msg brokerTs
XGrpLinkMem p -> Nothing <$ xGrpLinkMem gInfo' m'' conn' p
XGrpLinkMem p memberKey -> Nothing <$ xGrpLinkMem gInfo' m'' conn' p memberKey
XGrpLinkAcpt acceptance role memberId -> Nothing <$ xGrpLinkAcpt gInfo' m'' acceptance role memberId msg brokerTs
XGrpMemNew memInfo msgScope -> fmap ctx <$> xGrpMemNew gInfo' m'' memInfo msgScope msg brokerTs
XGrpMemIntro memInfo memRestrictions_ -> Nothing <$ xGrpMemIntro gInfo' m'' memInfo memRestrictions_
@@ -1083,53 +1088,30 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
forM_ mc_ $ \mc -> do
connReq_ <- withStore' $ \db -> getBusinessContactRequest db user groupId
sendGroupAutoReply mc connReq_
LDATA FixedLinkData {linkConnReq = cReq, rootKey = relayKey, linkEntityId} cData ->
LDATA FixedLinkData {linkConnReq = cReq} _cData ->
-- [async agent commands] CFGetConnShortLink continuation - join relay connection with resolved link
withCompletedCommand conn agentMsg $ \CommandData {cmdFunction} ->
case cmdFunction of
CFGetRelayDataJoin -> do
-- Update relay member with key, memberId and profile from link
relayLinkData_ <- liftIO $ decodeLinkUserData cData
case (relayLinkData_, linkEntityId) of
(Just RelayShortLinkData {relayProfile = p}, Just entityId) ->
withStore $ \db -> updateRelayMemberData db user m (MemberId entityId) (MemberKey relayKey) p
_ -> throwChatError $ CEException "relay link: no relay link data or entity id"
case cReq of
CRContactUri crData@ConnReqUriData {crClientData} -> do
let pqSup = PQSupportOff
lift (withAgent' $ \a -> connRequestPQSupport a pqSup cReq) >>= \case
Nothing -> throwChatError CEInvalidConnReq
Just (agentV, _) -> do
let chatV = agentToChatVersion agentV
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
cReqHash = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex}
-- Update connection with data derived from cReq, now available after getConnShortLinkAsync
withStore' $ \db -> updateConnLinkData db user conn cReq cReqHash groupLinkId chatV pqSup
let GroupMember {memberId = membershipMemId} = membership
incognitoProfile = fromLocalProfile <$> incognitoMembershipProfile gInfo
profileToSend = userProfileInGroup user gInfo incognitoProfile
memberPubKey <- case groupKeys gInfo of
Just GroupKeys {memberPrivKey} -> pure $ C.publicKey memberPrivKey
Nothing -> throwChatError $ CEInternalError "no group keys for channel membership"
dm <- encodeConnInfo $ XMember profileToSend membershipMemId (MemberKey memberPubKey)
subMode <- chatReadVar subscriptionMode
void $ joinAgentConnectionAsync user (Just conn) True cReq dm subMode
CFGetRelayDataAccept -> do
let GroupMember {memberId = MemberId expectedMemberId} = m
if linkEntityId == Just expectedMemberId
then do
relayProfile <- liftIO (decodeLinkUserData cData) >>= \case
Just RelayShortLinkData {relayProfile = p} -> pure p
Nothing -> throwChatError $ CEException "relay link: no relay link data"
(confId, m', relay) <- withStore $ \db -> do
confId <- getRelayConfId db m
liftIO $ updateGroupMemberStatus db userId m GSMemAccepted
(m', relay) <- setRelayLinkAccepted db vr user m (MemberKey relayKey) relayProfile
pure (confId, m', relay)
allowAgentConnectionAsync user conn confId XOk
toView $ CEvtGroupRelayUpdated user gInfo m' relay
else
-- TODO [relays] owner: TBC failed RelayStatus?
messageError "relay link: relay member ID mismatch"
CFGetShortLink -> case cReq of
CRContactUri crData@ConnReqUriData {crClientData} -> do
let pqSup = PQSupportOff
lift (withAgent' $ \a -> connRequestPQSupport a pqSup cReq) >>= \case
Nothing -> throwChatError CEInvalidConnReq
Just (agentV, _) -> do
let chatV = agentToChatVersion agentV
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
cReqHash = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex}
-- Update connection with data derived from cReq, now available after getConnShortLinkAsync
withStore' $ \db -> updateConnLinkData db user conn cReq cReqHash groupLinkId chatV pqSup
let GroupMember {memberId = membershipMemId} = membership
incognitoProfile = fromLocalProfile <$> incognitoMembershipProfile gInfo
profileToSend = userProfileInGroup user gInfo incognitoProfile
memberPubKey <- case groupKeys gInfo of
Just GroupKeys {memberPrivKey} -> pure $ C.publicKey memberPrivKey
Nothing -> throwChatError $ CEInternalError "no group keys for channel membership"
dm <- encodeConnInfo $ XMember profileToSend membershipMemId (MemberKey memberPubKey)
subMode <- chatReadVar subscriptionMode
void $ joinAgentConnectionAsync user (Just conn) True cReq dm subMode
_ -> throwChatError $ CECommandError "unexpected cmdFunction"
QCONT -> do
continued <- continueSending connEntity conn
@@ -2420,13 +2402,13 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
void $ processMemberProfileUpdate gInfo m p' (Just (msg, brokerTs))
pure $ memberEventDeliveryScope m
xGrpLinkMem :: GroupInfo -> GroupMember -> Connection -> Profile -> CM ()
xGrpLinkMem gInfo@GroupInfo {membership, businessChat} m@GroupMember {groupMemberId, memberCategory} Connection {viaGroupLink} p' = do
xGrpLinkMem :: GroupInfo -> GroupMember -> Connection -> Profile -> Maybe MemberKey -> CM ()
xGrpLinkMem gInfo@GroupInfo {membership, businessChat} m@GroupMember {groupMemberId, memberCategory} Connection {viaGroupLink} p' memberKey_ = do
xGrpLinkMemReceived <- withStore $ \db -> getXGrpLinkMemReceived db groupMemberId
if (viaGroupLink || isJust businessChat) && isNothing (memberContactId m) && memberCategory == GCHostMember && not xGrpLinkMemReceived
then do
m' <- processMemberProfileUpdate gInfo m p' Nothing
withStore' $ \db -> setXGrpLinkMemReceived db groupMemberId True
withStore' $ \db -> setXGrpLinkMemReceived db groupMemberId True memberKey_
let connectedIncognito = memberIncognito membership
probeMatchingMemberContact m' connectedIncognito
else messageError "x.grp.link.mem error: invalid group link host profile update"
@@ -3614,52 +3596,56 @@ runRelayRequestWorker a Worker {doWork} = do
-- Check if relay link already exists (recovery case)
case groupLink_ of
Right GroupLink {connLinkContact = CCLink _ sLnk_} ->
case sLnk_ of
Just sLnk -> acceptOwnerConnection rrd gInfo sLnk
Nothing -> throwChatError $ CEException "processRelayRequest: relay link doesn't have short link"
case (sLnk_, memberPubKey $ membership gInfo) of
(Just sLnk, Just k) -> acceptOwnerConnection rrd gInfo sLnk (MemberKey k)
(Nothing, _) -> throwChatError $ CEException "processRelayRequest: relay link doesn't have short link"
(_, Nothing) -> throwChatError $ CEException "processRelayRequest: no member key"
Left _ -> do
(gInfo', sLnk) <- getLinkDataCreateRelayLink rrd gInfo
acceptOwnerConnection rrd gInfo' sLnk
(gInfo', sLnk, memberKey) <- getLinkDataCreateRelayLink rrd gInfo
acceptOwnerConnection rrd gInfo' sLnk memberKey
where
getLinkDataCreateRelayLink :: RelayRequestData -> GroupInfo -> CM (GroupInfo, ShortLinkContact)
getLinkDataCreateRelayLink :: RelayRequestData -> GroupInfo -> CM (GroupInfo, ShortLinkContact, MemberKey)
getLinkDataCreateRelayLink RelayRequestData {reqGroupLink} gInfo = do
(FixedLinkData {linkEntityId, rootKey}, cData@(ContactLinkData _ UserContactData {owners})) <- getShortLinkConnReq NRMBackground user reqGroupLink
liftIO (decodeLinkUserData cData) >>= \case
Nothing -> throwChatError $ CEException "getLinkDataCreateRelayLink: no group link data"
Just GroupShortLinkData {groupProfile = gp} -> do
validateGroupProfile gp
((_, memberPrivKey), sLnk) <- createRelayLink gInfo
gVar <- asks random
(_, memberPrivKey) <- liftIO $ atomically $ C.generateKeyPair gVar
gInfo' <- withStore $ \db -> do
void $ updateGroupProfile db user gInfo gp
updateRelayGroupKeys db user gInfo linkEntityId rootKey memberPrivKey owners
getGroupInfo db vr user groupId
pure (gInfo', sLnk)
sLnk <- createRelayLink gInfo'
pure (gInfo', sLnk, MemberKey $ C.publicKey memberPrivKey)
where
validateGroupProfile :: GroupProfile -> CM ()
validateGroupProfile _groupProfile = do
-- TODO [relays] relay: validate group profile, verify owner's signature
pure ()
createRelayLink :: GroupInfo -> CM (C.KeyPairEd25519, ShortLinkContact)
createRelayLink gi = do
let GroupInfo {membership = relayMem} = gi
GroupMember {memberId = relayMemId, memberProfile = relayLP} = relayMem
MemberId relayMemberIdBS = relayMemId
userData = encodeShortLinkData $ RelayShortLinkData {relayProfile = fromLocalProfile relayLP}
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
createRelayLink :: GroupInfo -> CM ShortLinkContact
createRelayLink gi@GroupInfo {groupProfile} = do
-- TODO [relays] relay: set relay link data
-- TODO - link data: relay key for group, relay identity (profile, certificate, relay identity key)
-- TODO - starting role should be communicated in protocol from owner to relays
groupLinkId <- GroupLinkId <$> drgRandomBytes 16
subMode <- chatReadVar subscriptionMode
-- TODO [relays] starting role should be communicated in protocol from owner to relays
subRole <- asks $ channelSubscriberRole . config
let crClientData = encodeJSON $ CRDataGroup groupLinkId
(connId, (Just sigKeys, ccLink, _serviceId)) <- withAgent $ \a' -> createConnection a' NRMBackground (aUserId user) (Just relayMemberIdBS) True True SCMContact (Just userLinkData) (Just crClientData) CR.IKPQOff subMode
let userData = encodeShortLinkData $ GroupShortLinkData {groupProfile, publicGroupData = Nothing}
userLinkData = UserContactLinkData UserContactData {direct = True, owners = [], relays = [], userData}
crClientData = encodeJSON $ CRDataGroup groupLinkId
(connId, (ccLink, _serviceId)) <- withAgent $ \a' -> createConnection a' NRMBackground (aUserId user) True True SCMContact (Just userLinkData) (Just crClientData) CR.IKPQOff subMode
ccLink' <- createdGroupLink <$> shortenCreatedLink ccLink
sLnk <- case toShortLinkContact ccLink' of
Just sl -> pure sl
Nothing -> throwChatError $ CEException "failed to create relay link: no short link"
gVar <- asks random
void $ withFastStore $ \db -> createGroupLink db gVar user gi connId ccLink' groupLinkId subRole subMode
pure (sigKeys, sLnk)
acceptOwnerConnection :: RelayRequestData -> GroupInfo -> ShortLinkContact -> CM ()
acceptOwnerConnection RelayRequestData {relayInvId, reqChatVRange} gi relayLink = do
pure sLnk
acceptOwnerConnection :: RelayRequestData -> GroupInfo -> ShortLinkContact -> MemberKey -> CM ()
acceptOwnerConnection RelayRequestData {relayInvId, reqChatVRange} gi relayLink memberKey = do
ownerMember <- withStore $ \db -> getHostMember db vr user groupId
void $ acceptRelayJoinRequestAsync user uclId gi ownerMember relayInvId reqChatVRange relayLink
void $ acceptRelayJoinRequestAsync user uclId gi ownerMember relayInvId reqChatVRange relayLink memberKey
-- TODO [relays] relay: group invite accepted event, chat item (?)
pure ()
+8 -15
View File
@@ -432,10 +432,10 @@ data ChatMsgEvent (e :: MsgEncoding) where
XGrpAcpt :: MemberId -> ChatMsgEvent 'Json
XGrpLinkInv :: GroupLinkInvitation -> ChatMsgEvent 'Json
XGrpLinkReject :: GroupLinkRejection -> ChatMsgEvent 'Json
XGrpLinkMem :: Profile -> ChatMsgEvent 'Json
XGrpLinkMem :: Profile -> Maybe MemberKey -> ChatMsgEvent 'Json
XGrpLinkAcpt :: GroupAcceptance -> GroupMemberRole -> MemberId -> ChatMsgEvent 'Json
XGrpRelayInv :: GroupRelayInvitation -> ChatMsgEvent 'Json
XGrpRelayAcpt :: ShortLinkContact -> ChatMsgEvent 'Json
XGrpRelayAcpt :: ShortLinkContact -> MemberKey -> ChatMsgEvent 'Json
XGrpMemNew :: MemberInfo -> Maybe MsgScope -> ChatMsgEvent 'Json
XGrpMemIntro :: MemberInfo -> Maybe MemberRestrictions -> ChatMsgEvent 'Json
XGrpMemInv :: MemberId -> IntroInvitation -> ChatMsgEvent 'Json
@@ -1126,10 +1126,10 @@ toCMEventTag msg = case msg of
XGrpAcpt _ -> XGrpAcpt_
XGrpLinkInv _ -> XGrpLinkInv_
XGrpLinkReject _ -> XGrpLinkReject_
XGrpLinkMem _ -> XGrpLinkMem_
XGrpLinkMem _ _ -> XGrpLinkMem_
XGrpLinkAcpt {} -> XGrpLinkAcpt_
XGrpRelayInv _ -> XGrpRelayInv_
XGrpRelayAcpt _ -> XGrpRelayAcpt_
XGrpRelayAcpt _ _ -> XGrpRelayAcpt_
XGrpMemNew {} -> XGrpMemNew_
XGrpMemIntro _ _ -> XGrpMemIntro_
XGrpMemInv _ _ -> XGrpMemInv_
@@ -1278,10 +1278,10 @@ appJsonToCM AppMessageJson {v, msgId, event, params} = do
XGrpAcpt_ -> XGrpAcpt <$> p "memberId"
XGrpLinkInv_ -> XGrpLinkInv <$> p "groupLinkInvitation"
XGrpLinkReject_ -> XGrpLinkReject <$> p "groupLinkRejection"
XGrpLinkMem_ -> XGrpLinkMem <$> p "profile"
XGrpLinkMem_ -> XGrpLinkMem <$> p "profile" <*> opt "memberKey"
XGrpLinkAcpt_ -> XGrpLinkAcpt <$> p "acceptance" <*> p "role" <*> p "memberId"
XGrpRelayInv_ -> XGrpRelayInv <$> p "groupRelayInvitation"
XGrpRelayAcpt_ -> XGrpRelayAcpt <$> p "relayLink"
XGrpRelayAcpt_ -> XGrpRelayAcpt <$> p "relayLink" <*> p "memberKey"
XGrpMemNew_ -> XGrpMemNew <$> p "memberInfo" <*> opt "scope"
XGrpMemIntro_ -> XGrpMemIntro <$> p "memberInfo" <*> opt "memberRestrictions"
XGrpMemInv_ -> XGrpMemInv <$> p "memberId" <*> p "memberIntro"
@@ -1345,10 +1345,10 @@ chatToAppMessage chatMsg@ChatMessage {chatVRange, msgId, chatMsgEvent} = case en
XGrpAcpt memId -> o ["memberId" .= memId]
XGrpLinkInv groupLinkInv -> o ["groupLinkInvitation" .= groupLinkInv]
XGrpLinkReject groupLinkRjct -> o ["groupLinkRejection" .= groupLinkRjct]
XGrpLinkMem profile -> o ["profile" .= profile]
XGrpLinkMem profile memberKey -> o $ ("memberKey" .=? memberKey) ["profile" .= profile]
XGrpLinkAcpt acceptance role memberId -> o ["acceptance" .= acceptance, "role" .= role, "memberId" .= memberId]
XGrpRelayInv groupRelayInv -> o ["groupRelayInvitation" .= groupRelayInv]
XGrpRelayAcpt relayLink -> o ["relayLink" .= relayLink]
XGrpRelayAcpt relayLink memberKey -> o ["relayLink" .= relayLink, "memberKey" .= memberKey]
XGrpMemNew memInfo scope -> o $ ("scope" .=? scope) ["memberInfo" .= memInfo]
XGrpMemIntro memInfo memRestrictions -> o $ ("memberRestrictions" .=? memRestrictions) ["memberInfo" .= memInfo]
XGrpMemInv memId memIntro -> o ["memberId" .= memId, "memberIntro" .= memIntro]
@@ -1436,10 +1436,3 @@ $(JQ.deriveJSON defaultJSON ''PublicGroupData)
$(JQ.deriveJSON defaultJSON ''GroupShortLinkData)
data RelayShortLinkData = RelayShortLinkData
{ relayProfile :: Profile
}
deriving (Show)
$(JQ.deriveJSON defaultJSON ''RelayShortLinkData)
+14 -64
View File
@@ -88,9 +88,6 @@ module Simplex.Chat.Store.Groups
updateRelayStatus,
updateRelayStatusFromTo,
setRelayLinkAccepted,
setRelayLinkConfId,
getRelayConfId,
updateRelayMemberData,
setGroupInProgressDone,
createRelayRequestGroup,
updateRelayOwnStatusFromTo,
@@ -200,7 +197,7 @@ import Simplex.Chat.Types.MemberRelations (IntroductionDirection (..), MemberRel
import Simplex.Chat.Types.Preferences
import Simplex.Chat.Types.Shared
import Simplex.Chat.Types.UITheme
import Simplex.Messaging.Agent.Protocol (ConfirmationId, ConnId, CreatedConnLink (..), InvitationId, OwnerAuth (..), UserId)
import Simplex.Messaging.Agent.Protocol (ConnId, CreatedConnLink (..), InvitationId, OwnerAuth (..), UserId)
import Simplex.Messaging.Agent.Store.AgentStore (firstRow, fromOnlyBI, maybeFirstRow)
import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..))
import Simplex.Messaging.Agent.Store.Entity (DBEntityId)
@@ -1420,74 +1417,26 @@ updateRelayStatus_ db relayId relayStatus = do
currentTs <- getCurrentTime
DB.execute db "UPDATE group_relays SET relay_status = ?, updated_at = ? WHERE group_relay_id = ?" (relayStatus, currentTs, relayId)
setRelayLinkAccepted :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> MemberKey -> Profile -> ExceptT StoreError IO (GroupMember, GroupRelay)
setRelayLinkAccepted db vr user m (MemberKey relayKey) profile = do
let gmId = groupMemberId' m
currentTs <- liftIO getCurrentTime
liftIO $ DB.execute
db
[sql|
UPDATE group_relays
SET relay_status = ?, updated_at = ?
WHERE group_member_id = ?
|]
(RSAccepted, currentTs, gmId)
liftIO $ DB.execute
db
[sql|
UPDATE group_members
SET member_pub_key = ?, updated_at = ?
WHERE group_member_id = ?
|]
(relayKey, currentTs, gmId)
void $ updateMemberProfile db user m profile
(,) <$> getGroupMemberById db vr user gmId <*> getGroupRelayByGMId db gmId
setRelayLinkConfId :: DB.Connection -> GroupMember -> ConfirmationId -> ShortLinkContact -> IO ()
setRelayLinkConfId db m confId relayLink = do
setRelayLinkAccepted :: DB.Connection -> GroupRelay -> ShortLinkContact -> MemberKey -> IO GroupRelay
setRelayLinkAccepted db relay@GroupRelay {groupRelayId, groupMemberId} relayLink (MemberKey k) = do
currentTs <- getCurrentTime
DB.execute
db
[sql|
UPDATE group_relays
SET conf_id = ?, relay_link = ?, updated_at = ?
WHERE group_member_id = ?
SET relay_link = ?, relay_status = ?, updated_at = ?
WHERE group_relay_id = ?
|]
(confId, relayLink, currentTs, groupMemberId' m)
(relayLink, RSAccepted, currentTs, groupRelayId)
DB.execute
db
[sql|
UPDATE group_members
SET relay_link = ?, updated_at = ?
SET relay_link = ?, member_pub_key = ?, updated_at = ?
WHERE group_member_id = ?
|]
(relayLink, currentTs, groupMemberId' m)
getRelayConfId :: DB.Connection -> GroupMember -> ExceptT StoreError IO ConfirmationId
getRelayConfId db m =
ExceptT . firstRow fromOnly (SEGroupRelayNotFoundByMemberId $ groupMemberId' m) $
DB.query
db
[sql|
SELECT conf_id
FROM group_relays
WHERE group_member_id = ? AND conf_id IS NOT NULL
|]
(Only (groupMemberId' m))
updateRelayMemberData :: DB.Connection -> User -> GroupMember -> MemberId -> MemberKey -> Profile -> ExceptT StoreError IO ()
updateRelayMemberData db user m memberId (MemberKey relayKey) profile = do
currentTs <- liftIO getCurrentTime
liftIO $
DB.execute
db
[sql|
UPDATE group_members
SET member_id = ?, member_pub_key = ?, updated_at = ?
WHERE group_member_id = ?
|]
(memberId, relayKey, currentTs, groupMemberId' m)
void $ updateMemberProfile db user m profile
(relayLink, k, currentTs, groupMemberId)
pure relay {relayStatus = RSAccepted, relayLink = Just relayLink}
setGroupInProgressDone :: DB.Connection -> GroupInfo -> IO ()
setGroupInProgressDone db GroupInfo {groupId} = do
@@ -2902,13 +2851,14 @@ getXGrpLinkMemReceived db mId =
ExceptT . firstRow fromOnlyBI (SEGroupMemberNotFound mId) $
DB.query db "SELECT xgrplinkmem_received FROM group_members WHERE group_member_id = ?" (Only mId)
setXGrpLinkMemReceived :: DB.Connection -> GroupMemberId -> Bool -> IO ()
setXGrpLinkMemReceived db mId xGrpLinkMemReceived = do
setXGrpLinkMemReceived :: DB.Connection -> GroupMemberId -> Bool -> Maybe MemberKey -> IO ()
setXGrpLinkMemReceived db mId xGrpLinkMemReceived memberKey_ = do
currentTs <- getCurrentTime
let k = (\(MemberKey k') -> k') <$> memberKey_
DB.execute
db
"UPDATE group_members SET xgrplinkmem_received = ?, updated_at = ? WHERE group_member_id = ?"
(BI xGrpLinkMemReceived, currentTs, mId)
"UPDATE group_members SET xgrplinkmem_received = ?, member_pub_key = ?, updated_at = ? WHERE group_member_id = ?"
(BI xGrpLinkMemReceived, k, currentTs, mId)
createNewUnknownGroupMember :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> MemberId -> Text -> GroupMemberRole -> ExceptT StoreError IO GroupMember
createNewUnknownGroupMember db vr user@User {userId, userContactId} GroupInfo {groupId} memberId memberName unknownMemberRole = do
@@ -54,7 +54,6 @@ CREATE TABLE group_relays(
chat_relay_id BIGINT NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
relay_status TEXT NOT NULL,
relay_link BYTEA,
conf_id BYTEA,
created_at TEXT NOT NULL DEFAULT (now()),
updated_at TEXT NOT NULL DEFAULT (now())
);
@@ -5,6 +5,10 @@ module Simplex.Chat.Store.SQLite.Migrations.M20250813_delivery_tasks where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
-- TODO [relays] add later in new migration for MemberProfileUpdate delivery jobs:
-- TODO - ALTER TABLE group_members ADD COLUMN last_profile_delivery_ts TEXT;
-- TODO - ALTER TABLE group_members ADD COLUMN join_ts TEXT;
-- How columns correspond to types:
-- both tables:
@@ -67,7 +67,6 @@ CREATE TABLE group_relays(
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
relay_status TEXT NOT NULL,
relay_link BLOB,
conf_id BLOB,
created_at TEXT NOT NULL DEFAULT(datetime('now')),
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
) STRICT;
@@ -3472,14 +3472,6 @@ Query:
Plan:
SEARCH commands USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT conf_id
FROM group_relays
WHERE group_member_id = ? AND conf_id IS NOT NULL
Plan:
SEARCH group_relays USING INDEX idx_group_relays_group_member_id (group_member_id=?)
Query:
SELECT connection_id, agent_conn_id, conn_level, via_contact, via_user_contact_link, via_group_link, group_link_id, xcontact_id, custom_user_profile_id,
conn_status, conn_type, contact_conn_initiated, local_alias, contact_id, group_member_id, user_contact_link_id,
@@ -3919,14 +3911,6 @@ Query:
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET member_id = ?, member_pub_key = ?, updated_at = ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET member_relations_vector = ?
@@ -4895,14 +4879,6 @@ Query:
Plan:
SEARCH contacts USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET member_pub_key = ?, updated_at = ?
WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET member_relations_vector = ?, updated_at = ?
@@ -4945,7 +4921,7 @@ SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET relay_link = ?, updated_at = ?
SET relay_link = ?, member_pub_key = ?, updated_at = ?
WHERE group_member_id = ?
Plan:
@@ -4988,19 +4964,11 @@ SEARCH groups USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_relays
SET conf_id = ?, relay_link = ?, updated_at = ?
WHERE group_member_id = ?
SET relay_link = ?, relay_status = ?, updated_at = ?
WHERE group_relay_id = ?
Plan:
SEARCH group_relays USING INDEX idx_group_relays_group_member_id (group_member_id=?)
Query:
UPDATE group_relays
SET relay_status = ?, updated_at = ?
WHERE group_member_id = ?
Plan:
SEARCH group_relays USING INDEX idx_group_relays_group_member_id (group_member_id=?)
SEARCH group_relays USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_snd_item_statuses
@@ -6991,7 +6959,7 @@ Query: UPDATE group_members SET support_chat_ts = ? WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query: UPDATE group_members SET xgrplinkmem_received = ?, updated_at = ? WHERE group_member_id = ?
Query: UPDATE group_members SET xgrplinkmem_received = ?, member_pub_key = ?, updated_at = ? WHERE group_member_id = ?
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
@@ -765,7 +765,6 @@ CREATE TABLE group_relays(
chat_relay_id INTEGER NOT NULL REFERENCES chat_relays ON DELETE CASCADE,
relay_status TEXT NOT NULL,
relay_link BLOB,
conf_id BLOB,
created_at TEXT NOT NULL DEFAULT(datetime('now')),
updated_at TEXT NOT NULL DEFAULT(datetime('now'))
) STRICT;
+5 -8
View File
@@ -121,6 +121,7 @@ instance ToField AgentUserId where toField (AgentUserId uId) = toField uId
aUserId :: User -> UserId
aUserId User {agentUserId = AgentUserId uId} = uId
-- TODO [relays] filter out chat relay users where necessary (e.g. loading list of users for UI)
data User = User
{ userId :: UserId,
agentUserId :: AgentUserId,
@@ -1855,8 +1856,7 @@ data CommandFunction
| CFAckMessage -- not used
| CFDeleteConn -- not used
| CFSetShortLink
| CFGetRelayDataJoin
| CFGetRelayDataAccept
| CFGetShortLink
deriving (Eq, Show)
instance FromField CommandFunction where fromField = fromTextField_ textDecode
@@ -1875,8 +1875,7 @@ instance TextEncoding CommandFunction where
"ack_message" -> Just CFAckMessage
"delete_conn" -> Just CFDeleteConn
"set_short_link" -> Just CFSetShortLink
"get_relay_data_join" -> Just CFGetRelayDataJoin
"get_relay_data_accept" -> Just CFGetRelayDataAccept
"get_short_link" -> Just CFGetShortLink
_ -> Nothing
textEncode = \case
CFCreateConnGrpMemInv -> "create_conn"
@@ -1889,8 +1888,7 @@ instance TextEncoding CommandFunction where
CFAckMessage -> "ack_message"
CFDeleteConn -> "delete_conn"
CFSetShortLink -> "set_short_link"
CFGetRelayDataJoin -> "get_relay_data_join"
CFGetRelayDataAccept -> "get_relay_data_accept"
CFGetShortLink -> "get_short_link"
commandExpectedResponse :: CommandFunction -> AEvtTag
commandExpectedResponse = \case
@@ -1904,8 +1902,7 @@ commandExpectedResponse = \case
CFAckMessage -> t OK_
CFDeleteConn -> t OK_
CFSetShortLink -> t LINK_
CFGetRelayDataJoin -> t LDATA_
CFGetRelayDataAccept -> t LDATA_
CFGetShortLink -> t LDATA_
where
t = AEvtTag SAEConn
+1 -2
View File
@@ -474,7 +474,6 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
CEvtGroupLinkDataUpdated u g groupLink relays relaysChanged
| relaysChanged -> ttyUser u $ viewGroupLinkRelaysUpdated g groupLink relays
| otherwise -> []
CEvtGroupRelayUpdated {} -> []
CEvtJoinedGroupMember u g m -> ttyUser u $ viewJoinedGroupMember g m
CEvtHostConnected p h -> [plain $ "connected to " <> viewHostEvent p h]
CEvtHostDisconnected p h -> [plain $ "disconnected from " <> viewHostEvent p h]
@@ -2063,7 +2062,7 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case
GLPOk groupSLinkInfo_ groupSLinkData ->
let direct = maybe True (\(GroupShortLinkInfo {direct = d}) -> d) groupSLinkInfo_
in [grpLink $ if direct then "ok to connect directly" else "ok to connect via relays"]
<> [viewJSON groupSLinkData | testView]
<> [viewJSON groupSLinkData] -- | testView] -- TODO [relays] disable link data output in cli (uncomment testView)
GLPOwnLink g -> [grpLink "own link for group " <> ttyGroup' g]
GLPConnectingConfirmReconnect -> [grpLink "connecting, allowed to reconnect"]
GLPConnectingProhibit Nothing -> [grpLink "connecting"]