From da01adb212da59ffd8f4b13b9582a1523c730cc5 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:26:28 +0400 Subject: [PATCH 1/7] agent: async command to set connection short link (setConnShortLinkAsync) --- src/Simplex/Messaging/Agent.hs | 15 +++++++++++++++ src/Simplex/Messaging/Agent/Protocol.hs | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index 3130e0227..a10868653 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -49,6 +49,7 @@ module Simplex.Messaging.Agent deleteUser, connRequestPQSupport, createConnectionAsync, + setConnShortLinkAsync, joinConnectionAsync, allowConnectionAsync, acceptContactAsync, @@ -345,6 +346,11 @@ createConnectionAsync :: ConnectionModeI c => AgentClient -> UserId -> ACorrId - createConnectionAsync c userId aCorrId enableNtfs = withAgentEnv c .:. newConnAsync c userId aCorrId enableNtfs {-# INLINE createConnectionAsync #-} +-- | Create or update user's contact connection short link asynchronously, synchronous response is the short link +setConnShortLinkAsync :: AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AE () +setConnShortLinkAsync c = withAgentEnv c .::. setConnShortLinkAsync' c +{-# INLINE setConnShortLinkAsync #-} + -- | Join SMP agent connection (JOIN command) asynchronously, synchronous response is new connection id joinConnectionAsync :: AgentClient -> UserId -> ACorrId -> Bool -> ConnectionRequestUri c -> ConnInfo -> PQSupport -> SubscriptionMode -> AE ConnId joinConnectionAsync c userId aCorrId enableNtfs = withAgentEnv c .:: joinConnAsync c userId aCorrId enableNtfs @@ -886,6 +892,11 @@ checkClientNotices AgentClient {clientNotices, presetServers} (ProtoServerWithAu when (maybe True (ts <) expires_) $ throwError NOTICE {server = safeDecodeUtf8 $ strEncode $ L.head host, preset = isNothing srvKey, expiresAt = roundedToUTCTime <$> expires_} +setConnShortLinkAsync' :: AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AM () +setConnShortLinkAsync' c corrId connId cMode userLinkData clientData = + -- enqueue command SLINK + undefined + setConnShortLink' :: AgentClient -> NetworkRequestMode -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AM (ConnShortLink c) setConnShortLink' c nm connId cMode userLinkData clientData = withConnLock c connId "setConnShortLink" $ do @@ -1657,6 +1668,10 @@ runCommandProcessing c@AgentClient {subQ} connId server_ Worker {doWork} = do tryCommand . withNextSrv c userId storageSrvs triedHosts [] $ \srv -> do (CCLink cReq _, service) <- newRcvConnSrv c NRMBackground userId connId enableNtfs cMode Nothing Nothing pqEnc subMode srv notify $ INV (ACR cMode cReq) service + SLINK {} -> + -- create link (reuse setConnShortLink') + -- notify - LINK + undefined JOIN enableNtfs (ACR _ cReq@(CRInvitationUri ConnReqUriData {crSmpQueues = q :| _} _)) pqEnc subMode connInfo -> noServer $ do triedHosts <- newTVarIO S.empty tryCommand . withNextSrv c userId storageSrvs triedHosts [qServer q] $ \srv -> do diff --git a/src/Simplex/Messaging/Agent/Protocol.hs b/src/Simplex/Messaging/Agent/Protocol.hs index 05ebc1b27..4946fd8b0 100644 --- a/src/Simplex/Messaging/Agent/Protocol.hs +++ b/src/Simplex/Messaging/Agent/Protocol.hs @@ -382,6 +382,7 @@ type SndQueueSecured = Bool -- | Parameterized type for SMP agent events data AEvent (e :: AEntity) where INV :: AConnectionRequestUri -> Maybe ClientServiceId -> AEvent AEConn + LINK :: AConnShortLink -> AEvent AEConn CONF :: ConfirmationId -> PQSupport -> [SMPServer] -> ConnInfo -> AEvent AEConn -- ConnInfo is from sender, [SMPServer] will be empty only in v1 handshake REQ :: InvitationId -> PQSupport -> NonEmpty SMPServer -> ConnInfo -> AEvent AEConn -- ConnInfo is from sender INFO :: PQSupport -> ConnInfo -> AEvent AEConn @@ -435,6 +436,7 @@ deriving instance Show AEvtTag data ACommand = NEW Bool AConnectionMode InitialKeys SubscriptionMode -- response INV + | SLINK AConnectionMode AUserConnLinkData (Maybe CRClientData) -- response LINK | JOIN Bool AConnectionRequestUri PQSupport SubscriptionMode ConnInfo | LET ConfirmationId ConnInfo -- ConnInfo is from client | ACK AgentMsgId (Maybe MsgReceiptInfo) @@ -444,6 +446,7 @@ data ACommand data ACommandTag = NEW_ + | SLINK_ | JOIN_ | LET_ | ACK_ @@ -453,6 +456,7 @@ data ACommandTag data AEventTag (e :: AEntity) where INV_ :: AEventTag AEConn + LINK_ :: AEventTag AEConn CONF_ :: AEventTag AEConn REQ_ :: AEventTag AEConn INFO_ :: AEventTag AEConn @@ -499,6 +503,7 @@ deriving instance Show (AEventTag e) aCommandTag :: ACommand -> ACommandTag aCommandTag = \case NEW {} -> NEW_ + SLINK {} -> SLINK_ JOIN {} -> JOIN_ LET {} -> LET_ ACK {} -> ACK_ @@ -508,6 +513,7 @@ aCommandTag = \case aEventTag :: AEvent e -> AEventTag e aEventTag = \case INV {} -> INV_ + LINK _ -> LINK_ CONF {} -> CONF_ REQ {} -> REQ_ INFO {} -> INFO_ @@ -1712,6 +1718,8 @@ data UserConnLinkData c where UserInvLinkData :: UserLinkData -> UserConnLinkData 'CMInvitation UserContactLinkData :: UserContactData -> UserConnLinkData 'CMContact +data AUserConnLinkData = forall m. ConnectionModeI m => AUCLD (SConnectionMode m) (UserConnLinkData m) + linkUserData :: ConnLinkData c -> UserLinkData linkUserData = \case InvitationLinkData _ d -> d @@ -1777,6 +1785,10 @@ instance Encoding AConnLinkData where let cd = UserContactData {direct, owners, relays, userData} pure $ ACLD SCMContact $ ContactLinkData vr cd +instance Encoding AUserConnLinkData where + smpEncode = undefined + smpP = undefined + instance Encoding UserLinkData where smpEncode (UserLinkData s) = if B.length s <= 254 then smpEncode s else smpEncode ('\255', Large s) {-# INLINE smpEncode #-} @@ -1976,6 +1988,7 @@ instance StrEncoding ACommandTag where strP = A.takeTill (== ' ') >>= \case "NEW" -> pure NEW_ + "SLINK" -> pure SLINK_ "JOIN" -> pure JOIN_ "LET" -> pure LET_ "ACK" -> pure ACK_ @@ -1984,6 +1997,7 @@ instance StrEncoding ACommandTag where _ -> fail "bad ACommandTag" strEncode = \case NEW_ -> "NEW" + SLINK_ -> "SLINK" JOIN_ -> "JOIN" LET_ -> "LET" ACK_ -> "ACK" @@ -1995,6 +2009,7 @@ commandP binaryP = strP >>= \case NEW_ -> s (NEW <$> strP_ <*> strP_ <*> pqIKP <*> (strP <|> pure SMP.SMSubscribe)) + SLINK_ -> undefined JOIN_ -> s (JOIN <$> strP_ <*> strP_ <*> pqSupP <*> (strP_ <|> pure SMP.SMSubscribe) <*> binaryP) LET_ -> s (LET <$> A.takeTill (== ' ') <* A.space <*> binaryP) ACK_ -> s (ACK <$> A.decimal <*> optional (A.space *> binaryP)) @@ -2012,6 +2027,7 @@ commandP binaryP = serializeCommand :: ACommand -> ByteString serializeCommand = \case NEW ntfs cMode pqIK subMode -> s (NEW_, ntfs, cMode, pqIK, subMode) + SLINK {} -> undefined JOIN ntfs cReq pqSup subMode cInfo -> s (JOIN_, ntfs, cReq, pqSup, subMode, Str $ serializeBinary cInfo) LET confId cInfo -> B.unwords [s LET_, confId, serializeBinary cInfo] ACK mId rcptInfo_ -> s (ACK_, mId) <> maybe "" (B.cons ' ' . serializeBinary) rcptInfo_ From eca591123f94159131fba699be1a5345dafe9729 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 19 Dec 2025 13:30:54 +0400 Subject: [PATCH 2/7] rename --- src/Simplex/Messaging/Agent.hs | 6 +++--- src/Simplex/Messaging/Agent/Protocol.hs | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index a10868653..66ac7893b 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -346,7 +346,7 @@ createConnectionAsync :: ConnectionModeI c => AgentClient -> UserId -> ACorrId - createConnectionAsync c userId aCorrId enableNtfs = withAgentEnv c .:. newConnAsync c userId aCorrId enableNtfs {-# INLINE createConnectionAsync #-} --- | Create or update user's contact connection short link asynchronously, synchronous response is the short link +-- | Create or update user's contact connection short link (LSET command) asynchronously, no synchronous response setConnShortLinkAsync :: AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AE () setConnShortLinkAsync c = withAgentEnv c .::. setConnShortLinkAsync' c {-# INLINE setConnShortLinkAsync #-} @@ -894,7 +894,7 @@ checkClientNotices AgentClient {clientNotices, presetServers} (ProtoServerWithAu setConnShortLinkAsync' :: AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AM () setConnShortLinkAsync' c corrId connId cMode userLinkData clientData = - -- enqueue command SLINK + -- enqueue command LSET undefined setConnShortLink' :: AgentClient -> NetworkRequestMode -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AM (ConnShortLink c) @@ -1668,7 +1668,7 @@ runCommandProcessing c@AgentClient {subQ} connId server_ Worker {doWork} = do tryCommand . withNextSrv c userId storageSrvs triedHosts [] $ \srv -> do (CCLink cReq _, service) <- newRcvConnSrv c NRMBackground userId connId enableNtfs cMode Nothing Nothing pqEnc subMode srv notify $ INV (ACR cMode cReq) service - SLINK {} -> + LSET {} -> -- create link (reuse setConnShortLink') -- notify - LINK undefined diff --git a/src/Simplex/Messaging/Agent/Protocol.hs b/src/Simplex/Messaging/Agent/Protocol.hs index 4946fd8b0..525ebfeb2 100644 --- a/src/Simplex/Messaging/Agent/Protocol.hs +++ b/src/Simplex/Messaging/Agent/Protocol.hs @@ -436,7 +436,7 @@ deriving instance Show AEvtTag data ACommand = NEW Bool AConnectionMode InitialKeys SubscriptionMode -- response INV - | SLINK AConnectionMode AUserConnLinkData (Maybe CRClientData) -- response LINK + | LSET AConnectionMode AUserConnLinkData (Maybe CRClientData) -- response LINK | JOIN Bool AConnectionRequestUri PQSupport SubscriptionMode ConnInfo | LET ConfirmationId ConnInfo -- ConnInfo is from client | ACK AgentMsgId (Maybe MsgReceiptInfo) @@ -446,7 +446,7 @@ data ACommand data ACommandTag = NEW_ - | SLINK_ + | LSET_ | JOIN_ | LET_ | ACK_ @@ -503,7 +503,7 @@ deriving instance Show (AEventTag e) aCommandTag :: ACommand -> ACommandTag aCommandTag = \case NEW {} -> NEW_ - SLINK {} -> SLINK_ + LSET {} -> LSET_ JOIN {} -> JOIN_ LET {} -> LET_ ACK {} -> ACK_ @@ -1988,7 +1988,7 @@ instance StrEncoding ACommandTag where strP = A.takeTill (== ' ') >>= \case "NEW" -> pure NEW_ - "SLINK" -> pure SLINK_ + "LSET" -> pure LSET_ "JOIN" -> pure JOIN_ "LET" -> pure LET_ "ACK" -> pure ACK_ @@ -1997,7 +1997,7 @@ instance StrEncoding ACommandTag where _ -> fail "bad ACommandTag" strEncode = \case NEW_ -> "NEW" - SLINK_ -> "SLINK" + LSET_ -> "LSET" JOIN_ -> "JOIN" LET_ -> "LET" ACK_ -> "ACK" @@ -2009,7 +2009,7 @@ commandP binaryP = strP >>= \case NEW_ -> s (NEW <$> strP_ <*> strP_ <*> pqIKP <*> (strP <|> pure SMP.SMSubscribe)) - SLINK_ -> undefined + LSET_ -> undefined JOIN_ -> s (JOIN <$> strP_ <*> strP_ <*> pqSupP <*> (strP_ <|> pure SMP.SMSubscribe) <*> binaryP) LET_ -> s (LET <$> A.takeTill (== ' ') <* A.space <*> binaryP) ACK_ -> s (ACK <$> A.decimal <*> optional (A.space *> binaryP)) @@ -2027,7 +2027,7 @@ commandP binaryP = serializeCommand :: ACommand -> ByteString serializeCommand = \case NEW ntfs cMode pqIK subMode -> s (NEW_, ntfs, cMode, pqIK, subMode) - SLINK {} -> undefined + LSET {} -> undefined JOIN ntfs cReq pqSup subMode cInfo -> s (JOIN_, ntfs, cReq, pqSup, subMode, Str $ serializeBinary cInfo) LET confId cInfo -> B.unwords [s LET_, confId, serializeBinary cInfo] ACK mId rcptInfo_ -> s (ACK_, mId) <> maybe "" (B.cons ' ' . serializeBinary) rcptInfo_ From f736b02cb7a2d356e43f90633f7fb0008d44161d Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:30:39 +0400 Subject: [PATCH 3/7] wip --- src/Simplex/Messaging/Agent.hs | 21 ++++++---- src/Simplex/Messaging/Agent/Protocol.hs | 51 ++++++++++++++++++++++--- tests/AgentTests/EqInstances.hs | 14 +------ tests/AgentTests/FunctionalAPITests.hs | 5 ++- 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/Simplex/Messaging/Agent.hs b/src/Simplex/Messaging/Agent.hs index 66ac7893b..ae09312dd 100644 --- a/src/Simplex/Messaging/Agent.hs +++ b/src/Simplex/Messaging/Agent.hs @@ -347,7 +347,7 @@ createConnectionAsync c userId aCorrId enableNtfs = withAgentEnv c .:. newConnAs {-# INLINE createConnectionAsync #-} -- | Create or update user's contact connection short link (LSET command) asynchronously, no synchronous response -setConnShortLinkAsync :: AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AE () +setConnShortLinkAsync :: ConnectionModeI c => AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AE () setConnShortLinkAsync c = withAgentEnv c .::. setConnShortLinkAsync' c {-# INLINE setConnShortLinkAsync #-} @@ -892,10 +892,15 @@ checkClientNotices AgentClient {clientNotices, presetServers} (ProtoServerWithAu when (maybe True (ts <) expires_) $ throwError NOTICE {server = safeDecodeUtf8 $ strEncode $ L.head host, preset = isNothing srvKey, expiresAt = roundedToUTCTime <$> expires_} -setConnShortLinkAsync' :: AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AM () +setConnShortLinkAsync' :: forall c. ConnectionModeI c => AgentClient -> ACorrId -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AM () setConnShortLinkAsync' c corrId connId cMode userLinkData clientData = - -- enqueue command LSET - undefined + withConnLock c connId "setConnShortLinkAsync" $ do + SomeConn _ conn <- withStore c (`getConn` connId) + srv <- case (conn, cMode, userLinkData) of + (ContactConnection _ RcvQueue {server}, SCMContact, UserContactLinkData {}) -> pure server + (RcvConnection _ RcvQueue {server}, SCMInvitation, UserInvLinkData {}) -> pure server + _ -> throwE $ CMD PROHIBITED "setConnShortLinkAsync: invalid connection or mode" + enqueueCommand c corrId connId (Just srv) $ AClientCommand $ LSET (AUCLD cMode userLinkData) clientData setConnShortLink' :: AgentClient -> NetworkRequestMode -> ConnId -> SConnectionMode c -> UserConnLinkData c -> Maybe CRClientData -> AM (ConnShortLink c) setConnShortLink' c nm connId cMode userLinkData clientData = @@ -1668,10 +1673,10 @@ runCommandProcessing c@AgentClient {subQ} connId server_ Worker {doWork} = do tryCommand . withNextSrv c userId storageSrvs triedHosts [] $ \srv -> do (CCLink cReq _, service) <- newRcvConnSrv c NRMBackground userId connId enableNtfs cMode Nothing Nothing pqEnc subMode srv notify $ INV (ACR cMode cReq) service - LSET {} -> - -- create link (reuse setConnShortLink') - -- notify - LINK - undefined + LSET (AUCLD cMode userLinkData) clientData -> + withServer' . tryCommand $ do + link <- setConnShortLink' c NRMBackground connId cMode userLinkData clientData + notify $ LINK (ACSL cMode link) JOIN enableNtfs (ACR _ cReq@(CRInvitationUri ConnReqUriData {crSmpQueues = q :| _} _)) pqEnc subMode connInfo -> noServer $ do triedHosts <- newTVarIO S.empty tryCommand . withNextSrv c userId storageSrvs triedHosts [qServer q] $ \srv -> do diff --git a/src/Simplex/Messaging/Agent/Protocol.hs b/src/Simplex/Messaging/Agent/Protocol.hs index 525ebfeb2..080ea393a 100644 --- a/src/Simplex/Messaging/Agent/Protocol.hs +++ b/src/Simplex/Messaging/Agent/Protocol.hs @@ -112,6 +112,7 @@ module Simplex.Messaging.Agent.Protocol ServiceScheme, FixedLinkData (..), ConnLinkData (..), + AUserConnLinkData (..), UserConnLinkData (..), UserContactData (..), UserLinkData (..), @@ -436,7 +437,7 @@ deriving instance Show AEvtTag data ACommand = NEW Bool AConnectionMode InitialKeys SubscriptionMode -- response INV - | LSET AConnectionMode AUserConnLinkData (Maybe CRClientData) -- response LINK + | LSET AUserConnLinkData (Maybe CRClientData) -- response LINK | JOIN Bool AConnectionRequestUri PQSupport SubscriptionMode ConnInfo | LET ConfirmationId ConnInfo -- ConnInfo is from client | ACK AgentMsgId (Maybe MsgReceiptInfo) @@ -1709,8 +1710,10 @@ data UserContactData = UserContactData relays :: [ConnShortLink 'CMContact], userData :: UserLinkData } + deriving (Eq, Show) newtype UserLinkData = UserLinkData ByteString + deriving (Eq, Show) data AConnLinkData = forall m. ConnectionModeI m => ACLD (SConnectionMode m) (ConnLinkData m) @@ -1718,8 +1721,19 @@ data UserConnLinkData c where UserInvLinkData :: UserLinkData -> UserConnLinkData 'CMInvitation UserContactLinkData :: UserContactData -> UserConnLinkData 'CMContact +deriving instance Eq (UserConnLinkData m) + +deriving instance Show (UserConnLinkData m) + data AUserConnLinkData = forall m. ConnectionModeI m => AUCLD (SConnectionMode m) (UserConnLinkData m) +instance Eq AUserConnLinkData where + AUCLD m d == AUCLD m' d' = case testEquality m m' of + Just Refl -> d == d' + Nothing -> False + +deriving instance Show AUserConnLinkData + linkUserData :: ConnLinkData c -> UserLinkData linkUserData = \case InvitationLinkData _ d -> d @@ -1746,6 +1760,7 @@ data OwnerAuth = OwnerAuth -- Owner validation should detect and reject loops. authOwnerSig :: C.Signature 'C.Ed25519 } + deriving (Eq, Show) instance Encoding OwnerAuth where smpEncode OwnerAuth {ownerId, ownerKey, ownerSig, authOwnerId, authOwnerSig} = @@ -1785,9 +1800,35 @@ instance Encoding AConnLinkData where let cd = UserContactData {direct, owners, relays, userData} pure $ ACLD SCMContact $ ContactLinkData vr cd +instance ConnectionModeI c => Encoding (UserConnLinkData c) where + smpEncode = \case + UserInvLinkData userData -> smpEncode (CMInvitation, userData) + UserContactLinkData UserContactData {direct, owners, relays, userData} -> + B.concat [smpEncode (CMContact, direct), smpEncodeList owners, smpEncodeList relays, smpEncode userData] + smpP = (\(AUCLD _ d) -> checkConnMode d) <$?> smpP + {-# INLINE smpP #-} + instance Encoding AUserConnLinkData where - smpEncode = undefined - smpP = undefined + smpEncode (AUCLD _ d) = smpEncode d + {-# INLINE smpEncode #-} + smpP = + smpP >>= \case + CMInvitation -> do + userData <- smpP <* A.takeByteString -- ignoring tail for forward compatibility with the future link data encoding + pure $ AUCLD SCMInvitation $ UserInvLinkData userData + CMContact -> do + direct <- smpP + owners <- smpListP + relays <- smpListP + userData <- smpP <* A.takeByteString -- ignoring tail for forward compatibility with the future link data encoding + let cd = UserContactData {direct, owners, relays, userData} + pure $ AUCLD SCMContact $ UserContactLinkData cd + +instance StrEncoding AUserConnLinkData where + strEncode = smpEncode + {-# INLINE strEncode #-} + strP = smpP + {-# INLINE strP #-} instance Encoding UserLinkData where smpEncode (UserLinkData s) = if B.length s <= 254 then smpEncode s else smpEncode ('\255', Large s) @@ -2009,7 +2050,7 @@ commandP binaryP = strP >>= \case NEW_ -> s (NEW <$> strP_ <*> strP_ <*> pqIKP <*> (strP <|> pure SMP.SMSubscribe)) - LSET_ -> undefined + LSET_ -> s (LSET <$> strP <*> optional (A.space *> strP)) JOIN_ -> s (JOIN <$> strP_ <*> strP_ <*> pqSupP <*> (strP_ <|> pure SMP.SMSubscribe) <*> binaryP) LET_ -> s (LET <$> A.takeTill (== ' ') <* A.space <*> binaryP) ACK_ -> s (ACK <$> A.decimal <*> optional (A.space *> binaryP)) @@ -2027,7 +2068,7 @@ commandP binaryP = serializeCommand :: ACommand -> ByteString serializeCommand = \case NEW ntfs cMode pqIK subMode -> s (NEW_, ntfs, cMode, pqIK, subMode) - LSET {} -> undefined + LSET uld cd_ -> s (LSET_, uld) <> maybe "" (B.cons ' ' . s) cd_ JOIN ntfs cReq pqSup subMode cInfo -> s (JOIN_, ntfs, cReq, pqSup, subMode, Str $ serializeBinary cInfo) LET confId cInfo -> B.unwords [s LET_, confId, serializeBinary cInfo] ACK mId rcptInfo_ -> s (ACK_, mId) <> maybe "" (B.cons ' ' . serializeBinary) rcptInfo_ diff --git a/tests/AgentTests/EqInstances.hs b/tests/AgentTests/EqInstances.hs index 63c493861..817580723 100644 --- a/tests/AgentTests/EqInstances.hs +++ b/tests/AgentTests/EqInstances.hs @@ -5,7 +5,7 @@ module AgentTests.EqInstances where import Data.Type.Equality -import Simplex.Messaging.Agent.Protocol (ConnLinkData (..), OwnerAuth (..), UserContactData (..), UserLinkData (..)) +import Simplex.Messaging.Agent.Protocol (ConnLinkData (..)) import Simplex.Messaging.Agent.Store import Simplex.Messaging.Client (ProxiedRelay (..)) @@ -32,18 +32,6 @@ deriving instance Show (ConnLinkData c) deriving instance Eq (ConnLinkData c) -deriving instance Show UserContactData - -deriving instance Eq UserContactData - -deriving instance Show UserLinkData - -deriving instance Eq UserLinkData - -deriving instance Show OwnerAuth - -deriving instance Eq OwnerAuth - deriving instance Show ProxiedRelay deriving instance Eq ProxiedRelay diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index 1ceb675ab..e2e439fba 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -450,7 +450,7 @@ functionalAPITests ps = do it "should send multiple messages to the same connection" $ withSmpServer ps testSendMessagesB it "should send messages to the 2 connections" $ withSmpServer ps testSendMessagesB2 describe "Async agent commands" $ do - describe "connect using async agent commands" $ + fdescribe "connect using async agent commands" $ testBasicMatrix2 ps testAsyncCommands it "should restore and complete async commands on restart" $ testAsyncCommandsRestore ps @@ -2583,6 +2583,9 @@ testAsyncCommands sqSecured alice bob baseId = bobId <- createConnectionAsync alice 1 "1" True SCMInvitation IKPQOn SMSubscribe ("1", bobId', INV (ACR _ qInfo)) <- get alice liftIO $ bobId' `shouldBe` bobId + setConnShortLinkAsync alice "1a" bobId SCMInvitation (UserInvLinkData $ UserLinkData "test") Nothing + ("1a", bobId'', LINK (ACSL SCMInvitation _)) <- get alice + liftIO $ bobId'' `shouldBe` bobId aliceId <- joinConnectionAsync bob 1 "2" True qInfo "bob's connInfo" PQSupportOn SMSubscribe ("2", aliceId', JOINED sqSecured') <- get bob liftIO $ do From c047df4273748e1706eccc4e7c0923a7c2a29b4a Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:43:47 +0400 Subject: [PATCH 4/7] test --- tests/AgentTests/FunctionalAPITests.hs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index e2e439fba..9e42b8798 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -450,8 +450,10 @@ functionalAPITests ps = do it "should send multiple messages to the same connection" $ withSmpServer ps testSendMessagesB it "should send messages to the 2 connections" $ withSmpServer ps testSendMessagesB2 describe "Async agent commands" $ do - fdescribe "connect using async agent commands" $ + describe "connect using async agent commands" $ testBasicMatrix2 ps testAsyncCommands + fit "should add short link data using async agent command" $ + testSetConnShortLinkAsync ps it "should restore and complete async commands on restart" $ testAsyncCommandsRestore ps describe "accept connection using async command" $ @@ -2583,9 +2585,6 @@ testAsyncCommands sqSecured alice bob baseId = bobId <- createConnectionAsync alice 1 "1" True SCMInvitation IKPQOn SMSubscribe ("1", bobId', INV (ACR _ qInfo)) <- get alice liftIO $ bobId' `shouldBe` bobId - setConnShortLinkAsync alice "1a" bobId SCMInvitation (UserInvLinkData $ UserLinkData "test") Nothing - ("1a", bobId'', LINK (ACSL SCMInvitation _)) <- get alice - liftIO $ bobId'' `shouldBe` bobId aliceId <- joinConnectionAsync bob 1 "2" True qInfo "bob's connInfo" PQSupportOn SMSubscribe ("2", aliceId', JOINED sqSecured') <- get bob liftIO $ do @@ -2631,6 +2630,23 @@ testAsyncCommands sqSecured alice bob baseId = where msgId = subtract baseId +testSetConnShortLinkAsync :: (ASrvTransport, AStoreType) -> IO () +testSetConnShortLinkAsync ps = withAgentClients2 $ \alice bob -> + withSmpServerStoreLogOn ps testPort $ \_ -> runRight_ $ do + let userData = UserLinkData "test user data" + newLinkData = UserInvLinkData userData + (bobId, (CCLink qInfo _, _)) <- A.createConnection alice NRMInteractive 1 True True SCMInvitation (Just newLinkData) Nothing IKPQOn SMSubscribe + let updatedData = UserLinkData "updated user data" + setConnShortLinkAsync alice "1" bobId SCMInvitation (UserInvLinkData updatedData) Nothing + ("1", bobId', LINK (ACSL SCMInvitation _)) <- get alice + liftIO $ bobId' `shouldBe` bobId + (aliceId, _) <- joinConnection bob 1 True qInfo "bob's connInfo" SMSubscribe + ("", _, CONF confId _ "bob's connInfo") <- get alice + allowConnection alice bobId confId "alice's connInfo" + get alice ##> ("", bobId, CON) + get bob ##> ("", aliceId, INFO "alice's connInfo") + get bob ##> ("", aliceId, CON) + testAsyncCommandsRestore :: (ASrvTransport, AStoreType) -> IO () testAsyncCommandsRestore ps = do alice <- getSMPAgentClient' 1 agentCfg initAgentServers testDB From 1c204a5430aedaeee6ceaf152a28e2df04d8b448 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 19 Dec 2025 18:02:21 +0400 Subject: [PATCH 5/7] better test --- tests/AgentTests/FunctionalAPITests.hs | 29 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index 9e42b8798..97dec4472 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -2634,17 +2634,30 @@ testSetConnShortLinkAsync :: (ASrvTransport, AStoreType) -> IO () testSetConnShortLinkAsync ps = withAgentClients2 $ \alice bob -> withSmpServerStoreLogOn ps testPort $ \_ -> runRight_ $ do let userData = UserLinkData "test user data" - newLinkData = UserInvLinkData userData - (bobId, (CCLink qInfo _, _)) <- A.createConnection alice NRMInteractive 1 True True SCMInvitation (Just newLinkData) Nothing IKPQOn SMSubscribe + userCtData = UserContactData {direct = True, owners = [], relays = [], userData} + newLinkData = UserContactLinkData userCtData + (contactId, (CCLink qInfo (Just shortLink), _)) <- A.createConnection alice NRMInteractive 1 True True SCMContact (Just newLinkData) Nothing IKPQOn SMSubscribe + -- verify initial link data + (_, ContactLinkData _ userCtData') <- getConnShortLink bob 1 shortLink + liftIO $ userCtData' `shouldBe` userCtData + -- update link data async let updatedData = UserLinkData "updated user data" - setConnShortLinkAsync alice "1" bobId SCMInvitation (UserInvLinkData updatedData) Nothing - ("1", bobId', LINK (ACSL SCMInvitation _)) <- get alice - liftIO $ bobId' `shouldBe` bobId + updatedCtData = UserContactData {direct = False, owners = [], relays = [], userData = updatedData} + setConnShortLinkAsync alice "1" contactId SCMContact (UserContactLinkData updatedCtData) Nothing + ("1", contactId', LINK (ACSL SCMContact _)) <- get alice + liftIO $ contactId' `shouldBe` contactId + -- verify updated link data + (_, ContactLinkData _ updatedCtData') <- getConnShortLink bob 1 shortLink + liftIO $ updatedCtData' `shouldBe` updatedCtData + -- complete connection via contact address (aliceId, _) <- joinConnection bob 1 True qInfo "bob's connInfo" SMSubscribe - ("", _, CONF confId _ "bob's connInfo") <- get alice - allowConnection alice bobId confId "alice's connInfo" + ("", _, REQ invId _ "bob's connInfo") <- get alice + bobId <- A.prepareConnectionToAccept alice 1 True invId PQSupportOn + (_, Nothing) <- acceptContact alice 1 bobId True invId "alice's connInfo" PQSupportOn SMSubscribe + ("", _, CONF confId _ "alice's connInfo") <- get bob + allowConnection bob aliceId confId "bob's connInfo" + get alice ##> ("", bobId, INFO "bob's connInfo") get alice ##> ("", bobId, CON) - get bob ##> ("", aliceId, INFO "alice's connInfo") get bob ##> ("", aliceId, CON) testAsyncCommandsRestore :: (ASrvTransport, AStoreType) -> IO () From 92ee3cdbf40b1aee3bc399147af2308da9acc1d0 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 19 Dec 2025 19:30:54 +0400 Subject: [PATCH 6/7] enable tests --- tests/AgentTests/FunctionalAPITests.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index 97dec4472..92756f3d0 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -452,7 +452,7 @@ functionalAPITests ps = do describe "Async agent commands" $ do describe "connect using async agent commands" $ testBasicMatrix2 ps testAsyncCommands - fit "should add short link data using async agent command" $ + it "should add short link data using async agent command" $ testSetConnShortLinkAsync ps it "should restore and complete async commands on restart" $ testAsyncCommandsRestore ps From 7782228093932a1e8d45801d24f6b224d9229ab8 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 19 Dec 2025 19:41:00 +0400 Subject: [PATCH 7/7] add to test --- tests/AgentTests/FunctionalAPITests.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/AgentTests/FunctionalAPITests.hs b/tests/AgentTests/FunctionalAPITests.hs index 92756f3d0..5647550a9 100644 --- a/tests/AgentTests/FunctionalAPITests.hs +++ b/tests/AgentTests/FunctionalAPITests.hs @@ -2636,18 +2636,19 @@ testSetConnShortLinkAsync ps = withAgentClients2 $ \alice bob -> let userData = UserLinkData "test user data" userCtData = UserContactData {direct = True, owners = [], relays = [], userData} newLinkData = UserContactLinkData userCtData - (contactId, (CCLink qInfo (Just shortLink), _)) <- A.createConnection alice NRMInteractive 1 True True SCMContact (Just newLinkData) Nothing IKPQOn SMSubscribe + (cId, (CCLink qInfo (Just shortLink), _)) <- A.createConnection alice NRMInteractive 1 True True SCMContact (Just newLinkData) Nothing IKPQOn SMSubscribe -- verify initial link data (_, ContactLinkData _ userCtData') <- getConnShortLink bob 1 shortLink liftIO $ userCtData' `shouldBe` userCtData -- update link data async let updatedData = UserLinkData "updated user data" updatedCtData = UserContactData {direct = False, owners = [], relays = [], userData = updatedData} - setConnShortLinkAsync alice "1" contactId SCMContact (UserContactLinkData updatedCtData) Nothing - ("1", contactId', LINK (ACSL SCMContact _)) <- get alice - liftIO $ contactId' `shouldBe` contactId + setConnShortLinkAsync alice "1" cId SCMContact (UserContactLinkData updatedCtData) Nothing + ("1", cId', LINK (ACSL SCMContact shortLink')) <- get alice + liftIO $ cId' `shouldBe` cId + liftIO $ shortLink' `shouldBe` shortLink -- verify updated link data - (_, ContactLinkData _ updatedCtData') <- getConnShortLink bob 1 shortLink + (_, ContactLinkData _ updatedCtData') <- getConnShortLink bob 1 shortLink' liftIO $ updatedCtData' `shouldBe` updatedCtData -- complete connection via contact address (aliceId, _) <- joinConnection bob 1 True qInfo "bob's connInfo" SMSubscribe