diff --git a/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java b/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java index c5a49272..d9b66ad3 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java @@ -25,13 +25,11 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc { static final int RETRY_DELAY_SECS = 1; private final AtomicLong idCounter; + private final int maxTargetBlocks; - public BatchedElectrumServerRpc() { - this.idCounter = new AtomicLong(); - } - - public BatchedElectrumServerRpc(long idCounterValue) { + public BatchedElectrumServerRpc(long idCounterValue, int maxTargetBlocks) { this.idCounter = new AtomicLong(idCounterValue); + this.maxTargetBlocks = maxTargetBlocks; } @Override @@ -222,11 +220,19 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc { public Map getFeeEstimates(Transport transport, List targetBlocks) { PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport, idCounter).keysType(Integer.class).returnType(Double.class); for(Integer targetBlock : targetBlocks) { - batchRequest.add(targetBlock, "blockchain.estimatefee", targetBlock); + if(targetBlock <= maxTargetBlocks) { + batchRequest.add(targetBlock, "blockchain.estimatefee", targetBlock); + } } try { - return batchRequest.execute(); + Map result = batchRequest.execute(); + for(Integer targetBlock : targetBlocks) { + if(targetBlock > maxTargetBlocks) { + result.put(targetBlock, result.values().stream().mapToDouble(v -> v).min().orElse(0.0001d)); + } + } + return result; } catch(JsonRpcBatchException e) { throw new ElectrumServerRpcException("Error getting fee estimates from connected server: " + e.getErrors(), e); } catch(Exception e) { diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java index 17c3d60c..8afff6af 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java @@ -47,6 +47,8 @@ public class ElectrumServer { private static final Version FULCRUM_MIN_BATCHING_VERSION = new Version("1.6.0"); + private static final Version MEMPOOL_ELECTRS_MIN_BATCHING_VERSION = new Version("3.1.0"); + public static final String CORE_ELECTRUM_HOST = "127.0.0.1"; private static final int MINIMUM_BROADCASTS = 2; @@ -1021,15 +1023,15 @@ public class ElectrumServer { existingStatuses.add(status); } - public static boolean supportsBatching(List serverVersion) { - if(serverVersion.size() > 0) { - String server = serverVersion.get(0).toLowerCase(Locale.ROOT); + public static ServerCapability getServerCapability(List serverVersion) { + if(!serverVersion.isEmpty()) { + String server = serverVersion.getFirst().toLowerCase(Locale.ROOT); if(server.contains("electrumx")) { - return true; + return new ServerCapability(true); } if(server.startsWith("cormorant")) { - return true; + return new ServerCapability(true); } if(server.startsWith("electrs/")) { @@ -1041,7 +1043,7 @@ public class ElectrumServer { try { Version version = new Version(electrsVersion); if(version.compareTo(ELECTRS_MIN_BATCHING_VERSION) >= 0) { - return true; + return new ServerCapability(true); } } catch(Exception e) { //ignore @@ -1057,7 +1059,26 @@ public class ElectrumServer { try { Version version = new Version(fulcrumVersion); if(version.compareTo(FULCRUM_MIN_BATCHING_VERSION) >= 0) { - return true; + return new ServerCapability(true); + } + } catch(Exception e) { + //ignore + } + } + + if(server.startsWith("mempool-electrs")) { + String mempoolElectrsVersion = server.substring("mempool-electrs".length()).trim(); + int dashIndex = mempoolElectrsVersion.indexOf('-'); + String mempoolElectrsSuffix = ""; + if(dashIndex > -1) { + mempoolElectrsSuffix = mempoolElectrsVersion.substring(dashIndex); + mempoolElectrsVersion = mempoolElectrsVersion.substring(0, dashIndex); + } + try { + Version version = new Version(mempoolElectrsVersion); + if(version.compareTo(MEMPOOL_ELECTRS_MIN_BATCHING_VERSION) > 0 || + (version.compareTo(MEMPOOL_ELECTRS_MIN_BATCHING_VERSION) == 0 && (!mempoolElectrsSuffix.contains("dev") || mempoolElectrsSuffix.contains("dev-249848d")))) { + return new ServerCapability(true, 25); } } catch(Exception e) { //ignore @@ -1065,7 +1086,7 @@ public class ElectrumServer { } } - return false; + return new ServerCapability(false); } public static class ServerVersionService extends Service> { @@ -1196,9 +1217,10 @@ public class ElectrumServer { firstCall = false; //If electrumx is detected, we can upgrade to batched RPC. Electrs/EPS do not support batching. - if(supportsBatching(serverVersion)) { + ServerCapability serverCapability = getServerCapability(serverVersion); + if(serverCapability.supportsBatching()) { log.debug("Upgrading to batched JSON-RPC"); - electrumServerRpc = new BatchedElectrumServerRpc(electrumServerRpc.getIdCounterValue()); + electrumServerRpc = new BatchedElectrumServerRpc(electrumServerRpc.getIdCounterValue(), serverCapability.getMaxTargetBlocks()); } BlockHeaderTip tip; diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ServerCapability.java b/src/main/java/com/sparrowwallet/sparrow/net/ServerCapability.java new file mode 100644 index 00000000..6bb33c6b --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/net/ServerCapability.java @@ -0,0 +1,26 @@ +package com.sparrowwallet.sparrow.net; + +import com.sparrowwallet.sparrow.AppServices; + +public class ServerCapability { + private final boolean supportsBatching; + private final int maxTargetBlocks; + + public ServerCapability(boolean supportsBatching) { + this.supportsBatching = supportsBatching; + this.maxTargetBlocks = AppServices.TARGET_BLOCKS_RANGE.getLast(); + } + + public ServerCapability(boolean supportsBatching, int maxTargetBlocks) { + this.supportsBatching = supportsBatching; + this.maxTargetBlocks = maxTargetBlocks; + } + + public boolean supportsBatching() { + return supportsBatching; + } + + public int getMaxTargetBlocks() { + return maxTargetBlocks; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java b/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java index 9261f663..24a9eaf1 100644 --- a/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java +++ b/src/main/java/com/sparrowwallet/sparrow/preferences/ServerPreferencesController.java @@ -603,7 +603,8 @@ public class ServerPreferencesController extends PreferencesDetailController { testConnection.setGraphic(getGlyph(FontAwesome5.Glyph.CHECK_CIRCLE, "success")); if(serverVersion != null) { testResults.setText("Connected to " + serverVersion.get(0) + " on protocol version " + serverVersion.get(1)); - if(ElectrumServer.supportsBatching(serverVersion)) { + ServerCapability serverCapability = ElectrumServer.getServerCapability(serverVersion); + if(serverCapability.supportsBatching()) { testResults.setText(testResults.getText() + "\nBatched RPC enabled."); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java b/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java index 39908090..b5211a5f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/terminal/preferences/ServerTestDialog.java @@ -168,7 +168,8 @@ public class ServerTestDialog extends DialogWindow { testStatus.setText("Success"); if(serverVersion != null) { testResults.setText("Connected to " + serverVersion.get(0) + " on protocol version " + serverVersion.get(1)); - if(ElectrumServer.supportsBatching(serverVersion)) { + ServerCapability serverCapability = ElectrumServer.getServerCapability(serverVersion); + if(serverCapability.supportsBatching()) { testResults.setText(testResults.getText() + "\nBatched RPC enabled."); } }