fix: route ElectrumX through Tor proxy, add onion server
- ElectrumxClient accepts injected SocketFactory (lambda) so connections respect the user's Tor/proxy settings instead of leaking their IP through raw sockets - Add ProxiedSocketFactory for SOCKS5 proxy routing - Add .onion ElectrumX server as primary when Tor is enabled, with electrumx.testls.space as clearnet fallback - NamecoinNameResolver accepts serverListProvider lambda for dynamic server selection based on current Tor settings - RoleBasedHttpClientBuilder.socketFactoryForNip05() bridges Amethyst's Tor settings to the socket factory - NamecoinNameService now requires explicit init with proxy-aware client - Remove dead code: Nip05NamecoinAdapter (never referenced), NamecoinVerificationDisplay (never called) - Update design documentation
This commit is contained in:
+40
-17
@@ -42,6 +42,7 @@ import java.net.InetSocketAddress
|
||||
import java.net.Socket
|
||||
import java.security.MessageDigest
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import javax.net.SocketFactory
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
import javax.net.ssl.TrustManager
|
||||
@@ -97,6 +98,7 @@ data class ElectrumxServer(
|
||||
class ElectrumxClient(
|
||||
private val connectTimeoutMs: Long = 10_000L,
|
||||
private val readTimeoutMs: Long = 15_000L,
|
||||
private val socketFactory: () -> SocketFactory = { SocketFactory.getDefault() },
|
||||
) {
|
||||
private val json =
|
||||
Json {
|
||||
@@ -107,7 +109,7 @@ class ElectrumxClient(
|
||||
private val mutex = Mutex()
|
||||
|
||||
companion object {
|
||||
/** Well-known public Namecoin ElectrumX servers. */
|
||||
/** Well-known public Namecoin ElectrumX servers (clearnet). */
|
||||
val DEFAULT_SERVERS =
|
||||
listOf(
|
||||
ElectrumxServer("electrumx.testls.space", 50002, useSsl = true, trustAllCerts = true),
|
||||
@@ -115,6 +117,18 @@ class ElectrumxClient(
|
||||
ElectrumxServer("nmc2.lelux.fi", 50006, useSsl = true),
|
||||
)
|
||||
|
||||
/** Tor-preferred server list: onion primary, clearnet fallback. */
|
||||
val TOR_SERVERS =
|
||||
listOf(
|
||||
ElectrumxServer(
|
||||
"i665jpwsq46zlsdbnj4axgzd3s56uzey5uhotsnxzsknzbn36jaddsid.onion",
|
||||
50002,
|
||||
useSsl = true,
|
||||
trustAllCerts = true,
|
||||
),
|
||||
ElectrumxServer("electrumx.testls.space", 50002, useSsl = true, trustAllCerts = true),
|
||||
)
|
||||
|
||||
private const val PROTOCOL_VERSION = "1.4"
|
||||
|
||||
// Namecoin script opcodes
|
||||
@@ -155,10 +169,16 @@ class ElectrumxClient(
|
||||
}
|
||||
|
||||
/**
|
||||
* Try each default server in order until one succeeds.
|
||||
* Try each server in order until one succeeds.
|
||||
*
|
||||
* @param identifier Full Namecoin name, e.g. "d/example"
|
||||
* @param servers Ordered server list to try; defaults to [DEFAULT_SERVERS]
|
||||
*/
|
||||
suspend fun nameShowWithFallback(identifier: String): NameShowResult? {
|
||||
for (server in DEFAULT_SERVERS) {
|
||||
suspend fun nameShowWithFallback(
|
||||
identifier: String,
|
||||
servers: List<ElectrumxServer> = DEFAULT_SERVERS,
|
||||
): NameShowResult? {
|
||||
for (server in servers) {
|
||||
val result = nameShow(identifier, server)
|
||||
if (result != null) return result
|
||||
}
|
||||
@@ -412,22 +432,25 @@ class ElectrumxClient(
|
||||
return data
|
||||
}
|
||||
|
||||
private fun createSocket(server: ElectrumxServer): Socket =
|
||||
if (server.useSsl) {
|
||||
val factory =
|
||||
if (server.trustAllCerts) {
|
||||
trustAllSslFactory()
|
||||
} else {
|
||||
SSLSocketFactory.getDefault() as SSLSocketFactory
|
||||
}
|
||||
factory.createSocket().apply {
|
||||
private fun createSocket(server: ElectrumxServer): Socket {
|
||||
// Create the base socket through the injected factory, which
|
||||
// may route through a SOCKS proxy (e.g. Tor) if configured.
|
||||
val baseSocket =
|
||||
socketFactory().createSocket().apply {
|
||||
connect(InetSocketAddress(server.host, server.port), connectTimeoutMs.toInt())
|
||||
}
|
||||
} else {
|
||||
Socket().apply {
|
||||
connect(InetSocketAddress(server.host, server.port), connectTimeoutMs.toInt())
|
||||
|
||||
if (!server.useSsl) return baseSocket
|
||||
|
||||
// Upgrade to TLS over the already-connected (possibly proxied) socket.
|
||||
val sslFactory =
|
||||
if (server.trustAllCerts) {
|
||||
trustAllSslFactory()
|
||||
} else {
|
||||
SSLSocketFactory.getDefault() as SSLSocketFactory
|
||||
}
|
||||
}
|
||||
return sslFactory.createSocket(baseSocket, server.host, server.port, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SSLSocketFactory that accepts any certificate.
|
||||
|
||||
+2
-1
@@ -52,6 +52,7 @@ data class NamecoinNostrResult(
|
||||
class NamecoinNameResolver(
|
||||
private val electrumxClient: ElectrumxClient = ElectrumxClient(),
|
||||
private val lookupTimeoutMs: Long = 20_000L,
|
||||
private val serverListProvider: () -> List<ElectrumxServer> = { ElectrumxClient.DEFAULT_SERVERS },
|
||||
) {
|
||||
private val json =
|
||||
Json {
|
||||
@@ -165,7 +166,7 @@ class NamecoinNameResolver(
|
||||
// ── Lookup & Value Parsing ─────────────────────────────────────────
|
||||
|
||||
private suspend fun performLookup(parsed: ParsedIdentifier): NamecoinNostrResult? {
|
||||
val nameResult = electrumxClient.nameShowWithFallback(parsed.namecoinName) ?: return null
|
||||
val nameResult = electrumxClient.nameShowWithFallback(parsed.namecoinName, serverListProvider()) ?: return null
|
||||
val valueJson = tryParseJson(nameResult.value) ?: return null
|
||||
|
||||
return when (parsed.namespace) {
|
||||
|
||||
Reference in New Issue
Block a user