package lighthouse.protocol; import org.bitcoinj.core.*; import java.util.*; import java.util.concurrent.*; /** * A UTXOSource that overlays a real source, but queues up queries until told it can proceed and then queries all of * them at once. */ public class BatchingUTXOSource implements UTXOSource, Runnable { private int totalOutpoints = 0; private static class Element { private List<TransactionOutPoint> queued = new ArrayList<>(); private CompletableFuture<List<TransactionOutput>> future = new CompletableFuture<>(); } private List<Element> elements = new ArrayList<>(); private PeerUTXOMultiplexor multiplexor; public BatchingUTXOSource(PeerUTXOMultiplexor multiplexor) { this.multiplexor = multiplexor; } @Override public CompletableFuture<List<TransactionOutput>> getUTXOs(List<TransactionOutPoint> outPoints) { Element element = new Element(); element.queued.addAll(outPoints); totalOutpoints += outPoints.size(); elements.add(element); return element.future; } @Override public void run() { List<TransactionOutPoint> all = new ArrayList<>(totalOutpoints); for (Element element : elements) all.addAll(element.queued); if (all.isEmpty()) return; multiplexor.query(all).handle((result, ex) -> { if (ex != null) { for (Element element : elements) { element.future.completeExceptionally(ex); } } else { ListIterator<Element> iterator = elements.listIterator(); // Walk through each element in the hitmap, matching to the queried elements. int cursor = 0; for (int i = 0; i < all.size();) { Element element = iterator.next(); List<TransactionOutput> results = new ArrayList<>(); for (int j = 0; j < element.queued.size(); j++) { boolean hit = Utils.checkBitLE(result.getHitMap(), i++); if (hit) results.add(result.getOutputs().get(cursor++)); } element.future.complete(results); } } return null; }); } }