From bbea84c60529b30135e56876d272bed298743aba Mon Sep 17 00:00:00 2001 From: Alain Brenzikofer Date: Tue, 9 Jun 2026 14:32:36 +0200 Subject: [PATCH] support multiple fallback links for splx contact and channels --- scripts/resolver/README.md | 18 ++++++++++++++---- scripts/resolver/snrc-resolve.py | 23 ++++++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/scripts/resolver/README.md b/scripts/resolver/README.md index 49d1a4e89..78df84b9c 100644 --- a/scripts/resolver/README.md +++ b/scripts/resolver/README.md @@ -151,8 +151,11 @@ curl -s http://127.0.0.1:8000/resolve/foobar.testing | jq . "nickname": "Foo", "website": "https://foo.bar", "location": "", - "simplexContact": "https://smp16.simplex.im/a#Q_F00BA7", - "simplexChannel": "", + "simplexContact": [ + "https://smp16.simplex.im/a#Q_F00BA7", + "https://smp11.simplex.im/a#Q_F00BA8" + ], + "simplexChannel": [], "eth": null, "btc": "bc1qpzht4wp64yg7z6sgl07vvrnepyux740juynfcn", "xmr": "4ANzdVJFxLtCKcBgNGkFSEA41zJFgrTX93LWt9UR6xpg7YNCsdrSV817cw2xKT8NXeS5euBBqTApS2u8kRTxMhyiDGN3Qgt", @@ -162,6 +165,13 @@ curl -s http://127.0.0.1:8000/resolve/foobar.testing | jq . } ``` +`simplexContact` and `simplexChannel` are arrays so a name can advertise +multiple SMP servers for redundancy. Clients SHOULD try the URLs in +order; the first entry is the primary and the rest are fallbacks. The +on-chain text record stores them as a single comma-separated string +(`"url1,url2,url3"`); this resolver splits, trims whitespace, and drops +empty entries before returning. + All field names are lowercase-initial and contain no dots, so they map directly onto Haskell record fields and can be consumed via aeson's `Generic`-derived `FromJSON` without a key-rewriting layer. Equivalent @@ -173,8 +183,8 @@ data SnrcRecord = SnrcRecord , nickname :: Text , website :: Text , location :: Text - , simplexContact :: Text - , simplexChannel :: Text + , simplexContact :: [Text] + , simplexChannel :: [Text] , eth :: Maybe Text , btc :: Maybe Text , xmr :: Maybe Text diff --git a/scripts/resolver/snrc-resolve.py b/scripts/resolver/snrc-resolve.py index 469d9f22c..f3de0cdff 100755 --- a/scripts/resolver/snrc-resolve.py +++ b/scripts/resolver/snrc-resolve.py @@ -12,10 +12,15 @@ deployment on Ethereum mainnet (or any compatible ENS-shaped registry) and returns a flat JSON document with these fields: name, nickname, website, location, - simplexContact, simplexChannel, + simplexContact, simplexChannel, -- list[str], primary first eth, btc, xmr, dot, owner, resolver +`simplexContact` and `simplexChannel` are arrays so a name can advertise +multiple SMP servers for redundancy. Clients SHOULD try the URLs in the +order returned. The on-chain text record stores them as a single +comma-separated string; this resolver splits and trims into a list. + All keys are valid Haskell record-field identifiers (lowercase initial, no dots), so consumers can derive aeson FromJSON instances directly without a key-rewriting layer. @@ -367,6 +372,18 @@ TEXT_KEYS = [ ] +def split_csv(value: str) -> list: + """Split a comma-separated text record into an ordered list of entries. + + Trims whitespace around each element and drops empties so trailing + commas, doubled commas, and all-whitespace inputs all yield clean + output. Single-value records yield a 1-element list; empty inputs + yield `[]`. Used for `simplex.contact` / `simplex.channel`, which + store one-or-more SMP-server URLs as a single comma-joined string. + """ + return [item.strip() for item in value.split(",") if item.strip()] + + def resolve(name: str): tld = name.rsplit(".", 1)[-1] registry = REGISTRIES.get(tld) @@ -412,8 +429,8 @@ def resolve(name: str): "nickname": nickname, "website": texts.get("url", ""), "location": texts.get("location", ""), - "simplexContact": texts.get("simplex.contact", ""), - "simplexChannel": texts.get("simplex.channel", ""), + "simplexContact": split_csv(texts.get("simplex.contact", "")), + "simplexChannel": split_csv(texts.get("simplex.channel", "")), "eth": addr_multicoin(resolver_addr, node, COIN_ETH), "btc": addr_multicoin(resolver_addr, node, COIN_BTC), "xmr": addr_multicoin(resolver_addr, node, COIN_XMR),