more review fixes

This commit is contained in:
Alain Brenzikofer
2026-09-05 19:51:01 +02:00
parent 69cce33675
commit 783f0aa4be
13 changed files with 67 additions and 30 deletions
+1
View File
@@ -12,3 +12,4 @@ cabal.project.local~
*.tix
.coverage
__pycache__/
+7 -4
View File
@@ -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
+3 -2
View File
@@ -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)
+7 -7
View File
@@ -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
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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
+6 -4
View File
@@ -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
+3 -3
View File
@@ -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"
+1 -1
View File
@@ -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
+29 -1
View File
@@ -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 =
+5 -3
View File
@@ -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