mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-04 21:36:45 +00:00
refactor electrumserver into net package
This commit is contained in:
parent
7d8774c94c
commit
bb635a6c68
19 changed files with 479 additions and 403 deletions
|
@ -18,6 +18,7 @@ import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.sparrow.control.*;
|
import com.sparrowwallet.sparrow.control.*;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
import com.sparrowwallet.sparrow.io.*;
|
import com.sparrowwallet.sparrow.io.*;
|
||||||
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import com.sparrowwallet.sparrow.preferences.PreferencesDialog;
|
import com.sparrowwallet.sparrow.preferences.PreferencesDialog;
|
||||||
import com.sparrowwallet.sparrow.transaction.TransactionController;
|
import com.sparrowwallet.sparrow.transaction.TransactionController;
|
||||||
import com.sparrowwallet.sparrow.transaction.TransactionView;
|
import com.sparrowwallet.sparrow.transaction.TransactionView;
|
||||||
|
|
|
@ -140,13 +140,17 @@ public class TitledDescriptionPane extends TitledPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeArrow() {
|
private void removeArrow() {
|
||||||
|
removeArrow(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeArrow(int count) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Node arrow = this.lookup(".arrow");
|
Node arrow = this.lookup(".arrow");
|
||||||
if (arrow != null) {
|
if (arrow != null) {
|
||||||
arrow.setVisible(false);
|
arrow.setVisible(false);
|
||||||
arrow.setManaged(false);
|
arrow.setManaged(false);
|
||||||
} else {
|
} else if(count < 20) {
|
||||||
removeArrow();
|
removeArrow(count+1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.sparrowwallet.sparrow.event;
|
||||||
import com.sparrowwallet.drongo.KeyPurpose;
|
import com.sparrowwallet.drongo.KeyPurpose;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletNode;
|
import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.Utils;
|
||||||
|
import com.sparrowwallet.drongo.protocol.BlockHeader;
|
||||||
|
|
||||||
|
class BlockHeaderTip {
|
||||||
|
public int height;
|
||||||
|
public String hex;
|
||||||
|
|
||||||
|
public BlockHeader getBlockHeader() {
|
||||||
|
if(hex == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] blockHeaderBytes = Utils.hexToBytes(hex);
|
||||||
|
return new BlockHeader(blockHeaderBytes);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,47 +1,27 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import com.github.arteam.simplejsonrpc.client.*;
|
import com.github.arteam.simplejsonrpc.client.*;
|
||||||
import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder;
|
import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder;
|
||||||
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException;
|
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException;
|
||||||
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcException;
|
import com.github.arteam.simplejsonrpc.client.exception.JsonRpcException;
|
||||||
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcMethod;
|
|
||||||
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcParam;
|
|
||||||
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcService;
|
|
||||||
import com.github.arteam.simplejsonrpc.server.JsonRpcServer;
|
|
||||||
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;
|
||||||
import com.sparrowwallet.drongo.protocol.*;
|
import com.sparrowwallet.drongo.protocol.*;
|
||||||
import com.sparrowwallet.drongo.wallet.*;
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.sparrow.AppController;
|
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
|
||||||
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.NewBlockEvent;
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
import com.sparrowwallet.sparrow.event.WalletNodeHistoryChangedEvent;
|
import com.sparrowwallet.sparrow.io.ServerException;
|
||||||
import com.sparrowwallet.sparrow.wallet.SendController;
|
import com.sparrowwallet.sparrow.wallet.SendController;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.Service;
|
||||||
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;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
|
||||||
import javax.net.ssl.*;
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.Proxy;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.security.*;
|
|
||||||
import java.security.cert.Certificate;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.CertificateFactory;
|
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ElectrumServer {
|
public class ElectrumServer {
|
||||||
|
@ -630,283 +610,8 @@ public class ElectrumServer {
|
||||||
return Utils.bytesToHex(reversed);
|
return Utils.bytesToHex(reversed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ScriptHashTx {
|
static Map<String, String> getSubscribedScriptHashes() {
|
||||||
public static final ScriptHashTx ERROR_TX = new ScriptHashTx() {
|
return subscribedScriptHashes;
|
||||||
@Override
|
|
||||||
public BlockTransactionHash getBlockchainTransactionHash() {
|
|
||||||
return UNFETCHABLE_BLOCK_TRANSACTION;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public int height;
|
|
||||||
public String tx_hash;
|
|
||||||
public long fee;
|
|
||||||
|
|
||||||
public BlockTransactionHash getBlockchainTransactionHash() {
|
|
||||||
Sha256Hash hash = Sha256Hash.wrap(tx_hash);
|
|
||||||
return new BlockTransaction(hash, height, null, fee, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ScriptHashTx{height=" + height + ", tx_hash='" + tx_hash + '\'' + ", fee=" + fee + '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BlockHeaderTip {
|
|
||||||
public int height;
|
|
||||||
public String hex;
|
|
||||||
|
|
||||||
public BlockHeader getBlockHeader() {
|
|
||||||
if(hex == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] blockHeaderBytes = Utils.hexToBytes(hex);
|
|
||||||
return new BlockHeader(blockHeaderBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown=true)
|
|
||||||
private static class VerboseTransaction {
|
|
||||||
public String blockhash;
|
|
||||||
public long blocktime;
|
|
||||||
public int confirmations;
|
|
||||||
public String hash;
|
|
||||||
public String hex;
|
|
||||||
public int locktime;
|
|
||||||
public long size;
|
|
||||||
public String txid;
|
|
||||||
public int version;
|
|
||||||
|
|
||||||
public int getHeight() {
|
|
||||||
Integer currentHeight = AppController.getCurrentBlockHeight();
|
|
||||||
if(currentHeight != null) {
|
|
||||||
return currentHeight - confirmations + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getDate() {
|
|
||||||
return new Date(blocktime * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockTransaction getBlockTransaction() {
|
|
||||||
return new BlockTransaction(Sha256Hash.wrap(txid), getHeight(), getDate(), 0L, new Transaction(Utils.hexToBytes(hex)), blockhash == null ? null : Sha256Hash.wrap(blockhash));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonRpcService
|
|
||||||
public static class SubscriptionService {
|
|
||||||
@JsonRpcMethod("blockchain.headers.subscribe")
|
|
||||||
public void newBlockHeaderTip(@JsonRpcParam("header") final BlockHeaderTip header) {
|
|
||||||
Platform.runLater(() -> EventManager.get().post(new NewBlockEvent(header.height, header.getBlockHeader())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonRpcMethod("blockchain.scripthash.subscribe")
|
|
||||||
public void scriptHashStatusUpdated(@JsonRpcParam("scripthash") final String scriptHash, @JsonRpcParam("status") final String status) {
|
|
||||||
String oldStatus = subscribedScriptHashes.put(scriptHash, status);
|
|
||||||
if(Objects.equals(oldStatus, status)) {
|
|
||||||
log.warn("Received script hash status update, but status has not changed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform.runLater(() -> EventManager.get().post(new WalletNodeHistoryChangedEvent(scriptHash)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TcpTransport implements Transport, Closeable {
|
|
||||||
public static final int DEFAULT_PORT = 50001;
|
|
||||||
|
|
||||||
protected final HostAndPort server;
|
|
||||||
protected final SocketFactory socketFactory;
|
|
||||||
|
|
||||||
private Socket socket;
|
|
||||||
|
|
||||||
private String response;
|
|
||||||
|
|
||||||
private final ReentrantLock clientRequestLock = new ReentrantLock();
|
|
||||||
private boolean running = false;
|
|
||||||
private boolean reading = true;
|
|
||||||
|
|
||||||
private final JsonRpcServer jsonRpcServer = new JsonRpcServer();
|
|
||||||
private final SubscriptionService subscriptionService = new SubscriptionService();
|
|
||||||
|
|
||||||
public TcpTransport(HostAndPort server) {
|
|
||||||
this.server = server;
|
|
||||||
this.socketFactory = SocketFactory.getDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull String pass(@NotNull String request) throws IOException {
|
|
||||||
clientRequestLock.lock();
|
|
||||||
try {
|
|
||||||
writeRequest(request);
|
|
||||||
return readResponse();
|
|
||||||
} finally {
|
|
||||||
clientRequestLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeRequest(String request) throws IOException {
|
|
||||||
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
|
|
||||||
out.println(request);
|
|
||||||
out.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized String readResponse() {
|
|
||||||
while(reading) {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
//Restore interrupt status and continue
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reading = true;
|
|
||||||
|
|
||||||
notifyAll();
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void readInputLoop() throws ServerException {
|
|
||||||
while(running) {
|
|
||||||
try {
|
|
||||||
String received = readInputStream();
|
|
||||||
if(received.contains("method")) {
|
|
||||||
//Handle subscription notification
|
|
||||||
jsonRpcServer.handle(received, subscriptionService);
|
|
||||||
} else {
|
|
||||||
//Handle client's response
|
|
||||||
response = received;
|
|
||||||
reading = false;
|
|
||||||
notifyAll();
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
} catch(InterruptedException e) {
|
|
||||||
//Restore interrupt status and continue
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
} catch(IOException e) {
|
|
||||||
if(running) {
|
|
||||||
throw new ServerException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String readInputStream() throws IOException {
|
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
|
||||||
String response = in.readLine();
|
|
||||||
|
|
||||||
if(response == null) {
|
|
||||||
throw new IOException("Could not connect to server at " + Config.get().getElectrumServer());
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect() throws ServerException {
|
|
||||||
try {
|
|
||||||
socket = createSocket();
|
|
||||||
running = true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ServerException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConnected() {
|
|
||||||
return socket != null && running;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Socket createSocket() throws IOException {
|
|
||||||
return socketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if(socket != null) {
|
|
||||||
running = false;
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class TcpOverTlsTransport extends TcpTransport {
|
|
||||||
public static final int DEFAULT_PORT = 50002;
|
|
||||||
|
|
||||||
protected final SSLSocketFactory sslSocketFactory;
|
|
||||||
|
|
||||||
public TcpOverTlsTransport(HostAndPort server) throws NoSuchAlgorithmException, KeyManagementException {
|
|
||||||
super(server);
|
|
||||||
|
|
||||||
TrustManager[] trustAllCerts = new TrustManager[]{
|
|
||||||
new X509TrustManager() {
|
|
||||||
public X509Certificate[] getAcceptedIssuers() {
|
|
||||||
return new X509Certificate[0];
|
|
||||||
}
|
|
||||||
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
|
|
||||||
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
|
||||||
sslContext.init(null, trustAllCerts, new SecureRandom());
|
|
||||||
|
|
||||||
this.sslSocketFactory = sslContext.getSocketFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TcpOverTlsTransport(HostAndPort server, File crtFile) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
|
||||||
super(server);
|
|
||||||
|
|
||||||
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
|
|
||||||
|
|
||||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
||||||
keyStore.load(null, null);
|
|
||||||
keyStore.setCertificateEntry("electrumx", certificate);
|
|
||||||
|
|
||||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
|
||||||
trustManagerFactory.init(keyStore);
|
|
||||||
|
|
||||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
|
||||||
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
|
|
||||||
|
|
||||||
sslSocketFactory = sslContext.getSocketFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Socket createSocket() throws IOException {
|
|
||||||
SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT));
|
|
||||||
sslSocket.startHandshake();
|
|
||||||
|
|
||||||
return sslSocket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ProxyTcpOverTlsTransport extends TcpOverTlsTransport {
|
|
||||||
public static final int DEFAULT_PROXY_PORT = 1080;
|
|
||||||
|
|
||||||
private final HostAndPort proxy;
|
|
||||||
|
|
||||||
public ProxyTcpOverTlsTransport(HostAndPort server, HostAndPort proxy) throws KeyManagementException, NoSuchAlgorithmException {
|
|
||||||
super(server);
|
|
||||||
this.proxy = proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProxyTcpOverTlsTransport(HostAndPort server, File crtFile, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
|
||||||
super(server, crtFile);
|
|
||||||
this.proxy = proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Socket createSocket() throws IOException {
|
|
||||||
InetSocketAddress proxyAddr = new InetSocketAddress(proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT));
|
|
||||||
Socket underlying = new Socket(new Proxy(Proxy.Type.SOCKS, proxyAddr));
|
|
||||||
underlying.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(DEFAULT_PORT)));
|
|
||||||
SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(underlying, proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT), true);
|
|
||||||
sslSocket.startHandshake();
|
|
||||||
|
|
||||||
return sslSocket;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ServerVersionService extends Service<List<String>> {
|
public static class ServerVersionService extends Service<List<String>> {
|
||||||
|
@ -1180,88 +885,4 @@ public class ElectrumServer {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Protocol {
|
|
||||||
TCP {
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server) {
|
|
||||||
return new TcpTransport(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server, File serverCert) {
|
|
||||||
return new TcpTransport(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server, HostAndPort proxy) {
|
|
||||||
throw new UnsupportedOperationException("TCP protocol does not support proxying");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) {
|
|
||||||
throw new UnsupportedOperationException("TCP protocol does not support proxying");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
SSL{
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server) throws KeyManagementException, NoSuchAlgorithmException {
|
|
||||||
return new TcpOverTlsTransport(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
|
||||||
return new TcpOverTlsTransport(server, serverCert);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server, HostAndPort proxy) throws NoSuchAlgorithmException, KeyManagementException {
|
|
||||||
return new ProxyTcpOverTlsTransport(server, proxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
|
||||||
return new ProxyTcpOverTlsTransport(server, serverCert, proxy);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public abstract Transport getTransport(HostAndPort server) throws KeyManagementException, NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public abstract Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
|
|
||||||
|
|
||||||
public abstract Transport getTransport(HostAndPort server, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
|
|
||||||
|
|
||||||
public abstract Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
|
|
||||||
|
|
||||||
public HostAndPort getServerHostAndPort(String url) {
|
|
||||||
return HostAndPort.fromString(url.substring(this.toUrlString().length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toUrlString() {
|
|
||||||
return toString().toLowerCase() + "://";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toUrlString(String host) {
|
|
||||||
return toUrlString(HostAndPort.fromHost(host));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toUrlString(String host, int port) {
|
|
||||||
return toUrlString(HostAndPort.fromParts(host, port));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toUrlString(HostAndPort hostAndPort) {
|
|
||||||
return toUrlString() + hostAndPort.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Protocol getProtocol(String url) {
|
|
||||||
if(url.startsWith("tcp://")) {
|
|
||||||
return TCP;
|
|
||||||
}
|
|
||||||
if(url.startsWith("ssl://")) {
|
|
||||||
return SSL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
95
src/main/java/com/sparrowwallet/sparrow/net/Protocol.java
Normal file
95
src/main/java/com/sparrowwallet/sparrow/net/Protocol.java
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.github.arteam.simplejsonrpc.client.Transport;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
public enum Protocol {
|
||||||
|
TCP {
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server) {
|
||||||
|
return new TcpTransport(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, File serverCert) {
|
||||||
|
return new TcpTransport(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, HostAndPort proxy) {
|
||||||
|
throw new UnsupportedOperationException("TCP protocol does not support proxying");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) {
|
||||||
|
throw new UnsupportedOperationException("TCP protocol does not support proxying");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SSL {
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server) throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
|
return new TcpOverTlsTransport(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
return new TcpOverTlsTransport(server, serverCert);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, HostAndPort proxy) throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
|
return new ProxyTcpOverTlsTransport(server, proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
return new ProxyTcpOverTlsTransport(server, serverCert, proxy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract Transport getTransport(HostAndPort server) throws KeyManagementException, NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public abstract Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
|
||||||
|
|
||||||
|
public abstract Transport getTransport(HostAndPort server, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
|
||||||
|
|
||||||
|
public abstract Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
|
||||||
|
|
||||||
|
public HostAndPort getServerHostAndPort(String url) {
|
||||||
|
return HostAndPort.fromString(url.substring(this.toUrlString().length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUrlString() {
|
||||||
|
return toString().toLowerCase() + "://";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUrlString(String host) {
|
||||||
|
return toUrlString(HostAndPort.fromHost(host));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUrlString(String host, int port) {
|
||||||
|
return toUrlString(HostAndPort.fromParts(host, port));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toUrlString(HostAndPort hostAndPort) {
|
||||||
|
return toUrlString() + hostAndPort.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Protocol getProtocol(String url) {
|
||||||
|
if(url.startsWith("tcp://")) {
|
||||||
|
return TCP;
|
||||||
|
}
|
||||||
|
if(url.startsWith("ssl://")) {
|
||||||
|
return SSL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Proxy;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
|
||||||
|
public class ProxyTcpOverTlsTransport extends TcpOverTlsTransport {
|
||||||
|
public static final int DEFAULT_PROXY_PORT = 1080;
|
||||||
|
|
||||||
|
private final HostAndPort proxy;
|
||||||
|
|
||||||
|
public ProxyTcpOverTlsTransport(HostAndPort server, HostAndPort proxy) throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
|
super(server);
|
||||||
|
this.proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProxyTcpOverTlsTransport(HostAndPort server, File crtFile, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
super(server, crtFile);
|
||||||
|
this.proxy = proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Socket createSocket() throws IOException {
|
||||||
|
InetSocketAddress proxyAddr = new InetSocketAddress(proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT));
|
||||||
|
Socket underlying = new Socket(new Proxy(Proxy.Type.SOCKS, proxyAddr));
|
||||||
|
underlying.connect(new InetSocketAddress(server.getHost(), server.getPortOrDefault(DEFAULT_PORT)));
|
||||||
|
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(underlying, proxy.getHost(), proxy.getPortOrDefault(DEFAULT_PROXY_PORT), true);
|
||||||
|
sslSocket.startHandshake();
|
||||||
|
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
|
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||||
|
import com.sparrowwallet.drongo.wallet.BlockTransactionHash;
|
||||||
|
|
||||||
|
class ScriptHashTx {
|
||||||
|
public static final ScriptHashTx ERROR_TX = new ScriptHashTx() {
|
||||||
|
@Override
|
||||||
|
public BlockTransactionHash getBlockchainTransactionHash() {
|
||||||
|
return ElectrumServer.UNFETCHABLE_BLOCK_TRANSACTION;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public int height;
|
||||||
|
public String tx_hash;
|
||||||
|
public long fee;
|
||||||
|
|
||||||
|
public BlockTransactionHash getBlockchainTransactionHash() {
|
||||||
|
Sha256Hash hash = Sha256Hash.wrap(tx_hash);
|
||||||
|
return new BlockTransaction(hash, height, null, fee, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ScriptHashTx{height=" + height + ", tx_hash='" + tx_hash + '\'' + ", fee=" + fee + '}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcMethod;
|
||||||
|
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcParam;
|
||||||
|
import com.github.arteam.simplejsonrpc.core.annotation.JsonRpcService;
|
||||||
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
import com.sparrowwallet.sparrow.event.NewBlockEvent;
|
||||||
|
import com.sparrowwallet.sparrow.event.WalletNodeHistoryChangedEvent;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@JsonRpcService
|
||||||
|
public class SubscriptionService {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SubscriptionService.class);
|
||||||
|
|
||||||
|
@JsonRpcMethod("blockchain.headers.subscribe")
|
||||||
|
public void newBlockHeaderTip(@JsonRpcParam("header") final BlockHeaderTip header) {
|
||||||
|
Platform.runLater(() -> EventManager.get().post(new NewBlockEvent(header.height, header.getBlockHeader())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonRpcMethod("blockchain.scripthash.subscribe")
|
||||||
|
public void scriptHashStatusUpdated(@JsonRpcParam("scripthash") final String scriptHash, @JsonRpcParam("status") final String status) {
|
||||||
|
String oldStatus = ElectrumServer.getSubscribedScriptHashes().put(scriptHash, status);
|
||||||
|
if(Objects.equals(oldStatus, status)) {
|
||||||
|
log.warn("Received script hash status update, but status has not changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.runLater(() -> EventManager.get().post(new WalletNodeHistoryChangedEvent(scriptHash)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
public class TcpOverTlsTransport extends TcpTransport {
|
||||||
|
public static final int DEFAULT_PORT = 50002;
|
||||||
|
|
||||||
|
protected final SSLSocketFactory sslSocketFactory;
|
||||||
|
|
||||||
|
public TcpOverTlsTransport(HostAndPort server) throws NoSuchAlgorithmException, KeyManagementException {
|
||||||
|
super(server);
|
||||||
|
|
||||||
|
TrustManager[] trustAllCerts = new TrustManager[]{
|
||||||
|
new X509TrustManager() {
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return new X509Certificate[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkClientTrusted(X509Certificate[] certs, String authType) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] certs, String authType) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
sslContext.init(null, trustAllCerts, new SecureRandom());
|
||||||
|
|
||||||
|
this.sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TcpOverTlsTransport(HostAndPort server, File crtFile) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
super(server);
|
||||||
|
|
||||||
|
Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate(new FileInputStream(crtFile));
|
||||||
|
|
||||||
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
|
keyStore.load(null, null);
|
||||||
|
keyStore.setCertificateEntry("electrumx", certificate);
|
||||||
|
|
||||||
|
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||||
|
trustManagerFactory.init(keyStore);
|
||||||
|
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||||
|
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||||
|
|
||||||
|
sslSocketFactory = sslContext.getSocketFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Socket createSocket() throws IOException {
|
||||||
|
SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT));
|
||||||
|
sslSocket.startHandshake();
|
||||||
|
|
||||||
|
return sslSocket;
|
||||||
|
}
|
||||||
|
}
|
130
src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java
Normal file
130
src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.github.arteam.simplejsonrpc.client.Transport;
|
||||||
|
import com.github.arteam.simplejsonrpc.server.JsonRpcServer;
|
||||||
|
import com.google.common.net.HostAndPort;
|
||||||
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
|
import com.sparrowwallet.sparrow.io.ServerException;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.net.SocketFactory;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
public class TcpTransport implements Transport, Closeable {
|
||||||
|
public static final int DEFAULT_PORT = 50001;
|
||||||
|
|
||||||
|
protected final HostAndPort server;
|
||||||
|
protected final SocketFactory socketFactory;
|
||||||
|
|
||||||
|
private Socket socket;
|
||||||
|
|
||||||
|
private String response;
|
||||||
|
|
||||||
|
private final ReentrantLock clientRequestLock = new ReentrantLock();
|
||||||
|
private boolean running = false;
|
||||||
|
private boolean reading = true;
|
||||||
|
|
||||||
|
private final JsonRpcServer jsonRpcServer = new JsonRpcServer();
|
||||||
|
private final SubscriptionService subscriptionService = new SubscriptionService();
|
||||||
|
|
||||||
|
public TcpTransport(HostAndPort server) {
|
||||||
|
this.server = server;
|
||||||
|
this.socketFactory = SocketFactory.getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String pass(@NotNull String request) throws IOException {
|
||||||
|
clientRequestLock.lock();
|
||||||
|
try {
|
||||||
|
writeRequest(request);
|
||||||
|
return readResponse();
|
||||||
|
} finally {
|
||||||
|
clientRequestLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeRequest(String request) throws IOException {
|
||||||
|
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
|
||||||
|
out.println(request);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized String readResponse() {
|
||||||
|
while(reading) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
//Restore interrupt status and continue
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reading = true;
|
||||||
|
|
||||||
|
notifyAll();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void readInputLoop() throws ServerException {
|
||||||
|
while(running) {
|
||||||
|
try {
|
||||||
|
String received = readInputStream();
|
||||||
|
if(received.contains("method")) {
|
||||||
|
//Handle subscription notification
|
||||||
|
jsonRpcServer.handle(received, subscriptionService);
|
||||||
|
} else {
|
||||||
|
//Handle client's response
|
||||||
|
response = received;
|
||||||
|
reading = false;
|
||||||
|
notifyAll();
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
//Restore interrupt status and continue
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(running) {
|
||||||
|
throw new ServerException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String readInputStream() throws IOException {
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||||
|
String response = in.readLine();
|
||||||
|
|
||||||
|
if(response == null) {
|
||||||
|
throw new IOException("Could not connect to server at " + Config.get().getElectrumServer());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect() throws ServerException {
|
||||||
|
try {
|
||||||
|
socket = createSocket();
|
||||||
|
running = true;
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new ServerException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return socket != null && running;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Socket createSocket() throws IOException {
|
||||||
|
return socketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if(socket != null) {
|
||||||
|
running = false;
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.sparrowwallet.sparrow.net;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.sparrowwallet.drongo.Utils;
|
||||||
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
|
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||||
|
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||||
|
import com.sparrowwallet.sparrow.AppController;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
class VerboseTransaction {
|
||||||
|
public String blockhash;
|
||||||
|
public long blocktime;
|
||||||
|
public int confirmations;
|
||||||
|
public String hash;
|
||||||
|
public String hex;
|
||||||
|
public int locktime;
|
||||||
|
public long size;
|
||||||
|
public String txid;
|
||||||
|
public int version;
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
Integer currentHeight = AppController.getCurrentBlockHeight();
|
||||||
|
if(currentHeight != null) {
|
||||||
|
return currentHeight - confirmations + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getDate() {
|
||||||
|
return new Date(blocktime * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockTransaction getBlockTransaction() {
|
||||||
|
return new BlockTransaction(Sha256Hash.wrap(txid), getHeight(), getDate(), 0L, new Transaction(Utils.hexToBytes(hex)), blockhash == null ? null : Sha256Hash.wrap(blockhash));
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,8 @@ import com.sparrowwallet.sparrow.control.UnlabeledToggleSwitch;
|
||||||
import com.sparrowwallet.sparrow.event.ConnectionEvent;
|
import com.sparrowwallet.sparrow.event.ConnectionEvent;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
import com.sparrowwallet.sparrow.io.Config;
|
import com.sparrowwallet.sparrow.io.Config;
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
|
import com.sparrowwallet.sparrow.net.Protocol;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.concurrent.WorkerStateEvent;
|
import javafx.concurrent.WorkerStateEvent;
|
||||||
|
@ -154,10 +155,10 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
|
|
||||||
String electrumServer = config.getElectrumServer();
|
String electrumServer = config.getElectrumServer();
|
||||||
if(electrumServer != null) {
|
if(electrumServer != null) {
|
||||||
ElectrumServer.Protocol protocol = ElectrumServer.Protocol.getProtocol(electrumServer);
|
Protocol protocol = Protocol.getProtocol(electrumServer);
|
||||||
|
|
||||||
if(protocol != null) {
|
if(protocol != null) {
|
||||||
boolean ssl = protocol.equals(ElectrumServer.Protocol.SSL);
|
boolean ssl = protocol.equals(Protocol.SSL);
|
||||||
useSsl.setSelected(ssl);
|
useSsl.setSelected(ssl);
|
||||||
certificate.setDisable(!ssl);
|
certificate.setDisable(!ssl);
|
||||||
certificateSelect.setDisable(!ssl);
|
certificateSelect.setDisable(!ssl);
|
||||||
|
@ -270,8 +271,8 @@ public class ServerPreferencesController extends PreferencesDetailController {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ElectrumServer.Protocol getProtocol() {
|
private Protocol getProtocol() {
|
||||||
return (useSsl.isSelected() ? ElectrumServer.Protocol.SSL : ElectrumServer.Protocol.TCP);
|
return (useSsl.isSelected() ? Protocol.SSL : Protocol.TCP);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getHost(String text) {
|
private String getHost(String text) {
|
||||||
|
|
|
@ -12,9 +12,8 @@ import com.sparrowwallet.sparrow.control.*;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands;
|
||||||
import com.sparrowwallet.sparrow.io.Device;
|
import com.sparrowwallet.sparrow.io.Device;
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import com.sparrowwallet.sparrow.io.Storage;
|
import com.sparrowwallet.sparrow.io.Storage;
|
||||||
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
@ -647,9 +646,7 @@ public class HeadersController extends TransactionFormController implements Init
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(signingDevices.isEmpty()) {
|
signingDevices.addAll(AppController.getDevices().stream().filter(device -> device.getNeedsPinSent() || device.getNeedsPassphraseSent()).collect(Collectors.toList()));
|
||||||
signingDevices = AppController.getDevices().stream().filter(device -> device.getNeedsPinSent() || device.getNeedsPassphraseSent()).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceSignDialog dlg = new DeviceSignDialog(signingDevices.isEmpty() ? null : signingDevices, headersForm.getPsbt());
|
DeviceSignDialog dlg = new DeviceSignDialog(signingDevices.isEmpty() ? null : signingDevices, headersForm.getPsbt());
|
||||||
dlg.initModality(Modality.NONE);
|
dlg.initModality(Modality.NONE);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import com.sparrowwallet.drongo.protocol.TransactionInput;
|
||||||
import com.sparrowwallet.drongo.protocol.TransactionOutput;
|
import com.sparrowwallet.drongo.protocol.TransactionOutput;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||||
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
import com.sparrowwallet.drongo.wallet.BlockTransaction;
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
|
|
@ -14,12 +14,11 @@ import com.sparrowwallet.sparrow.control.ScriptArea;
|
||||||
import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent;
|
import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.BlockTransactionOutputsFetchedEvent;
|
import com.sparrowwallet.sparrow.event.BlockTransactionOutputsFetchedEvent;
|
||||||
import com.sparrowwallet.sparrow.event.ViewTransactionEvent;
|
import com.sparrowwallet.sparrow.event.ViewTransactionEvent;
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.Hyperlink;
|
import javafx.scene.control.Hyperlink;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import org.fxmisc.richtext.CodeArea;
|
|
||||||
import tornadofx.control.Field;
|
import tornadofx.control.Field;
|
||||||
import tornadofx.control.Fieldset;
|
import tornadofx.control.Fieldset;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.sparrowwallet.sparrow.transaction;
|
package com.sparrowwallet.sparrow.transaction;
|
||||||
|
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import com.sparrowwallet.sparrow.AppController;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.control.TransactionHexArea;
|
import com.sparrowwallet.sparrow.control.TransactionHexArea;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import com.sparrowwallet.drongo.wallet.WalletNode;
|
||||||
import com.sparrowwallet.sparrow.AppController;
|
import com.sparrowwallet.sparrow.AppController;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
import com.sparrowwallet.sparrow.io.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import com.sparrowwallet.sparrow.io.Storage;
|
import com.sparrowwallet.sparrow.io.Storage;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
Loading…
Reference in a new issue