rename TxConfidencePool to TxConfidenceTable

This commit is contained in:
Devrandom 2014-12-01 11:32:56 -08:00 committed by Mike Hearn
parent 2dfeee55b6
commit 1ba568e069
10 changed files with 79 additions and 82 deletions

View File

@ -93,7 +93,7 @@ public class BloomFilter extends Message {
*
* <p>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 TxConfidencePool#MAX_SIZE}). See the comment in processBlock(FilteredBlock)
* doing the downloading (default is {@link TxConfidenceTable#MAX_SIZE}). See the comment in processBlock(FilteredBlock)
* for more information on this restriction.</p>
*
* <p>randomNonce is a tweak for the hash function used to prevent some theoretical DoS attacks.

View File

@ -6,19 +6,19 @@ package org.bitcoinj.core;
* through {@link AbstractBlockChain#getContext()}.
*/
public class Context {
protected TxConfidencePool confidencePool;
protected TxConfidenceTable confidenceTable;
protected Context() {
confidencePool = new TxConfidencePool();
confidenceTable = new TxConfidenceTable();
}
/**
* Returns the {@link TxConfidencePool} created by this context. The pool tracks advertised
* Returns the {@link TxConfidenceTable} created by this context. 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 TxConfidencePool getConfidencePool() {
return confidencePool;
public TxConfidenceTable getConfidenceTable() {
return confidenceTable;
}
}

View File

@ -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 TxConfidencePool} object which doesn't try to keep track of all pending transactions,
* this is different to the {@link TxConfidenceTable} 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.
*/

View File

@ -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 TxConfidencePool confidencePool;
private final TxConfidenceTable confidenceTable;
// Each wallet added to the peer will be notified of downloaded transaction data.
private final CopyOnWriteArrayList<Wallet> wallets;
// A time before which we only download block headers, after that point we download block bodies.
@ -165,8 +165,8 @@ public class Peer extends PeerSocketHandler {
}
/**
* <p>Construct a peer that reads/writes from the given block chain and memory pool. Transactions stored in a memory
* pool will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that
* <p>Construct a peer that reads/writes from the given block chain. Transactions stored in a {@link org.bitcoinj.core.TxConfidenceTable}
* will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that
* the transaction is valid.</p>
*
* <p>Note that this does <b>NOT</b> make a connection to the given remoteAddress, it only creates a handler for a
@ -184,8 +184,8 @@ public class Peer extends PeerSocketHandler {
}
/**
* <p>Construct a peer that reads/writes from the given block chain and memory pool. Transactions stored in a memory
* pool will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that
* <p>Construct a peer that reads/writes from the given block chain. Transactions stored in a {@link org.bitcoinj.core.TxConfidenceTable}
* will have their confidence levels updated when a peer announces it, to reflect the greater likelyhood that
* the transaction is valid.</p>
*
* <p>Note that this does <b>NOT</b> make a connection to the given remoteAddress, it only creates a handler for a
@ -211,7 +211,7 @@ public class Peer extends PeerSocketHandler {
this.isAcked = false;
this.pendingPings = new CopyOnWriteArrayList<PendingPing>();
this.wallets = new CopyOnWriteArrayList<Wallet>();
this.confidencePool = chain.getContext().getConfidencePool();
this.confidenceTable = chain.getContext().getConfidenceTable();
}
/**
@ -583,9 +583,9 @@ public class Peer extends PeerSocketHandler {
lock.lock();
try {
log.debug("{}: Received tx {}", getAddress(), tx.getHashAsString());
if (confidencePool != null) {
if (confidenceTable != null) {
// We may get back a different transaction object.
tx = confidencePool.seen(tx, getAddress());
tx = confidenceTable.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 {
* <p>Note that dependencies downloaded this way will not trigger the onTransaction method of event listeners.</p>
*/
public ListenableFuture<List<Transaction>> downloadDependencies(Transaction tx) {
checkNotNull(confidencePool, "Must have a configured MemoryPool object to download dependencies.");
checkNotNull(confidenceTable, "Must have a configured TxConfidenceTable 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<Object> downloadDependenciesInternal(final Transaction tx,
final Object marker,
final List<Transaction> results) {
checkNotNull(confidencePool, "Must have a configured MemoryPool object to download dependencies.");
checkNotNull(confidenceTable, "Must have a configured TxConfidenceTable object to download dependencies.");
final SettableFuture<Object> 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 = confidencePool.get(hash);
Transaction dep = confidenceTable.get(hash);
if (dep == null) {
needToRequest.add(hash);
} else {
@ -1043,7 +1043,7 @@ public class Peer extends PeerSocketHandler {
Iterator<InventoryItem> it = transactions.iterator();
while (it.hasNext()) {
InventoryItem item = it.next();
if (confidencePool == null) {
if (confidenceTable == 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 (confidencePool.maybeWasSeen(item.hash)) {
if (confidenceTable.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.
confidencePool.seen(item.hash, this.getAddress());
confidenceTable.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.</p>
*/
public void setBloomFilter(BloomFilter filter) {
setBloomFilter(filter, confidencePool != null || vDownloadData);
setBloomFilter(filter, confidenceTable != null || vDownloadData);
}
/**

View File

@ -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 TxConfidencePool confidencePool;
private final TxConfidenceTable confidenceTable;
// 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;
confidencePool = chain.getContext().getConfidencePool();
confidenceTable = chain.getContext().getConfidenceTable();
inactives = new PriorityQueue<PeerAddress>(1, new Comparator<PeerAddress>() {
@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 confidence pool first.
Transaction tx = confidencePool.get(item.hash);
Transaction tx = confidenceTable.get(item.hash);
if (tx != null) {
transactions.add(tx);
it.remove();
@ -1321,11 +1321,11 @@ public class PeerGroup implements TransactionBroadcaster {
}
/**
* Use {@link org.bitcoinj.core.Context#getConfidencePool()} instead.
* Use {@link org.bitcoinj.core.Context#getConfidenceTable()} instead.
*/
@Deprecated
public TxConfidencePool getConfidencePool() {
return confidencePool;
public TxConfidenceTable getConfidenceTable() {
return confidenceTable;
}
/**

View File

@ -16,9 +16,6 @@
package org.bitcoinj.core;
import org.bitcoinj.core.AbstractPeerEventListener;
import org.bitcoinj.core.RejectMessage;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.utils.Threading;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
@ -101,7 +98,7 @@ public class TransactionBroadcast {
// a big effect.
List<Peer> 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.getConfidencePool().intern(tx);
pinnedTx = peerGroup.getConfidenceTable().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)

View File

@ -44,9 +44,9 @@ import static com.google.common.base.Preconditions.checkState;
* <p>It is <b>not</b> 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.</p>
*/
public class TxConfidencePool {
private static final Logger log = LoggerFactory.getLogger(TxConfidencePool.class);
protected ReentrantLock lock = Threading.lock("txconfidencepool");
public class TxConfidenceTable {
private static final Logger log = LoggerFactory.getLogger(TxConfidenceTable.class);
protected ReentrantLock lock = Threading.lock("txconfidencetable");
// For each transaction we may have seen:
// - only its hash in an inv packet
@ -72,27 +72,27 @@ public class TxConfidencePool {
// allowing us to delete the associated entry (the tx itself has already gone away).
WeakTransactionReference tx;
}
private LinkedHashMap<Sha256Hash, Entry> pool;
private LinkedHashMap<Sha256Hash, Entry> table;
// 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 pool entries
// This ReferenceQueue gets entries added to it when they are only weakly reachable, ie, the TxConfidenceTable is the
// only thing that is tracking the transaction anymore. We check it from time to time and delete table 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.
private ReferenceQueue<Transaction> referenceQueue;
/** The max size of a memory pool created with the no-args constructor. */
/** The max size of a table created with the no-args constructor. */
public static final int MAX_SIZE = 1000;
/**
* Creates a memory pool that will track at most the given number of transactions (allowing you to bound memory
* Creates a table that will track at most the given number of transactions (allowing you to bound memory
* usage).
* @param size Max number of transactions to track. The pool will fill up to this size then stop growing.
* @param size Max number of transactions to track. The table will fill up to this size then stop growing.
*/
public TxConfidencePool(final int size) {
pool = new LinkedHashMap<Sha256Hash, Entry>() {
public TxConfidenceTable(final int size) {
table = new LinkedHashMap<Sha256Hash, Entry>() {
@Override
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, TxConfidencePool.Entry> entry) {
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, TxConfidenceTable.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,20 +102,20 @@ public class TxConfidencePool {
}
/**
* Creates a memory pool that will track at most {@link TxConfidencePool#MAX_SIZE} entries. You should normally use
* Creates a table that will track at most {@link TxConfidenceTable#MAX_SIZE} entries. You should normally use
* this constructor.
*/
public TxConfidencePool() {
public TxConfidenceTable() {
this(MAX_SIZE);
}
/**
* If any transactions have expired due to being only weakly reachable through us, go ahead and delete their
* pool entries - it means we downloaded the transaction and sent it to various event listeners, none of
* table 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.
*/
private void cleanPool() {
private void cleanTable() {
lock.lock();
try {
Reference<? extends Transaction> ref;
@ -123,7 +123,7 @@ public class TxConfidencePool {
// 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.
pool.remove(txRef.hash);
table.remove(txRef.hash);
}
} finally {
lock.unlock();
@ -136,8 +136,8 @@ public class TxConfidencePool {
public int numBroadcastPeers(Sha256Hash txHash) {
lock.lock();
try {
cleanPool();
Entry entry = pool.get(txHash);
cleanTable();
Entry entry = table.get(txHash);
if (entry == null) {
// No such TX known.
return 0;
@ -151,7 +151,7 @@ public class TxConfidencePool {
// 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.
pool.remove(txHash);
table.remove(txHash);
return 0;
} else {
checkState(entry.addresses == null);
@ -171,8 +171,8 @@ public class TxConfidencePool {
public Transaction intern(Transaction tx) {
lock.lock();
try {
cleanPool();
Entry entry = pool.get(tx.getHash());
cleanTable();
Entry entry = table.get(tx.getHash());
if (entry != null) {
// This TX or its hash have been previously interned.
if (entry.tx != null) {
@ -193,7 +193,7 @@ public class TxConfidencePool {
Set<PeerAddress> addrs = entry.addresses;
entry.addresses = null;
TransactionConfidence confidence = tx.getConfidence();
log.debug("Adding tx [{}] {} to the memory pool",
log.debug("Adding tx [{}] {} to the confidence table",
confidence.numBroadcastPeers(), tx.getHashAsString());
for (PeerAddress a : addrs) {
markBroadcast(a, tx);
@ -206,7 +206,7 @@ public class TxConfidencePool {
log.debug("Provided with a downloaded transaction we didn't see announced yet: {}", tx.getHashAsString());
entry = new Entry();
entry.tx = new WeakTransactionReference(tx, referenceQueue);
pool.put(tx.getHash(), entry);
table.put(tx.getHash(), entry);
return tx;
}
} finally {
@ -238,8 +238,8 @@ public class TxConfidencePool {
public void seen(Sha256Hash hash, PeerAddress byPeer) {
lock.lock();
try {
cleanPool();
Entry entry = pool.get(hash);
cleanTable();
Entry entry = table.get(hash);
if (entry != null) {
// This TX or its hash have been previously announced.
if (entry.tx != null) {
@ -265,7 +265,7 @@ public class TxConfidencePool {
// TODO: Using hashsets here is inefficient compared to just having an array.
entry.addresses = new HashSet<PeerAddress>();
entry.addresses.add(byPeer);
pool.put(hash, entry);
table.put(hash, entry);
log.info("{}: Peer announced new transaction [1] {}", byPeer, hash);
}
} finally {
@ -289,7 +289,7 @@ public class TxConfidencePool {
public Transaction get(Sha256Hash hash) {
lock.lock();
try {
Entry entry = pool.get(hash);
Entry entry = table.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 TxConfidencePool {
public boolean maybeWasSeen(Sha256Hash hash) {
lock.lock();
try {
Entry entry = pool.get(hash);
Entry entry = table.get(hash);
return entry != null;
} finally {
lock.unlock();

View File

@ -46,34 +46,34 @@ public class MemoryPoolTest {
@Test
public void canonicalInstance() throws Exception {
TxConfidencePool pool = new TxConfidencePool();
TxConfidenceTable table = new TxConfidenceTable();
// 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()));
assertEquals(tx1, pool.seen(tx1, address1));
assertEquals(0, table.numBroadcastPeers(tx1.getHash()));
assertEquals(tx1, table.seen(tx1, address1));
assertEquals(1, tx1.getConfidence().numBroadcastPeers());
assertEquals(1, pool.numBroadcastPeers(tx1.getHash()));
assertEquals(tx1, pool.seen(tx2, address2));
assertEquals(1, table.numBroadcastPeers(tx1.getHash()));
assertEquals(tx1, table.seen(tx2, address2));
assertEquals(2, tx1.getConfidence().numBroadcastPeers());
assertEquals(2, pool.numBroadcastPeers(tx1.getHash()));
assertEquals(tx1, pool.get(tx1.getHash()));
assertEquals(2, table.numBroadcastPeers(tx1.getHash()));
assertEquals(tx1, table.get(tx1.getHash()));
}
@Test
public void invAndDownload() throws Exception {
TxConfidencePool pool = new TxConfidencePool();
TxConfidenceTable table = new TxConfidenceTable();
// 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);
assertEquals(1, pool.numBroadcastPeers(tx1.getHash()));
assertTrue(pool.maybeWasSeen(tx1.getHash()));
pool.seen(tx1.getHash(), address2);
assertEquals(2, pool.numBroadcastPeers(tx1.getHash()));
Transaction t = pool.seen(tx1, address1);
assertEquals(0, table.numBroadcastPeers(tx1.getHash()));
table.seen(tx1.getHash(), address1);
assertEquals(1, table.numBroadcastPeers(tx1.getHash()));
assertTrue(table.maybeWasSeen(tx1.getHash()));
table.seen(tx1.getHash(), address2);
assertEquals(2, table.numBroadcastPeers(tx1.getHash()));
Transaction t = table.seen(tx1, address1);
assertEquals(2, t.getConfidence().numBroadcastPeers());
// And now we see another inv.
pool.seen(tx1.getHash(), address3);
table.seen(tx1.getHash(), address3);
assertEquals(3, t.getConfidence().numBroadcastPeers());
assertEquals(3, pool.numBroadcastPeers(tx1.getHash()));
assertEquals(3, table.numBroadcastPeers(tx1.getHash()));
}
}

View File

@ -376,7 +376,7 @@ public class PeerGroupTest extends TestWithPeerGroup {
inbound(p2, inv);
assertTrue(outbound(p2) instanceof GetDataMessage);
assertEquals(0, tx.getConfidence().numBroadcastPeers());
assertTrue(peerGroup.getConfidencePool().maybeWasSeen(tx.getHash()));
assertTrue(peerGroup.getConfidenceTable().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);

View File

@ -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 TxConfidencePool confidencePool;
private TxConfidenceTable confidenceTable;
private final AtomicBoolean fail = new AtomicBoolean(false);
@ -77,7 +77,7 @@ public class PeerTest extends TestWithNetworkConnections {
public void setUp() throws Exception {
super.setUp();
confidencePool = blockChain.getContext().getConfidencePool();
confidenceTable = blockChain.getContext().getConfidenceTable();
VersionMessage ver = new VersionMessage(unitTestParams, 100);
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 4000);
peer = new Peer(unitTestParams, ver, new PeerAddress(address), blockChain);
@ -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(confidencePool.maybeWasSeen(tx.getHash()));
assertTrue(confidenceTable.maybeWasSeen(tx.getHash()));
// Advertising to peer2 results in no getdata message.
inbound(writeTarget2, inv);