mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
add built in tor
This commit is contained in:
parent
4bc446724d
commit
2e0ca1b4fa
17 changed files with 190 additions and 19 deletions
15
build.gradle
15
build.gradle
|
@ -9,7 +9,7 @@ def sparrowVersion = '0.9.3'
|
||||||
def os = org.gradle.internal.os.OperatingSystem.current()
|
def os = org.gradle.internal.os.OperatingSystem.current()
|
||||||
def osName = os.getFamilyName()
|
def osName = os.getFamilyName()
|
||||||
if(os.macOsX) {
|
if(os.macOsX) {
|
||||||
osName = "mac"
|
osName = "osx"
|
||||||
}
|
}
|
||||||
|
|
||||||
group "com.sparrowwallet"
|
group "com.sparrowwallet"
|
||||||
|
@ -19,6 +19,7 @@ repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven { url 'https://oss.sonatype.org/content/groups/public' }
|
maven { url 'https://oss.sonatype.org/content/groups/public' }
|
||||||
maven { url 'https://mymavenrepo.com/repo/29EACwkkGcoOKnbx3bxN/' }
|
maven { url 'https://mymavenrepo.com/repo/29EACwkkGcoOKnbx3bxN/' }
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(AbstractArchiveTask) {
|
tasks.withType(AbstractArchiveTask) {
|
||||||
|
@ -54,6 +55,7 @@ dependencies {
|
||||||
implementation('com.github.sarxos:webcam-capture:0.3.13-SNAPSHOT') {
|
implementation('com.github.sarxos:webcam-capture:0.3.13-SNAPSHOT') {
|
||||||
exclude group: 'com.nativelibs4java', module: 'bridj'
|
exclude group: 'com.nativelibs4java', module: 'bridj'
|
||||||
}
|
}
|
||||||
|
implementation("com.sparrowwallet:netlayer-jpms-${osName}:0.6.8")
|
||||||
implementation('de.codecentric.centerdevice:centerdevice-nsmenufx:2.1.7')
|
implementation('de.codecentric.centerdevice:centerdevice-nsmenufx:2.1.7')
|
||||||
implementation('org.controlsfx:controlsfx:11.0.2' ) {
|
implementation('org.controlsfx:controlsfx:11.0.2' ) {
|
||||||
exclude group: 'org.openjfx', module: 'javafx-base'
|
exclude group: 'org.openjfx', module: 'javafx-base'
|
||||||
|
@ -80,8 +82,8 @@ compileJava {
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
doLast {
|
doLast {
|
||||||
delete fileTree("$buildDir/resources/main/external").matching {
|
delete fileTree("$buildDir/resources/main/native").matching {
|
||||||
exclude "$osName/**"
|
exclude "${osName}/**"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +103,8 @@ run {
|
||||||
"--add-opens=javafx.controls/com.sun.javafx.scene.control=centerdevice.nsmenufx",
|
"--add-opens=javafx.controls/com.sun.javafx.scene.control=centerdevice.nsmenufx",
|
||||||
"--add-opens=javafx.graphics/com.sun.javafx.menu=centerdevice.nsmenufx",
|
"--add-opens=javafx.graphics/com.sun.javafx.menu=centerdevice.nsmenufx",
|
||||||
"--add-opens=javafx.graphics/com.sun.glass.ui=com.sparrowwallet.sparrow",
|
"--add-opens=javafx.graphics/com.sun.glass.ui=com.sparrowwallet.sparrow",
|
||||||
"--add-opens=javafx.graphics/com.sun.javafx.application=com.sparrowwallet.sparrow"]
|
"--add-opens=javafx.graphics/com.sun.javafx.application=com.sparrowwallet.sparrow",
|
||||||
|
"--add-opens=java.base/java.net=com.sparrowwallet.sparrow"]
|
||||||
|
|
||||||
if(os.macOsX) {
|
if(os.macOsX) {
|
||||||
applicationDefaultJvmArgs += ["-Xdock:name=Sparrow", "-Xdock:icon=/Users/scy/git/sparrow/src/main/resources/sparrow.png",
|
applicationDefaultJvmArgs += ["-Xdock:name=Sparrow", "-Xdock:icon=/Users/scy/git/sparrow/src/main/resources/sparrow.png",
|
||||||
|
@ -118,6 +121,7 @@ jlink {
|
||||||
requires 'javafx.base'
|
requires 'javafx.base'
|
||||||
requires 'com.fasterxml.jackson.databind'
|
requires 'com.fasterxml.jackson.databind'
|
||||||
requires 'jdk.crypto.cryptoki'
|
requires 'jdk.crypto.cryptoki'
|
||||||
|
requires 'java.management'
|
||||||
}
|
}
|
||||||
|
|
||||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--ignore-signing-information', '--exclude-files', '**.png']
|
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--ignore-signing-information', '--exclude-files', '**.png']
|
||||||
|
@ -139,6 +143,7 @@ jlink {
|
||||||
"--add-opens=javafx.graphics/com.sun.glass.ui=com.sparrowwallet.sparrow",
|
"--add-opens=javafx.graphics/com.sun.glass.ui=com.sparrowwallet.sparrow",
|
||||||
"--add-opens=javafx.graphics/com.sun.glass.ui.mac=com.sparrowwallet.merged.module",
|
"--add-opens=javafx.graphics/com.sun.glass.ui.mac=com.sparrowwallet.merged.module",
|
||||||
"--add-opens=javafx.graphics/com.sun.javafx.application=com.sparrowwallet.sparrow",
|
"--add-opens=javafx.graphics/com.sun.javafx.application=com.sparrowwallet.sparrow",
|
||||||
|
"--add-opens=java.base/java.net=com.sparrowwallet.sparrow",
|
||||||
"--add-reads=com.sparrowwallet.merged.module=java.desktop"]
|
"--add-reads=com.sparrowwallet.merged.module=java.desktop"]
|
||||||
}
|
}
|
||||||
addExtraDependencies("javafx")
|
addExtraDependencies("javafx")
|
||||||
|
@ -160,7 +165,7 @@ jlink {
|
||||||
}
|
}
|
||||||
if(os.macOsX) {
|
if(os.macOsX) {
|
||||||
installerOptions += ['--mac-sign', '--mac-signing-key-user-name', 'Craig Raw (UPLVMSK9D7)']
|
installerOptions += ['--mac-sign', '--mac-signing-key-user-name', 'Craig Raw (UPLVMSK9D7)']
|
||||||
imageOptions += ['--icon', 'src/main/deploy/package/mac/sparrow.icns', '--resource-dir', 'src/main/deploy/package/mac/']
|
imageOptions += ['--icon', 'src/main/deploy/package/osx/sparrow.icns', '--resource-dir', 'src/main/deploy/package/osx/']
|
||||||
installerType = "dmg"
|
installerType = "dmg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,6 +280,13 @@ public class AppController implements Initializable {
|
||||||
connectionService.setPeriod(new Duration(SERVER_PING_PERIOD));
|
connectionService.setPeriod(new Duration(SERVER_PING_PERIOD));
|
||||||
connectionService.setRestartOnFailure(true);
|
connectionService.setRestartOnFailure(true);
|
||||||
|
|
||||||
|
EventManager.get().register(connectionService);
|
||||||
|
connectionService.statusProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if(connectionService.isRunning()) {
|
||||||
|
EventManager.get().post(new StatusEvent(newValue));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
connectionService.setOnSucceeded(successEvent -> {
|
connectionService.setOnSucceeded(successEvent -> {
|
||||||
changeMode = false;
|
changeMode = false;
|
||||||
onlineProperty.setValue(true);
|
onlineProperty.setValue(true);
|
||||||
|
@ -350,8 +357,8 @@ public class AppController implements Initializable {
|
||||||
tk.createQuitMenuItem(MainApp.APP_NAME));
|
tk.createQuitMenuItem(MainApp.APP_NAME));
|
||||||
tk.setApplicationMenu(defaultApplicationMenu);
|
tk.setApplicationMenu(defaultApplicationMenu);
|
||||||
|
|
||||||
fileMenu.getItems().removeIf(item -> item.getStyleClass().contains("macHide"));
|
fileMenu.getItems().removeIf(item -> item.getStyleClass().contains("osxHide"));
|
||||||
helpMenu.getItems().removeIf(item -> item.getStyleClass().contains("macHide"));
|
helpMenu.getItems().removeIf(item -> item.getStyleClass().contains("osxHide"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1155,7 +1162,7 @@ public class AppController implements Initializable {
|
||||||
public void statusUpdated(StatusEvent event) {
|
public void statusUpdated(StatusEvent event) {
|
||||||
statusBar.setText(event.getStatus());
|
statusBar.setText(event.getStatus());
|
||||||
|
|
||||||
PauseTransition wait = new PauseTransition(Duration.seconds(10));
|
PauseTransition wait = new PauseTransition(Duration.seconds(20));
|
||||||
wait.setOnFinished((e) -> {
|
wait.setOnFinished((e) -> {
|
||||||
if(statusBar.getText().equals(event.getStatus())) {
|
if(statusBar.getText().equals(event.getStatus())) {
|
||||||
statusBar.setText("");
|
statusBar.setText("");
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.sparrowwallet.sparrow.event;
|
||||||
|
|
||||||
|
public class TorStatusEvent {
|
||||||
|
private final String status;
|
||||||
|
|
||||||
|
public TorStatusEvent(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ import java.nio.file.attribute.PosixFilePermission;
|
||||||
import java.nio.file.attribute.PosixFilePermissions;
|
import java.nio.file.attribute.PosixFilePermissions;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.GZIPInputStream;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
@ -200,7 +199,7 @@ public class Hwi {
|
||||||
File hwiExecutable = Config.get().getHwi();
|
File hwiExecutable = Config.get().getHwi();
|
||||||
if(hwiExecutable != null && hwiExecutable.exists()) {
|
if(hwiExecutable != null && hwiExecutable.exists()) {
|
||||||
if(command.isTestFirst() && (!hwiExecutable.getAbsolutePath().contains(TEMP_FILE_PREFIX) || !testHwi(hwiExecutable))) {
|
if(command.isTestFirst() && (!hwiExecutable.getAbsolutePath().contains(TEMP_FILE_PREFIX) || !testHwi(hwiExecutable))) {
|
||||||
if(Platform.getCurrent().getPlatformId().toLowerCase().equals("mac")) {
|
if(Platform.getCurrent() == Platform.OSX) {
|
||||||
deleteDirectory(hwiExecutable.getParentFile());
|
deleteDirectory(hwiExecutable.getParentFile());
|
||||||
} else {
|
} else {
|
||||||
hwiExecutable.delete();
|
hwiExecutable.delete();
|
||||||
|
@ -218,7 +217,7 @@ public class Hwi {
|
||||||
//The check will still happen on first invocation, but will not thereafter
|
//The check will still happen on first invocation, but will not thereafter
|
||||||
//See https://github.com/bitcoin-core/HWI/issues/327 for details
|
//See https://github.com/bitcoin-core/HWI/issues/327 for details
|
||||||
if(platform == Platform.OSX) {
|
if(platform == Platform.OSX) {
|
||||||
InputStream inputStream = Hwi.class.getResourceAsStream("/external/mac/hwi-1.1.2-mac-amd64-signed.zip");
|
InputStream inputStream = Hwi.class.getResourceAsStream("/native/osx/x64/hwi-1.1.2-mac-amd64-signed.zip");
|
||||||
Path tempHwiDirPath = Files.createTempDirectory(TEMP_FILE_PREFIX, PosixFilePermissions.asFileAttribute(ownerExecutableWritable));
|
Path tempHwiDirPath = Files.createTempDirectory(TEMP_FILE_PREFIX, PosixFilePermissions.asFileAttribute(ownerExecutableWritable));
|
||||||
File tempHwiDir = tempHwiDirPath.toFile();
|
File tempHwiDir = tempHwiDirPath.toFile();
|
||||||
//tempHwiDir.deleteOnExit();
|
//tempHwiDir.deleteOnExit();
|
||||||
|
@ -249,10 +248,10 @@ public class Hwi {
|
||||||
InputStream inputStream;
|
InputStream inputStream;
|
||||||
Path tempExecPath;
|
Path tempExecPath;
|
||||||
if(platform == Platform.WINDOWS) {
|
if(platform == Platform.WINDOWS) {
|
||||||
inputStream = Hwi.class.getResourceAsStream("/external/windows/hwi.exe");
|
inputStream = Hwi.class.getResourceAsStream("/native/windows/x64/hwi.exe");
|
||||||
tempExecPath = Files.createTempFile(TEMP_FILE_PREFIX, null);
|
tempExecPath = Files.createTempFile(TEMP_FILE_PREFIX, null);
|
||||||
} else {
|
} else {
|
||||||
inputStream = Hwi.class.getResourceAsStream("/external/linux/hwi");
|
inputStream = Hwi.class.getResourceAsStream("/native/linux/x64/hwi");
|
||||||
tempExecPath = Files.createTempFile(TEMP_FILE_PREFIX, null, PosixFilePermissions.asFileAttribute(ownerExecutableWritable));
|
tempExecPath = Files.createTempFile(TEMP_FILE_PREFIX, null, PosixFilePermissions.asFileAttribute(ownerExecutableWritable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.sparrowwallet.sparrow.net;
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
import com.github.arteam.simplejsonrpc.client.Transport;
|
import com.github.arteam.simplejsonrpc.client.Transport;
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.google.common.net.HostAndPort;
|
import com.google.common.net.HostAndPort;
|
||||||
import com.sparrowwallet.drongo.KeyPurpose;
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
|
@ -8,8 +9,11 @@ import com.sparrowwallet.drongo.protocol.*;
|
||||||
import com.sparrowwallet.drongo.wallet.*;
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.sparrow.event.ConnectionEvent;
|
import com.sparrowwallet.sparrow.event.ConnectionEvent;
|
||||||
import com.sparrowwallet.sparrow.event.FeeRatesUpdatedEvent;
|
import com.sparrowwallet.sparrow.event.FeeRatesUpdatedEvent;
|
||||||
|
import com.sparrowwallet.sparrow.event.TorStatusEvent;
|
||||||
import com.sparrowwallet.sparrow.io.Config;
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
import com.sparrowwallet.sparrow.wallet.SendController;
|
import com.sparrowwallet.sparrow.wallet.SendController;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.Service;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
|
@ -578,6 +582,7 @@ public class ElectrumServer {
|
||||||
private boolean firstCall = true;
|
private boolean firstCall = true;
|
||||||
private Thread reader;
|
private Thread reader;
|
||||||
private long feeRatesRetrievedAt;
|
private long feeRatesRetrievedAt;
|
||||||
|
private StringProperty statusProperty = new SimpleStringProperty();
|
||||||
|
|
||||||
public ConnectionService() {
|
public ConnectionService() {
|
||||||
this(true);
|
this(true);
|
||||||
|
@ -675,6 +680,15 @@ public class ElectrumServer {
|
||||||
public void uncaughtException(Thread t, Throwable e) {
|
public void uncaughtException(Thread t, Throwable e) {
|
||||||
log.error("Uncaught error in ConnectionService", e);
|
log.error("Uncaught error in ConnectionService", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void torStatus(TorStatusEvent event) {
|
||||||
|
statusProperty.set(event.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringProperty statusProperty() {
|
||||||
|
return statusProperty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ReadRunnable implements Runnable {
|
public static class ReadRunnable implements Runnable {
|
||||||
|
|
|
@ -14,12 +14,16 @@ public enum Protocol {
|
||||||
TCP {
|
TCP {
|
||||||
@Override
|
@Override
|
||||||
public Transport getTransport(HostAndPort server) {
|
public Transport getTransport(HostAndPort server) {
|
||||||
|
if(isOnionAddress(server)) {
|
||||||
|
return new TorTcpTransport(server);
|
||||||
|
}
|
||||||
|
|
||||||
return new TcpTransport(server);
|
return new TcpTransport(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transport getTransport(HostAndPort server, File serverCert) {
|
public Transport getTransport(HostAndPort server, File serverCert) {
|
||||||
return new TcpTransport(server);
|
return getTransport(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,11 +39,19 @@ public enum Protocol {
|
||||||
SSL {
|
SSL {
|
||||||
@Override
|
@Override
|
||||||
public Transport getTransport(HostAndPort server) throws KeyManagementException, NoSuchAlgorithmException {
|
public Transport getTransport(HostAndPort server) throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
|
if(isOnionAddress(server)) {
|
||||||
|
return new TorTcpOverTlsTransport(server);
|
||||||
|
}
|
||||||
|
|
||||||
return new TcpOverTlsTransport(server);
|
return new TcpOverTlsTransport(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
public Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
if(isOnionAddress(server)) {
|
||||||
|
return new TorTcpOverTlsTransport(server, serverCert);
|
||||||
|
}
|
||||||
|
|
||||||
return new TcpOverTlsTransport(server, serverCert);
|
return new TcpOverTlsTransport(server, serverCert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +94,10 @@ public enum Protocol {
|
||||||
return toUrlString() + hostAndPort.toString();
|
return toUrlString() + hostAndPort.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isOnionAddress(HostAndPort server) {
|
||||||
|
return server.getHost().toLowerCase().endsWith(".onion");
|
||||||
|
}
|
||||||
|
|
||||||
public static Protocol getProtocol(String url) {
|
public static Protocol getProtocol(String url) {
|
||||||
if(url.startsWith("tcp://")) {
|
if(url.startsWith("tcp://")) {
|
||||||
return TCP;
|
return TCP;
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
public class TorTcpOverTlsTransport extends TcpOverTlsTransport {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TorTcpOverTlsTransport.class);
|
||||||
|
|
||||||
|
public TorTcpOverTlsTransport(HostAndPort server) throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
|
super(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TorTcpOverTlsTransport(HostAndPort server, File crtFile) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
super(server, crtFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Socket createSocket() throws IOException {
|
||||||
|
TorTcpTransport torTcpTransport = new TorTcpTransport(server);
|
||||||
|
Socket socket = torTcpTransport.createSocket();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field socketField = socket.getClass().getDeclaredField("socket");
|
||||||
|
socketField.setAccessible(true);
|
||||||
|
Socket innerSocket = (Socket)socketField.get(socket);
|
||||||
|
Field connectedField = innerSocket.getClass().getSuperclass().getDeclaredField("connected");
|
||||||
|
connectedField.setAccessible(true);
|
||||||
|
connectedField.set(innerSocket, true);
|
||||||
|
} catch(Exception e) {
|
||||||
|
log.error("Could not set socket connected status", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, server.getHost(), server.getPortOrDefault(DEFAULT_PORT), true);
|
||||||
|
sslSocket.startHandshake();
|
||||||
|
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
import com.sparrowwallet.sparrow.event.StatusEvent;
|
||||||
|
import com.sparrowwallet.sparrow.event.TorStatusEvent;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import org.berndpruenster.netlayer.tor.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
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 static final String TOR_DIR_PREFIX = "tor";
|
||||||
|
|
||||||
|
public TorTcpTransport(HostAndPort server) {
|
||||||
|
super(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Socket createSocket() throws IOException {
|
||||||
|
if(Tor.getDefault() == null) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,8 @@ import org.controlsfx.validation.ValidationSupport;
|
||||||
import org.controlsfx.validation.Validator;
|
import org.controlsfx.validation.Validator;
|
||||||
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -36,6 +38,8 @@ import java.security.cert.CertificateFactory;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ServerPreferencesController extends PreferencesDetailController {
|
public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ServerPreferencesController.class);
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TextField host;
|
private TextField host;
|
||||||
|
|
||||||
|
@ -140,13 +144,20 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.ELLIPSIS_H, null));
|
testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.ELLIPSIS_H, null));
|
||||||
|
|
||||||
ElectrumServer.ConnectionService connectionService = new ElectrumServer.ConnectionService(false);
|
ElectrumServer.ConnectionService connectionService = new ElectrumServer.ConnectionService(false);
|
||||||
connectionService.setPeriod(Duration.minutes(1));
|
connectionService.setPeriod(Duration.ZERO);
|
||||||
|
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);
|
||||||
ConnectionEvent connectionEvent = (ConnectionEvent)connectionService.getValue();
|
ConnectionEvent connectionEvent = (ConnectionEvent)connectionService.getValue();
|
||||||
showConnectionSuccess(connectionEvent.getServerVersion(), connectionEvent.getServerBanner());
|
showConnectionSuccess(connectionEvent.getServerVersion(), connectionEvent.getServerBanner());
|
||||||
connectionService.cancel();
|
connectionService.cancel();
|
||||||
});
|
});
|
||||||
connectionService.setOnFailed(workerStateEvent -> {
|
connectionService.setOnFailed(workerStateEvent -> {
|
||||||
|
EventManager.get().unregister(connectionService);
|
||||||
showConnectionFailure(workerStateEvent);
|
showConnectionFailure(workerStateEvent);
|
||||||
connectionService.cancel();
|
connectionService.cancel();
|
||||||
});
|
});
|
||||||
|
@ -230,6 +241,7 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
|
|
||||||
private void showConnectionFailure(WorkerStateEvent failEvent) {
|
private void showConnectionFailure(WorkerStateEvent failEvent) {
|
||||||
Throwable e = failEvent.getSource().getException();
|
Throwable e = failEvent.getSource().getException();
|
||||||
|
log.error("Connection error", e);
|
||||||
String reason = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
|
String reason = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
|
||||||
if(e.getCause() != null && e.getCause() instanceof SSLHandshakeException) {
|
if(e.getCause() != null && e.getCause() instanceof SSLHandshakeException) {
|
||||||
reason = "SSL Handshake Error\n" + reason;
|
reason = "SSL Handshake Error\n" + reason;
|
||||||
|
|
|
@ -20,6 +20,7 @@ open module com.sparrowwallet.sparrow {
|
||||||
requires com.fasterxml.jackson.databind;
|
requires com.fasterxml.jackson.databind;
|
||||||
requires cbor;
|
requires cbor;
|
||||||
requires webcam.capture;
|
requires webcam.capture;
|
||||||
|
requires netlayer.jpms;
|
||||||
requires centerdevice.nsmenufx;
|
requires centerdevice.nsmenufx;
|
||||||
requires slf4j.api;
|
requires slf4j.api;
|
||||||
}
|
}
|
|
@ -29,11 +29,11 @@
|
||||||
<SeparatorMenuItem />
|
<SeparatorMenuItem />
|
||||||
<MenuItem mnemonicParsing="false" text="Import Wallet..." onAction="#importWallet"/>
|
<MenuItem mnemonicParsing="false" text="Import Wallet..." onAction="#importWallet"/>
|
||||||
<MenuItem fx:id="exportWallet" mnemonicParsing="false" text="Export Wallet..." onAction="#exportWallet"/>
|
<MenuItem fx:id="exportWallet" mnemonicParsing="false" text="Export Wallet..." onAction="#exportWallet"/>
|
||||||
<SeparatorMenuItem styleClass="macHide" />
|
<SeparatorMenuItem styleClass="osxHide" />
|
||||||
<MenuItem styleClass="macHide" mnemonicParsing="false" text="Preferences..." onAction="#openPreferences"/>
|
<MenuItem styleClass="osxHide" mnemonicParsing="false" text="Preferences..." onAction="#openPreferences"/>
|
||||||
<SeparatorMenuItem />
|
<SeparatorMenuItem />
|
||||||
<MenuItem mnemonicParsing="false" text="Close Tab" onAction="#closeTab"/>
|
<MenuItem mnemonicParsing="false" text="Close Tab" onAction="#closeTab"/>
|
||||||
<MenuItem styleClass="macHide" mnemonicParsing="false" text="Quit" onAction="#quit"/>
|
<MenuItem styleClass="osxHide" mnemonicParsing="false" text="Quit" onAction="#quit"/>
|
||||||
</items>
|
</items>
|
||||||
</Menu>
|
</Menu>
|
||||||
<fx:define>
|
<fx:define>
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
<MenuItem mnemonicParsing="false" text="Sign/Verify Message" onAction="#signVerifyMessage"/>
|
<MenuItem mnemonicParsing="false" text="Sign/Verify Message" onAction="#signVerifyMessage"/>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Menu fx:id="helpMenu" mnemonicParsing="false" text="Help">
|
<Menu fx:id="helpMenu" mnemonicParsing="false" text="Help">
|
||||||
<MenuItem styleClass="macHide" mnemonicParsing="false" text="About Sparrow" onAction="#showAbout"/>
|
<MenuItem styleClass="osxHide" mnemonicParsing="false" text="About Sparrow" onAction="#showAbout"/>
|
||||||
</Menu>
|
</Menu>
|
||||||
</menus>
|
</menus>
|
||||||
</MenuBar>
|
</MenuBar>
|
||||||
|
|
Loading…
Reference in a new issue