refactor transport and speedup private server delay on connection failure

This commit is contained in:
Craig Raw 2022-07-27 11:02:01 +02:00
parent 04917c45b6
commit 258fe34101
5 changed files with 53 additions and 28 deletions

View file

@ -73,9 +73,11 @@ public class AppServices {
private static final int SERVER_PING_PERIOD_SECS = 60; private static final int SERVER_PING_PERIOD_SECS = 60;
private static final int PUBLIC_SERVER_RETRY_PERIOD_SECS = 3; private static final int PUBLIC_SERVER_RETRY_PERIOD_SECS = 3;
private static final int PRIVATE_SERVER_RETRY_PERIOD_SECS = 15;
public static final int ENUMERATE_HW_PERIOD_SECS = 30; public static final int ENUMERATE_HW_PERIOD_SECS = 30;
private static final int RATES_PERIOD_SECS = 5 * 60; 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 int CONNECTION_DELAY_SECS = 2;
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");
private static final String TOR_DEFAULT_PROXY_CIRCUIT_ID = "default"; private static final String TOR_DEFAULT_PROXY_CIRCUIT_ID = "default";
@ -251,7 +253,7 @@ public class AppServices {
private ElectrumServer.ConnectionService createConnectionService() { private ElectrumServer.ConnectionService createConnectionService() {
ElectrumServer.ConnectionService connectionService = new ElectrumServer.ConnectionService(); ElectrumServer.ConnectionService connectionService = new ElectrumServer.ConnectionService();
//Delay startup on first connection to Bitcoin Core to allow any unencrypted wallets to open first //Delay startup on first connection to Bitcoin Core to allow any unencrypted wallets to open first
connectionService.setDelay(Config.get().getServerType() == ServerType.BITCOIN_CORE ? Duration.seconds(2) : Duration.ZERO); connectionService.setDelay(Config.get().getServerType() == ServerType.BITCOIN_CORE ? Duration.seconds(CONNECTION_DELAY_SECS) : Duration.ZERO);
connectionService.setPeriod(Duration.seconds(SERVER_PING_PERIOD_SECS)); connectionService.setPeriod(Duration.seconds(SERVER_PING_PERIOD_SECS));
connectionService.setRestartOnFailure(true); connectionService.setRestartOnFailure(true);
EventManager.get().register(connectionService); EventManager.get().register(connectionService);
@ -323,6 +325,8 @@ public class AppServices {
if(Config.get().getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER) { if(Config.get().getServerType() == ServerType.PUBLIC_ELECTRUM_SERVER) {
Config.get().changePublicServer(); Config.get().changePublicServer();
connectionService.setPeriod(Duration.seconds(PUBLIC_SERVER_RETRY_PERIOD_SECS)); connectionService.setPeriod(Duration.seconds(PUBLIC_SERVER_RETRY_PERIOD_SECS));
} else {
connectionService.setPeriod(Duration.seconds(PRIVATE_SERVER_RETRY_PERIOD_SECS));
} }
log.debug("Connection failed", failEvent.getSource().getException()); log.debug("Connection failed", failEvent.getSource().getException());

View file

@ -0,0 +1,11 @@
package com.sparrowwallet.sparrow.net;
import com.github.arteam.simplejsonrpc.client.Transport;
import java.io.Closeable;
public interface CloseableTransport extends Transport, Closeable {
void connect() throws ServerException;
boolean isConnected();
boolean isClosed();
}

View file

@ -47,7 +47,7 @@ public class ElectrumServer {
public static final BlockTransaction UNFETCHABLE_BLOCK_TRANSACTION = new BlockTransaction(Sha256Hash.ZERO_HASH, 0, null, null, null); public static final BlockTransaction UNFETCHABLE_BLOCK_TRANSACTION = new BlockTransaction(Sha256Hash.ZERO_HASH, 0, null, null, null);
private static Transport transport; private static CloseableTransport transport;
private static final Map<String, List<String>> subscribedScriptHashes = Collections.synchronizedMap(new HashMap<>()); private static final Map<String, List<String>> subscribedScriptHashes = Collections.synchronizedMap(new HashMap<>());
@ -63,7 +63,7 @@ public class ElectrumServer {
private static final Pattern RPC_WALLET_LOADING_PATTERN = Pattern.compile(".*\"(Wallet loading failed:[^\"]*)\".*"); private static final Pattern RPC_WALLET_LOADING_PATTERN = Pattern.compile(".*\"(Wallet loading failed:[^\"]*)\".*");
private static synchronized Transport getTransport() throws ServerException { private static synchronized CloseableTransport getTransport() throws ServerException {
if(transport == null) { if(transport == null) {
try { try {
String electrumServer = null; String electrumServer = null;
@ -133,8 +133,8 @@ public class ElectrumServer {
} }
public void connect() throws ServerException { public void connect() throws ServerException {
TcpTransport tcpTransport = (TcpTransport)getTransport(); CloseableTransport closeableTransport = getTransport();
tcpTransport.connect(); closeableTransport.connect();
} }
public void ping() throws ServerException { public void ping() throws ServerException {
@ -163,12 +163,15 @@ public class ElectrumServer {
} }
public static synchronized void closeActiveConnection() throws ServerException { public static synchronized void closeActiveConnection() throws ServerException {
if(transport != null) {
closeConnection(transport);
transport = null;
}
}
private static void closeConnection(Closeable closeableTransport) throws ServerException {
try { try {
if(transport != null) { closeableTransport.close();
Closeable closeableTransport = (Closeable)transport;
closeableTransport.close();
transport = null;
}
} catch (IOException e) { } catch (IOException e) {
throw new ServerException(e); throw new ServerException(e);
} }

View file

@ -13,7 +13,7 @@ import java.security.cert.CertificateException;
public enum Protocol { public enum Protocol {
TCP { TCP {
@Override @Override
public Transport getTransport(HostAndPort server) { public CloseableTransport getTransport(HostAndPort server) {
if(isOnionAddress(server)) { if(isOnionAddress(server)) {
return new TorTcpTransport(server); return new TorTcpTransport(server);
} }
@ -22,24 +22,24 @@ public enum Protocol {
} }
@Override @Override
public Transport getTransport(HostAndPort server, File serverCert) { public CloseableTransport getTransport(HostAndPort server, File serverCert) {
return getTransport(server); return getTransport(server);
} }
@Override @Override
public Transport getTransport(HostAndPort server, HostAndPort proxy) { public CloseableTransport getTransport(HostAndPort server, HostAndPort proxy) {
//Avoid using a TorSocket if a proxy is specified, even if a .onion address //Avoid using a TorSocket if a proxy is specified, even if a .onion address
return new TcpTransport(server, proxy); return new TcpTransport(server, proxy);
} }
@Override @Override
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) { public CloseableTransport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) {
return getTransport(server, proxy); return getTransport(server, proxy);
} }
}, },
SSL { SSL {
@Override @Override
public Transport getTransport(HostAndPort server) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { public CloseableTransport getTransport(HostAndPort server) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
if(isOnionAddress(server)) { if(isOnionAddress(server)) {
return new TorTcpOverTlsTransport(server); return new TorTcpOverTlsTransport(server);
} }
@ -48,7 +48,7 @@ public enum Protocol {
} }
@Override @Override
public Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { public CloseableTransport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
if(isOnionAddress(server)) { if(isOnionAddress(server)) {
return new TorTcpOverTlsTransport(server, serverCert); return new TorTcpOverTlsTransport(server, serverCert);
} }
@ -57,44 +57,44 @@ public enum Protocol {
} }
@Override @Override
public Transport getTransport(HostAndPort server, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { public CloseableTransport getTransport(HostAndPort server, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
return new ProxyTcpOverTlsTransport(server, proxy); return new ProxyTcpOverTlsTransport(server, proxy);
} }
@Override @Override
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { public CloseableTransport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
return new ProxyTcpOverTlsTransport(server, serverCert, proxy); return new ProxyTcpOverTlsTransport(server, serverCert, proxy);
} }
}, },
HTTP { HTTP {
@Override @Override
public Transport getTransport(HostAndPort server) { public CloseableTransport getTransport(HostAndPort server) {
throw new UnsupportedOperationException("No transport supported for HTTP"); throw new UnsupportedOperationException("No transport supported for HTTP");
} }
@Override @Override
public Transport getTransport(HostAndPort server, File serverCert) { public CloseableTransport getTransport(HostAndPort server, File serverCert) {
throw new UnsupportedOperationException("No transport supported for HTTP"); throw new UnsupportedOperationException("No transport supported for HTTP");
} }
@Override @Override
public Transport getTransport(HostAndPort server, HostAndPort proxy) { public CloseableTransport getTransport(HostAndPort server, HostAndPort proxy) {
throw new UnsupportedOperationException("No transport supported for HTTP"); throw new UnsupportedOperationException("No transport supported for HTTP");
} }
@Override @Override
public Transport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) { public CloseableTransport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) {
throw new UnsupportedOperationException("No transport supported for HTTP"); throw new UnsupportedOperationException("No transport supported for HTTP");
} }
}; };
public abstract Transport getTransport(HostAndPort server) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException; public abstract CloseableTransport getTransport(HostAndPort server) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
public abstract Transport getTransport(HostAndPort server, File serverCert) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException; public abstract CloseableTransport 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 CloseableTransport 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 abstract CloseableTransport getTransport(HostAndPort server, File serverCert, HostAndPort proxy) throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
public HostAndPort getServerHostAndPort(String url) { public HostAndPort getServerHostAndPort(String url) {
return HostAndPort.fromString(url.substring(this.toUrlString().length())); return HostAndPort.fromString(url.substring(this.toUrlString().length()));

View file

@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
public class TcpTransport implements Transport, Closeable, TimeoutCounter { public class TcpTransport implements CloseableTransport, TimeoutCounter {
private static final Logger log = LoggerFactory.getLogger(TcpTransport.class); private static final Logger log = LoggerFactory.getLogger(TcpTransport.class);
public static final int DEFAULT_PORT = 50001; public static final int DEFAULT_PORT = 50001;
@ -46,6 +46,7 @@ public class TcpTransport implements Transport, Closeable, TimeoutCounter {
private final ReentrantLock clientRequestLock = new ReentrantLock(); private final ReentrantLock clientRequestLock = new ReentrantLock();
private boolean running = false; private boolean running = false;
private volatile boolean reading = true; private volatile boolean reading = true;
private boolean closed = false;
private boolean firstRead = true; private boolean firstRead = true;
private int readTimeoutIndex; private int readTimeoutIndex;
private int requestIdCount = 1; private int requestIdCount = 1;
@ -223,6 +224,7 @@ public class TcpTransport implements Transport, Closeable, TimeoutCounter {
public void connect() throws ServerException { public void connect() throws ServerException {
try { try {
socket = createSocket(); socket = createSocket();
log.debug("Created " + socket);
socket.setSoTimeout(SOCKET_READ_TIMEOUT_MILLIS); socket.setSoTimeout(SOCKET_READ_TIMEOUT_MILLIS);
running = true; running = true;
} catch(SSLHandshakeException e) { } catch(SSLHandshakeException e) {
@ -237,18 +239,23 @@ public class TcpTransport implements Transport, Closeable, TimeoutCounter {
} }
public boolean isConnected() { public boolean isConnected() {
return socket != null && running; return socket != null && running && !closed;
} }
protected Socket createSocket() throws IOException { protected Socket createSocket() throws IOException {
return socketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT)); return socketFactory.createSocket(server.getHost(), server.getPortOrDefault(DEFAULT_PORT));
} }
public boolean isClosed() {
return closed;
}
@Override @Override
public void close() throws IOException { public void close() throws IOException {
if(socket != null) { if(socket != null) {
socket.close(); socket.close();
} }
closed = true;
} }
@Override @Override