From ec7cec67a7b6fc0811ca7dfa06b1c644bf294d2f Mon Sep 17 00:00:00 2001 From: Devrandom Date: Wed, 5 Nov 2014 10:37:20 -0800 Subject: [PATCH] Rename MemoryPool to TxConfidencePool and make singleton --- .../java/org/bitcoinj/core/BloomFilter.java | 2 +- .../org/bitcoinj/core/MemoryPoolMessage.java | 2 +- .../org/bitcoinj/core/NetworkParameters.java | 6 +++ .../src/main/java/org/bitcoinj/core/Peer.java | 26 ++++++------ .../java/org/bitcoinj/core/PeerGroup.java | 14 +++---- .../bitcoinj/core/TransactionBroadcast.java | 2 +- ...{MemoryPool.java => TxConfidencePool.java} | 40 +++++++++---------- .../org/bitcoinj/core/MemoryPoolTest.java | 4 +- .../java/org/bitcoinj/core/PeerGroupTest.java | 2 +- .../test/java/org/bitcoinj/core/PeerTest.java | 10 ++--- 10 files changed, 57 insertions(+), 51 deletions(-) rename core/src/main/java/org/bitcoinj/core/{MemoryPool.java => TxConfidencePool.java} (92%) diff --git a/core/src/main/java/org/bitcoinj/core/BloomFilter.java b/core/src/main/java/org/bitcoinj/core/BloomFilter.java index a67db6c2..92c796b0 100644 --- a/core/src/main/java/org/bitcoinj/core/BloomFilter.java +++ b/core/src/main/java/org/bitcoinj/core/BloomFilter.java @@ -93,7 +93,7 @@ public class BloomFilter extends Message { * *

In order for filtered block download to function efficiently, the number of matched transactions in any given * block should be less than (with some headroom) the maximum size of the MemoryPool used by the Peer - * doing the downloading (default is {@link MemoryPool#MAX_SIZE}). See the comment in processBlock(FilteredBlock) + * doing the downloading (default is {@link TxConfidencePool#MAX_SIZE}). See the comment in processBlock(FilteredBlock) * for more information on this restriction.

* *

randomNonce is a tweak for the hash function used to prevent some theoretical DoS attacks. diff --git a/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java b/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java index 5c90fbbd..a56d3ecf 100644 --- a/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java +++ b/core/src/main/java/org/bitcoinj/core/MemoryPoolMessage.java @@ -22,7 +22,7 @@ import java.io.OutputStream; /** * The "mempool" message asks a remote peer to announce all transactions in its memory pool, possibly restricted by * any Bloom filter set on the connection. The list of transaction hashes comes back in an inv message. Note that - * this is different to the {@link MemoryPool} object which doesn't try to keep track of all pending transactions, + * this is different to the {@link TxConfidencePool} object which doesn't try to keep track of all pending transactions, * it's just a holding area for transactions that a part of the app may find interesting. The mempool message has * no fields. */ diff --git a/core/src/main/java/org/bitcoinj/core/NetworkParameters.java b/core/src/main/java/org/bitcoinj/core/NetworkParameters.java index a1486d0e..535e98fd 100644 --- a/core/src/main/java/org/bitcoinj/core/NetworkParameters.java +++ b/core/src/main/java/org/bitcoinj/core/NetworkParameters.java @@ -77,6 +77,7 @@ public abstract class NetworkParameters implements Serializable { protected byte[] alertSigningKey; protected int bip32HeaderPub; protected int bip32HeaderPriv; + transient protected TxConfidencePool confidencePool; /** * See getId(). This may be null for old deserialized wallets. In that case we derive it heuristically @@ -97,6 +98,7 @@ public abstract class NetworkParameters implements Serializable { protected NetworkParameters() { alertSigningKey = SATOSHI_KEY; genesisBlock = createGenesis(this); + confidencePool = new TxConfidencePool(); } private static Block createGenesis(NetworkParameters n) { @@ -356,4 +358,8 @@ public abstract class NetworkParameters implements Serializable { public int getBip32HeaderPriv() { return bip32HeaderPriv; } + + public TxConfidencePool getConfidencePool() { + return confidencePool; + } } diff --git a/core/src/main/java/org/bitcoinj/core/Peer.java b/core/src/main/java/org/bitcoinj/core/Peer.java index afe274b1..7329e763 100644 --- a/core/src/main/java/org/bitcoinj/core/Peer.java +++ b/core/src/main/java/org/bitcoinj/core/Peer.java @@ -89,7 +89,7 @@ public class Peer extends PeerSocketHandler { private final AtomicInteger blocksAnnounced = new AtomicInteger(); // A class that tracks recent transactions that have been broadcast across the network, counts how many // peers announced them and updates the transaction confidence data. It is passed to each Peer. - private final MemoryPool memoryPool; + private final TxConfidencePool confidencePool; // Each wallet added to the peer will be notified of downloaded transaction data. private final CopyOnWriteArrayList wallets; // A time before which we only download block headers, after that point we download block bodies. @@ -179,7 +179,7 @@ public class Peer extends PeerSocketHandler { * used to keep track of which peers relayed transactions and offer more descriptive logging.

*/ public Peer(NetworkParameters params, VersionMessage ver, PeerAddress remoteAddress, - @Nullable AbstractBlockChain chain, @Nullable MemoryPool mempool) { + @Nullable AbstractBlockChain chain, @Nullable TxConfidencePool mempool) { this(params, ver, remoteAddress, chain, mempool, true); } @@ -198,7 +198,7 @@ public class Peer extends PeerSocketHandler { * used to keep track of which peers relayed transactions and offer more descriptive logging.

*/ public Peer(NetworkParameters params, VersionMessage ver, PeerAddress remoteAddress, - @Nullable AbstractBlockChain chain, @Nullable MemoryPool mempool, boolean downloadTxDependencies) { + @Nullable AbstractBlockChain chain, @Nullable TxConfidencePool mempool, boolean downloadTxDependencies) { super(params, remoteAddress); this.params = Preconditions.checkNotNull(params); this.versionMessage = Preconditions.checkNotNull(ver); @@ -211,7 +211,7 @@ public class Peer extends PeerSocketHandler { this.isAcked = false; this.pendingPings = new CopyOnWriteArrayList(); this.wallets = new CopyOnWriteArrayList(); - this.memoryPool = mempool; + this.confidencePool = mempool; } /** @@ -583,9 +583,9 @@ public class Peer extends PeerSocketHandler { lock.lock(); try { log.debug("{}: Received tx {}", getAddress(), tx.getHashAsString()); - if (memoryPool != null) { + if (confidencePool != null) { // We may get back a different transaction object. - tx = memoryPool.seen(tx, getAddress()); + tx = confidencePool.seen(tx, getAddress()); } fTx = tx; // Label the transaction as coming in from the P2P network (as opposed to being created by us, direct import, @@ -686,7 +686,7 @@ public class Peer extends PeerSocketHandler { *

Note that dependencies downloaded this way will not trigger the onTransaction method of event listeners.

*/ public ListenableFuture> downloadDependencies(Transaction tx) { - checkNotNull(memoryPool, "Must have a configured MemoryPool object to download dependencies."); + checkNotNull(confidencePool, "Must have a configured MemoryPool object to download dependencies."); TransactionConfidence.ConfidenceType txConfidence = tx.getConfidence().getConfidenceType(); Preconditions.checkArgument(txConfidence != TransactionConfidence.ConfidenceType.BUILDING); log.info("{}: Downloading dependencies of {}", getAddress(), tx.getHashAsString()); @@ -712,7 +712,7 @@ public class Peer extends PeerSocketHandler { private ListenableFuture downloadDependenciesInternal(final Transaction tx, final Object marker, final List results) { - checkNotNull(memoryPool, "Must have a configured MemoryPool object to download dependencies."); + checkNotNull(confidencePool, "Must have a configured MemoryPool object to download dependencies."); final SettableFuture resultFuture = SettableFuture.create(); final Sha256Hash rootTxHash = tx.getHash(); // We want to recursively grab its dependencies. This is so listeners can learn important information like @@ -727,7 +727,7 @@ public class Peer extends PeerSocketHandler { for (TransactionInput input : tx.getInputs()) { // There may be multiple inputs that connect to the same transaction. Sha256Hash hash = input.getOutpoint().getHash(); - Transaction dep = memoryPool.get(hash); + Transaction dep = confidencePool.get(hash); if (dep == null) { needToRequest.add(hash); } else { @@ -1043,7 +1043,7 @@ public class Peer extends PeerSocketHandler { Iterator it = transactions.iterator(); while (it.hasNext()) { InventoryItem item = it.next(); - if (memoryPool == null) { + if (confidencePool == null) { if (downloadData) { // If there's no memory pool only download transactions if we're configured to. getdata.addItem(item); @@ -1055,7 +1055,7 @@ public class Peer extends PeerSocketHandler { // peers run at different speeds. However to conserve bandwidth on mobile devices we try to only download a // transaction once. This means we can miss broadcasts if the peer disconnects between sending us an inv and // sending us the transaction: currently we'll never try to re-fetch after a timeout. - if (memoryPool.maybeWasSeen(item.hash)) { + if (confidencePool.maybeWasSeen(item.hash)) { // Some other peer already announced this so don't download. it.remove(); } else { @@ -1063,7 +1063,7 @@ public class Peer extends PeerSocketHandler { getdata.addItem(item); } // This can trigger transaction confidence listeners. - memoryPool.seen(item.hash, this.getAddress()); + confidencePool.seen(item.hash, this.getAddress()); } } @@ -1528,7 +1528,7 @@ public class Peer extends PeerSocketHandler { * unset a filter, though the underlying p2p protocol does support it.

*/ public void setBloomFilter(BloomFilter filter) { - setBloomFilter(filter, memoryPool != null || vDownloadData); + setBloomFilter(filter, confidencePool != null || vDownloadData); } /** diff --git a/core/src/main/java/org/bitcoinj/core/PeerGroup.java b/core/src/main/java/org/bitcoinj/core/PeerGroup.java index f2d6ae64..345d5c3f 100644 --- a/core/src/main/java/org/bitcoinj/core/PeerGroup.java +++ b/core/src/main/java/org/bitcoinj/core/PeerGroup.java @@ -121,7 +121,7 @@ public class PeerGroup implements TransactionBroadcaster { @GuardedBy("lock") private boolean downloadTxDependencies; // A class that tracks recent transactions that have been broadcast across the network, counts how many // peers announced them and updates the transaction confidence data. It is passed to each Peer. - private final MemoryPool memoryPool; + private final TxConfidencePool confidencePool; // How many connections we want to have open at the current time. If we lose connections, we'll try opening more // until we reach this count. @GuardedBy("lock") private int maxConnections; @@ -335,7 +335,7 @@ public class PeerGroup implements TransactionBroadcaster { downloadTxDependencies = true; - memoryPool = new MemoryPool(); + confidencePool = params.getConfidencePool(); inactives = new PriorityQueue(1, new Comparator() { @SuppressWarnings("FieldAccessNotGuarded") // only called when inactives is accessed, and lock is held then. @@ -516,7 +516,7 @@ public class PeerGroup implements TransactionBroadcaster { while (it.hasNext()) { InventoryItem item = it.next(); // Check the mempool first. - Transaction tx = memoryPool.get(item.hash); + Transaction tx = confidencePool.get(item.hash); if (tx != null) { transactions.add(tx); it.remove(); @@ -1137,7 +1137,7 @@ public class PeerGroup implements TransactionBroadcaster { ver.bestHeight = chain == null ? 0 : chain.getBestChainHeight(); ver.time = Utils.currentTimeSeconds(); - Peer peer = new Peer(params, ver, address, chain, memoryPool, downloadTxDependencies); + Peer peer = new Peer(params, ver, address, chain, confidencePool, downloadTxDependencies); peer.addEventListener(startupListener, Threading.SAME_THREAD); peer.setMinProtocolVersion(vMinRequiredProtocolVersion); pendingPeers.add(peer); @@ -1321,13 +1321,13 @@ public class PeerGroup implements TransactionBroadcaster { } /** - * Returns the {@link MemoryPool} created by this peer group to synchronize its peers. The pool tracks advertised + * Returns the {@link TxConfidencePool} created by this peer group to synchronize its peers. The pool tracks advertised * and downloaded transactions so their confidence can be measured as a proportion of how many peers announced it. * With an un-tampered with internet connection, the more peers announce a transaction the more confidence you can * have that it's really valid. */ - public MemoryPool getMemoryPool() { - return memoryPool; + public TxConfidencePool getConfidencePool() { + return confidencePool; } /** diff --git a/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java b/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java index f3528a1d..2e337c25 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionBroadcast.java @@ -101,7 +101,7 @@ public class TransactionBroadcast { // a big effect. List peers = peerGroup.getConnectedPeers(); // snapshots // We intern the tx here so we are using a canonical version of the object (as it's unfortunately mutable). - pinnedTx = peerGroup.getMemoryPool().intern(tx); + pinnedTx = peerGroup.getConfidencePool().intern(tx); // Prepare to send the transaction by adding a listener that'll be called when confidence changes. // Only bother with this if we might actually hear back: if (minConnections > 1) diff --git a/core/src/main/java/org/bitcoinj/core/MemoryPool.java b/core/src/main/java/org/bitcoinj/core/TxConfidencePool.java similarity index 92% rename from core/src/main/java/org/bitcoinj/core/MemoryPool.java rename to core/src/main/java/org/bitcoinj/core/TxConfidencePool.java index 8b9f9452..2a814f7c 100644 --- a/core/src/main/java/org/bitcoinj/core/MemoryPool.java +++ b/core/src/main/java/org/bitcoinj/core/TxConfidencePool.java @@ -44,9 +44,9 @@ import static com.google.common.base.Preconditions.checkState; *

It is not at this time directly equivalent to the Satoshi clients memory pool, which tracks * all transactions not currently included in the best chain - it's simply a cache.

*/ -public class MemoryPool { - private static final Logger log = LoggerFactory.getLogger(MemoryPool.class); - protected ReentrantLock lock = Threading.lock("mempool"); +public class TxConfidencePool { + private static final Logger log = LoggerFactory.getLogger(TxConfidencePool.class); + protected ReentrantLock lock = Threading.lock("txconfidencepool"); // For each transaction we may have seen: // - only its hash in an inv packet @@ -72,10 +72,10 @@ public class MemoryPool { // allowing us to delete the associated entry (the tx itself has already gone away). WeakTransactionReference tx; } - private LinkedHashMap memoryPool; + private LinkedHashMap pool; // This ReferenceQueue gets entries added to it when they are only weakly reachable, ie, the MemoryPool is the - // only thing that is tracking the transaction anymore. We check it from time to time and delete memoryPool entries + // only thing that is tracking the transaction anymore. We check it from time to time and delete pool entries // corresponding to expired transactions. In this way memory usage of the system is in line with however many // transactions you actually care to track the confidence of. We can still end up with lots of hashes being stored // if our peers flood us with invs but the MAX_SIZE param caps this. @@ -89,10 +89,10 @@ public class MemoryPool { * usage). * @param size Max number of transactions to track. The pool will fill up to this size then stop growing. */ - public MemoryPool(final int size) { - memoryPool = new LinkedHashMap() { + public TxConfidencePool(final int size) { + pool = new LinkedHashMap() { @Override - protected boolean removeEldestEntry(Map.Entry entry) { + protected boolean removeEldestEntry(Map.Entry entry) { // An arbitrary choice to stop the memory used by tracked transactions getting too huge in the event // of some kind of DoS attack. return size() > size; @@ -102,16 +102,16 @@ public class MemoryPool { } /** - * Creates a memory pool that will track at most {@link MemoryPool#MAX_SIZE} entries. You should normally use + * Creates a memory pool that will track at most {@link TxConfidencePool#MAX_SIZE} entries. You should normally use * this constructor. */ - public MemoryPool() { + public TxConfidencePool() { this(MAX_SIZE); } /** * If any transactions have expired due to being only weakly reachable through us, go ahead and delete their - * memoryPool entries - it means we downloaded the transaction and sent it to various event listeners, none of + * pool entries - it means we downloaded the transaction and sent it to various event listeners, none of * which bothered to keep a reference. Typically, this is because the transaction does not involve any keys that * are relevant to any of our wallets. */ @@ -123,7 +123,7 @@ public class MemoryPool { // Find which transaction got deleted by the GC. WeakTransactionReference txRef = (WeakTransactionReference) ref; // And remove the associated map entry so the other bits of memory can also be reclaimed. - memoryPool.remove(txRef.hash); + pool.remove(txRef.hash); } } finally { lock.unlock(); @@ -137,7 +137,7 @@ public class MemoryPool { lock.lock(); try { cleanPool(); - Entry entry = memoryPool.get(txHash); + Entry entry = pool.get(txHash); if (entry == null) { // No such TX known. return 0; @@ -151,7 +151,7 @@ public class MemoryPool { // We previously downloaded this transaction, but nothing cared about it so the garbage collector threw // it away. We also deleted the set that tracked which peers had seen it. Treat this case as a zero and // just delete it from the map. - memoryPool.remove(txHash); + pool.remove(txHash); return 0; } else { checkState(entry.addresses == null); @@ -172,7 +172,7 @@ public class MemoryPool { lock.lock(); try { cleanPool(); - Entry entry = memoryPool.get(tx.getHash()); + Entry entry = pool.get(tx.getHash()); if (entry != null) { // This TX or its hash have been previously interned. if (entry.tx != null) { @@ -206,7 +206,7 @@ public class MemoryPool { log.debug("Provided with a downloaded transaction we didn't see announced yet: {}", tx.getHashAsString()); entry = new Entry(); entry.tx = new WeakTransactionReference(tx, referenceQueue); - memoryPool.put(tx.getHash(), entry); + pool.put(tx.getHash(), entry); return tx; } } finally { @@ -239,7 +239,7 @@ public class MemoryPool { lock.lock(); try { cleanPool(); - Entry entry = memoryPool.get(hash); + Entry entry = pool.get(hash); if (entry != null) { // This TX or its hash have been previously announced. if (entry.tx != null) { @@ -265,7 +265,7 @@ public class MemoryPool { // TODO: Using hashsets here is inefficient compared to just having an array. entry.addresses = new HashSet(); entry.addresses.add(byPeer); - memoryPool.put(hash, entry); + pool.put(hash, entry); log.info("{}: Peer announced new transaction [1] {}", byPeer, hash); } } finally { @@ -289,7 +289,7 @@ public class MemoryPool { public Transaction get(Sha256Hash hash) { lock.lock(); try { - Entry entry = memoryPool.get(hash); + Entry entry = pool.get(hash); if (entry == null) return null; // Unknown. if (entry.tx == null) return null; // Seen but only in advertisements. if (entry.tx.get() == null) return null; // Was downloaded but garbage collected. @@ -309,7 +309,7 @@ public class MemoryPool { public boolean maybeWasSeen(Sha256Hash hash) { lock.lock(); try { - Entry entry = memoryPool.get(hash); + Entry entry = pool.get(hash); return entry != null; } finally { lock.unlock(); diff --git a/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java b/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java index 2a85afb8..29c7fead 100644 --- a/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java +++ b/core/src/test/java/org/bitcoinj/core/MemoryPoolTest.java @@ -46,7 +46,7 @@ public class MemoryPoolTest { @Test public void canonicalInstance() throws Exception { - MemoryPool pool = new MemoryPool(); + TxConfidencePool pool = new TxConfidencePool(); // Check that if we repeatedly send it the same transaction but with different objects, we get back the same // canonical instance with the confidences update. assertEquals(0, pool.numBroadcastPeers(tx1.getHash())); @@ -61,7 +61,7 @@ public class MemoryPoolTest { @Test public void invAndDownload() throws Exception { - MemoryPool pool = new MemoryPool(); + TxConfidencePool pool = new TxConfidencePool(); // Base case: we see a transaction announced twice and then download it. The count is in the confidence object. assertEquals(0, pool.numBroadcastPeers(tx1.getHash())); pool.seen(tx1.getHash(), address1); diff --git a/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java b/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java index 24a25bbd..9589546e 100644 --- a/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java +++ b/core/src/test/java/org/bitcoinj/core/PeerGroupTest.java @@ -375,7 +375,7 @@ public class PeerGroupTest extends TestWithPeerGroup { inbound(p2, inv); assertTrue(outbound(p2) instanceof GetDataMessage); assertEquals(0, tx.getConfidence().numBroadcastPeers()); - assertTrue(peerGroup.getMemoryPool().maybeWasSeen(tx.getHash())); + assertTrue(peerGroup.getConfidencePool().maybeWasSeen(tx.getHash())); assertNull(event[0]); // Peer 1 advertises the tx, we don't do anything as it's already been requested. inbound(p1, inv); diff --git a/core/src/test/java/org/bitcoinj/core/PeerTest.java b/core/src/test/java/org/bitcoinj/core/PeerTest.java index a510bf6c..0d579548 100644 --- a/core/src/test/java/org/bitcoinj/core/PeerTest.java +++ b/core/src/test/java/org/bitcoinj/core/PeerTest.java @@ -56,7 +56,7 @@ public class PeerTest extends TestWithNetworkConnections { private Peer peer; private InboundMessageQueuer writeTarget; private static final int OTHER_PEER_CHAIN_HEIGHT = 110; - private MemoryPool memoryPool; + private TxConfidencePool confidencePool; private final AtomicBoolean fail = new AtomicBoolean(false); @@ -77,10 +77,10 @@ public class PeerTest extends TestWithNetworkConnections { public void setUp() throws Exception { super.setUp(); - memoryPool = new MemoryPool(); + confidencePool = new TxConfidencePool(); VersionMessage ver = new VersionMessage(unitTestParams, 100); InetSocketAddress address = new InetSocketAddress("127.0.0.1", 4000); - peer = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain, memoryPool); + peer = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain, confidencePool); peer.addWallet(wallet); } @@ -269,7 +269,7 @@ public class PeerTest extends TestWithNetworkConnections { // Check co-ordination of which peer to download via the memory pool. VersionMessage ver = new VersionMessage(unitTestParams, 100); InetSocketAddress address = new InetSocketAddress("127.0.0.1", 4242); - Peer peer2 = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain, memoryPool); + Peer peer2 = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain, confidencePool); peer2.addWallet(wallet); VersionMessage peerVersion = new VersionMessage(unitTestParams, OTHER_PEER_CHAIN_HEIGHT); peerVersion.clientVersion = 70001; @@ -291,7 +291,7 @@ public class PeerTest extends TestWithNetworkConnections { GetDataMessage message = (GetDataMessage)outbound(writeTarget); assertEquals(1, message.getItems().size()); assertEquals(tx.getHash(), message.getItems().get(0).hash); - assertTrue(memoryPool.maybeWasSeen(tx.getHash())); + assertTrue(confidencePool.maybeWasSeen(tx.getHash())); // Advertising to peer2 results in no getdata message. inbound(writeTarget2, inv);