mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
move internal tor to a top level app service, support non-ssl proxying
This commit is contained in:
parent
095518e858
commit
e3d7bb57ee
18 changed files with 422 additions and 163 deletions
|
@ -1273,14 +1273,45 @@ public class AppController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void connectionStart(ConnectionStartEvent event) {
|
||||||
|
statusUpdated(new StatusEvent(event.getStatus(), 120));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void connectionFailed(ConnectionFailedEvent event) {
|
||||||
|
String reason = event.getException().getCause() != null ? event.getException().getCause().getMessage() : event.getException().getMessage();
|
||||||
|
String status = "Connection error: " + reason;
|
||||||
|
statusUpdated(new StatusEvent(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void connection(ConnectionEvent event) {
|
||||||
|
String status = "Connected to " + Config.get().getServerAddress() + " at height " + event.getBlockHeight();
|
||||||
|
statusUpdated(new StatusEvent(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void disconnection(DisconnectionEvent event) {
|
||||||
|
serverToggle.setDisable(false);
|
||||||
|
if(!AppServices.isConnecting() && !AppServices.isConnected() && !statusBar.getText().startsWith("Connection error")) {
|
||||||
|
statusUpdated(new StatusEvent("Disconnected"));
|
||||||
|
}
|
||||||
|
if(statusTimeline == null || statusTimeline.getStatus() != Animation.Status.RUNNING) {
|
||||||
|
statusBar.setProgress(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void bwtBootStatus(BwtBootStatusEvent event) {
|
public void bwtBootStatus(BwtBootStatusEvent event) {
|
||||||
serverToggle.setDisable(true);
|
serverToggle.setDisable(true);
|
||||||
|
if(AppServices.isConnecting()) {
|
||||||
statusUpdated(new StatusEvent(event.getStatus(), 60));
|
statusUpdated(new StatusEvent(event.getStatus(), 60));
|
||||||
if(statusTimeline == null || statusTimeline.getStatus() != Animation.Status.RUNNING) {
|
if(statusTimeline == null || statusTimeline.getStatus() != Animation.Status.RUNNING) {
|
||||||
statusBar.setProgress(0.01);
|
statusBar.setProgress(0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void bwtSyncStatus(BwtSyncStatusEvent event) {
|
public void bwtSyncStatus(BwtSyncStatusEvent event) {
|
||||||
|
@ -1313,14 +1344,21 @@ public class AppController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void disconnection(DisconnectionEvent event) {
|
public void torBootStatus(TorBootStatusEvent event) {
|
||||||
|
serverToggle.setDisable(true);
|
||||||
|
statusUpdated(new StatusEvent(event.getStatus(), 120));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void torFailedStatus(TorFailedStatusEvent event) {
|
||||||
serverToggle.setDisable(false);
|
serverToggle.setDisable(false);
|
||||||
if(!AppServices.isConnecting() && !AppServices.isConnected() && !statusBar.getText().startsWith("Connection error")) {
|
statusUpdated(new StatusEvent(event.getStatus()));
|
||||||
statusUpdated(new StatusEvent("Disconnected"));
|
|
||||||
}
|
|
||||||
if(statusTimeline == null || statusTimeline.getStatus() != Animation.Status.RUNNING) {
|
|
||||||
statusBar.setProgress(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void torReadyStatus(TorReadyStatusEvent event) {
|
||||||
|
serverToggle.setDisable(false);
|
||||||
|
statusUpdated(new StatusEvent(event.getStatus()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.sparrowwallet.sparrow;
|
package com.sparrowwallet.sparrow;
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
import com.sparrowwallet.drongo.address.Address;
|
import com.sparrowwallet.drongo.address.Address;
|
||||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
|
@ -31,11 +32,14 @@ import javafx.scene.text.Font;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import org.berndpruenster.netlayer.tor.Tor;
|
||||||
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.net.InetSocketAddress;
|
||||||
|
import java.net.Proxy;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
|
@ -45,9 +49,9 @@ import java.util.stream.Collectors;
|
||||||
public class AppServices {
|
public class AppServices {
|
||||||
private static final Logger log = LoggerFactory.getLogger(AppServices.class);
|
private static final Logger log = LoggerFactory.getLogger(AppServices.class);
|
||||||
|
|
||||||
private static final int SERVER_PING_PERIOD = 1 * 60 * 1000;
|
private static final int SERVER_PING_PERIOD_SECS = 60;
|
||||||
private static final int ENUMERATE_HW_PERIOD = 30 * 1000;
|
private static final int ENUMERATE_HW_PERIOD_SECS = 30;
|
||||||
private static final int RATES_PERIOD = 5 * 60 * 1000;
|
private static final int RATES_PERIOD_SECS = 5 * 60;
|
||||||
private static final int VERSION_CHECK_PERIOD_HOURS = 24;
|
private static final int VERSION_CHECK_PERIOD_HOURS = 24;
|
||||||
private static final ExchangeSource DEFAULT_EXCHANGE_SOURCE = ExchangeSource.COINGECKO;
|
private static final ExchangeSource DEFAULT_EXCHANGE_SOURCE = ExchangeSource.COINGECKO;
|
||||||
private static final Currency DEFAULT_FIAT_CURRENCY = Currency.getInstance("USD");
|
private static final Currency DEFAULT_FIAT_CURRENCY = Currency.getInstance("USD");
|
||||||
|
@ -68,6 +72,8 @@ public class AppServices {
|
||||||
|
|
||||||
private VersionCheckService versionCheckService;
|
private VersionCheckService versionCheckService;
|
||||||
|
|
||||||
|
private TorService torService;
|
||||||
|
|
||||||
private static Integer currentBlockHeight;
|
private static Integer currentBlockHeight;
|
||||||
|
|
||||||
private static Map<Integer, Double> targetBlockFeeRates;
|
private static Map<Integer, Double> targetBlockFeeRates;
|
||||||
|
@ -86,14 +92,10 @@ public class AppServices {
|
||||||
@Override
|
@Override
|
||||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean online) {
|
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean online) {
|
||||||
if(online) {
|
if(online) {
|
||||||
restartService(connectionService);
|
if(Config.get().requiresTor() && !isTorRunning()) {
|
||||||
|
torService.start();
|
||||||
if(ratesService.getExchangeSource() != ExchangeSource.NONE) {
|
} else {
|
||||||
restartService(ratesService);
|
restartServices();
|
||||||
}
|
|
||||||
|
|
||||||
if(Config.get().isCheckNewVersions()) {
|
|
||||||
restartService(versionCheckService);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
connectionService.cancel();
|
connectionService.cancel();
|
||||||
|
@ -103,6 +105,44 @@ public class AppServices {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public AppServices(MainApp application) {
|
||||||
|
this.application = application;
|
||||||
|
EventManager.get().register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
Config config = Config.get();
|
||||||
|
connectionService = createConnectionService();
|
||||||
|
ratesService = createRatesService(config.getExchangeSource(), config.getFiatCurrency());
|
||||||
|
versionCheckService = createVersionCheckService();
|
||||||
|
torService = createTorService();
|
||||||
|
|
||||||
|
onlineProperty.addListener(onlineServicesListener);
|
||||||
|
|
||||||
|
if(config.getMode() == Mode.ONLINE) {
|
||||||
|
if(config.requiresTor()) {
|
||||||
|
torService.start();
|
||||||
|
} else {
|
||||||
|
restartServices();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restartServices() {
|
||||||
|
Config config = Config.get();
|
||||||
|
if(config.hasServerAddress()) {
|
||||||
|
restartService(connectionService);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.isFetchRates()) {
|
||||||
|
restartService(ratesService);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config.isCheckNewVersions()) {
|
||||||
|
restartService(versionCheckService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void restartService(ScheduledService<?> service) {
|
private void restartService(ScheduledService<?> service) {
|
||||||
if(service.isRunning()) {
|
if(service.isRunning()) {
|
||||||
service.cancel();
|
service.cancel();
|
||||||
|
@ -117,33 +157,6 @@ public class AppServices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppServices(MainApp application) {
|
|
||||||
this.application = application;
|
|
||||||
EventManager.get().register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
Config config = Config.get();
|
|
||||||
connectionService = createConnectionService();
|
|
||||||
if(config.getMode() == Mode.ONLINE && config.getServerAddress() != null && !config.getServerAddress().isEmpty()) {
|
|
||||||
connectionService.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExchangeSource source = config.getExchangeSource() != null ? config.getExchangeSource() : DEFAULT_EXCHANGE_SOURCE;
|
|
||||||
Currency currency = config.getFiatCurrency() != null ? config.getFiatCurrency() : DEFAULT_FIAT_CURRENCY;
|
|
||||||
ratesService = createRatesService(source, currency);
|
|
||||||
if(config.getMode() == Mode.ONLINE && source != ExchangeSource.NONE) {
|
|
||||||
ratesService.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
versionCheckService = createVersionCheckService();
|
|
||||||
if(config.getMode() == Mode.ONLINE && config.isCheckNewVersions()) {
|
|
||||||
versionCheckService.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
onlineProperty.addListener(onlineServicesListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if(connectionService != null) {
|
if(connectionService != null) {
|
||||||
connectionService.cancel();
|
connectionService.cancel();
|
||||||
|
@ -156,20 +169,23 @@ public class AppServices {
|
||||||
if(versionCheckService != null) {
|
if(versionCheckService != null) {
|
||||||
versionCheckService.cancel();
|
versionCheckService.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(Tor.getDefault() != null) {
|
||||||
|
Tor.getDefault().shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ElectrumServer.ConnectionService createConnectionService() {
|
private ElectrumServer.ConnectionService createConnectionService() {
|
||||||
ElectrumServer.ConnectionService connectionService = new ElectrumServer.ConnectionService();
|
ElectrumServer.ConnectionService connectionService = new ElectrumServer.ConnectionService();
|
||||||
connectionService.setPeriod(new Duration(SERVER_PING_PERIOD));
|
connectionService.setPeriod(Duration.seconds(SERVER_PING_PERIOD_SECS));
|
||||||
connectionService.setRestartOnFailure(true);
|
connectionService.setRestartOnFailure(true);
|
||||||
|
|
||||||
EventManager.get().register(connectionService);
|
EventManager.get().register(connectionService);
|
||||||
connectionService.statusProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
if(connectionService.isRunning()) {
|
connectionService.setOnRunning(workerStateEvent -> {
|
||||||
EventManager.get().post(new StatusEvent(newValue));
|
if(!ElectrumServer.isConnected()) {
|
||||||
|
EventManager.get().post(new ConnectionStartEvent(Config.get().getServerAddress()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connectionService.setOnSucceeded(successEvent -> {
|
connectionService.setOnSucceeded(successEvent -> {
|
||||||
connectionService.setRestartOnFailure(true);
|
connectionService.setRestartOnFailure(true);
|
||||||
|
|
||||||
|
@ -201,8 +217,10 @@ public class AppServices {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExchangeSource.RatesService createRatesService(ExchangeSource exchangeSource, Currency currency) {
|
private ExchangeSource.RatesService createRatesService(ExchangeSource exchangeSource, Currency currency) {
|
||||||
ExchangeSource.RatesService ratesService = new ExchangeSource.RatesService(exchangeSource, currency);
|
ExchangeSource.RatesService ratesService = new ExchangeSource.RatesService(
|
||||||
ratesService.setPeriod(new Duration(RATES_PERIOD));
|
exchangeSource == null ? DEFAULT_EXCHANGE_SOURCE : exchangeSource,
|
||||||
|
currency == null ? DEFAULT_FIAT_CURRENCY : currency);
|
||||||
|
ratesService.setPeriod(Duration.seconds(RATES_PERIOD_SECS));
|
||||||
ratesService.setRestartOnFailure(true);
|
ratesService.setRestartOnFailure(true);
|
||||||
|
|
||||||
ratesService.setOnSucceeded(successEvent -> {
|
ratesService.setOnSucceeded(successEvent -> {
|
||||||
|
@ -230,7 +248,7 @@ public class AppServices {
|
||||||
|
|
||||||
private Hwi.ScheduledEnumerateService createDeviceEnumerateService() {
|
private Hwi.ScheduledEnumerateService createDeviceEnumerateService() {
|
||||||
Hwi.ScheduledEnumerateService enumerateService = new Hwi.ScheduledEnumerateService(null);
|
Hwi.ScheduledEnumerateService enumerateService = new Hwi.ScheduledEnumerateService(null);
|
||||||
enumerateService.setPeriod(new Duration(ENUMERATE_HW_PERIOD));
|
enumerateService.setPeriod(Duration.seconds(ENUMERATE_HW_PERIOD_SECS));
|
||||||
enumerateService.setOnSucceeded(workerStateEvent -> {
|
enumerateService.setOnSucceeded(workerStateEvent -> {
|
||||||
List<Device> devices = enumerateService.getValue();
|
List<Device> devices = enumerateService.getValue();
|
||||||
|
|
||||||
|
@ -247,6 +265,45 @@ public class AppServices {
|
||||||
return enumerateService;
|
return enumerateService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TorService createTorService() {
|
||||||
|
TorService torService = new TorService();
|
||||||
|
torService.setPeriod(Duration.hours(1000));
|
||||||
|
torService.setRestartOnFailure(true);
|
||||||
|
|
||||||
|
torService.setOnRunning(workerStateEvent -> {
|
||||||
|
EventManager.get().post(new TorBootStatusEvent());
|
||||||
|
});
|
||||||
|
torService.setOnSucceeded(workerStateEvent -> {
|
||||||
|
Tor.setDefault(torService.getValue());
|
||||||
|
torService.cancel();
|
||||||
|
restartServices();
|
||||||
|
EventManager.get().post(new TorReadyStatusEvent());
|
||||||
|
});
|
||||||
|
torService.setOnFailed(workerStateEvent -> {
|
||||||
|
EventManager.get().post(new TorFailedStatusEvent(workerStateEvent.getSource().getException()));
|
||||||
|
});
|
||||||
|
|
||||||
|
return torService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isTorRunning() {
|
||||||
|
return Tor.getDefault() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Proxy getProxy() {
|
||||||
|
Config config = Config.get();
|
||||||
|
if(config.isUseProxy()) {
|
||||||
|
HostAndPort proxy = HostAndPort.fromString(config.getProxyServer());
|
||||||
|
InetSocketAddress proxyAddress = new InetSocketAddress(proxy.getHost(), proxy.getPortOrDefault(ProxyTcpOverTlsTransport.DEFAULT_PROXY_PORT));
|
||||||
|
return new Proxy(Proxy.Type.SOCKS, proxyAddress);
|
||||||
|
} else if(AppServices.isTorRunning()) {
|
||||||
|
InetSocketAddress proxyAddress = new InetSocketAddress("localhost", TorService.PROXY_PORT);
|
||||||
|
return new Proxy(Proxy.Type.SOCKS, proxyAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
static void initialize(MainApp application) {
|
static void initialize(MainApp application) {
|
||||||
INSTANCE = new AppServices(application);
|
INSTANCE = new AppServices(application);
|
||||||
}
|
}
|
||||||
|
@ -404,16 +461,6 @@ public class AppServices {
|
||||||
targetBlockFeeRates = event.getTargetBlockFeeRates();
|
targetBlockFeeRates = event.getTargetBlockFeeRates();
|
||||||
addMempoolRateSizes(event.getMempoolRateSizes());
|
addMempoolRateSizes(event.getMempoolRateSizes());
|
||||||
minimumRelayFeeRate = event.getMinimumRelayFeeRate();
|
minimumRelayFeeRate = event.getMinimumRelayFeeRate();
|
||||||
String banner = event.getServerBanner();
|
|
||||||
String status = "Connected to " + Config.get().getServerAddress() + " at height " + event.getBlockHeight();
|
|
||||||
EventManager.get().post(new StatusEvent(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void connectionFailed(ConnectionFailedEvent event) {
|
|
||||||
String reason = event.getException().getCause() != null ? event.getException().getCause().getMessage() : event.getException().getMessage();
|
|
||||||
String status = "Connection error: " + reason;
|
|
||||||
EventManager.get().post(new StatusEvent(status));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
|
|
||||||
|
public class ConnectionStartEvent {
|
||||||
|
private final String status;
|
||||||
|
|
||||||
|
public ConnectionStartEvent(String serverAddress) {
|
||||||
|
this.status = AppServices.isTorRunning() ? "Tor running, connecting to " + serverAddress + "..." : "Connecting to " + serverAddress + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
public class TorBootStatusEvent extends TorStatusEvent {
|
||||||
|
public TorBootStatusEvent() {
|
||||||
|
super("Starting Tor...");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
public class TorFailedStatusEvent extends TorStatusEvent {
|
||||||
|
private final Throwable exception;
|
||||||
|
|
||||||
|
public TorFailedStatusEvent(Throwable exception) {
|
||||||
|
super("Tor failed to start: " + (exception.getCause() != null ?
|
||||||
|
(exception.getCause().getMessage().contains("Failed to bind") ? exception.getCause().getMessage() + " Is a Tor proxy already running?" : exception.getCause().getMessage() ) :
|
||||||
|
exception.getMessage()));
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Throwable getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
public class TorReadyStatusEvent extends TorStatusEvent {
|
||||||
|
public TorReadyStatusEvent() {
|
||||||
|
super("Tor started");
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,10 +4,7 @@ import com.google.gson.*;
|
||||||
import com.sparrowwallet.drongo.BitcoinUnit;
|
import com.sparrowwallet.drongo.BitcoinUnit;
|
||||||
import com.sparrowwallet.sparrow.Mode;
|
import com.sparrowwallet.sparrow.Mode;
|
||||||
import com.sparrowwallet.sparrow.Theme;
|
import com.sparrowwallet.sparrow.Theme;
|
||||||
import com.sparrowwallet.sparrow.net.CoreAuthType;
|
import com.sparrowwallet.sparrow.net.*;
|
||||||
import com.sparrowwallet.sparrow.net.ExchangeSource;
|
|
||||||
import com.sparrowwallet.sparrow.net.FeeRatesSource;
|
|
||||||
import com.sparrowwallet.sparrow.net.ServerType;
|
|
||||||
import com.sparrowwallet.sparrow.wallet.FeeRatesSelection;
|
import com.sparrowwallet.sparrow.wallet.FeeRatesSelection;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -143,6 +140,10 @@ public class Config {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFetchRates() {
|
||||||
|
return getExchangeSource() != ExchangeSource.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
public ExchangeSource getExchangeSource() {
|
public ExchangeSource getExchangeSource() {
|
||||||
return exchangeSource;
|
return exchangeSource;
|
||||||
}
|
}
|
||||||
|
@ -269,10 +270,27 @@ public class Config {
|
||||||
flush();
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasServerAddress() {
|
||||||
|
return getServerAddress() != null && !getServerAddress().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public String getServerAddress() {
|
public String getServerAddress() {
|
||||||
return getServerType() == ServerType.BITCOIN_CORE ? getCoreServer() : getElectrumServer();
|
return getServerType() == ServerType.BITCOIN_CORE ? getCoreServer() : getElectrumServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean requiresTor() {
|
||||||
|
if(isUseProxy() || !hasServerAddress()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Protocol protocol = Protocol.getProtocol(getServerAddress());
|
||||||
|
if(protocol == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol.isOnionAddress(protocol.getServerHostAndPort(getServerAddress()));
|
||||||
|
}
|
||||||
|
|
||||||
public String getCoreServer() {
|
public String getCoreServer() {
|
||||||
return coreServer;
|
return coreServer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,6 @@ public class Bwt {
|
||||||
this.terminating = false;
|
this.terminating = false;
|
||||||
this.ready = false;
|
this.ready = false;
|
||||||
this.shutdownPtr = null;
|
this.shutdownPtr = null;
|
||||||
Platform.runLater(() -> EventManager.get().post(new BwtShutdownEvent()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRunning() {
|
public boolean isRunning() {
|
||||||
|
|
|
@ -780,7 +780,6 @@ public class ElectrumServer {
|
||||||
private final ReentrantLock bwtStartLock = new ReentrantLock();
|
private final ReentrantLock bwtStartLock = new ReentrantLock();
|
||||||
private final Condition bwtStartCondition = bwtStartLock.newCondition();
|
private final Condition bwtStartCondition = bwtStartLock.newCondition();
|
||||||
private Throwable bwtStartException;
|
private Throwable bwtStartException;
|
||||||
private final StringProperty statusProperty = new SimpleStringProperty();
|
|
||||||
|
|
||||||
public ConnectionService() {
|
public ConnectionService() {
|
||||||
this(true);
|
this(true);
|
||||||
|
@ -930,13 +929,16 @@ public class ElectrumServer {
|
||||||
Bwt.DisconnectionService disconnectionService = bwt.getDisconnectionService();
|
Bwt.DisconnectionService disconnectionService = bwt.getDisconnectionService();
|
||||||
disconnectionService.setOnSucceeded(workerStateEvent -> {
|
disconnectionService.setOnSucceeded(workerStateEvent -> {
|
||||||
ElectrumServer.bwtElectrumServer = null;
|
ElectrumServer.bwtElectrumServer = null;
|
||||||
|
if(subscribe) {
|
||||||
|
EventManager.get().post(new BwtShutdownEvent());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
disconnectionService.setOnFailed(workerStateEvent -> {
|
disconnectionService.setOnFailed(workerStateEvent -> {
|
||||||
log.error("Failed to stop BWT", workerStateEvent.getSource().getException());
|
log.error("Failed to stop BWT", workerStateEvent.getSource().getException());
|
||||||
});
|
});
|
||||||
Platform.runLater(disconnectionService::start);
|
disconnectionService.start();
|
||||||
} else {
|
} else if(subscribe) {
|
||||||
Platform.runLater(() -> EventManager.get().post(new DisconnectionEvent()));
|
EventManager.get().post(new DisconnectionEvent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -951,11 +953,6 @@ public class ElectrumServer {
|
||||||
log.error("Uncaught error in ConnectionService", e);
|
log.error("Uncaught error in ConnectionService", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void torStatus(TorStatusEvent event) {
|
|
||||||
statusProperty.set(event.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void bwtElectrumReadyStatus(BwtElectrumReadyStatusEvent event) {
|
public void bwtElectrumReadyStatus(BwtElectrumReadyStatusEvent event) {
|
||||||
if(this.isRunning()) {
|
if(this.isRunning()) {
|
||||||
|
@ -984,10 +981,6 @@ public class ElectrumServer {
|
||||||
bwtStartLock.unlock();
|
bwtStartLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public StringProperty statusProperty() {
|
|
||||||
return statusProperty;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ReadRunnable implements Runnable {
|
public static class ReadRunnable implements Runnable {
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package com.sparrowwallet.sparrow.net;
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
import com.google.common.net.HostAndPort;
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.event.ExchangeRatesUpdatedEvent;
|
import com.sparrowwallet.sparrow.event.ExchangeRatesUpdatedEvent;
|
||||||
import com.sparrowwallet.sparrow.io.Config;
|
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.Service;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
|
@ -13,7 +12,6 @@ import org.slf4j.LoggerFactory;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -52,7 +50,7 @@ public enum ExchangeSource {
|
||||||
|
|
||||||
private CoinbaseRates getRates() {
|
private CoinbaseRates getRates() {
|
||||||
String url = "https://api.coinbase.com/v2/exchange-rates?currency=BTC";
|
String url = "https://api.coinbase.com/v2/exchange-rates?currency=BTC";
|
||||||
Proxy proxy = getProxy();
|
Proxy proxy = AppServices.getProxy();
|
||||||
|
|
||||||
try(InputStream is = (proxy == null ? new URL(url).openStream() : new URL(url).openConnection(proxy).getInputStream()); Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
try(InputStream is = (proxy == null ? new URL(url).openStream() : new URL(url).openConnection(proxy).getInputStream()); Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
|
@ -83,7 +81,7 @@ public enum ExchangeSource {
|
||||||
|
|
||||||
private CoinGeckoRates getRates() {
|
private CoinGeckoRates getRates() {
|
||||||
String url = "https://api.coingecko.com/api/v3/exchange_rates";
|
String url = "https://api.coingecko.com/api/v3/exchange_rates";
|
||||||
Proxy proxy = getProxy();
|
Proxy proxy = AppServices.getProxy();
|
||||||
|
|
||||||
try(InputStream is = (proxy == null ? new URL(url).openStream() : new URL(url).openConnection(proxy).getInputStream()); Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
try(InputStream is = (proxy == null ? new URL(url).openStream() : new URL(url).openConnection(proxy).getInputStream()); Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
|
@ -116,17 +114,6 @@ public enum ExchangeSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Proxy getProxy() {
|
|
||||||
Config config = Config.get();
|
|
||||||
if(config.isUseProxy()) {
|
|
||||||
HostAndPort proxy = HostAndPort.fromString(config.getProxyServer());
|
|
||||||
InetSocketAddress proxyAddress = new InetSocketAddress(proxy.getHost(), proxy.getPortOrDefault(ProxyTcpOverTlsTransport.DEFAULT_PROXY_PORT));
|
|
||||||
return new Proxy(Proxy.Type.SOCKS, proxyAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
|
|
|
@ -28,12 +28,13 @@ public enum Protocol {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transport getTransport(HostAndPort server, HostAndPort proxy) {
|
public Transport getTransport(HostAndPort server, HostAndPort proxy) {
|
||||||
throw new UnsupportedOperationException("TCP protocol does not support proxying");
|
//Avoid using a TorSocket if a proxy is specified, even if a .onion address
|
||||||
|
return new TcpTransport(server, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) {
|
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) {
|
||||||
throw new UnsupportedOperationException("TCP protocol does not support proxying");
|
return getTransport(server, proxy);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
SSL {
|
SSL {
|
||||||
|
@ -116,7 +117,7 @@ public enum Protocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOnionAddress(HostAndPort server) {
|
public boolean isOnionAddress(HostAndPort server) {
|
||||||
return server.getHost().toLowerCase().endsWith(".onion");
|
return server.getHost().toLowerCase().endsWith(TorService.TOR_ADDRESS_SUFFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Protocol getProtocol(String url) {
|
public static Protocol getProtocol(String url) {
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import static com.sparrowwallet.sparrow.net.ProxyTcpOverTlsTransport.DEFAULT_PROXY_PORT;
|
||||||
|
|
||||||
|
public class ProxySocketFactory extends SocketFactory {
|
||||||
|
private final Proxy proxy;
|
||||||
|
|
||||||
|
public ProxySocketFactory() {
|
||||||
|
this(Proxy.NO_PROXY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProxySocketFactory(HostAndPort proxyHostAndPort) {
|
||||||
|
this(getSocksProxy(proxyHostAndPort.getHost(), proxyHostAndPort.getPortOrDefault(DEFAULT_PROXY_PORT)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProxySocketFactory(Proxy proxy) {
|
||||||
|
this.proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket() {
|
||||||
|
return new Socket(proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String address, int port) throws IOException {
|
||||||
|
return createSocket(new InetSocketAddress(address, port), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(String address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||||
|
return createSocket(new InetSocketAddress(address, port), new InetSocketAddress(localAddress, localPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress address, int port) throws IOException {
|
||||||
|
return createSocket(new InetSocketAddress(address, port), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
|
||||||
|
return createSocket(new InetSocketAddress(address, port), new InetSocketAddress(localAddress, localPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Socket createSocket(InetSocketAddress address, InetSocketAddress bindAddress) throws IOException {
|
||||||
|
Socket socket = new Socket(proxy);
|
||||||
|
if(bindAddress != null) {
|
||||||
|
socket.bind(bindAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.connect(address);
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Proxy getSocksProxy(String proxyAddress, int proxyPort) {
|
||||||
|
return new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyAddress, proxyPort));
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,8 +47,12 @@ public class TcpTransport implements Transport, Closeable {
|
||||||
private final Gson gson = new Gson();
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
public TcpTransport(HostAndPort server) {
|
public TcpTransport(HostAndPort server) {
|
||||||
|
this(server, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TcpTransport(HostAndPort server, HostAndPort proxy) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.socketFactory = SocketFactory.getDefault();
|
this.socketFactory = (proxy == null ? SocketFactory.getDefault() : new ProxySocketFactory(proxy));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
61
src/main/java/com/sparrowwallet/sparrow/net/TorService.java
Normal file
61
src/main/java/com/sparrowwallet/sparrow/net/TorService.java
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import javafx.concurrent.ScheduledService;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import net.freehaven.tor.control.TorControlError;
|
||||||
|
import org.berndpruenster.netlayer.tor.NativeTor;
|
||||||
|
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.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to start internal Tor (including a Tor proxy running on localhost:9050)
|
||||||
|
*
|
||||||
|
* This is a ScheduledService to take advantage of the retry on failure behaviour
|
||||||
|
*/
|
||||||
|
public class TorService extends ScheduledService<NativeTor> {
|
||||||
|
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";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Task<NativeTor> createTask() {
|
||||||
|
return new Task<>() {
|
||||||
|
protected NativeTor call() throws IOException {
|
||||||
|
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("DisableNetwork", "0");
|
||||||
|
Torrc override = new Torrc(torrcOptionsMap);
|
||||||
|
|
||||||
|
return new NativeTor(torInstallDir, Collections.emptyList(), override);
|
||||||
|
} catch(TorCtlException e) {
|
||||||
|
log.error("Failed to start Tor", e);
|
||||||
|
if(e.getCause() instanceof TorControlError) {
|
||||||
|
throw new IOException("Failed to start Tor", e.getCause());
|
||||||
|
} else {
|
||||||
|
throw new IOException("Failed to start Tor", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,11 @@
|
||||||
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.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.event.StatusEvent;
|
|
||||||
import com.sparrowwallet.sparrow.event.TorStatusEvent;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import org.berndpruenster.netlayer.tor.*;
|
import org.berndpruenster.netlayer.tor.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
|
|
||||||
public class TorTcpTransport extends TcpTransport {
|
public class TorTcpTransport extends TcpTransport {
|
||||||
public static final String TOR_DIR_PREFIX = "tor";
|
public static final String TOR_DIR_PREFIX = "tor";
|
||||||
|
@ -21,34 +14,15 @@ public class TorTcpTransport extends TcpTransport {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TorTcpTransport(HostAndPort server, HostAndPort proxy) {
|
||||||
|
super(server, proxy);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Socket createSocket() throws IOException {
|
protected Socket createSocket() throws IOException {
|
||||||
if(Tor.getDefault() == null) {
|
if(!AppServices.isTorRunning()) {
|
||||||
Platform.runLater(() -> {
|
throw new IllegalStateException("Can't create Tor socket, Tor is not running");
|
||||||
String status = "Starting Tor...";
|
|
||||||
EventManager.get().post(new TorStatusEvent(status));
|
|
||||||
});
|
|
||||||
|
|
||||||
Path path = Files.createTempDirectory(TOR_DIR_PREFIX);
|
|
||||||
File torInstallDir = path.toFile();
|
|
||||||
torInstallDir.deleteOnExit();
|
|
||||||
try {
|
|
||||||
LinkedHashMap<String, String> torrcOptionsMap = new LinkedHashMap<>();
|
|
||||||
torrcOptionsMap.put("DisableNetwork", "0");
|
|
||||||
Torrc override = new Torrc(torrcOptionsMap);
|
|
||||||
|
|
||||||
NativeTor nativeTor = new NativeTor(torInstallDir, Collections.emptyList(), override);
|
|
||||||
Tor.setDefault(nativeTor);
|
|
||||||
} catch(TorCtlException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
String status = "Tor running, connecting to " + server.toString() + "...";
|
|
||||||
EventManager.get().post(new TorStatusEvent(status));
|
|
||||||
});
|
|
||||||
|
|
||||||
return new TorSocket(server.getHost(), server.getPort(), "sparrow");
|
return new TorSocket(server.getHost(), server.getPort(), "sparrow");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.sparrowwallet.drongo.address.Address;
|
||||||
import com.sparrowwallet.drongo.address.InvalidAddressException;
|
import com.sparrowwallet.drongo.address.InvalidAddressException;
|
||||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.MainApp;
|
import com.sparrowwallet.sparrow.MainApp;
|
||||||
import com.sparrowwallet.sparrow.event.VersionUpdatedEvent;
|
import com.sparrowwallet.sparrow.event.VersionUpdatedEvent;
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
|
@ -15,6 +16,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.Proxy;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
@ -44,7 +46,8 @@ public class VersionCheckService extends ScheduledService<VersionUpdatedEvent> {
|
||||||
|
|
||||||
private VersionCheck getVersionCheck() throws IOException {
|
private VersionCheck getVersionCheck() throws IOException {
|
||||||
URL url = new URL(VERSION_CHECK_URL);
|
URL url = new URL(VERSION_CHECK_URL);
|
||||||
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
|
Proxy proxy = AppServices.getProxy();
|
||||||
|
HttpsURLConnection conn = (HttpsURLConnection)(proxy == null ? url.openConnection() : url.openConnection(proxy));
|
||||||
|
|
||||||
try(InputStreamReader reader = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)) {
|
try(InputStreamReader reader = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)) {
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
|
|
|
@ -14,7 +14,6 @@ import com.sparrowwallet.sparrow.io.Config;
|
||||||
import com.sparrowwallet.sparrow.net.*;
|
import com.sparrowwallet.sparrow.net.*;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.concurrent.WorkerStateEvent;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
|
@ -22,6 +21,8 @@ import javafx.stage.DirectoryChooser;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import net.freehaven.tor.control.TorControlError;
|
||||||
|
import org.berndpruenster.netlayer.tor.Tor;
|
||||||
import org.controlsfx.glyphfont.Glyph;
|
import org.controlsfx.glyphfont.Glyph;
|
||||||
import org.controlsfx.validation.ValidationResult;
|
import org.controlsfx.validation.ValidationResult;
|
||||||
import org.controlsfx.validation.ValidationSupport;
|
import org.controlsfx.validation.ValidationSupport;
|
||||||
|
@ -121,6 +122,8 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
|
|
||||||
private final ValidationSupport validationSupport = new ValidationSupport();
|
private final ValidationSupport validationSupport = new ValidationSupport();
|
||||||
|
|
||||||
|
private TorService torService;
|
||||||
|
|
||||||
private ElectrumServer.ConnectionService connectionService;
|
private ElectrumServer.ConnectionService connectionService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -242,13 +245,6 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
proxyHost.setText(proxyHost.getText().trim());
|
proxyHost.setText(proxyHost.getText().trim());
|
||||||
proxyHost.setDisable(!newValue);
|
proxyHost.setDisable(!newValue);
|
||||||
proxyPort.setDisable(!newValue);
|
proxyPort.setDisable(!newValue);
|
||||||
|
|
||||||
if(newValue) {
|
|
||||||
electrumUseSsl.setSelected(true);
|
|
||||||
electrumUseSsl.setDisable(true);
|
|
||||||
} else {
|
|
||||||
electrumUseSsl.setDisable(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
boolean isConnected = AppServices.isConnecting() || AppServices.isConnected();
|
boolean isConnected = AppServices.isConnecting() || AppServices.isConnected();
|
||||||
|
@ -263,7 +259,12 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
testConnection.setOnAction(event -> {
|
testConnection.setOnAction(event -> {
|
||||||
testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.ELLIPSIS_H, null));
|
testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.ELLIPSIS_H, null));
|
||||||
testResults.setText("Connecting to " + config.getServerAddress() + "...");
|
testResults.setText("Connecting to " + config.getServerAddress() + "...");
|
||||||
|
|
||||||
|
if(Config.get().requiresTor() && Tor.getDefault() == null) {
|
||||||
|
startTor();
|
||||||
|
} else {
|
||||||
startElectrumConnection();
|
startElectrumConnection();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
editConnection.managedProperty().bind(editConnection.visibleProperty());
|
editConnection.managedProperty().bind(editConnection.visibleProperty());
|
||||||
|
@ -340,11 +341,6 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
proxyHost.setDisable(!config.isUseProxy());
|
proxyHost.setDisable(!config.isUseProxy());
|
||||||
proxyPort.setDisable(!config.isUseProxy());
|
proxyPort.setDisable(!config.isUseProxy());
|
||||||
|
|
||||||
if(config.isUseProxy()) {
|
|
||||||
electrumUseSsl.setSelected(true);
|
|
||||||
electrumUseSsl.setDisable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
String proxyServer = config.getProxyServer();
|
String proxyServer = config.getProxyServer();
|
||||||
if(proxyServer != null) {
|
if(proxyServer != null) {
|
||||||
HostAndPort server = HostAndPort.fromString(proxyServer);
|
HostAndPort server = HostAndPort.fromString(proxyServer);
|
||||||
|
@ -357,6 +353,32 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
setFieldsEditable(!isConnected);
|
setFieldsEditable(!isConnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startTor() {
|
||||||
|
if(torService != null && torService.isRunning()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
torService = new TorService();
|
||||||
|
torService.setPeriod(Duration.hours(1000));
|
||||||
|
torService.setRestartOnFailure(false);
|
||||||
|
|
||||||
|
torService.setOnRunning(workerStateEvent -> {
|
||||||
|
testResults.setText(testResults.getText() + "\nStarting Tor...");
|
||||||
|
});
|
||||||
|
torService.setOnSucceeded(workerStateEvent -> {
|
||||||
|
Tor.setDefault(torService.getValue());
|
||||||
|
torService.cancel();
|
||||||
|
testResults.setText(testResults.getText() + "\nTor started");
|
||||||
|
startElectrumConnection();
|
||||||
|
});
|
||||||
|
torService.setOnFailed(workerStateEvent -> {
|
||||||
|
testResults.setText(testResults.getText() + "\nTor failed to start");
|
||||||
|
showConnectionFailure(workerStateEvent.getSource().getException());
|
||||||
|
});
|
||||||
|
|
||||||
|
torService.start();
|
||||||
|
}
|
||||||
|
|
||||||
private void startElectrumConnection() {
|
private void startElectrumConnection() {
|
||||||
if(connectionService != null && connectionService.isRunning()) {
|
if(connectionService != null && connectionService.isRunning()) {
|
||||||
connectionService.cancel();
|
connectionService.cancel();
|
||||||
|
@ -364,10 +386,8 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
|
|
||||||
connectionService = new ElectrumServer.ConnectionService(false);
|
connectionService = new ElectrumServer.ConnectionService(false);
|
||||||
connectionService.setPeriod(Duration.hours(1));
|
connectionService.setPeriod(Duration.hours(1));
|
||||||
|
connectionService.setRestartOnFailure(false);
|
||||||
EventManager.get().register(connectionService);
|
EventManager.get().register(connectionService);
|
||||||
connectionService.statusProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
testResults.setText(testResults.getText() + "\n" + newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
connectionService.setOnSucceeded(successEvent -> {
|
connectionService.setOnSucceeded(successEvent -> {
|
||||||
EventManager.get().unregister(connectionService);
|
EventManager.get().unregister(connectionService);
|
||||||
|
@ -379,8 +399,7 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
});
|
});
|
||||||
connectionService.setOnFailed(workerStateEvent -> {
|
connectionService.setOnFailed(workerStateEvent -> {
|
||||||
EventManager.get().unregister(connectionService);
|
EventManager.get().unregister(connectionService);
|
||||||
showConnectionFailure(workerStateEvent);
|
showConnectionFailure(workerStateEvent.getSource().getException());
|
||||||
connectionService.cancel();
|
|
||||||
});
|
});
|
||||||
connectionService.start();
|
connectionService.start();
|
||||||
}
|
}
|
||||||
|
@ -421,13 +440,15 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showConnectionFailure(WorkerStateEvent failEvent) {
|
private void showConnectionFailure(Throwable exception) {
|
||||||
Throwable e = failEvent.getSource().getException();
|
log.error("Connection error", exception);
|
||||||
log.error("Connection error", e);
|
String reason = exception.getCause() != null ? exception.getCause().getMessage() : exception.getMessage();
|
||||||
String reason = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
|
if(exception.getCause() != null && exception.getCause() instanceof SSLHandshakeException) {
|
||||||
if(e.getCause() != null && e.getCause() instanceof SSLHandshakeException) {
|
|
||||||
reason = "SSL Handshake Error\n" + reason;
|
reason = "SSL Handshake Error\n" + reason;
|
||||||
}
|
}
|
||||||
|
if(exception.getCause() != null && exception.getCause() instanceof TorControlError && exception.getCause().getMessage().contains("Failed to bind")) {
|
||||||
|
reason += "\nIs a Tor proxy already running on port " + TorService.PROXY_PORT + "?";
|
||||||
|
}
|
||||||
|
|
||||||
testResults.setText("Could not connect:\n\n" + reason);
|
testResults.setText("Could not connect:\n\n" + reason);
|
||||||
testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.EXCLAMATION_CIRCLE, "failure"));
|
testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.EXCLAMATION_CIRCLE, "failure"));
|
||||||
|
|
|
@ -26,4 +26,5 @@ open module com.sparrowwallet.sparrow {
|
||||||
requires jcommander;
|
requires jcommander;
|
||||||
requires slf4j.api;
|
requires slf4j.api;
|
||||||
requires bwt.jni;
|
requires bwt.jni;
|
||||||
|
requires jtorctl;
|
||||||
}
|
}
|
Loading…
Reference in a new issue