From 796f68640c0dce1d6cc8053e23e08767f28b6fc1 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 22 Dec 2021 11:16:55 +0200 Subject: [PATCH] use unique (per session) integers as ids for all paged server queries --- .../sparrow/net/BatchedElectrumServerRpc.java | 10 +-- .../sparrow/net/PagedBatchRequestBuilder.java | 77 +++++++++++++------ 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java b/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java index 564931e2..60da5cd2 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java @@ -73,7 +73,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc { @Override @SuppressWarnings("unchecked") public Map getScriptHashHistory(Transport transport, Wallet wallet, Map pathScriptHashes, boolean failOnError) { - PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport).keysType(String.class).returnType(ScriptHashTx[].class); + PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport, idCounter).keysType(String.class).returnType(ScriptHashTx[].class); EventManager.get().post(new WalletHistoryStatusEvent(wallet, true, "Loading transactions for " + nodeRangesToString(pathScriptHashes.keySet()))); for(String path : pathScriptHashes.keySet()) { @@ -101,7 +101,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc { @Override @SuppressWarnings("unchecked") public Map getScriptHashMempool(Transport transport, Wallet wallet, Map pathScriptHashes, boolean failOnError) { - PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport).keysType(String.class).returnType(ScriptHashTx[].class); + PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport, idCounter).keysType(String.class).returnType(ScriptHashTx[].class); for(String path : pathScriptHashes.keySet()) { batchRequest.add(path, "blockchain.scripthash.get_mempool", pathScriptHashes.get(path)); @@ -128,7 +128,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc { @Override @SuppressWarnings("unchecked") public Map subscribeScriptHashes(Transport transport, Wallet wallet, Map pathScriptHashes) { - PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport).keysType(String.class).returnType(String.class); + PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport, idCounter).keysType(String.class).returnType(String.class); EventManager.get().post(new WalletHistoryStatusEvent(wallet, true, "Finding transactions for " + nodeRangesToString(pathScriptHashes.keySet()))); for(String path : pathScriptHashes.keySet()) { @@ -148,7 +148,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc { @Override @SuppressWarnings("unchecked") public Map getBlockHeaders(Transport transport, Wallet wallet, Set blockHeights) { - PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport).keysType(Integer.class).returnType(String.class); + PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport, idCounter).keysType(Integer.class).returnType(String.class); EventManager.get().post(new WalletHistoryStatusEvent(wallet, true, "Retrieving " + blockHeights.size() + " block headers")); for(Integer height : blockHeights) { @@ -167,7 +167,7 @@ public class BatchedElectrumServerRpc implements ElectrumServerRpc { @Override @SuppressWarnings("unchecked") public Map getTransactions(Transport transport, Wallet wallet, Set txids) { - PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport).keysType(String.class).returnType(String.class); + PagedBatchRequestBuilder batchRequest = PagedBatchRequestBuilder.create(transport, idCounter).keysType(String.class).returnType(String.class); EventManager.get().post(new WalletHistoryStatusEvent(wallet, true, "Retrieving " + txids.size() + " transactions")); for(String txid : txids) { diff --git a/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java b/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java index fc8739b1..7bcbee05 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/PagedBatchRequestBuilder.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.atomic.AtomicLong; import static com.sparrowwallet.sparrow.net.BatchedElectrumServerRpc.MAX_RETRIES; import static com.sparrowwallet.sparrow.net.BatchedElectrumServerRpc.RETRY_DELAY; @@ -18,8 +19,10 @@ import static com.sparrowwallet.sparrow.net.BatchedElectrumServerRpc.RETRY_DELAY public class PagedBatchRequestBuilder extends AbstractBuilder { public static final int DEFAULT_PAGE_SIZE = 500; + private final AtomicLong counter; + @NotNull - private final List requests; + private final List> requests; /** * Type of request ids @@ -41,17 +44,19 @@ public class PagedBatchRequestBuilder extends AbstractBuilder { * @param transport transport for request performing * @param mapper mapper for JSON processing */ - public PagedBatchRequestBuilder(@NotNull Transport transport, @NotNull ObjectMapper mapper) { - this(transport, mapper, new ArrayList(), null, null); + public PagedBatchRequestBuilder(@NotNull Transport transport, @NotNull ObjectMapper mapper, AtomicLong counter) { + this(transport, mapper, new ArrayList>(), null, null, counter); } public PagedBatchRequestBuilder(@NotNull Transport transport, @NotNull ObjectMapper mapper, - @NotNull List requests, - @Nullable Class keysType, @Nullable Class returnType) { + @NotNull List> requests, + @Nullable Class keysType, @Nullable Class returnType, + @Nullable AtomicLong counter) { super(transport, mapper); this.requests = requests; this.keysType = keysType; this.returnType = returnType; + this.counter = counter; } /** @@ -63,8 +68,8 @@ public class PagedBatchRequestBuilder extends AbstractBuilder { * @return the current builder */ @NotNull - public PagedBatchRequestBuilder add(Object id, @NotNull String method, @NotNull Object param) { - requests.add(new Request(id, method, param)); + public PagedBatchRequestBuilder add(K id, @NotNull String method, @NotNull Object param) { + requests.add(new Request(id, counter == null ? null : counter.incrementAndGet(), method, param)); return this; } @@ -77,7 +82,7 @@ public class PagedBatchRequestBuilder extends AbstractBuilder { * @return a new builder */ public PagedBatchRequestBuilder keysType(@NotNull Class keysClass) { - return new PagedBatchRequestBuilder(transport, mapper, requests, keysClass, returnType); + return new PagedBatchRequestBuilder(transport, mapper, new ArrayList>(), keysClass, returnType, counter); } /** @@ -89,7 +94,7 @@ public class PagedBatchRequestBuilder extends AbstractBuilder { * @return a new builder */ public PagedBatchRequestBuilder returnType(@NotNull Class valuesClass) { - return new PagedBatchRequestBuilder(transport, mapper, requests, keysType, valuesClass); + return new PagedBatchRequestBuilder(transport, mapper, requests, keysType, valuesClass, counter); } /** @@ -102,21 +107,35 @@ public class PagedBatchRequestBuilder extends AbstractBuilder { Map allResults = new HashMap<>(); JsonRpcClient client = new JsonRpcClient(transport); - List> pages = Lists.partition(requests, getPageSize()); - for(List page : pages) { - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(keysType).returnType(returnType); - for(Request request : page) { - if(request.id instanceof String strReq) { - batchRequest.add(strReq, request.method, request.param); - } else if(request.id instanceof Integer intReq) { - batchRequest.add(intReq, request.method, request.param); - } else { - throw new IllegalArgumentException("Id of class " + request.id.getClass().getName() + " not supported"); + List>> pages = Lists.partition(requests, getPageSize()); + for(List> page : pages) { + if(counter != null) { + Map counterIdMap = new HashMap<>(); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(Long.class).returnType(returnType); + for(Request request : page) { + counterIdMap.put(request.counterId, request.id); + batchRequest.add(request.counterId, request.method, request.param); } - } - Map pageResult = new RetryLogic>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute); - allResults.putAll(pageResult); + Map pageResult = new RetryLogic>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute); + for(Map.Entry pageEntry : pageResult.entrySet()) { + allResults.put(counterIdMap.get(pageEntry.getKey()), pageEntry.getValue()); + } + } else { + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(keysType).returnType(returnType); + for(Request request : page) { + if(request.id instanceof String strReq) { + batchRequest.add(strReq, request.method, request.param); + } else if(request.id instanceof Integer intReq) { + batchRequest.add(intReq, request.method, request.param); + } else { + throw new IllegalArgumentException("Id of class " + request.id.getClass().getName() + " not supported"); + } + } + + Map pageResult = new RetryLogic>(MAX_RETRIES, RETRY_DELAY, List.of(IllegalStateException.class, IllegalArgumentException.class)).getResult(batchRequest::execute); + allResults.putAll(pageResult); + } } return allResults; @@ -138,8 +157,18 @@ public class PagedBatchRequestBuilder extends AbstractBuilder { */ @NotNull public static PagedBatchRequestBuilder create(Transport transport) { - return new PagedBatchRequestBuilder(transport, new ObjectMapper()); + return new PagedBatchRequestBuilder(transport, new ObjectMapper(), null); } - private static record Request(Object id, String method, Object param) {} + /** + * Creates a builder of a JSON-RPC batch request in initial state with a counter for request ids + * + * @return batch request builder + */ + @NotNull + public static PagedBatchRequestBuilder create(Transport transport, AtomicLong counter) { + return new PagedBatchRequestBuilder(transport, new ObjectMapper(), counter); + } + + private static record Request(K id, Long counterId, String method, Object param) {} }