update tor to 0.4.7.13 using kmp-tor library

This commit is contained in:
Craig Raw
2023-03-28 14:38:20 +02:00
parent acab50cdcd
commit faa5a11c94
24 changed files with 435 additions and 213 deletions
@@ -50,7 +50,6 @@ import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.util.Duration;
import org.berndpruenster.netlayer.tor.Tor;
import org.controlsfx.glyphfont.Glyph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -252,7 +251,7 @@ public class AppServices {
}
if(Tor.getDefault() != null) {
Tor.getDefault().shutdown();
Tor.getDefault().getTorManager().destroy(true, success -> {});
}
}
@@ -414,23 +413,6 @@ public class AppServices {
EventManager.get().post(new TorReadyStatusEvent());
});
torService.setOnFailed(workerStateEvent -> {
Throwable exception = workerStateEvent.getSource().getException();
if(exception instanceof TorServerAlreadyBoundException) {
String proxyServer = Config.get().getProxyServer();
if(proxyServer == null || proxyServer.equals("")) {
proxyServer = "localhost:9050";
Config.get().setProxyServer(proxyServer);
}
if(proxyServer.equals("localhost:9050") || proxyServer.equals("127.0.0.1:9050")) {
Config.get().setUseProxy(true);
torService.cancel();
restartServices();
EventManager.get().post(new TorExternalStatusEvent());
return;
}
}
EventManager.get().post(new TorFailedStatusEvent(workerStateEvent.getSource().getException()));
});
@@ -490,8 +472,7 @@ public class AppServices {
InetSocketAddress proxyAddress = new InetSocketAddress(proxyHostAndPort.getHost(), proxyHostAndPort.getPortOrDefault(ProxyTcpOverTlsTransport.DEFAULT_PROXY_PORT));
proxy = new Proxy(Proxy.Type.SOCKS, proxyAddress);
} else if(AppServices.isTorRunning()) {
InetSocketAddress proxyAddress = new InetSocketAddress("localhost", TorService.PROXY_PORT);
proxy = new Proxy(Proxy.Type.SOCKS, proxyAddress);
proxy = Tor.getDefault().getProxy();
}
//Setting new proxy authentication credentials will force a new Tor circuit to be created
@@ -546,7 +527,7 @@ public class AppServices {
public static HostAndPort getTorProxy() {
return AppServices.isTorRunning() ?
HostAndPort.fromParts("localhost", TorService.PROXY_PORT) :
Tor.getDefault().getProxyHostAndPort() :
(Config.get().getProxyServer() == null || Config.get().getProxyServer().isEmpty() || !Config.get().isUseProxy() ? null : HostAndPort.fromString(Config.get().getProxyServer()));
}
@@ -1,17 +0,0 @@
package com.sparrowwallet.sparrow;
import com.sparrowwallet.drongo.LogHandler;
import com.sparrowwallet.sparrow.event.TorStatusEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
public class TorLogHandler implements LogHandler {
private static final Logger log = LoggerFactory.getLogger(TorLogHandler.class);
@Override
public void handleLog(String threadName, Level level, String message, String loggerName, long timestamp, StackTraceElement[] callerData) {
log.debug(message);
EventManager.get().post(new TorStatusEvent(message));
}
}
@@ -6,6 +6,7 @@ import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.io.Config;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Node;
@@ -33,15 +34,15 @@ public class TorStatusLabel extends Label {
}
public void update() {
boolean proxyInUse = AppServices.isUsingProxy();
boolean internal = AppServices.isTorRunning();
if(!proxyInUse || internal) {
if(!Config.get().isUseProxy()) {
torConnectionTest.cancel();
if(internal) {
if(AppServices.isTorRunning()) {
setTooltip(new Tooltip("Internal Tor proxy enabled"));
}
} else if(!torConnectionTest.isRunning()) {
if(torConnectionTest.getState() == Worker.State.CANCELLED || torConnectionTest.getState() == Worker.State.FAILED) {
torConnectionTest.reset();
}
torConnectionTest.setPeriod(Duration.seconds(20.0));
torConnectionTest.setBackoffStrategy(null);
torConnectionTest.setOnSucceeded(workerStateEvent -> {
@@ -1,13 +1,10 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.sparrow.net.TorServerAlreadyBoundException;
public class TorFailedStatusEvent extends TorStatusEvent {
private final Throwable exception;
public TorFailedStatusEvent(Throwable exception) {
super("Tor failed to start: " + (exception instanceof TorServerAlreadyBoundException ? exception.getCause().getMessage() + " Is a Tor proxy already running?" :
(exception.getCause() != null ? exception.getCause().getMessage() : exception.getMessage())));
super("Tor failed to start: " + (exception.getCause() != null ? exception.getCause().getMessage() : exception.getMessage()));
this.exception = exception;
}
@@ -128,7 +128,7 @@ public class Auth47 {
}
Proxy proxy = AppServices.getProxy();
if(proxy == null && callback.getHost().toLowerCase(Locale.ROOT).endsWith(TorService.TOR_ADDRESS_SUFFIX)) {
if(proxy == null && callback.getHost().toLowerCase(Locale.ROOT).endsWith(Tor.TOR_ADDRESS_SUFFIX)) {
throw new Auth47Exception("A Tor proxy must be configured to authenticate this resource.");
}
@@ -184,7 +184,7 @@ public class Bwt {
private HostAndPort getTorProxy() {
return AppServices.isTorRunning() ?
HostAndPort.fromParts("127.0.0.1", TorService.PROXY_PORT) :
Tor.getDefault().getProxyHostAndPort() :
(Config.get().getProxyServer() == null || Config.get().getProxyServer().isEmpty() || !Config.get().isUseProxy() ? null : HostAndPort.fromString(Config.get().getProxyServer().replace("localhost", "127.0.0.1")));
}
@@ -108,7 +108,7 @@ public class LnurlAuth {
}
Proxy proxy = AppServices.getProxy();
if(proxy == null && callback.getHost().toLowerCase(Locale.ROOT).endsWith(TorService.TOR_ADDRESS_SUFFIX)) {
if(proxy == null && callback.getHost().toLowerCase(Locale.ROOT).endsWith(Tor.TOR_ADDRESS_SUFFIX)) {
throw new LnurlAuthException("A Tor proxy must be configured to authenticate this resource.");
}
@@ -151,7 +151,7 @@ public enum Protocol {
}
public static boolean isOnionHost(String host) {
return host != null && host.toLowerCase(Locale.ROOT).endsWith(TorService.TOR_ADDRESS_SUFFIX);
return host != null && host.toLowerCase(Locale.ROOT).endsWith(Tor.TOR_ADDRESS_SUFFIX);
}
public static boolean isOnionAddress(Server server) {
@@ -32,7 +32,7 @@ public class ProxyTcpOverTlsTransport extends TcpOverTlsTransport {
protected void createSocket() throws IOException {
InetSocketAddress proxyAddr = new InetSocketAddress(proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT));
socket = new Socket(new Proxy(Proxy.Type.SOCKS, proxyAddr));
socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(Protocol.SSL.getDefaultPort())));
socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(getDefaultPort())));
socket = sslSocketFactory.createSocket(socket, proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT), true);
startHandshake((SSLSocket)socket);
}
@@ -125,4 +125,9 @@ public class TcpOverTlsTransport extends TcpTransport {
return Storage.getCertificateFile(server.getHost()) == null;
}
@Override
protected int getDefaultPort() {
return Protocol.SSL.getDefaultPort();
}
}
@@ -254,7 +254,11 @@ public class TcpTransport implements CloseableTransport, TimeoutCounter {
protected void createSocket() throws IOException {
socket = socketFactory.createSocket();
socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(Protocol.TCP.getDefaultPort())));
socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(getDefaultPort())));
}
protected int getDefaultPort() {
return Protocol.TCP.getDefaultPort();
}
public boolean isClosed() {
@@ -0,0 +1,158 @@
package com.sparrowwallet.sparrow.net;
import com.google.common.net.HostAndPort;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.TorStatusEvent;
import com.sparrowwallet.sparrow.io.Storage;
import io.matthewnelson.kmp.tor.KmpTorLoaderJvm;
import io.matthewnelson.kmp.tor.PlatformInstaller;
import io.matthewnelson.kmp.tor.TorConfigProviderJvm;
import io.matthewnelson.kmp.tor.binary.extract.TorBinaryResource;
import io.matthewnelson.kmp.tor.common.address.Port;
import io.matthewnelson.kmp.tor.common.address.ProxyAddress;
import io.matthewnelson.kmp.tor.controller.common.config.TorConfig;
import io.matthewnelson.kmp.tor.controller.common.file.Path;
import io.matthewnelson.kmp.tor.ext.callback.manager.CallbackTorManager;
import io.matthewnelson.kmp.tor.manager.TorManager;
import io.matthewnelson.kmp.tor.manager.common.event.TorManagerEvent;
import io.matthewnelson.kmp.tor.manager.util.PortUtil;
import org.controlsfx.tools.Platform;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
public class Tor {
private static final Logger log = LoggerFactory.getLogger(Tor.class);
public static final String TOR_DIR = "tor";
public static final String TOR_ADDRESS_SUFFIX = ".onion";
private static Tor tor;
private final Path path = Path.invoke(Storage.getSparrowHome().getAbsolutePath()).builder().addSegment(TOR_DIR).build();
private final CallbackTorManager instance;
private ProxyAddress socksAddress;
public Tor() {
Platform platform = Platform.getCurrent();
String arch = System.getProperty("os.arch");
PlatformInstaller installer;
PlatformInstaller.InstallOption installOption = PlatformInstaller.InstallOption.CleanInstallIfMissing;
if(platform == Platform.OSX) {
if(arch.equals("aarch64")) {
installer = PlatformInstaller.macosArm64(installOption);
} else {
installer = PlatformInstaller.macosX64(installOption);
}
} else if(platform == Platform.WINDOWS) {
installer = PlatformInstaller.mingwX64(installOption);
} else if(platform == Platform.UNIX) {
if(arch.equals("aarch64")) {
TorBinaryResource linuxArm64 = TorBinaryResource.from(TorBinaryResource.OS.Linux, "arm64",
"588496f3164d52b91f17e4db3372d8dfefa6366a8df265eebd4a28d4128992aa",
List.of("libevent-2.1.so.7.gz", "libstdc++.so.6.gz", "libcrypto.so.1.1.gz", "tor.gz", "libssl.so.1.1.gz"));
installer = PlatformInstaller.custom(installOption, linuxArm64);
} else {
installer = PlatformInstaller.linuxX64(installOption);
}
} else {
throw new UnsupportedOperationException("Sparrow's bundled Tor is not supported on " + platform + " " + arch);
}
TorConfigProviderJvm torConfigProviderJvm = new TorConfigProviderJvm() {
@NotNull
@Override
public Path getWorkDir() {
return path.builder().addSegment("work").build();
}
@NotNull
@Override
public Path getCacheDir() {
return path.builder().addSegment("cache").build();
}
@NotNull
@Override
protected TorConfig provide() {
TorConfig.Builder builder = new TorConfig.Builder();
TorConfig.Setting.DormantCanceledByStartup dormantCanceledByStartup = new TorConfig.Setting.DormantCanceledByStartup();
dormantCanceledByStartup.set(TorConfig.Option.AorTorF.getTrue());
builder.put(dormantCanceledByStartup);
return builder.build();
}
};
KmpTorLoaderJvm jvmLoader = new KmpTorLoaderJvm(installer, torConfigProviderJvm);
TorManager torManager = TorManager.newInstance(jvmLoader);
torManager.debug(true);
torManager.addListener(new TorManagerEvent.Listener() {
@Override
public void managerEventWarn(@NotNull String message) {
log.warn(message);
EventManager.get().post(new TorStatusEvent(message));
}
@Override
public void managerEventInfo(@NotNull String message) {
log.info(message);
EventManager.get().post(new TorStatusEvent(message));
}
@Override
public void managerEventDebug(@NotNull String message) {
log.debug(message);
EventManager.get().post(new TorStatusEvent(message));
}
@Override
public void managerEventAddressInfo(@NotNull TorManagerEvent.AddressInfo info) {
if(info.isNull) {
socksAddress = null;
} else {
socksAddress = info.socksInfoToProxyAddress().iterator().next();
}
}
});
this.instance = new CallbackTorManager(torManager, uncaughtException -> {
log.error("Uncaught exception from CallbackTorManager", uncaughtException);
});
}
public static Tor getDefault() {
return Tor.tor;
}
public static void setDefault(Tor tor) {
Tor.tor = tor;
}
public CallbackTorManager getTorManager() {
return instance;
}
public Proxy getProxy() {
if(socksAddress == null) {
throw new IllegalStateException("Tor socks proxy is not yet instantiated");
}
return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksAddress.address.getValue(), socksAddress.port.getValue()));
}
public HostAndPort getProxyHostAndPort() {
return HostAndPort.fromParts(socksAddress.address.getValue(), socksAddress.port.getValue());
}
public static boolean isRunningExternally() {
return !PortUtil.isTcpPortAvailable(Port.invoke(9050));
}
}
@@ -1,11 +0,0 @@
package com.sparrowwallet.sparrow.net;
public class TorServerAlreadyBoundException extends TorServerException {
public TorServerAlreadyBoundException(Throwable cause) {
super(cause);
}
public TorServerAlreadyBoundException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -5,6 +5,10 @@ public class TorServerException extends ServerException {
super(cause);
}
public TorServerException(String message) {
super(message);
}
public TorServerException(String message, Throwable cause) {
super(message, cause);
}
@@ -1,86 +1,85 @@
package com.sparrowwallet.sparrow.net;
import io.matthewnelson.kmp.tor.ext.callback.manager.CallbackTorManager;
import io.matthewnelson.kmp.tor.manager.common.event.TorManagerEvent;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import net.freehaven.tor.control.TorControlError;
import org.berndpruenster.netlayer.tor.*;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Service to start internal Tor (including a Tor proxy running on localhost:9050)
* Service to start internal Tor (including a Tor proxy running on localhost:9050, or another port if unavailable)
*
* This is a ScheduledService to take advantage of the retry on failure behaviour
*/
public class TorService extends ScheduledService<NativeTor> {
public class TorService extends ScheduledService<Tor> {
private static final Logger log = LoggerFactory.getLogger(TorService.class);
public static final int PROXY_PORT = 9050;
public static final String TOR_DIR_PREFIX = "tor";
public static final String TOR_ADDRESS_SUFFIX = ".onion";
private final ReentrantLock startupLock = new ReentrantLock();
private final Condition startupCondition = startupLock.newCondition();
@Override
protected Task<NativeTor> createTask() {
protected Task<Tor> createTask() {
return new Task<>() {
protected NativeTor call() throws IOException, TorServerException {
if(Tor.getDefault() == null) {
Path path = Files.createTempDirectory(TOR_DIR_PREFIX);
File torInstallDir = path.toFile();
torInstallDir.deleteOnExit();
try {
LinkedHashMap<String, String> torrcOptionsMap = new LinkedHashMap<>();
torrcOptionsMap.put("SocksPort", Integer.toString(PROXY_PORT));
torrcOptionsMap.put("HashedControlPassword", "16:D780432418F09B06609940000924317D3B9DF522A3191F8F4E597E9329");
torrcOptionsMap.put("DisableNetwork", "0");
Torrc override = new Torrc(torrcOptionsMap);
private Exception startupException;
return new NativeTor(torInstallDir, Collections.emptyList(), override);
} catch(TorCtlException e) {
if(e.getCause() instanceof TorControlError) {
if(e.getCause().getMessage().contains("Failed to bind")) {
throw new TorServerAlreadyBoundException("Tor server already bound", e.getCause());
@Override
protected Tor call() throws Exception {
Tor tor = Tor.getDefault();
if(tor == null) {
tor = new Tor();
CallbackTorManager callbackTorManager = tor.getTorManager();
callbackTorManager.addListener(new TorManagerEvent.Listener() {
@Override
public void managerEventAddressInfo(@NotNull TorManagerEvent.AddressInfo info) {
if(!info.isNull) {
try {
startupLock.lock();
startupCondition.signalAll();
} finally {
startupLock.unlock();
}
}
log.error("Failed to start Tor", e);
throw new TorServerException("Failed to start Tor", e.getCause());
} else {
log.error("Failed to start Tor", e);
throw new TorServerException("Failed to start Tor", e);
}
});
callbackTorManager.start(throwable -> {
if(throwable instanceof Exception exception) {
startupException = exception;
} else {
startupException = new Exception(throwable);
}
log.error("Error", throwable);
try {
startupLock.lock();
startupCondition.signalAll();
} finally {
startupLock.unlock();
}
}, success -> {
log.info("Tor daemon started successfully");
});
try {
startupLock.lock();
if(!startupCondition.await(5, TimeUnit.MINUTES)) {
throw new TorStartupException("Tor failed to start after 5 minutes, giving up");
}
if(startupException != null) {
throw startupException;
}
} finally {
startupLock.unlock();
}
}
return null;
return tor;
}
};
}
public static Socket getControlSocket() {
Tor tor = Tor.getDefault();
if(tor != null) {
try {
Class<?> torClass = Class.forName("org.berndpruenster.netlayer.tor.Tor");
Field torControllerField = torClass.getDeclaredField("torController");
torControllerField.setAccessible(true);
TorController torController = (TorController)torControllerField.get(tor);
Class<?> torControllerClass = Class.forName("org.berndpruenster.netlayer.tor.TorController");
Field socketField = torControllerClass.getDeclaredField("socket");
socketField.setAccessible(true);
return (Socket)socketField.get(torController);
} catch(Exception e) {
log.error("Error retrieving Tor control socket", e);
}
}
return null;
}
}
@@ -0,0 +1,15 @@
package com.sparrowwallet.sparrow.net;
public class TorStartupException extends TorServerException {
public TorStartupException(Throwable cause) {
super(cause);
}
public TorStartupException(String message) {
super(message);
}
public TorStartupException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -27,21 +27,15 @@ public class TorTcpOverTlsTransport extends TcpOverTlsTransport {
@Override
protected void createSocket() throws IOException {
TorTcpTransport torTcpTransport = new TorTcpTransport(server);
TorTcpTransport torTcpTransport = new TorTcpTransport(server) {
@Override
protected int getDefaultPort() {
return Protocol.SSL.getDefaultPort();
}
};
torTcpTransport.createSocket();
socket = torTcpTransport.socket;
try {
Field socketField = socket.getClass().getDeclaredField("socket");
socketField.setAccessible(true);
Socket innerSocket = (Socket)socketField.get(socket);
Field connectedField = innerSocket.getClass().getSuperclass().getDeclaredField("connected");
connectedField.setAccessible(true);
connectedField.set(innerSocket, true);
} catch(Exception e) {
log.error("Could not set socket connected status", e);
}
socket = sslSocketFactory.createSocket(socket, server.getHost(), server.getPortOrDefault(Protocol.SSL.getDefaultPort()), true);
startHandshake((SSLSocket)socket);
}
@@ -2,14 +2,12 @@ package com.sparrowwallet.sparrow.net;
import com.google.common.net.HostAndPort;
import com.sparrowwallet.sparrow.AppServices;
import org.berndpruenster.netlayer.tor.*;
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
public class TorTcpTransport extends TcpTransport {
public static final String TOR_DIR_PREFIX = "tor";
public TorTcpTransport(HostAndPort server) {
super(server);
}
@@ -24,6 +22,7 @@ public class TorTcpTransport extends TcpTransport {
throw new IllegalStateException("Can't create Tor socket, Tor is not running");
}
socket = new TorSocket(server.getHost(), server.getPortOrDefault(Protocol.TCP.getDefaultPort()), "sparrow");
socket = new Socket(Tor.getDefault().getProxy());
socket.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(getDefaultPort())));
}
}
@@ -28,8 +28,6 @@ import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.util.StringConverter;
import org.berndpruenster.netlayer.tor.Tor;
import org.controlsfx.control.SegmentedButton;
import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.validation.ValidationResult;
@@ -494,20 +492,6 @@ public class ServerPreferencesController extends PreferencesDetailController {
torService.cancel();
testResults.appendText("\nTor failed to start");
showConnectionFailure(workerStateEvent.getSource().getException());
Throwable exception = workerStateEvent.getSource().getException();
if(Config.get().getServerType() == ServerType.ELECTRUM_SERVER &&
exception instanceof TorServerAlreadyBoundException &&
useProxyOriginal == null && !useProxy.isSelected() &&
(proxyHost.getText().isEmpty() || proxyHost.getText().equals("localhost") || proxyHost.getText().equals("127.0.0.1")) &&
(proxyPort.getText().isEmpty() || proxyPort.getText().equals("9050"))) {
useProxy.setSelected(true);
proxyHost.setText("localhost");
proxyPort.setText("9050");
useProxyOriginal = false;
testResults.appendText("\n\nAssuming Tor proxy is running on port 9050 and trying again...");
startElectrumConnection();
}
});
torService.start();
@@ -658,8 +642,6 @@ public class ServerPreferencesController extends PreferencesDetailController {
reason = tlsServerException.getMessage() + "\n\n" + reason;
} else if(exception instanceof ProxyServerException) {
reason += ". Check if the proxy server is running.";
} else if(exception instanceof TorServerAlreadyBoundException) {
reason += "\nIs a Tor proxy already running on port " + TorService.PROXY_PORT + "?";
} else if(reason != null && (reason.contains("Check if Bitcoin Core is running") || reason.contains("Could not connect to Bitcoin Core RPC"))) {
reason += "\n\nSee https://sparrowwallet.com/docs/connect-node.html";
} else if(reason != null && (reason.startsWith("Cannot connect to hidden service"))) {
@@ -15,7 +15,6 @@ import com.sparrowwallet.sparrow.terminal.SparrowTerminal;
import javafx.application.Platform;
import javafx.scene.control.ButtonType;
import javafx.util.Duration;
import org.berndpruenster.netlayer.tor.Tor;
import java.io.File;
import java.text.DateFormat;
@@ -203,8 +202,6 @@ public class ServerTestDialog extends DialogWindow {
reason = tlsServerException.getMessage() + "\n\n" + reason;
} else if(exception instanceof ProxyServerException) {
reason += ". Check if the proxy server is running.";
} else if(exception instanceof TorServerAlreadyBoundException) {
reason += "\nIs a Tor proxy already running on port " + TorService.PROXY_PORT + "?";
} else if(reason != null && reason.contains("Check if Bitcoin Core is running")) {
reason += "\n\nSee https://sparrowwallet.com/docs/connect-node.html";
}
@@ -2,8 +2,10 @@ package com.sparrowwallet.sparrow.whirlpool.tor;
import com.google.common.net.HostAndPort;
import com.samourai.tor.client.TorClientService;
import com.sparrowwallet.sparrow.net.TorService;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.net.Tor;
import com.sparrowwallet.sparrow.whirlpool.Whirlpool;
import io.matthewnelson.kmp.tor.controller.common.control.usecase.TorControlSignal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -23,13 +25,12 @@ public class SparrowTorClientService extends TorClientService {
public void changeIdentity() {
HostAndPort proxy = whirlpool.getTorProxy();
if(proxy != null) {
Socket controlSocket = TorService.getControlSocket();
if(controlSocket != null) {
try {
writeNewNym(controlSocket);
} catch(Exception e) {
log.warn("Error sending NEWNYM to " + controlSocket, e);
}
if(AppServices.isTorRunning()) {
Tor.getDefault().getTorManager().signal(TorControlSignal.Signal.NewNym, throwable -> {
log.warn("Failed to signal newnym");
}, successEvent -> {
log.info("Signalled newnym for new Tor circuit");
});
} else {
HostAndPort control = HostAndPort.fromParts(proxy.getHost(), proxy.getPort() + 1);
try(Socket socket = new Socket(control.getHost(), control.getPort())) {