mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-09-13 04:37:13 +00:00
more review fixes
This commit is contained in:
@@ -12,3 +12,4 @@ cabal.project.local~
|
||||
*.tix
|
||||
.coverage
|
||||
|
||||
__pycache__/
|
||||
|
||||
@@ -200,10 +200,13 @@ then be quoted at list price while the registrar charges the premium. Configure
|
||||
`SNRC_CONTROLLER_<TLD>` wherever `SNRC_REGISTRAR_<TLD>` is set, and upgrade this
|
||||
service before the routers that query it.
|
||||
|
||||
Upgrade the resolver before the router that queries it. A resolver without this
|
||||
status reports a name in its auction as plain `expired`, which reads as "free at
|
||||
the ordinary price" — the price the registrar actually charges is still the
|
||||
premium one, so the quote is wrong until the resolver is current.
|
||||
**Upgrade this service before the routers that query it.** Routers from v22 hash
|
||||
the 2LD of every query, and two things only this version does are needed to
|
||||
answer them: decoding a bracket label that sits under a subname
|
||||
(`sub.[<hash>].tld`, which an older resolver hashes as literal text and so
|
||||
answers about a node nobody asked about), and reporting `auction` at all — an
|
||||
older resolver calls a name in its auction plain `expired`, which reads as "free
|
||||
at the ordinary price" while the registrar charges the premium.
|
||||
|
||||
### Why a name is reserved
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -66,8 +66,8 @@ Unrecognised payloads fall back to `0x`-prefixed raw hex.
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import sys
|
||||
import time
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from urllib.parse import unquote, urlparse
|
||||
from urllib.request import Request, urlopen
|
||||
@@ -289,7 +289,8 @@ def name_status(name: str):
|
||||
}
|
||||
|
||||
# nameExpires and reservedNames are keyed on uint256(keccak(label)).
|
||||
# Decoded for a 2LD only, the same rule node_of applies to the node.
|
||||
# Only the 2LD's label is a registry key, wherever in the name it sits, so it
|
||||
# is the only one decoded - the same rule node_of applies to the node.
|
||||
label = labels[-2]
|
||||
if is_encoded_labelhash(label):
|
||||
token = int(label[1:-1], 16)
|
||||
|
||||
@@ -67,7 +67,7 @@ module Simplex.Messaging.Agent
|
||||
deleteConnShortLink,
|
||||
getConnShortLink,
|
||||
resolveSimplexName,
|
||||
simplexNameAvailability,
|
||||
getSimplexNameAvailability,
|
||||
getConnLinkPrivKey,
|
||||
deleteLocalInvShortLink,
|
||||
changeConnectionUser,
|
||||
@@ -465,9 +465,9 @@ resolveSimplexName :: AgentClient -> NetworkRequestMode -> UserId -> SimplexDoma
|
||||
resolveSimplexName c nm userId domain = withAgentEnv c $ resolveSimplexName' c nm userId domain
|
||||
{-# INLINE resolveSimplexName #-}
|
||||
|
||||
simplexNameAvailability :: AgentClient -> NetworkRequestMode -> UserId -> SimplexDomain -> AE NameAvailability
|
||||
simplexNameAvailability c nm userId domain = withAgentEnv c $ simplexNameAvailability' c nm userId domain
|
||||
{-# INLINE simplexNameAvailability #-}
|
||||
getSimplexNameAvailability :: AgentClient -> NetworkRequestMode -> UserId -> SimplexDomain -> AE NameAvailability
|
||||
getSimplexNameAvailability c nm userId domain = withAgentEnv c $ getSimplexNameAvailability' c nm userId domain
|
||||
{-# INLINE getSimplexNameAvailability #-}
|
||||
|
||||
getConnLinkPrivKey :: AgentClient -> ConnId -> AE (Maybe C.PrivateKeyEd25519)
|
||||
getConnLinkPrivKey c = withAgentEnv c . getConnLinkPrivKey' c
|
||||
@@ -1279,10 +1279,10 @@ resolveSimplexName' c nm userId domain = do
|
||||
resolverSrv <- getNextNameServer c userId
|
||||
resolveName c nm userId resolverSrv domain
|
||||
|
||||
simplexNameAvailability' :: AgentClient -> NetworkRequestMode -> UserId -> SimplexDomain -> AM NameAvailability
|
||||
simplexNameAvailability' c nm userId domain = do
|
||||
getSimplexNameAvailability' :: AgentClient -> NetworkRequestMode -> UserId -> SimplexDomain -> AM NameAvailability
|
||||
getSimplexNameAvailability' c nm userId domain = do
|
||||
resolverSrv <- getNextNameServer c userId
|
||||
nameAvailability c nm userId resolverSrv domain
|
||||
getNameAvailability c nm userId resolverSrv domain
|
||||
|
||||
changeConnectionUser' :: AgentClient -> UserId -> ConnId -> UserId -> AM ()
|
||||
changeConnectionUser' c oldUserId connId newUserId = do
|
||||
|
||||
@@ -69,7 +69,7 @@ module Simplex.Messaging.Agent.Client
|
||||
secureGetQueueLink,
|
||||
getQueueLink,
|
||||
resolveName,
|
||||
nameAvailability,
|
||||
getNameAvailability,
|
||||
getNextNameServer,
|
||||
enableQueueNotifications,
|
||||
EnableQueueNtfReq (..),
|
||||
@@ -2004,8 +2004,8 @@ resolveName c nm userId server domain =
|
||||
|
||||
-- | Ask whether a name can be registered, by the same proxy-preferred path as
|
||||
-- `resolveName`.
|
||||
nameAvailability :: AgentClient -> NetworkRequestMode -> UserId -> SMPServer -> SimplexDomain -> AM NameAvailability
|
||||
nameAvailability c nm userId server domain =
|
||||
getNameAvailability :: AgentClient -> NetworkRequestMode -> UserId -> SMPServer -> SimplexDomain -> AM NameAvailability
|
||||
getNameAvailability c nm userId server domain =
|
||||
snd <$> sendOrProxySMPCommand c nm userId server "" "NAVL" NoEntity availViaProxy availDirectly
|
||||
where
|
||||
availViaProxy smp proxySess = proxyNameAvailability smp nm proxySess domain
|
||||
|
||||
@@ -110,7 +110,7 @@ import Simplex.Messaging.Server.Env.STM as Env
|
||||
import Simplex.Messaging.Server.Expiration
|
||||
import Simplex.Messaging.Server.MsgStore
|
||||
import Simplex.Messaging.Server.MsgStore.Journal (JournalMsgStore, JournalQueue (..), getJournalQueueMessages)
|
||||
import Simplex.Messaging.Server.Names (NamesEnv, closeNamesEnv, nameAvailability, resolveName)
|
||||
import Simplex.Messaging.Server.Names (NamesEnv, closeNamesEnv, getNameAvailability, resolveName)
|
||||
import Simplex.Messaging.Server.MsgStore.STM
|
||||
import Simplex.Messaging.Server.MsgStore.Types
|
||||
import Simplex.Messaging.Server.NtfStore
|
||||
@@ -1499,7 +1499,7 @@ client
|
||||
nameAvailMsg nenv d = do
|
||||
st <- asks (rslvStats . serverStats)
|
||||
(selector, msg) <-
|
||||
liftIO (nameAvailability nenv d) <&> \case
|
||||
liftIO (getNameAvailability nenv d) <&> \case
|
||||
Right a -> (rslvSucc, NAVAIL a)
|
||||
Left e -> (rslvResolverErrs, ERR $ NAME e)
|
||||
incStat (selector st) $> msg
|
||||
|
||||
@@ -11,7 +11,7 @@ module Simplex.Messaging.Server.Names
|
||||
newNamesEnv,
|
||||
closeNamesEnv,
|
||||
pingEndpoint,
|
||||
nameAvailability,
|
||||
getNameAvailability,
|
||||
resolveName,
|
||||
)
|
||||
where
|
||||
@@ -75,8 +75,8 @@ resolveName env d = do
|
||||
|
||||
-- | Whether a name can be registered. Same timeout and failure handling as
|
||||
-- 'resolveName', which is the other question this server asks the resolver.
|
||||
nameAvailability :: NamesEnv -> SimplexDomain -> IO (Either NameErrorType NameAvailability)
|
||||
nameAvailability env d = do
|
||||
getNameAvailability :: NamesEnv -> SimplexDomain -> IO (Either NameErrorType NameAvailability)
|
||||
getNameAvailability env d = do
|
||||
r <- E.try (timeout (resolverTimeoutMs (config env) * 1000) (fetchAvail env d))
|
||||
case r of
|
||||
Right result -> pure (fromMaybe (Left (RESOLVER "timeout")) result)
|
||||
@@ -114,7 +114,9 @@ mapAvailability NameStatusResp {nsStatus, nsExpires, nsGraceEnds, nsAuctionEnds,
|
||||
"registered" -> Right $ NATaken nsExpires
|
||||
-- registered, but its records point nowhere
|
||||
"noResolver" -> Right $ NATaken nsExpires
|
||||
s -> Left (RESOLVER s)
|
||||
-- the resolver's own word for what it could not do, bounded because it is
|
||||
-- its text, not ours, and it travels to the client inside ERR
|
||||
s -> Left (RESOLVER (T.take 32 s))
|
||||
where
|
||||
-- A lapsed name missing the deadline or price that its status carries:
|
||||
-- withholding it is safer than quoting the ordinary price, but its expiry is
|
||||
|
||||
@@ -465,11 +465,11 @@ prometheusMetrics sm rtm ts =
|
||||
in "# Names\n\
|
||||
\# -----\n\
|
||||
\\n\
|
||||
\# HELP simplex_smp_names_reqs Total RSLV requests forwarded to this server.\n\
|
||||
\# HELP simplex_smp_names_reqs Total RSLV and NAVL requests forwarded to this server.\n\
|
||||
\# TYPE simplex_smp_names_reqs counter\n\
|
||||
\simplex_smp_names_reqs " <> mshow _rslvReqs <> "\n# rslvReqs\n\
|
||||
\\n\
|
||||
\# HELP simplex_smp_names_success NameRecord successfully resolved and returned.\n\
|
||||
\# HELP simplex_smp_names_success NameRecord resolved, or availability answered.\n\
|
||||
\# TYPE simplex_smp_names_success counter\n\
|
||||
\simplex_smp_names_success " <> mshow _rslvSucc <> "\n# rslvSucc\n\
|
||||
\\n\
|
||||
@@ -481,7 +481,7 @@ prometheusMetrics sm rtm ts =
|
||||
\# TYPE simplex_smp_names_resolver_errs counter\n\
|
||||
\simplex_smp_names_resolver_errs " <> mshow _rslvResolverErrs <> "\n# rslvResolverErrs\n\
|
||||
\\n\
|
||||
\# HELP simplex_smp_names_disabled RSLV requests rejected because no resolver is configured (names role off).\n\
|
||||
\# HELP simplex_smp_names_disabled RSLV and NAVL requests rejected because no resolver is configured (names role off).\n\
|
||||
\# TYPE simplex_smp_names_disabled counter\n\
|
||||
\simplex_smp_names_disabled " <> mshow _rslvDisabled <> "\n# rslvDisabled\n\
|
||||
\\n"
|
||||
|
||||
@@ -97,7 +97,7 @@ hashedDomain d@SimplexDomain {nameTLD, domain}
|
||||
| nameTLD == TLDWeb || isLabelHash domain = d
|
||||
| otherwise = d {domain = "[" <> labelHash <> "]"}
|
||||
where
|
||||
labelHash = decodeLatin1 $ BAE.convertToBase BAE.Base16 (hash (encodeUtf8 domain) :: Digest Keccak_256)
|
||||
labelHash = decodeLatin1 $ BAE.convertToBase BAE.Base16 (hash (encodeUtf8 (T.toLower domain)) :: Digest Keccak_256)
|
||||
|
||||
-- | Cap the name at 253 bytes (DNS full-domain limit)
|
||||
boundedNonSpace :: A.Parser ByteString
|
||||
|
||||
@@ -23,7 +23,7 @@ import qualified NamesResolverServer as NRS
|
||||
import SMPAgentClient
|
||||
import SMPClient
|
||||
import SMPNamesTests (testNameRecord)
|
||||
import Simplex.Messaging.Agent (resolveSimplexName)
|
||||
import Simplex.Messaging.Agent (resolveSimplexName, getSimplexNameAvailability)
|
||||
import Simplex.Messaging.Agent.Client (AgentClient)
|
||||
import Simplex.Messaging.Agent.Env.SQLite (InitialAgentServers (..), ServerCfg, ServerRoles (..), presetServerCfg)
|
||||
import Simplex.Messaging.Agent.Protocol (AgentErrorType (..))
|
||||
@@ -86,6 +86,34 @@ resolveNameTests = do
|
||||
it "surfaces as SMP host (NAME (RESOLVER ..))" testBackendError
|
||||
describe "success path" $
|
||||
it "returns NameRecord" testDirectSuccess
|
||||
describe "name availability" $ do
|
||||
it "answers through the agent's own server selection" testAvailSuccess
|
||||
it "answers NAME NO_RESOLVER when the chosen server has none" testAvailNoResolver
|
||||
it "fails agent-side with NO_NAME_SERVERS when no server has the names role" testAvailNoNameServers
|
||||
|
||||
testAvailSuccess :: HasCallStack => IO ()
|
||||
testAvailSuccess =
|
||||
withDirectResolver (status404, "{\"error\":\"unregistered\"}") $ \c -> do
|
||||
r <- runExceptT $ getSimplexNameAvailability c NRMInteractive 1 (SimplexDomain TLDSimplex "alice" [])
|
||||
case r of
|
||||
Right a -> a `shouldBe` SMP.NAVailable
|
||||
_ -> expectationFailure $ "expected Right NAVailable, got: " <> show r
|
||||
|
||||
testAvailNoResolver :: HasCallStack => IO ()
|
||||
testAvailNoResolver =
|
||||
withNoResolver $ \c -> do
|
||||
r <- runExceptT $ getSimplexNameAvailability c NRMInteractive 1 (SimplexDomain TLDSimplex "alice" [])
|
||||
case r of
|
||||
Left (SMP _ (SMP.NAME SMP.NO_RESOLVER)) -> pure ()
|
||||
_ -> expectationFailure $ "expected Left (SMP _ (NAME NO_RESOLVER)), got: " <> show r
|
||||
|
||||
testAvailNoNameServers :: HasCallStack => IO ()
|
||||
testAvailNoNameServers =
|
||||
withNoNameServers $ \c -> do
|
||||
r <- runExceptT $ getSimplexNameAvailability c NRMInteractive 1 (SimplexDomain TLDSimplex "alice" [])
|
||||
case r of
|
||||
Left NO_NAME_SERVERS -> pure ()
|
||||
_ -> expectationFailure $ "expected Left NO_NAME_SERVERS, got: " <> show r
|
||||
|
||||
testDirectNotFound :: HasCallStack => IO ()
|
||||
testDirectNotFound =
|
||||
|
||||
@@ -22,7 +22,7 @@ import Simplex.Messaging.Server.Main (validateUrl)
|
||||
import Simplex.Messaging.Server.Names
|
||||
( NamesConfig (..),
|
||||
RpcAuth (..),
|
||||
nameAvailability,
|
||||
getNameAvailability,
|
||||
newNamesEnv,
|
||||
pingEndpoint,
|
||||
resolveName,
|
||||
@@ -149,12 +149,14 @@ availabilitySpec = do
|
||||
refuses status400 "{\"error\":\"tldNotConfigured\"}" (RESOLVER "tldNotConfigured")
|
||||
it "a TLD with no registrar, so status could not be read, is a resolver error" $
|
||||
refuses status200 "{\"status\":\"unknown\",\"expires\":null}" (RESOLVER "unknown")
|
||||
it "a status longer than the error carries is bounded, not passed through" $
|
||||
refuses status502 (jsonBody ("{\"error\":\"" <> replicate 400 'e' <> "\"}")) (RESOLVER (T.replicate 32 "e"))
|
||||
it "a body that is not the resolver's JSON is never NOT_FOUND" $
|
||||
refuses status404 "<html>gateway</html>" (RESOLVER "HTTP 404")
|
||||
it "a body past the configured cap is a resolver error" $
|
||||
withResolverServer (resolveResp status200 (jsonBody ("{\"status\":\"registered\",\"pad\":\"" <> replicate 400 'x' <> "\"}"))) $ \port _ -> do
|
||||
env <- newNamesEnv (testNamesConfig port) {resolverMaxResponseBytes = 200}
|
||||
nameAvailability env navlDomain `shouldReturn` Left (RESOLVER "response too large")
|
||||
getNameAvailability env navlDomain `shouldReturn` Left (RESOLVER "response too large")
|
||||
it "every answer survives the wire" $
|
||||
mapM_
|
||||
(\a -> smpDecode (smpEncode a) `shouldBe` Right a)
|
||||
@@ -177,7 +179,7 @@ availabilitySpec = do
|
||||
asks_ st body expected =
|
||||
withResolverServer (resolveResp st body) $ \port _ -> do
|
||||
env <- newNamesEnv (testNamesConfig port)
|
||||
nameAvailability env navlDomain `shouldReturn` expected
|
||||
getNameAvailability env navlDomain `shouldReturn` expected
|
||||
navlDomain = SimplexDomain {nameTLD = TLDSimplex, domain = "alice", subDomain = []}
|
||||
|
||||
parseNameSpec :: Spec
|
||||
|
||||
Reference in New Issue
Block a user