diff --git a/src/main/java/org/qortal/crosschain/ElectrumX.java b/src/main/java/org/qortal/crosschain/ElectrumX.java index 0708dfc1..87641607 100644 --- a/src/main/java/org/qortal/crosschain/ElectrumX.java +++ b/src/main/java/org/qortal/crosschain/ElectrumX.java @@ -5,19 +5,7 @@ import java.math.BigDecimal; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Random; -import java.util.Scanner; -import java.util.Set; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -50,6 +38,9 @@ public class ElectrumX extends BitcoinyBlockchainProvider { /** Error message sent by some ElectrumX servers when they don't support returning verbose transactions. */ private static final String VERBOSE_TRANSACTIONS_UNSUPPORTED_MESSAGE = "verbose transactions are currently unsupported"; + private static final int RESPONSE_TIME_READINGS = 5; + private static final long MAX_AVG_RESPONSE_TIME = 1000L; // ms + public static class Server { String hostname; @@ -57,6 +48,7 @@ public class ElectrumX extends BitcoinyBlockchainProvider { ConnectionType connectionType; int port; + private List responseTimes = new ArrayList<>(); public Server(String hostname, ConnectionType connectionType, int port) { this.hostname = hostname; @@ -64,6 +56,25 @@ public class ElectrumX extends BitcoinyBlockchainProvider { this.port = port; } + public void addResponseTime(long responseTime) { + while (this.responseTimes.size() > RESPONSE_TIME_READINGS) { + this.responseTimes.remove(0); + } + this.responseTimes.add(responseTime); + } + + public long averageResponseTime() { + if (this.responseTimes.size() < RESPONSE_TIME_READINGS) { + // Not enough readings yet + return 0L; + } + OptionalDouble average = this.responseTimes.stream().mapToDouble(a -> a).average(); + if (average.isPresent()) { + return Double.valueOf(average.getAsDouble()).longValue(); + } + return 0L; + } + @Override public boolean equals(Object other) { if (other == this) @@ -539,6 +550,17 @@ public class ElectrumX extends BitcoinyBlockchainProvider { while (haveConnection()) { Object response = connectedRpc(method, params); + + // If we have more servers and this one replied slowly, try another + if (!this.remainingServers.isEmpty()) { + long averageResponseTime = this.currentServer.averageResponseTime(); + if (averageResponseTime > MAX_AVG_RESPONSE_TIME) { + LOGGER.info("Slow average response time {}ms from {} - trying another server...", this.currentServer.hostname, averageResponseTime); + this.closeServer(); + break; + } + } + if (response != null) return response; @@ -628,6 +650,7 @@ public class ElectrumX extends BitcoinyBlockchainProvider { String request = requestJson.toJSONString() + "\n"; LOGGER.trace(() -> String.format("Request: %s", request)); + long startTime = System.currentTimeMillis(); final String response; try { @@ -638,7 +661,11 @@ public class ElectrumX extends BitcoinyBlockchainProvider { return null; } + long endTime = System.currentTimeMillis(); + long responseTime = endTime-startTime; + LOGGER.trace(() -> String.format("Response: %s", response)); + LOGGER.info(() -> String.format("Time taken: %dms", endTime-startTime)); if (response.isEmpty()) // Empty response - try another server? @@ -649,6 +676,11 @@ public class ElectrumX extends BitcoinyBlockchainProvider { // Unexpected response - try another server? return null; + // Keep track of response times + if (this.currentServer != null) { + this.currentServer.addResponseTime(responseTime); + } + JSONObject responseJson = (JSONObject) responseObj; Object errorObj = responseJson.get("error");