diff --git a/core/src/main/java/com/google/bitcoin/core/Peer.java b/core/src/main/java/com/google/bitcoin/core/Peer.java index 6d45bd7a1..aae5df1ed 100644 --- a/core/src/main/java/com/google/bitcoin/core/Peer.java +++ b/core/src/main/java/com/google/bitcoin/core/Peer.java @@ -1241,4 +1241,35 @@ public class Peer { return null; } } + + /** + *

Sets a Bloom filter on this connection. This will cause the given {@link BloomFilter} object to be sent to the + * remote peer and if either a memory pool has been set using {@link Peer#setMemoryPool(MemoryPool)} or the + * downloadData property is true, a {@link MemoryPoolMessage} is sent as well to trigger downloading of any + * pending transactions that may be relevant.

+ * + *

The Peer does not keep a reference to the BloomFilter. Also, it will not automatically request filters + * from any wallets added using {@link Peer#addWallet(Wallet)}. This is to allow callers to avoid redundantly + * recalculating the same filter repeatedly when using multiple peers together.

+ * + *

Therefore, you should not use this method if your app uses a {@link PeerGroup}. It is called for you.

+ * + *

If the remote peer doesn't support Bloom filtering, then this call is ignored.

+ */ + public void setBloomFilter(BloomFilter filter) throws IOException { + if (!getPeerVersionMessage().isBloomFilteringSupported()) + return; + boolean shouldQueryMemPool; + synchronized (this) { + shouldQueryMemPool = memoryPool != null || downloadData.get(); + } + log.info("{}: Sending Bloom filter{}", this, shouldQueryMemPool ? " and querying mempool" : ""); + ChannelFuture future = sendMessage(filter); + if (shouldQueryMemPool) + future.addListener(new ChannelFutureListener() { + public void operationComplete(ChannelFuture future) throws Exception { + sendMessage(new MemoryPoolMessage()); + } + }); + } } diff --git a/core/src/main/java/com/google/bitcoin/core/PeerGroup.java b/core/src/main/java/com/google/bitcoin/core/PeerGroup.java index b32840329..6d62a7db2 100644 --- a/core/src/main/java/com/google/bitcoin/core/PeerGroup.java +++ b/core/src/main/java/com/google/bitcoin/core/PeerGroup.java @@ -580,16 +580,12 @@ public class PeerGroup extends AbstractIdleService { for (Wallet w : wallets) filter.merge(w.getBloomFilter(lastBloomFilterElementCount, bloomFilterFPRate, bloomFilterTweak)); bloomFilter = filter; - for (Peer peer : peers) { - if (peer.getPeerVersionMessage().isBloomFilteringSupported()) { - try { - log.info("{}: Sending peer an updated Bloom Filter.", peer); - peer.sendMessage(filter); - } catch (IOException e) { - throw new RuntimeException(e); - } + for (Peer peer : peers) + try { + peer.setBloomFilter(filter); + } catch (IOException e) { + throw new RuntimeException(e); } - } } // Do this last so that bloomFilter is already set when it gets called. setFastCatchupTimeSecs(earliestKeyTime); @@ -719,11 +715,7 @@ public class PeerGroup extends AbstractIdleService { // aren't relevant to our wallet. We may still receive some false positives, which is // OK because it helps improve wallet privacy. Old nodes will just ignore the message. try { - if (bloomFilter != null && peer.getPeerVersionMessage().isBloomFilteringSupported()) { - log.info("{}: Sending Bloom filter and querying memory pool", peer); - peer.sendMessage(bloomFilter); - peer.sendMessage(new MemoryPoolMessage()); - } + if (bloomFilter != null) peer.setBloomFilter(bloomFilter); } catch (IOException e) { } // That was quick...already disconnected // Link the peer to the memory pool so broadcast transactions have their confidence levels updated. peer.setMemoryPool(memoryPool); diff --git a/core/src/test/java/com/google/bitcoin/core/PeerGroupTest.java b/core/src/test/java/com/google/bitcoin/core/PeerGroupTest.java index 6a5251560..9ed5de31f 100644 --- a/core/src/test/java/com/google/bitcoin/core/PeerGroupTest.java +++ b/core/src/test/java/com/google/bitcoin/core/PeerGroupTest.java @@ -311,12 +311,13 @@ public class PeerGroupTest extends TestWithPeerGroup { peerGroup.addWallet(wallet); // Transaction announced to the first peer. InventoryMessage inv1 = (InventoryMessage) outbound(p1); + assertTrue(outbound(p1) instanceof BloomFilter); // Filter is recalculated. + assertTrue(outbound(p1) instanceof MemoryPoolMessage); assertEquals(t3.getHash(), inv1.getItems().get(0).hash); // Peer asks for the transaction, and get it. GetDataMessage getdata = new GetDataMessage(params); getdata.addItem(inv1.getItems().get(0)); inbound(p1, getdata); - assertTrue(outbound(p1) instanceof BloomFilter); // Filter is recalculated. Transaction t4 = (Transaction) outbound(p1); assertEquals(t3, t4);