upgrade internal tor to 0.4.8.15

This commit is contained in:
Craig Raw 2025-04-17 14:17:26 +02:00
parent be8b56e355
commit 71ac72e9f6
6 changed files with 128 additions and 299 deletions

View file

@ -19,19 +19,6 @@ if(System.getProperty("os.arch") == "aarch64") {
} }
def headless = "true".equals(System.getProperty("java.awt.headless")) 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" group "com.sparrowwallet"
version "${sparrowVersion}" version "${sparrowVersion}"
@ -88,25 +75,9 @@ dependencies {
implementation('com.sparrowwallet:hummingbird:1.7.4') implementation('com.sparrowwallet:hummingbird:1.7.4')
implementation('co.nstant.in:cbor:0.9') implementation('co.nstant.in:cbor:0.9')
implementation('org.openpnp:openpnp-capture-java:0.0.28-5') implementation('org.openpnp:openpnp-capture-java:0.0.28-5')
implementation("io.matthewnelson.kotlin-components:kmp-tor:${vTor}-${vKmpTor}") { implementation("io.matthewnelson.kmp-tor:runtime:2.2.0")
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' implementation("io.matthewnelson.kmp-tor:resource-exec-tor-gpl:408.15.0")
} implementation('org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.10.1') {
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') {
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common' exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-common'
} }
implementation('de.codecentric.centerdevice:centerdevice-nsmenufx:2.1.7') 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.net=com.sparrowwallet.sparrow",
"--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.base/java.io=com.google.gson",
"--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", "--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=kotlin.stdlib=kotlinx.coroutines.core",
"--add-reads=org.flywaydb.core=java.desktop"] "--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.net=com.sparrowwallet.sparrow",
"--add-opens=java.base/java.io=com.google.gson", "--add-opens=java.base/java.io=com.google.gson",
"--add-opens=java.smartcardio/sun.security.smartcardio=com.sparrowwallet.sparrow", "--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.desktop",
"--add-reads=com.sparrowwallet.merged.module=java.sql", "--add-reads=com.sparrowwallet.merged.module=java.sql",
"--add-reads=com.sparrowwallet.merged.module=com.sparrowwallet.sparrow", "--add-reads=com.sparrowwallet.merged.module=com.sparrowwallet.sparrow",
@ -472,119 +451,6 @@ extraJavaModuleInfo {
exports('net.coobird.thumbnailator') exports('net.coobird.thumbnailator')
requires('java.desktop') 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') { module('org.jcommander:jcommander', 'org.jcommander') {
exports('com.beust.jcommander') exports('com.beust.jcommander')
} }

View file

@ -261,7 +261,7 @@ public class AppServices {
} }
if(Tor.getDefault() != null) { if(Tor.getDefault() != null) {
Tor.getDefault().getTorManager().destroy(true, success -> {}); Tor.getDefault().close();
} }
} }

View file

@ -1,134 +1,89 @@
package com.sparrowwallet.sparrow.net; package com.sparrowwallet.sparrow.net;
import com.google.common.net.HostAndPort; 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.EventManager;
import com.sparrowwallet.sparrow.event.TorStatusEvent; import com.sparrowwallet.sparrow.event.TorStatusEvent;
import com.sparrowwallet.sparrow.io.Storage; import com.sparrowwallet.sparrow.io.Storage;
import io.matthewnelson.kmp.tor.KmpTorLoaderJvm; import io.matthewnelson.kmp.tor.resource.exec.tor.ResourceLoaderTorExec;
import io.matthewnelson.kmp.tor.PlatformInstaller; import io.matthewnelson.kmp.tor.runtime.Action;
import io.matthewnelson.kmp.tor.TorConfigProviderJvm; import io.matthewnelson.kmp.tor.runtime.RuntimeEvent;
import io.matthewnelson.kmp.tor.binary.extract.TorBinaryResource; import io.matthewnelson.kmp.tor.runtime.TorListeners;
import io.matthewnelson.kmp.tor.common.address.Port; import io.matthewnelson.kmp.tor.runtime.TorRuntime;
import io.matthewnelson.kmp.tor.common.address.ProxyAddress; import io.matthewnelson.kmp.tor.runtime.core.OnEvent;
import io.matthewnelson.kmp.tor.controller.common.config.TorConfig; import io.matthewnelson.kmp.tor.runtime.core.OnFailure;
import io.matthewnelson.kmp.tor.controller.common.file.Path; import io.matthewnelson.kmp.tor.runtime.core.OnSuccess;
import io.matthewnelson.kmp.tor.ext.callback.manager.CallbackTorManager; import io.matthewnelson.kmp.tor.runtime.core.TorEvent;
import io.matthewnelson.kmp.tor.manager.TorManager; import io.matthewnelson.kmp.tor.runtime.core.ctrl.TorCmd;
import io.matthewnelson.kmp.tor.manager.common.event.TorManagerEvent; import io.matthewnelson.kmp.tor.runtime.core.net.IPSocketAddress;
import io.matthewnelson.kmp.tor.manager.util.PortUtil;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.File;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; 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); private static final Logger log = LoggerFactory.getLogger(Tor.class);
public static final String TOR_DIR = "tor"; 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"; public static final String TOR_ADDRESS_SUFFIX = ".onion";
private static Tor tor; private static Tor tor;
private final Path path = Path.invoke(Storage.getSparrowHome().getAbsolutePath()).builder().addSegment(TOR_DIR).build(); private final TorRuntime instance;
private final CallbackTorManager instance; private IPSocketAddress socksAddress;
private ProxyAddress socksAddress;
public Tor() { public Tor(OnEvent<TorListeners> listener) {
OsType osType = OsType.getCurrent(); Path path = Path.of(Storage.getSparrowHome().getAbsolutePath()).resolve(TOR_DIR);
String arch = System.getProperty("os.arch"); File oldInstallDir = path.resolve(WORK_DIR).resolve(OLD_INSTALL_DIR).toFile();
PlatformInstaller installer; if(oldInstallDir.exists()) {
PlatformInstaller.InstallOption installOption = PlatformInstaller.InstallOption.CleanInstallIfMissing; IOUtils.deleteDirectory(oldInstallDir);
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);
} }
TorConfigProviderJvm torConfigProviderJvm = new TorConfigProviderJvm() { TorRuntime.Environment env = TorRuntime.Environment.Builder(
@NotNull path.resolve(WORK_DIR).toFile(),
@Override path.resolve(CACHE_DIR).toFile(),
public Path getWorkDir() { ResourceLoaderTorExec::getOrCreate
return path.builder().addSegment("work").build(); );
}
@NotNull env.debug = true;
@Override
public Path getCacheDir() {
return path.builder().addSegment("cache").build();
}
@NotNull instance = TorRuntime.Builder(env, b -> {
@Override RuntimeEvent.entries().forEach(event -> {
protected TorConfig provide() { switch(event) {
TorConfig.Builder builder = new TorConfig.Builder(); case RuntimeEvent.LOG.ERROR _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> {
log.error(data.toString());
TorConfig.Setting.DormantCanceledByStartup dormantCanceledByStartup = new TorConfig.Setting.DormantCanceledByStartup(); EventManager.get().post(new TorStatusEvent(data.toString()));
dormantCanceledByStartup.set(TorConfig.Option.AorTorF.getTrue()); });
builder.put(dormantCanceledByStartup); case RuntimeEvent.LOG.WARN _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> {
log.warn(data.toString());
TorConfig.Setting.Ports.Control controlPort = new TorConfig.Setting.Ports.Control(); EventManager.get().post(new TorStatusEvent(data.toString()));
controlPort.set(TorConfig.Option.AorDorPort.Auto.INSTANCE); });
builder.put(controlPort); case RuntimeEvent.LOG.INFO _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> {
log.info(data.toString());
return builder.build(); EventManager.get().post(new TorStatusEvent(data.toString()));
} });
}; case RuntimeEvent.LOG.DEBUG _ -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> {
log.debug(data.toString());
KmpTorLoaderJvm jvmLoader = new KmpTorLoaderJvm(installer, torConfigProviderJvm); EventManager.get().post(new TorStatusEvent(data.toString()));
TorManager torManager = TorManager.newInstance(jvmLoader); });
case null, default -> b.observerStatic(event, OnEvent.Executor.Immediate.INSTANCE, data -> {
torManager.debug(true); log.trace(data.toString());
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 -> { b.observerStatic(RuntimeEvent.LISTENERS.INSTANCE, new TorSocksClient());
log.error("Uncaught exception from CallbackTorManager", uncaughtException); 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; Tor.tor = tor;
} }
public CallbackTorManager getTorManager() { public TorRuntime getTorRuntime() {
return instance; return instance;
} }
@ -149,14 +104,34 @@ public class Tor {
throw new IllegalStateException("Tor socks proxy is not yet instantiated"); 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() { 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() { public void changeIdentity() {
return !PortUtil.isTcpPortAvailable(Port.invoke(9050)); 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<TorListeners> {
@Override
public void invoke(TorListeners torListeners) {
socksAddress = torListeners.socks.isEmpty() ? null : torListeners.socks.iterator().next();
}
} }
} }

View file

@ -1,10 +1,12 @@
package com.sparrowwallet.sparrow.net; package com.sparrowwallet.sparrow.net;
import io.matthewnelson.kmp.tor.ext.callback.manager.CallbackTorManager; import io.matthewnelson.kmp.tor.runtime.Action;
import io.matthewnelson.kmp.tor.manager.common.event.TorManagerEvent; 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.ScheduledService;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -32,37 +34,25 @@ public class TorService extends ScheduledService<Tor> {
protected Tor call() throws Exception { protected Tor call() throws Exception {
Tor tor = Tor.getDefault(); Tor tor = Tor.getDefault();
if(tor == null) { if(tor == null) {
tor = new Tor(); tor = new Tor(new StartupListener());
CallbackTorManager callbackTorManager = tor.getTorManager(); TorRuntime torRuntime = tor.getTorRuntime();
callbackTorManager.addListener(new TorManagerEvent.Listener() {
@Override torRuntime.enqueue(Action.StartDaemon, throwable -> {
public void managerEventAddressInfo(@NotNull TorManagerEvent.AddressInfo info) { if(throwable instanceof Reply.Error replyError && !replyError.replies.isEmpty()) {
if(!info.isNull) { startupException = new TorStartupException(replyError.replies.getFirst().message);
try { } else if(throwable instanceof Exception exception) {
startupLock.lock();
startupCondition.signalAll();
} finally {
startupLock.unlock();
}
}
}
});
callbackTorManager.start(throwable -> {
if(throwable instanceof Exception exception) {
startupException = exception; startupException = exception;
} else { } else {
startupException = new Exception(throwable); startupException = new Exception(throwable);
} }
log.error("Error", throwable); log.error("Error starting Tor daemon", throwable);
try { try {
startupLock.lock(); startupLock.lock();
startupCondition.signalAll(); startupCondition.signalAll();
} finally { } finally {
startupLock.unlock(); startupLock.unlock();
} }
}, success -> { }, _ -> log.info("Tor daemon started successfully"));
log.info("Tor daemon started successfully");
});
try { try {
startupLock.lock(); startupLock.lock();
@ -82,4 +72,18 @@ public class TorService extends ScheduledService<Tor> {
} }
}; };
} }
private class StartupListener implements OnEvent<TorListeners> {
@Override
public void invoke(TorListeners torListeners) {
if(!torListeners.socks.isEmpty()) {
try {
startupLock.lock();
startupCondition.signalAll();
} finally {
startupLock.unlock();
}
}
}
}
} }

View file

@ -3,7 +3,6 @@ package com.sparrowwallet.sparrow.net;
import com.google.common.net.HostAndPort; import com.google.common.net.HostAndPort;
import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import io.matthewnelson.kmp.tor.controller.common.control.usecase.TorControlSignal;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -13,7 +12,6 @@ import java.net.SocketTimeoutException;
import java.nio.file.AccessDeniedException; import java.nio.file.AccessDeniedException;
import java.nio.file.FileSystemException; import java.nio.file.FileSystemException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -24,11 +22,7 @@ public class TorUtils {
public static void changeIdentity(HostAndPort proxy) { public static void changeIdentity(HostAndPort proxy) {
if(AppServices.isTorRunning()) { if(AppServices.isTorRunning()) {
Tor.getDefault().getTorManager().signal(TorControlSignal.Signal.NewNym, throwable -> { Tor.getDefault().changeIdentity();
log.warn("Failed to signal newnym");
}, successEvent -> {
log.info("Signalled newnym for new Tor circuit");
});
} else { } else {
HostAndPort control = HostAndPort.fromParts(proxy.getHost(), proxy.getPort() + 1); HostAndPort control = HostAndPort.fromParts(proxy.getHost(), proxy.getPort() + 1);
try(Socket socket = new Socket(control.getHost(), control.getPort())) { try(Socket socket = new Socket(control.getHost(), control.getPort())) {

View file

@ -20,18 +20,8 @@ open module com.sparrowwallet.sparrow {
requires org.jetbrains.annotations; requires org.jetbrains.annotations;
requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.annotation;
requires kotlin.stdlib; requires io.matthewnelson.kmp.tor.runtime;
requires kmp.tor.jvm; requires io.matthewnelson.kmp.tor.resource.exec.tor;
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 kotlinx.coroutines.javafx; requires kotlinx.coroutines.javafx;
requires org.slf4j; requires org.slf4j;
requires com.google.gson; requires com.google.gson;