package lighthouse.protocol; import com.google.common.util.concurrent.*; import org.bitcoinj.core.*; import org.slf4j.*; import javax.annotation.*; import java.util.*; import java.util.concurrent.*; import static com.google.common.base.Preconditions.*; /** * A UTXOSource that repeats the given query on all provided peers, waits for the response from all of them * to come in, and then verifies that they all match. If there's any inconsistency at all, or if any query fails, * the future completes exceptionally. */ public class PeerUTXOMultiplexor { private static final Logger log = LoggerFactory.getLogger(PeerUTXOMultiplexor.class); private List<Peer> peers; public PeerUTXOMultiplexor(List<Peer> peers) { checkArgument(!peers.isEmpty()); this.peers = peers; } public CompletableFuture<UTXOsMessage> query(List<TransactionOutPoint> outPoints) { CompletableFuture<UTXOsMessage> result = new CompletableFuture<>(); try { List<ListenableFuture<UTXOsMessage>> futures = new LinkedList<>(); for (Peer peer: peers) { log.info("Sending UTXO query to {}", peer); futures.add(peer.getUTXOs(outPoints)); } Futures.addCallback(Futures.allAsList(futures), new FutureCallback<List<UTXOsMessage>>() { @Override public void onSuccess(@Nullable List<UTXOsMessage> val) { checkNotNull(val); // Verify they are all the same. UTXOsMessage template = val.get(0); for (UTXOsMessage response : val) { if (!response.equals(template)) { log.error("Got inconsistent UTXO answers from peer: {}", response); result.completeExceptionally(new Ex.InconsistentUTXOAnswers()); return; } } result.complete(template); } @Override public void onFailure(@Nonnull Throwable throwable) { result.completeExceptionally(throwable); } }); } catch (Exception e) { result.completeExceptionally(e); } return result; } }