@@ -28,14 +28,15 @@ import Data.Bifunctor (first)
2828import qualified Data.ByteString.Base64 as B64
2929import Data.ByteString.Char8 (ByteString )
3030import qualified Data.ByteString.Char8 as B
31+ import Data.Foldable (foldrM )
3132import Data.Hashable (hash )
3233import qualified Data.IntSet as IS
3334import Data.List.NonEmpty (NonEmpty )
35+ import Data.Maybe (catMaybes )
3436import Data.String (IsString (.. ))
3537import Data.Type.Equality
3638import qualified Data.X509.Validation as XV
3739import GHC.Stack (withFrozenCallStack )
38- import qualified Network.TLS as TLS
3940import SMPClient
4041import qualified Simplex.Messaging.Crypto as C
4142import Simplex.Messaging.Encoding
@@ -82,6 +83,7 @@ serverTests = do
8283 describe " Concurrent sending and delivery" testConcurrentSendDelivery
8384 describe " Service message subscriptions" $ do
8485 testServiceDeliverSubscribe
86+ testServiceUpgradeAndDowngrade
8587 describe " Store log" testWithStoreLog
8688 describe " Restore messages" testRestoreMessages
8789 describe " Restore messages (old / v2)" testRestoreExpireMessages
@@ -135,10 +137,15 @@ serviceSignSendRecv h pk serviceKey t = do
135137 [r] <- signSendRecv_ h pk (Just serviceKey) t
136138 pure r
137139
140+ serviceSignSendRecv2 :: forall c p . (Transport c , PartyI p ) => THandleSMP c 'TClient -> C. APrivateAuthKey -> C. PrivateKeyEd25519 -> (ByteString , EntityId , Command p ) -> IO (Transmission (Either ErrorType BrokerMsg ), Transmission (Either ErrorType BrokerMsg ))
141+ serviceSignSendRecv2 h pk serviceKey t = do
142+ [r1, r2] <- signSendRecv_ h pk (Just serviceKey) t
143+ pure (r1, r2)
144+
138145signSendRecv_ :: forall c p . (Transport c , PartyI p ) => THandleSMP c 'TClient -> C. APrivateAuthKey -> Maybe C. PrivateKeyEd25519 -> (ByteString , EntityId , Command p ) -> IO (NonEmpty (Transmission (Either ErrorType BrokerMsg )))
139146signSendRecv_ h pk serviceKey_ t = do
140147 signSend_ h pk serviceKey_ t
141- liftIO $ tGetClient h
148+ tGetClient h
142149
143150signSend_ :: forall c p . (Transport c , PartyI p ) => THandleSMP c 'TClient -> C. APrivateAuthKey -> Maybe C. PrivateKeyEd25519 -> (ByteString , EntityId , Command p ) -> IO ()
144151signSend_ h@ THandle {params} (C. APrivateAuthKey a pk) serviceKey_ (corrId, qId, cmd) = do
@@ -667,18 +674,18 @@ testConcurrentSendDelivery =
667674
668675testServiceDeliverSubscribe :: SpecWith (ASrvTransport , AStoreType )
669676testServiceDeliverSubscribe =
670- it " " $ \ (at@ (ATransport t), msType) -> do
677+ it " should create queue as service and subscribe with SUBS after reconnect " $ \ (at@ (ATransport t), msType) -> do
671678 g <- C. newRandom
672679 creds <- genCredentials g Nothing (0 , 2400 ) " localhost"
673680 let (_fp, tlsCred) = tlsCredentials [creds]
674681 serviceKeys@ (_, servicePK) <- atomically $ C. generateKeyPair g
675682 let aServicePK = C. APrivateAuthKey C. SEd25519 servicePK
676- withSmpServerConfigOn at (cfgMS msType) testPort $ \ _ -> runClient t $ \ h -> do
683+ withSmpServerConfigOn at (cfgMS msType) testPort $ \ _ -> runSMPClient t $ \ h -> do
677684 (rPub, rKey) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
678685 (dhPub, dhPriv :: C. PrivateKeyX25519 ) <- atomically $ C. generateKeyPair g
679686 (sPub, sKey) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
680687
681- (rId, sId, dec, serviceId) <- runServiceClient t (tlsCred, serviceKeys) $ \ sh -> do
688+ (rId, sId, dec, serviceId) <- runSMPServiceClient t (tlsCred, serviceKeys) $ \ sh -> do
682689 Resp " 1" NoEntity (ERR SERVICE ) <- signSendRecv sh rKey (" 1" , NoEntity , New rPub dhPub)
683690 Resp " 2" NoEntity (Ids_ rId sId srvDh serviceId) <- serviceSignSendRecv sh rKey servicePK (" 2" , NoEntity , New rPub dhPub)
684691 let dec = decryptMsgV3 $ C. dh' srvDh dhPriv
@@ -697,35 +704,149 @@ testServiceDeliverSubscribe =
697704 Resp " 9" _ OK <- signSendRecv h sKey (" 9" , sId, _SEND " hello 3" )
698705 pure (rId, sId, dec, serviceId)
699706
700- runServiceClient t (tlsCred, serviceKeys) $ \ sh -> do
707+ runSMPServiceClient t (tlsCred, serviceKeys) $ \ sh -> do
701708 Resp " 10" NoEntity (ERR (CMD NO_AUTH )) <- signSendRecv sh aServicePK (" 10" , NoEntity , SUBS )
702- mId3 <- signSendRecv sh aServicePK (" 11" , serviceId, SUBS ) >>= \ case -- possible race between SOKS response and MSG
703- Resp " 11" serviceId' (SOKS n) -> do
704- n `shouldBe` 1
705- serviceId' `shouldBe` serviceId
706- Resp " " rId'' (Msg mId3 msg3) <- tGet1 sh
707- rId'' `shouldBe` rId
708- dec mId3 msg3 `shouldBe` Right " hello 3"
709- pure mId3
710- Resp " " rId'' (Msg mId3 msg3) -> do
711- rId'' `shouldBe` rId
712- dec mId3 msg3 `shouldBe` Right " hello 3"
713- Resp " 11" serviceId' (SOKS n) <- tGet1 sh
714- n `shouldBe` 1
715- serviceId' `shouldBe` serviceId
716- pure mId3
717- r -> error $ " unexpected response " <> take 100 (show r)
709+ signSend_ sh aServicePK Nothing (" 11" , serviceId, SUBS )
710+ [mId3] <-
711+ fmap catMaybes $
712+ receiveInAnyOrder -- race between SOKS and MSG, clients can handle it
713+ sh
714+ [ \ case
715+ Resp " 11" serviceId' (SOKS n) -> do
716+ n `shouldBe` 1
717+ serviceId' `shouldBe` serviceId
718+ pure $ Just Nothing
719+ _ -> pure Nothing ,
720+ \ case
721+ Resp " " rId'' (Msg mId3 msg3) -> do
722+ rId'' `shouldBe` rId
723+ dec mId3 msg3 `shouldBe` Right " hello 3"
724+ pure $ Just $ Just mId3
725+ _ -> pure Nothing
726+ ]
718727 Resp " 12" _ OK <- signSendRecv sh rKey (" 12" , rId, ACK mId3)
719728 Resp " 14" _ OK <- signSendRecv h sKey (" 14" , sId, _SEND " hello 4" )
720729 Resp " " _ (Msg mId4 msg4) <- tGet1 sh
721730 dec mId4 msg4 `shouldBe` Right " hello 4"
722731 Resp " 15" _ OK <- signSendRecv sh rKey (" 15" , rId, ACK mId4)
723732 pure ()
733+
734+ testServiceUpgradeAndDowngrade :: SpecWith (ASrvTransport , AStoreType )
735+ testServiceUpgradeAndDowngrade =
736+ it " should create queue as client and switch to service and back" $ \ (at@ (ATransport t), msType) -> do
737+ g <- C. newRandom
738+ creds <- genCredentials g Nothing (0 , 2400 ) " localhost"
739+ let (_fp, tlsCred) = tlsCredentials [creds]
740+ serviceKeys@ (_, servicePK) <- atomically $ C. generateKeyPair g
741+ let aServicePK = C. APrivateAuthKey C. SEd25519 servicePK
742+ withSmpServerConfigOn at (cfgMS msType) testPort $ \ _ -> runSMPClient t $ \ h -> do
743+ (rPub, rKey) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
744+ (dhPub, dhPriv :: C. PrivateKeyX25519 ) <- atomically $ C. generateKeyPair g
745+ (sPub, sKey) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
746+ (rPub2, rKey2) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
747+ (dhPub2, dhPriv2 :: C. PrivateKeyX25519 ) <- atomically $ C. generateKeyPair g
748+ (sPub2, sKey2) <- atomically $ C. generateAuthKeyPair C. SEd25519 g
749+
750+ (rId, sId, dec) <- runSMPClient t $ \ sh -> do
751+ Resp " 1" NoEntity (Ids rId sId srvDh) <- signSendRecv sh rKey (" 1" , NoEntity , New rPub dhPub)
752+ let dec = decryptMsgV3 $ C. dh' srvDh dhPriv
753+ Resp " 2" sId' OK <- signSendRecv h sKey (" 2" , sId, SKEY sPub)
754+ sId' `shouldBe` sId
755+ Resp " 3" _ OK <- signSendRecv h sKey (" 3" , sId, _SEND " hello" )
756+ Resp " " rId' (Msg mId1 msg1) <- tGet1 sh
757+ rId' `shouldBe` rId
758+ dec mId1 msg1 `shouldBe` Right " hello"
759+ Resp " 4" _ OK <- signSendRecv sh rKey (" 4" , rId, ACK mId1)
760+ Resp " 5" _ OK <- signSendRecv h sKey (" 5" , sId, _SEND " hello 2" )
761+ pure (rId, sId, dec)
762+
763+ -- split to prevent message delivery
764+ (rId2, sId2, dec2) <- runSMPClient t $ \ sh -> do
765+ Resp " 6" NoEntity (Ids rId2 sId2 srvDh2) <- signSendRecv sh rKey2 (" 6" , NoEntity , New rPub2 dhPub2)
766+ let dec2 = decryptMsgV3 $ C. dh' srvDh2 dhPriv2
767+ Resp " 7" sId2' OK <- signSendRecv h sKey2 (" 7" , sId2, SKEY sPub2)
768+ sId2' `shouldBe` sId2
769+ pure (rId2, sId2, dec2)
770+
771+ serviceId <- runSMPServiceClient t (tlsCred, serviceKeys) $ \ sh -> do
772+ Resp " 8" _ (ERR SERVICE ) <- signSendRecv sh rKey (" 8" , rId, SUB )
773+ (Resp " 9" rId' (SOK (Just serviceId)), Resp " " rId'' (Msg mId2 msg2)) <- serviceSignSendRecv2 sh rKey servicePK (" 9" , rId, SUB )
774+ rId' `shouldBe` rId
775+ rId'' `shouldBe` rId
776+ dec mId2 msg2 `shouldBe` Right " hello 2"
777+ (Resp " 10" rId2' (SOK (Just serviceId'))) <- serviceSignSendRecv sh rKey2 servicePK (" 10" , rId2, SUB )
778+ rId2' `shouldBe` rId2
779+ serviceId' `shouldBe` serviceId
780+ Resp " 10.1" _ OK <- signSendRecv sh rKey (" 10.1" , rId, ACK mId2)
781+ pure serviceId
782+
783+ Resp " 11" _ OK <- signSendRecv h sKey (" 11" , sId, _SEND " hello 3.1" )
784+ Resp " 12" _ OK <- signSendRecv h sKey2 (" 12" , sId2, _SEND " hello 3.2" )
785+
786+ runSMPServiceClient t (tlsCred, serviceKeys) $ \ sh -> do
787+ signSend_ sh aServicePK Nothing (" 14" , serviceId, SUBS )
788+ [(rKey3_1, rId3_1, mId3_1), (rKey3_2, rId3_2, mId3_2)] <-
789+ fmap catMaybes $
790+ receiveInAnyOrder -- race between SOKS and MSG, clients can handle it
791+ sh
792+ [ \ case
793+ Resp " 14" serviceId' (SOKS n) -> do
794+ n `shouldBe` 2
795+ serviceId' `shouldBe` serviceId
796+ pure $ Just Nothing
797+ _ -> pure Nothing ,
798+ \ case
799+ Resp " " rId'' (Msg mId3 msg3) | rId'' == rId -> do
800+ dec mId3 msg3 `shouldBe` Right " hello 3.1"
801+ pure $ Just $ Just (rKey, rId, mId3)
802+ _ -> pure Nothing ,
803+ \ case
804+ Resp " " rId'' (Msg mId3 msg3) | rId'' == rId2 -> do
805+ dec2 mId3 msg3 `shouldBe` Right " hello 3.2"
806+ pure $ Just $ Just (rKey2, rId2, mId3)
807+ _ -> pure Nothing
808+ ]
809+ Resp " 15" _ OK <- signSendRecv sh rKey3_1 (" 15" , rId3_1, ACK mId3_1)
810+ Resp " 16" _ OK <- signSendRecv sh rKey3_2 (" 16" , rId3_2, ACK mId3_2)
811+ pure ()
812+
813+ Resp " 17" _ OK <- signSendRecv h sKey (" 17" , sId, _SEND " hello 4" )
814+
815+ runSMPClient t $ \ sh -> do
816+ Resp " 18" _ (ERR SERVICE ) <- signSendRecv sh aServicePK (" 18" , serviceId, SUBS )
817+ (Resp " 19" rId' (SOK Nothing ), Resp " " rId'' (Msg mId4 msg4)) <- signSendRecv2 sh rKey (" 19" , rId, SUB )
818+ rId' `shouldBe` rId
819+ rId'' `shouldBe` rId
820+ dec mId4 msg4 `shouldBe` Right " hello 4"
821+ Resp " 20" _ OK <- signSendRecv sh rKey (" 20" , rId, ACK mId4)
822+ Resp " 21" _ OK <- signSendRecv h sKey (" 21" , sId, _SEND " hello 5" )
823+ Resp " " _ (Msg mId5 msg5) <- tGet1 sh
824+ dec mId5 msg5 `shouldBe` Right " hello 5"
825+ Resp " 22" _ OK <- signSendRecv sh rKey (" 22" , rId, ACK mId5)
826+
827+ Resp " 23" rId2' (SOK Nothing ) <- signSendRecv sh rKey2 (" 23" , rId2, SUB )
828+ rId2' `shouldBe` rId2
829+ Resp " 24" _ OK <- signSendRecv h sKey (" 24" , sId, _SEND " hello 6" )
830+ Resp " " _ (Msg mId6 msg6) <- tGet1 sh
831+ dec mId6 msg6 `shouldBe` Right " hello 6"
832+ Resp " 25" _ OK <- signSendRecv sh rKey (" 25" , rId, ACK mId6)
833+ pure ()
834+
835+ receiveInAnyOrder :: (HasCallStack , Transport c ) => THandleSMP c 'TClient -> [(CorrId , EntityId , Either ErrorType BrokerMsg ) -> IO (Maybe b )] -> IO [b ]
836+ receiveInAnyOrder h = fmap reverse . go []
724837 where
725- runClient :: Transport c => TProxy c 'TServer -> (THandleSMP c 'TClient -> IO a ) -> IO a
726- runClient _ test' = testSMPClient test'
727- runServiceClient :: Transport c => TProxy c 'TServer -> (TLS. Credential , C. KeyPairEd25519 ) -> (THandleSMP c 'TClient -> IO a ) -> IO a
728- runServiceClient _ serviceCreds test' = testSMPServiceClient serviceCreds test'
838+ go rs [] = pure rs
839+ go rs ps = withFrozenCallStack $ do
840+ r <- 5000000 `timeout` get >>= maybe (error " inAnyOrder timeout" ) pure
841+ (r_, ps') <- foldrM (choose r) (Nothing , [] ) ps
842+ case r_ of
843+ Just r' -> go (r' : rs) ps'
844+ Nothing -> error $ " unexpected event: " <> show r
845+ get = do
846+ [r] <- tGetClient h
847+ pure r
848+ choose r p (Nothing , ps') = (maybe (Nothing , p : ps') ((,ps') . Just )) <$> p r
849+ choose _ p (Just r, ps') = pure (Just r, p : ps')
729850
730851testWithStoreLog :: SpecWith (ASrvTransport , AStoreType )
731852testWithStoreLog =
0 commit comments