diff --git a/cabal.project b/cabal.project index ed8a2fea4b..e7b30d6735 100644 --- a/cabal.project +++ b/cabal.project @@ -21,7 +21,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: c9c2d19074a809ba505f393b41aa20ac7b437aa7 + tag: 92b3d0492c5cf7730642774f925b6c1d61f04f54 source-repository-package type: git diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 7dfa57e7ea..2e48d7615c 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -4730,10 +4730,12 @@ dispatchResolvedRecord vr nm user ni@SimplexNameInfo {nameType} NameRecord {nrSi -- | Pick the link from the @NameRecord@ matching the queried name type. -- A missing per-type field (record exists but advertises no link of this kind) --- is treated as "not found" — same UX as a local-store miss. -firstNameLink :: SimplexNameType -> Maybe Text -> Maybe Text -> SimplexNameInfo -> Either ChatError Text -firstNameLink nameType simplexChannel simplexContact ni = - maybe (Left $ ChatError $ CESimplexNameNotFound ni) Right link +-- is treated as "not found" — same UX as a local-store miss. The text fields +-- on 'NameRecord' use the empty string as the absent sentinel. +firstNameLink :: SimplexNameType -> Text -> Text -> SimplexNameInfo -> Either ChatError Text +firstNameLink nameType simplexChannel simplexContact ni + | T.null link = Left $ ChatError $ CESimplexNameNotFound ni + | otherwise = Right link where link = case nameType of NTPublicGroup -> simplexChannel @@ -4786,13 +4788,12 @@ apiVerifySimplexName user chatRef = do let resolvedLink = case nameType' of NTContact -> nrSimplexContact NTPublicGroup -> nrSimplexChannel - case resolvedLink of - Just lnk | linksMatch lnk storedLink -> do + if not (T.null resolvedLink) && linksMatch resolvedLink storedLink + then do ts <- liftIO getCurrentTime withStore' $ \db -> persistVerified db ts toView $ CEvtSimplexNameVerified user chatRef claim ts - _ -> - toView $ CEvtSimplexNameVerifyFailed user chatRef claim SNVFLinkMismatch + else toView $ CEvtSimplexNameVerifyFailed user chatRef claim SNVFLinkMismatch Left NameNotRegistered -> toView $ CEvtSimplexNameVerifyFailed user chatRef claim SNVFNameNotRegistered Left ResolverUnavailable -> diff --git a/tests/ResolveNameTests.hs b/tests/ResolveNameTests.hs index cd0bcf2be1..aeb76554cf 100644 --- a/tests/ResolveNameTests.hs +++ b/tests/ResolveNameTests.hs @@ -85,31 +85,32 @@ resolveNameTests = do other -> expectationFailure $ "expected ChatErrorAgent, got " <> show other -- firstNameLink is the pure link-picker used by dispatchResolvedRecord: -- it selects nrSimplexContact for NTContact, nrSimplexChannel for NTPublicGroup. - -- A Nothing for the queried type collapses to CESimplexNameNotFound so the UX - -- is identical to a local-store miss. + -- The text fields use the empty string as the "absent" sentinel; an empty + -- link for the queried type collapses to CESimplexNameNotFound so the UX is + -- identical to a local-store miss. describe "firstNameLink" $ do - it "picks nrSimplexContact for NTContact" $ - case firstNameLink NTContact (Just channelLink) (Just contactLink) aliceNi of + it "NTContact path picks simplexContact" $ + case firstNameLink NTContact channelLink contactLink aliceNi of Right lnk -> lnk `shouldBe` contactLink Left e -> expectationFailure $ "expected Right, got " <> show e - it "picks nrSimplexChannel for NTPublicGroup" $ - case firstNameLink NTPublicGroup (Just channelLink) (Just contactLink) groupNi of + it "NTPublicGroup path picks simplexChannel" $ + case firstNameLink NTPublicGroup channelLink contactLink groupNi of Right lnk -> lnk `shouldBe` channelLink Left e -> expectationFailure $ "expected Right, got " <> show e - it "returns CESimplexNameNotFound when nrSimplexContact is Nothing for NTContact" $ - case firstNameLink NTContact (Just channelLink) Nothing aliceNi of + it "empty Text returns NotFound" $ + case firstNameLink NTContact "" "" aliceNi of Left (ChatError (CESimplexNameNotFound ni)) -> ni `shouldBe` aliceNi other -> expectationFailure $ "expected CESimplexNameNotFound, got " <> show other - it "returns CESimplexNameNotFound when nrSimplexChannel is Nothing for NTPublicGroup" $ - case firstNameLink NTPublicGroup Nothing (Just contactLink) groupNi of + -- Each name advertises a per-type link; cross-type fallback would silently + -- connect to the wrong target, so a populated off-type slot must not satisfy + -- the queried type. + it "cross-type contact link with NTPublicGroup returns NotFound" $ + case firstNameLink NTPublicGroup "" contactLink groupNi of Left (ChatError (CESimplexNameNotFound ni)) -> ni `shouldBe` groupNi other -> expectationFailure $ "expected CESimplexNameNotFound, got " <> show other - -- NTContact ignores nrSimplexChannel even when nrSimplexContact is Nothing. - -- The resolver-side semantics say each name advertises a per-type link; - -- cross-type fallback would silently connect to the wrong target. - it "does not fall back to nrSimplexChannel for NTContact" $ - case firstNameLink NTContact (Just channelLink) Nothing aliceNi of - Left (ChatError (CESimplexNameNotFound _)) -> pure () + it "cross-type channel link with NTContact returns NotFound" $ + case firstNameLink NTContact channelLink "" aliceNi of + Left (ChatError (CESimplexNameNotFound ni)) -> ni `shouldBe` aliceNi other -> expectationFailure $ "expected CESimplexNameNotFound, got " <> show other -- linksMatch is the byte-equal-after-normalize comparator that gates -- APIVerifySimplexName. The agent's simplex:/ scheme and the server-hostname @@ -192,11 +193,11 @@ sampleRecord :: NameRecord sampleRecord = NameRecord { nrName = "alice", - nrNickname = Nothing, - nrWebsite = Nothing, - nrLocation = Nothing, - nrSimplexContact = Nothing, - nrSimplexChannel = Nothing, + nrNickname = "", + nrWebsite = "", + nrLocation = "", + nrSimplexContact = "", + nrSimplexChannel = "", nrEth = Nothing, nrBtc = Nothing, nrXmr = Nothing,