3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-11 17:55:53 +00:00

Rename MemoryPool to TxConfidencePool and make singleton

This commit is contained in:
Devrandom 2014-11-05 10:37:20 -08:00 committed by Mike Hearn
parent 4854099cde
commit ec7cec67a7
10 changed files with 57 additions and 51 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 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.</p>
*
* <p>randomNonce is a tweak for the hash function used to prevent some theoretical DoS attacks.

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 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.
*/

View File

@ -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;
}
}

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 MemoryPool memoryPool;
private final TxConfidencePool confidencePool;
// 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.
@ -179,7 +179,7 @@ public class Peer extends PeerSocketHandler {
* used to keep track of which peers relayed transactions and offer more descriptive logging.</p>
*/
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.</p>
*/
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<PendingPing>();
this.wallets = new CopyOnWriteArrayList<Wallet>();
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 {
* <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(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<Object> downloadDependenciesInternal(final Transaction tx,
final Object marker,
final List<Transaction> 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<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 = 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<InventoryItem> 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.</p>
*/
public void setBloomFilter(BloomFilter filter) {
setBloomFilter(filter, memoryPool != null || vDownloadData);
setBloomFilter(filter, confidencePool != 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 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<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 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;
}
/**

View File

@ -101,7 +101,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.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)

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 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<Sha256Hash, Entry> memoryPool;
private LinkedHashMap<Sha256Hash, Entry> 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<Sha256Hash, Entry>() {
public TxConfidencePool(final int size) {
pool = new LinkedHashMap<Sha256Hash, Entry>() {
@Override
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, Entry> entry) {
protected boolean removeEldestEntry(Map.Entry<Sha256Hash, TxConfidencePool.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<PeerAddress>();
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();

View File

@ -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);

View File

@ -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);

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 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);