mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
improve tor identity management
This commit is contained in:
parent
6f95dbe309
commit
c18a2f4388
6 changed files with 97 additions and 8 deletions
|
@ -2,11 +2,16 @@ package com.sparrowwallet.sparrow;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.LogHandler;
|
import com.sparrowwallet.drongo.LogHandler;
|
||||||
import com.sparrowwallet.sparrow.event.TorStatusEvent;
|
import com.sparrowwallet.sparrow.event.TorStatusEvent;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.slf4j.event.Level;
|
import org.slf4j.event.Level;
|
||||||
|
|
||||||
public class TorLogHandler implements LogHandler {
|
public class TorLogHandler implements LogHandler {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TorLogHandler.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleLog(String threadName, Level level, String message, String loggerName, long timestamp, StackTraceElement[] callerData) {
|
public void handleLog(String threadName, Level level, String message, String loggerName, long timestamp, StackTraceElement[] callerData) {
|
||||||
|
log.debug(message);
|
||||||
EventManager.get().post(new TorStatusEvent(message));
|
EventManager.get().post(new TorStatusEvent(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import com.sparrowwallet.sparrow.whirlpool.WhirlpoolException;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import org.controlsfx.glyphfont.Glyph;
|
import org.controlsfx.glyphfont.Glyph;
|
||||||
|
import org.controlsfx.tools.Platform;
|
||||||
|
|
||||||
public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
public MixStatusCell() {
|
public MixStatusCell() {
|
||||||
|
@ -68,7 +69,8 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
Tooltip tt = new Tooltip();
|
Tooltip tt = new Tooltip();
|
||||||
tt.setText(mixFailReason.getMessage() + (mixError == null ? "" : ": " + mixError) +
|
tt.setText(mixFailReason.getMessage() + (mixError == null ? "" : ": " + mixError) +
|
||||||
"\nMix failures are generally caused by peers disconnecting during a mix." +
|
"\nMix failures are generally caused by peers disconnecting during a mix." +
|
||||||
"\nMake sure your internet connection is stable and the computer is configured to prevent sleeping.");
|
"\nMake sure your internet connection is stable and the computer is configured to prevent sleeping." +
|
||||||
|
"\nTo prevent sleeping, use the " + getPlatformSleepConfig() + " or enable the function in the Tools menu.");
|
||||||
setTooltip(tt);
|
setTooltip(tt);
|
||||||
} else {
|
} else {
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
|
@ -76,6 +78,17 @@ public class MixStatusCell extends TreeTableCell<Entry, UtxoEntry.MixStatus> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getPlatformSleepConfig() {
|
||||||
|
Platform platform = Platform.getCurrent();
|
||||||
|
if(platform == Platform.OSX) {
|
||||||
|
return "OSX System Preferences";
|
||||||
|
} else if(platform == Platform.WINDOWS) {
|
||||||
|
return "Windows Control Panel";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "system power settings";
|
||||||
|
}
|
||||||
|
|
||||||
private void setMixProgress(MixProgress mixProgress) {
|
private void setMixProgress(MixProgress mixProgress) {
|
||||||
if(mixProgress.getMixStep() != MixStep.FAIL) {
|
if(mixProgress.getMixStep() != MixStep.FAIL) {
|
||||||
ProgressIndicator progressIndicator = getProgressIndicator();
|
ProgressIndicator progressIndicator = getProgressIndicator();
|
||||||
|
|
|
@ -3,15 +3,14 @@ package com.sparrowwallet.sparrow.net;
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import net.freehaven.tor.control.TorControlError;
|
import net.freehaven.tor.control.TorControlError;
|
||||||
import org.berndpruenster.netlayer.tor.NativeTor;
|
import org.berndpruenster.netlayer.tor.*;
|
||||||
import org.berndpruenster.netlayer.tor.Tor;
|
|
||||||
import org.berndpruenster.netlayer.tor.TorCtlException;
|
|
||||||
import org.berndpruenster.netlayer.tor.Torrc;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.Socket;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -40,6 +39,7 @@ public class TorService extends ScheduledService<NativeTor> {
|
||||||
try {
|
try {
|
||||||
LinkedHashMap<String, String> torrcOptionsMap = new LinkedHashMap<>();
|
LinkedHashMap<String, String> torrcOptionsMap = new LinkedHashMap<>();
|
||||||
torrcOptionsMap.put("SocksPort", Integer.toString(PROXY_PORT));
|
torrcOptionsMap.put("SocksPort", Integer.toString(PROXY_PORT));
|
||||||
|
torrcOptionsMap.put("HashedControlPassword", "16:D780432418F09B06609940000924317D3B9DF522A3191F8F4E597E9329");
|
||||||
torrcOptionsMap.put("DisableNetwork", "0");
|
torrcOptionsMap.put("DisableNetwork", "0");
|
||||||
Torrc override = new Torrc(torrcOptionsMap);
|
Torrc override = new Torrc(torrcOptionsMap);
|
||||||
|
|
||||||
|
@ -62,4 +62,25 @@ public class TorService extends ScheduledService<NativeTor> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import com.sparrowwallet.sparrow.whirlpool.dataPersister.SparrowDataPersister;
|
||||||
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowDataSource;
|
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowDataSource;
|
||||||
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowMinerFeeSupplier;
|
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowMinerFeeSupplier;
|
||||||
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowPostmixHandler;
|
import com.sparrowwallet.sparrow.whirlpool.dataSource.SparrowPostmixHandler;
|
||||||
|
import com.sparrowwallet.sparrow.whirlpool.tor.SparrowTorClientService;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
@ -87,7 +88,7 @@ public class Whirlpool {
|
||||||
this.whirlpoolServer = WhirlpoolServer.valueOf(network.getName().toUpperCase());
|
this.whirlpoolServer = WhirlpoolServer.valueOf(network.getName().toUpperCase());
|
||||||
this.httpClientService = new JavaHttpClientService(torProxy);
|
this.httpClientService = new JavaHttpClientService(torProxy);
|
||||||
this.stompClientService = new JavaStompClientService(httpClientService);
|
this.stompClientService = new JavaStompClientService(httpClientService);
|
||||||
this.torClientService = new WhirlpoolTorClientService();
|
this.torClientService = new SparrowTorClientService(this);
|
||||||
|
|
||||||
this.whirlpoolWalletService = new WhirlpoolWalletService();
|
this.whirlpoolWalletService = new WhirlpoolWalletService();
|
||||||
this.config = computeWhirlpoolWalletConfig(torProxy);
|
this.config = computeWhirlpoolWalletConfig(torProxy);
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
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.whirlpool.Whirlpool;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
public class SparrowTorClientService extends TorClientService {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SparrowTorClientService.class);
|
||||||
|
|
||||||
|
private final Whirlpool whirlpool;
|
||||||
|
|
||||||
|
public SparrowTorClientService(Whirlpool whirlpool) {
|
||||||
|
this.whirlpool = whirlpool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HostAndPort control = HostAndPort.fromParts(proxy.getHost(), proxy.getPort() + 1);
|
||||||
|
try(Socket socket = new Socket(control.getHost(), control.getPort())) {
|
||||||
|
writeNewNym(socket);
|
||||||
|
} catch(Exception e) {
|
||||||
|
log.warn("Error connecting to " + control + ", no Tor ControlPort configured?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeNewNym(Socket socket) throws IOException {
|
||||||
|
log.debug("Sending NEWNYM to " + socket);
|
||||||
|
socket.getOutputStream().write("AUTHENTICATE \"\"\r\n".getBytes());
|
||||||
|
socket.getOutputStream().write("SIGNAL NEWNYM\r\n".getBytes());
|
||||||
|
}
|
||||||
|
}
|
|
@ -145,7 +145,7 @@
|
||||||
<PasswordField fx:id="corePass"/>
|
<PasswordField fx:id="corePass"/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field text="Use Proxy:">
|
<Field text="Use Proxy:">
|
||||||
<UnlabeledToggleSwitch fx:id="coreUseProxy"/>
|
<UnlabeledToggleSwitch fx:id="coreUseProxy"/><HelpLabel helpText="Bitcoin Core RPC onion URLs, and all other non-RPC external addresses will be connected via this proxy if configured." />
|
||||||
</Field>
|
</Field>
|
||||||
<Field text="Proxy URL:">
|
<Field text="Proxy URL:">
|
||||||
<TextField fx:id="coreProxyHost" />
|
<TextField fx:id="coreProxyHost" />
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
</Button>
|
</Button>
|
||||||
</Field>
|
</Field>
|
||||||
<Field text="Use Proxy:">
|
<Field text="Use Proxy:">
|
||||||
<UnlabeledToggleSwitch fx:id="useProxy"/>
|
<UnlabeledToggleSwitch fx:id="useProxy"/><HelpLabel helpText="All external addresses will be connected via this proxy if configured." />
|
||||||
</Field>
|
</Field>
|
||||||
<Field text="Proxy URL:">
|
<Field text="Proxy URL:">
|
||||||
<TextField fx:id="proxyHost" />
|
<TextField fx:id="proxyHost" />
|
||||||
|
|
Loading…
Reference in a new issue