Build 47: username lookup over clearnet, off Tor
The read-only NIP-05 username lookups (resolve + check_availability) carry no secret and need no anonymity — routing them over Tor is what made choosing a username slow. They now use a fast clearnet HTTPS GET (reqwest + rustls/ring, so it still cross-compiles to Android), falling back to Tor only if clearnet is blocked. The anonymity-critical paths (slatepack delivery, NIP-98-signed ops) are untouched.
This commit is contained in:
Generated
+164
-2
@@ -1176,7 +1176,7 @@ version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90dbd31c98227229239363921e60fcf5e558e43ec69094d46fc4996f08d1d5bc"
|
||||
dependencies = [
|
||||
"bitcoin_hashes 0.12.0",
|
||||
"bitcoin_hashes 0.14.100",
|
||||
"serde",
|
||||
"unicode-normalization",
|
||||
]
|
||||
@@ -4087,6 +4087,7 @@ dependencies = [
|
||||
"qrcodegen",
|
||||
"rand 0.9.2",
|
||||
"regex",
|
||||
"reqwest 0.12.28",
|
||||
"rfd",
|
||||
"ring 0.16.20",
|
||||
"rkv",
|
||||
@@ -4456,7 +4457,7 @@ dependencies = [
|
||||
"log",
|
||||
"rand 0.6.5",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest 0.10.10",
|
||||
"ring 0.16.20",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -5034,6 +5035,22 @@ dependencies = [
|
||||
"tokio-rustls 0.23.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
|
||||
dependencies = [
|
||||
"http 1.4.0",
|
||||
"hyper 1.8.1",
|
||||
"hyper-util",
|
||||
"rustls 0.23.40",
|
||||
"tokio 1.49.0",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tower-service",
|
||||
"webpki-roots 1.0.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-socks2"
|
||||
version = "0.9.1"
|
||||
@@ -5097,13 +5114,16 @@ version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes 1.11.1",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http-body 1.0.1",
|
||||
"hyper 1.8.1",
|
||||
"ipnet",
|
||||
"libc",
|
||||
"percent-encoding",
|
||||
"pin-project-lite 0.2.16",
|
||||
"socket2 0.6.2",
|
||||
"tokio 1.49.0",
|
||||
@@ -6004,6 +6024,12 @@ dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
@@ -7974,6 +8000,61 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||
dependencies = [
|
||||
"bytes 1.11.1",
|
||||
"cfg_aliases",
|
||||
"pin-project-lite 0.2.16",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls 0.23.40",
|
||||
"socket2 0.6.2",
|
||||
"thiserror 2.0.18",
|
||||
"tokio 1.49.0",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
|
||||
dependencies = [
|
||||
"bytes 1.11.1",
|
||||
"getrandom 0.3.4",
|
||||
"lru-slab",
|
||||
"rand 0.9.2",
|
||||
"ring 0.17.14",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls 0.23.40",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.18",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2 0.6.2",
|
||||
"tracing",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.6.13"
|
||||
@@ -8505,6 +8586,44 @@ dependencies = [
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes 1.11.1",
|
||||
"futures-core",
|
||||
"http 1.4.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.8.1",
|
||||
"hyper-rustls 0.27.9",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"pin-project-lite 0.2.16",
|
||||
"quinn",
|
||||
"rustls 0.23.40",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio 1.49.0",
|
||||
"tokio-rustls 0.26.4",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 1.0.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "resvg"
|
||||
version = "0.45.1"
|
||||
@@ -8904,6 +9023,7 @@ version = "1.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
|
||||
dependencies = [
|
||||
"web-time",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -9851,6 +9971,9 @@ name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synchronoise"
|
||||
@@ -11628,6 +11751,45 @@ dependencies = [
|
||||
"tor-memquota",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite 0.2.16",
|
||||
"sync_wrapper",
|
||||
"tokio 1.49.0",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"bytes 1.11.1",
|
||||
"futures-util",
|
||||
"http 1.4.0",
|
||||
"http-body 1.0.1",
|
||||
"pin-project-lite 0.2.16",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
|
||||
@@ -99,6 +99,10 @@ tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] }
|
||||
regex = "1"
|
||||
base64 = "0.22"
|
||||
hex = "0.4"
|
||||
## clearnet HTTP for the public NIP-05 username lookup (rustls, no native TLS
|
||||
## so it cross-compiles to Android) — read-only name→key, kept off the mixnet
|
||||
## on purpose because it's a "simple API lookup" and speed matters there.
|
||||
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
|
||||
|
||||
## tor
|
||||
arti-client = { version = "0.42.0", features = ["static", "pt-client", "onion-service-service", "onion-service-client"] }
|
||||
|
||||
+28
-3
@@ -68,6 +68,22 @@ fn is_valid_hostname(d: &str) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
/// A plain clearnet HTTPS GET (rustls). Used only for the public, read-only
|
||||
/// NIP-05 username lookups: those carry no secret and don't need anonymity,
|
||||
/// and routing them over Tor/the mixnet is exactly what made choosing a name
|
||||
/// feel slow. Returns (status, body).
|
||||
async fn clearnet_get(url: &str) -> Option<(u16, String)> {
|
||||
let client = reqwest::Client::builder()
|
||||
.user_agent("goblin-wallet")
|
||||
.timeout(std::time::Duration::from_secs(8))
|
||||
.build()
|
||||
.ok()?;
|
||||
let resp = client.get(url).send().await.ok()?;
|
||||
let status = resp.status().as_u16();
|
||||
let body = resp.text().await.ok()?;
|
||||
Some((status, body))
|
||||
}
|
||||
|
||||
/// Resolve a NIP-05 identifier (user@domain) to a pubkey + relay hints.
|
||||
pub async fn resolve(name: &str, domain: &str) -> Option<Nip05Resolution> {
|
||||
let url = format!(
|
||||
@@ -75,7 +91,11 @@ pub async fn resolve(name: &str, domain: &str) -> Option<Nip05Resolution> {
|
||||
domain,
|
||||
urlencode(name)
|
||||
);
|
||||
let body = Tor::http_request("GET", url, None, vec![]).await?;
|
||||
// Fast clearnet lookup first; fall back to Tor if clearnet is blocked.
|
||||
let body = match clearnet_get(&url).await {
|
||||
Some((200, b)) => b,
|
||||
_ => Tor::http_request("GET", url, None, vec![]).await?,
|
||||
};
|
||||
parse_well_known(&body, name)
|
||||
}
|
||||
|
||||
@@ -123,8 +143,13 @@ pub async fn check_availability(server: &str, name: &str) -> Availability {
|
||||
server.trim_end_matches('/'),
|
||||
urlencode(name)
|
||||
);
|
||||
let Some(body) = Tor::http_request("GET", url, None, vec![]).await else {
|
||||
return Availability::Unknown;
|
||||
// Fast clearnet lookup first; fall back to Tor if clearnet is blocked.
|
||||
let body = match clearnet_get(&url).await {
|
||||
Some((200, b)) => b,
|
||||
_ => match Tor::http_request("GET", url, None, vec![]).await {
|
||||
Some(b) => b,
|
||||
None => return Availability::Unknown,
|
||||
},
|
||||
};
|
||||
let Ok(doc) = serde_json::from_str::<Value>(&body) else {
|
||||
return Availability::Unknown;
|
||||
|
||||
Reference in New Issue
Block a user