From 71ac72e9f623e3e9c092a8dbfa49feff518f4f07 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Thu, 17 Apr 2025 14:17:26 +0200 Subject: [PATCH] upgrade internal tor to 0.4.8.15 --- build.gradle | 156 +------------- .../sparrowwallet/sparrow/AppServices.java | 2 +- .../com/sparrowwallet/sparrow/net/Tor.java | 195 ++++++++---------- .../sparrowwallet/sparrow/net/TorService.java | 52 ++--- .../sparrowwallet/sparrow/net/TorUtils.java | 8 +- src/main/java/module-info.java | 14 +- 6 files changed, 128 insertions(+), 299 deletions(-) diff --git a/build.gradle b/build.gradle index c04407a9..e24e6fed 100644 --- a/build.gradle +++ b/build.gradle @@ -19,19 +19,6 @@ if(System.getProperty("os.arch") == "aarch64") { } def headless = "true".equals(System.getProperty("java.awt.headless")) -def vTor = '4.7.13-4' -def vKmpTor = '1.4.3' -def kmpOs = osName -if(os.macOsX) { - kmpOs = "macos" -} else if(os.windows) { - kmpOs = "mingw" -} -def kmpArch = "x64" -if(System.getProperty("os.arch") == "aarch64") { - kmpArch = "arm64" -} - group "com.sparrowwallet" version "${sparrowVersion}" @@ -88,25 +75,9 @@ dependencies { implementation('com.sparrowwallet:hummingbird:1.7.4') implementation('co.nstant.in:cbor:0.9') implementation('org.openpnp:openpnp-capture-java:0.0.28-5') - implementation("io.matthewnelson.kotlin-components:kmp-tor:${vTor}-${vKmpTor}") { - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' - } - if(kmpOs == "linux" && kmpArch == "arm64") { - implementation("com.sparrowwallet.kmp-tor-binary:kmp-tor-binary-${kmpOs}${kmpArch}-jvm:${vTor}") { - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' - } - } else { - implementation("io.matthewnelson.kotlin-components:kmp-tor-binary-${kmpOs}${kmpArch}:${vTor}") { - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' - } - } - implementation("io.matthewnelson.kotlin-components:kmp-tor-binary-extract:${vTor}") { - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' - } - implementation("io.matthewnelson.kotlin-components:kmp-tor-ext-callback-manager:${vKmpTor}") { - exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' - } - implementation('org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.7.1') { + implementation("io.matthewnelson.kmp-tor:runtime:2.2.0") + implementation("io.matthewnelson.kmp-tor:resource-exec-tor-gpl:408.15.0") + implementation('org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.10.1') { exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' } implementation('de.codecentric.centerdevice:centerdevice-nsmenufx:2.1.7') @@ -190,6 +161,10 @@ application { "--add-opens=java.base/java.net=com.sparrowwallet.sparrow", "--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", + "--add-opens=io.matthewnelson.kmp.tor.resource.geoip/io.matthewnelson.kmp.tor.resource.geoip=io.matthewnelson.kmp.tor.common.core", + "--add-exports=io.matthewnelson.kmp.tor.runtime.ctrl/io.matthewnelson.kmp.tor.runtime.ctrl.internal=io.matthewnelson.kmp.tor.runtime", + "--add-reads=io.matthewnelson.kmp.tor.runtime=io.matthewnelson.kmp.tor.common.core", + "--add-reads=io.matthewnelson.kmp.tor.runtime.ctrl=io.matthewnelson.kmp.process", "--add-reads=kotlin.stdlib=kotlinx.coroutines.core", "--add-reads=org.flywaydb.core=java.desktop"] @@ -238,6 +213,10 @@ jlink { "--add-opens=java.base/java.net=com.sparrowwallet.sparrow", "--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", + "--add-opens=io.matthewnelson.kmp.tor.resource.geoip/io.matthewnelson.kmp.tor.resource.geoip=io.matthewnelson.kmp.tor.common.core", + "--add-exports=io.matthewnelson.kmp.tor.runtime.ctrl/io.matthewnelson.kmp.tor.runtime.ctrl.internal=io.matthewnelson.kmp.tor.runtime", + "--add-reads=io.matthewnelson.kmp.tor.runtime=io.matthewnelson.kmp.tor.common.core", + "--add-reads=io.matthewnelson.kmp.tor.runtime.ctrl=io.matthewnelson.kmp.process", "--add-reads=com.sparrowwallet.merged.module=java.desktop", "--add-reads=com.sparrowwallet.merged.module=java.sql", "--add-reads=com.sparrowwallet.merged.module=com.sparrowwallet.sparrow", @@ -472,119 +451,6 @@ extraJavaModuleInfo { exports('net.coobird.thumbnailator') requires('java.desktop') } - module("io.matthewnelson.kotlin-components:kmp-tor-jvm", 'kmp.tor.jvm') { - exports('io.matthewnelson.kmp.tor') - requires('kmp.tor.binary.extract.jvm') - requires('kmp.tor.manager.jvm') - requires('kmp.tor.manager.common.jvm') - requires('kmp.tor.controller.common.jvm') - requires('kotlin.stdlib') - requires('kotlinx.coroutines.core') - requires('java.management') - } - if(kmpOs == "linux" && kmpArch == "arm64") { - module("com.sparrowwallet.kmp-tor-binary:kmp-tor-binary-${kmpOs}${kmpArch}-jvm", "kmp.tor.binary.${kmpOs}${kmpArch}") { - exports("io.matthewnelson.kmp.tor.resource.${kmpOs}.${kmpArch}") - exports("kmptor.${kmpOs}.${kmpArch}") - } - } else { - module("io.matthewnelson.kotlin-components:kmp-tor-binary-${kmpOs}${kmpArch}-jvm", "kmp.tor.binary.${kmpOs}${kmpArch}") { - exports("io.matthewnelson.kmp.tor.binary.${kmpOs}.${kmpArch}") - exports("kmptor.${kmpOs}.${kmpArch}") - } - } - module("io.matthewnelson.kotlin-components:kmp-tor-binary-extract-jvm", 'kmp.tor.binary.extract.jvm') { - exports('io.matthewnelson.kmp.tor.binary.extract') - exports('io.matthewnelson.kmp.tor.binary.extract.internal') - requires('kotlin.stdlib') - requires("kmp.tor.binary.${kmpOs}${kmpArch}") - requires('kmp.tor.binary.geoip.jvm') - } - module("io.matthewnelson.kotlin-components:kmp-tor-manager-jvm", 'kmp.tor.manager.jvm') { - exports('io.matthewnelson.kmp.tor.manager') - exports('io.matthewnelson.kmp.tor.manager.util') - requires('kmp.tor.controller.common.jvm') - requires('kmp.tor.manager.common.jvm') - requires('kotlin.stdlib') - requires('kotlinx.coroutines.core') - requires('kotlinx.atomicfu') - requires('kmp.tor.controller.jvm') - requires('kmp.tor.common.jvm') - } - module("io.matthewnelson.kotlin-components:kmp-tor-manager-common-jvm", 'kmp.tor.manager.common.jvm') { - exports('io.matthewnelson.kmp.tor.manager.common') - exports('io.matthewnelson.kmp.tor.manager.common.event') - exports('io.matthewnelson.kmp.tor.manager.common.state') - requires('kmp.tor.controller.common.jvm') - requires('kmp.tor.common.jvm') - requires('kotlin.stdlib') - } - module("io.matthewnelson.kotlin-components:kmp-tor-controller-common-jvm", 'kmp.tor.controller.common.jvm') { - exports('io.matthewnelson.kmp.tor.controller.common.config') - exports('io.matthewnelson.kmp.tor.controller.common.file') - exports('io.matthewnelson.kmp.tor.controller.common.control') - exports('io.matthewnelson.kmp.tor.controller.common.control.usecase') - exports('io.matthewnelson.kmp.tor.controller.common.events') - exports('io.matthewnelson.kmp.tor.controller.common.exceptions') - requires('kmp.tor.common.jvm') - requires('kotlin.stdlib') - requires('kotlinx.atomicfu') - } - module("io.matthewnelson.kotlin-components:kmp-tor-common-jvm", 'kmp.tor.common.jvm') { - exports('io.matthewnelson.kmp.tor.common.address') - requires('parcelize.jvm') - requires('kotlin.stdlib') - } - module("io.matthewnelson.kotlin-components:kmp-tor-controller-jvm", 'kmp.tor.controller.jvm') { - exports('io.matthewnelson.kmp.tor.controller.internal.controller') - requires('kmp.tor.common.jvm') - requires('kmp.tor.controller.common.jvm') - requires('kotlinx.coroutines.core') - requires('kotlin.stdlib') - requires('kotlinx.atomicfu') - requires('encoding.core.jvm') - requires('encoding.base16.jvm') - } - module("io.matthewnelson.kotlin-components:kmp-tor-ext-callback-common-jvm", 'kmp.tor.ext.callback.common.jvm') { - exports('io.matthewnelson.kmp.tor.ext.callback.common') - } - module("io.matthewnelson.kotlin-components:kmp-tor-ext-callback-manager-jvm", 'kmp.tor.ext.callback.manager.jvm') { - exports('io.matthewnelson.kmp.tor.ext.callback.manager') - requires('kmp.tor.manager.jvm') - requires('kmp.tor.ext.callback.common.jvm') - requires('kmp.tor.ext.callback.manager.common.jvm') - requires('kmp.tor.ext.callback.controller.common.jvm') - requires('kmp.tor.manager.common.jvm') - requires('kmp.tor.controller.common.jvm') - requires('kotlin.stdlib') - requires('kotlinx.coroutines.core') - } - module("io.matthewnelson.kotlin-components:kmp-tor-ext-callback-manager-common-jvm", 'kmp.tor.ext.callback.manager.common.jvm') { - exports('io.matthewnelson.kmp.tor.ext.callback.manager.common') - requires('kmp.tor.ext.callback.controller.common.jvm') - } - module("io.matthewnelson.kotlin-components:kmp-tor-ext-callback-controller-common-jvm", 'kmp.tor.ext.callback.controller.common.jvm') { - exports('io.matthewnelson.kmp.tor.ext.callback.controller.common.control') - exports('io.matthewnelson.kmp.tor.ext.callback.controller.common.control.usecase') - } - module("io.matthewnelson.kotlin-components:kmp-tor-binary-geoip-jvm", 'kmp.tor.binary.geoip.jvm') { - exports('io.matthewnelson.kmp.tor.binary.geoip') - exports('kmptor') - } - module("base16-jvm-2.0.0.jar", 'encoding.base16.jvm', "2.0.0") { - exports('io.matthewnelson.encoding.base16') - requires('encoding.core.jvm') - requires('kotlin.stdlib') - } - module("base32-jvm-2.0.0.jar", 'encoding.base32.jvm', "2.0.0") - module("base64-jvm-2.0.0.jar", 'encoding.base64.jvm', "2.0.0") - module("core-jvm-2.0.0.jar", 'encoding.core.jvm', "2.0.0") { - exports('io.matthewnelson.encoding.core') - requires('kotlin.stdlib') - } - module("parcelize-jvm-0.1.2.jar", 'parcelize.jvm', "0.1.2") { - exports('io.matthewnelson.component.parcelize') - } module('org.jcommander:jcommander', 'org.jcommander') { exports('com.beust.jcommander') } diff --git a/src/main/java/com/sparrowwallet/sparrow/AppServices.java b/src/main/java/com/sparrowwallet/sparrow/AppServices.java index 04cf8d08..b6f27f87 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppServices.java @@ -261,7 +261,7 @@ public class AppServices { } if(Tor.getDefault() != null) { - Tor.getDefault().getTorManager().destroy(true, success -> {}); + Tor.getDefault().close(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/Tor.java b/src/main/java/com/sparrowwallet/sparrow/net/Tor.java index 97a5613d..4d4ec7b8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/Tor.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/Tor.java @@ -1,134 +1,89 @@ package com.sparrowwallet.sparrow.net; import com.google.common.net.HostAndPort; -import com.sparrowwallet.drongo.OsType; +import com.sparrowwallet.drongo.IOUtils; 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.jetbrains.annotations.NotNull; +import io.matthewnelson.kmp.tor.resource.exec.tor.ResourceLoaderTorExec; +import io.matthewnelson.kmp.tor.runtime.Action; +import io.matthewnelson.kmp.tor.runtime.RuntimeEvent; +import io.matthewnelson.kmp.tor.runtime.TorListeners; +import io.matthewnelson.kmp.tor.runtime.TorRuntime; +import io.matthewnelson.kmp.tor.runtime.core.OnEvent; +import io.matthewnelson.kmp.tor.runtime.core.OnFailure; +import io.matthewnelson.kmp.tor.runtime.core.OnSuccess; +import io.matthewnelson.kmp.tor.runtime.core.TorEvent; +import io.matthewnelson.kmp.tor.runtime.core.ctrl.TorCmd; +import io.matthewnelson.kmp.tor.runtime.core.net.IPSocketAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.Closeable; +import java.io.File; import java.net.InetSocketAddress; import java.net.Proxy; -import java.util.List; +import java.nio.file.Path; -public class Tor { +public class Tor implements Closeable { private static final Logger log = LoggerFactory.getLogger(Tor.class); public static final String TOR_DIR = "tor"; + public static final String WORK_DIR = "work"; + public static final String CACHE_DIR = "cache"; + public static final String OLD_INSTALL_DIR = ".kmptor"; 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; + private final TorRuntime instance; + private IPSocketAddress socksAddress; - public Tor() { - OsType osType = OsType.getCurrent(); - String arch = System.getProperty("os.arch"); - PlatformInstaller installer; - PlatformInstaller.InstallOption installOption = PlatformInstaller.InstallOption.CleanInstallIfMissing; - - if(osType == OsType.MACOS) { - if(arch.equals("aarch64")) { - installer = PlatformInstaller.macosArm64(installOption); - } else { - installer = PlatformInstaller.macosX64(installOption); - } - } else if(osType == OsType.WINDOWS) { - installer = PlatformInstaller.mingwX64(installOption); - } else if(osType == OsType.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 " + osType + " " + arch); + public Tor(OnEvent listener) { + Path path = Path.of(Storage.getSparrowHome().getAbsolutePath()).resolve(TOR_DIR); + File oldInstallDir = path.resolve(WORK_DIR).resolve(OLD_INSTALL_DIR).toFile(); + if(oldInstallDir.exists()) { + IOUtils.deleteDirectory(oldInstallDir); } - TorConfigProviderJvm torConfigProviderJvm = new TorConfigProviderJvm() { - @NotNull - @Override - public Path getWorkDir() { - return path.builder().addSegment("work").build(); - } + TorRuntime.Environment env = TorRuntime.Environment.Builder( + path.resolve(WORK_DIR).toFile(), + path.resolve(CACHE_DIR).toFile(), + ResourceLoaderTorExec::getOrCreate + ); - @NotNull - @Override - public Path getCacheDir() { - return path.builder().addSegment("cache").build(); - } + env.debug = true; - @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); - - TorConfig.Setting.Ports.Control controlPort = new TorConfig.Setting.Ports.Control(); - controlPort.set(TorConfig.Option.AorDorPort.Auto.INSTANCE); - builder.put(controlPort); - - 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(); + instance = TorRuntime.Builder(env, b -> { + RuntimeEvent.entries().forEach(event -> { + switch(event) { + case RuntimeEvent.LOG.ERROR _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> { + log.error(data.toString()); + EventManager.get().post(new TorStatusEvent(data.toString())); + }); + case RuntimeEvent.LOG.WARN _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> { + log.warn(data.toString()); + EventManager.get().post(new TorStatusEvent(data.toString())); + }); + case RuntimeEvent.LOG.INFO _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> { + log.info(data.toString()); + EventManager.get().post(new TorStatusEvent(data.toString())); + }); + case RuntimeEvent.LOG.DEBUG _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> { + log.debug(data.toString()); + EventManager.get().post(new TorStatusEvent(data.toString())); + }); + case null, default -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> { + log.trace(data.toString()); + }); } - } - }); + }); - this.instance = new CallbackTorManager(torManager, uncaughtException -> { - log.error("Uncaught exception from CallbackTorManager", uncaughtException); + b.observerStatic(RuntimeEvent.LISTENERS.INSTANCE, new TorSocksClient()); + b.observerStatic(RuntimeEvent.LISTENERS.INSTANCE, listener); + + b.required(TorEvent.ERR.INSTANCE); + b.required(TorEvent.WARN.INSTANCE); }); } @@ -140,7 +95,7 @@ public class Tor { Tor.tor = tor; } - public CallbackTorManager getTorManager() { + public TorRuntime getTorRuntime() { return instance; } @@ -149,14 +104,34 @@ public class Tor { throw new IllegalStateException("Tor socks proxy is not yet instantiated"); } - return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksAddress.address.getValue(), socksAddress.port.getValue())); + return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(socksAddress.address.value, socksAddress.port.value)); } public HostAndPort getProxyHostAndPort() { - return HostAndPort.fromParts(socksAddress.address.getValue(), socksAddress.port.getValue()); + return HostAndPort.fromParts(socksAddress.address.value, socksAddress.port.value); } - public static boolean isRunningExternally() { - return !PortUtil.isTcpPortAvailable(Port.invoke(9050)); + public void changeIdentity() { + if(instance != null) { + instance.enqueue(TorCmd.Signal.NewNym.INSTANCE, throwable -> { + log.warn("Failed to signal newnym", throwable); + }, _ -> { + log.info("Signalled newnym for new Tor circuit"); + }); + } + } + + @Override + public void close() { + if(instance != null) { + instance.enqueue(Action.StopDaemon, OnFailure.noOp(), OnSuccess.noOp()); + } + } + + private class TorSocksClient implements OnEvent { + @Override + public void invoke(TorListeners torListeners) { + socksAddress = torListeners.socks.isEmpty() ? null : torListeners.socks.iterator().next(); + } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TorService.java b/src/main/java/com/sparrowwallet/sparrow/net/TorService.java index ab4251ed..e6593cf2 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TorService.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TorService.java @@ -1,10 +1,12 @@ package com.sparrowwallet.sparrow.net; -import io.matthewnelson.kmp.tor.ext.callback.manager.CallbackTorManager; -import io.matthewnelson.kmp.tor.manager.common.event.TorManagerEvent; +import io.matthewnelson.kmp.tor.runtime.Action; +import io.matthewnelson.kmp.tor.runtime.TorListeners; +import io.matthewnelson.kmp.tor.runtime.TorRuntime; +import io.matthewnelson.kmp.tor.runtime.core.OnEvent; +import io.matthewnelson.kmp.tor.runtime.core.ctrl.Reply; import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; -import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,37 +34,25 @@ public class TorService extends ScheduledService { 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(); - } - } - } - }); - callbackTorManager.start(throwable -> { - if(throwable instanceof Exception exception) { + tor = new Tor(new StartupListener()); + TorRuntime torRuntime = tor.getTorRuntime(); + + torRuntime.enqueue(Action.StartDaemon, throwable -> { + if(throwable instanceof Reply.Error replyError && !replyError.replies.isEmpty()) { + startupException = new TorStartupException(replyError.replies.getFirst().message); + } else if(throwable instanceof Exception exception) { startupException = exception; } else { startupException = new Exception(throwable); } - log.error("Error", throwable); + log.error("Error starting Tor daemon", throwable); try { startupLock.lock(); startupCondition.signalAll(); } finally { startupLock.unlock(); } - }, success -> { - log.info("Tor daemon started successfully"); - }); + }, _ -> log.info("Tor daemon started successfully")); try { startupLock.lock(); @@ -82,4 +72,18 @@ public class TorService extends ScheduledService { } }; } + + private class StartupListener implements OnEvent { + @Override + public void invoke(TorListeners torListeners) { + if(!torListeners.socks.isEmpty()) { + try { + startupLock.lock(); + startupCondition.signalAll(); + } finally { + startupLock.unlock(); + } + } + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TorUtils.java b/src/main/java/com/sparrowwallet/sparrow/net/TorUtils.java index e012acd7..47e48cbb 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TorUtils.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TorUtils.java @@ -3,7 +3,6 @@ package com.sparrowwallet.sparrow.net; import com.google.common.net.HostAndPort; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.sparrow.AppServices; -import io.matthewnelson.kmp.tor.controller.common.control.usecase.TorControlSignal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,7 +12,6 @@ import java.net.SocketTimeoutException; import java.nio.file.AccessDeniedException; import java.nio.file.FileSystemException; import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -24,11 +22,7 @@ public class TorUtils { public static void changeIdentity(HostAndPort proxy) { 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"); - }); + Tor.getDefault().changeIdentity(); } else { HostAndPort control = HostAndPort.fromParts(proxy.getHost(), proxy.getPort() + 1); try(Socket socket = new Socket(control.getHost(), control.getPort())) { diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 73d025e3..25d92341 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -20,18 +20,8 @@ open module com.sparrowwallet.sparrow { requires org.jetbrains.annotations; requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.annotation; - requires kotlin.stdlib; - requires kmp.tor.jvm; - requires kmp.tor.binary.extract.jvm; - requires kmp.tor.common.jvm; - requires kmp.tor.controller.common.jvm; - requires kmp.tor.manager.jvm; - requires kmp.tor.manager.common.jvm; - requires kmp.tor.ext.callback.manager.jvm; - requires kmp.tor.ext.callback.common.jvm; - requires kmp.tor.ext.callback.manager.common.jvm; - requires kmp.tor.ext.callback.controller.common.jvm; - requires parcelize.jvm; + requires io.matthewnelson.kmp.tor.runtime; + requires io.matthewnelson.kmp.tor.resource.exec.tor; requires kotlinx.coroutines.javafx; requires org.slf4j; requires com.google.gson;