3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-15 03:35:52 +00:00

Make the Context object be a global object that is propagated between threads, and created on demand in the PeerGroup constructor. This isn't complete but is a sketch of where we might go. Context propagation is mostly auto-magical at the moment and will become less so with time, making it easier to use multiple instances of the library from the same thread simultaneously. At the moment you would potentially hit issues if using multiple instances from the same thread though and we should fix that before landing (aka don't break devrandom).

This commit is contained in:
Mike Hearn 2015-02-25 21:41:58 +01:00
parent ece8d9a347
commit 257aacf81b
17 changed files with 176 additions and 126 deletions

View File

@ -86,7 +86,6 @@ public abstract class AbstractBlockChain {
/** Keeps a map of block hashes to StoredBlocks. */
private final BlockStore blockStore;
private final Context context;
/**
* Tracks the top of the best known chain.<p>
@ -150,11 +149,6 @@ public abstract class AbstractBlockChain {
this.params = params;
this.listeners = new CopyOnWriteArrayList<ListenerRegistration<BlockChainListener>>();
for (BlockChainListener l : listeners) addListener(l, Threading.SAME_THREAD);
context = new Context();
}
public Context getContext() {
return context;
}
/**

View File

@ -1,5 +1,10 @@
package org.bitcoinj.core;
import org.slf4j.*;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* The Context object holds various objects that are scoped to a specific instantiation of bitcoinj for a specific
* network. You can get an instance of this class through {@link AbstractBlockChain#getContext()}. At the momemnt it
@ -7,10 +12,73 @@ package org.bitcoinj.core;
* other global configuration of use.
*/
public class Context {
private static final Logger log = LoggerFactory.getLogger(Context.class);
protected TxConfidenceTable confidenceTable;
protected Context() {
/**
* Creates a new context object. For now, this will be done for you by the framework. Eventually you will be
* expected to do this yourself in the same manner as fetching a NetworkParameters object (at the start of your app).
*/
public Context() {
confidenceTable = new TxConfidenceTable();
lastConstructed = this;
// We may already have a context in our TLS slot. This can happen a lot during unit tests, so just ignore it.
slot.set(this);
}
private static volatile Context lastConstructed;
private static final ThreadLocal<Context> slot = new ThreadLocal<Context>();
/**
* Returns the current context that is associated with the <b>calling thread</b>. BitcoinJ is an API that has thread
* affinity: much like OpenGL it expects each thread that accesses it to have been configured with a global Context
* object. This method returns that. Note that to help you develop, this method will <i>also</i> propagate whichever
* context was created last onto the current thread, if it's missing. However it will print an error when doing so
* because propagation of contexts is meant to be done manually: this is so two libraries or subsystems that
* independently use bitcoinj (or possibly alt coin forks of it) can operate correctly.
*
* @throws java.lang.IllegalStateException if no context exists at all.
*/
public static Context get() {
Context tls = slot.get();
if (tls == null) {
if (lastConstructed == null)
throw new IllegalStateException("You must construct a Context object before using bitcoinj!");
slot.set(lastConstructed);
log.error("Performing thread fixup: you are accessing bitcoinj via a thread that has not had any context set on it.");
log.error("This error has been corrected for, but doing this makes your app less robust.");
log.error("You should use Context.propagate() or a ContextPropagatingThreadFactory.");
log.error("Please refer to the user guide for more information about this.");
// TODO: Actually write the user guide section about this.
// TODO: If the above TODO makes it past the 0.13 release, kick Mike and tell him he sucks.
return lastConstructed;
} else {
return tls;
}
}
// A temporary internal shim designed to help us migrate internally in a way that doesn't wreck source compatibility.
static Context getOrCreate() {
try {
return get();
} catch (IllegalStateException e) {
log.warn("Implicitly creating context. This is a migration step and this message will eventually go away.");
new Context();
return slot.get();
}
}
/**
* Sets the given context as the current thread context. You should use this if you create your own threads that
* want to create core BitcoinJ objects. Generally, if a class can accept a Context in its constructor and might
* be used (even indirectly) by a thread, you will want to call this first. Your task may be simplified by using
* a {@link org.bitcoinj.utils.ContextPropagatingThreadFactory}.
*
* @throws java.lang.IllegalStateException if this thread already has a context
*/
public static void propagate(Context context) {
checkState(slot.get() == null);
slot.set(checkNotNull(context));
}
/**

View File

@ -21,7 +21,7 @@ import org.bitcoinj.script.Script;
import org.bitcoinj.script.Script.VerifyFlag;
import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.store.FullPrunedBlockStore;
import org.bitcoinj.utils.DaemonThreadFactory;
import org.bitcoinj.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -123,7 +123,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
// TODO: execute in order of largest transaction (by input count) first
ExecutorService scriptVerificationExecutor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors(), new DaemonThreadFactory());
Runtime.getRuntime().availableProcessors(), new ContextPropagatingThreadFactory("Script verification"));
/** A job submitted to the executor which verifies signatures. */
private static class Verifier implements Callable<VerificationException> {

View File

@ -58,6 +58,7 @@ public class Peer extends PeerSocketHandler {
private final NetworkParameters params;
private final AbstractBlockChain blockChain;
private final Context context;
// onPeerDisconnected should not be called directly by Peers when a PeerGroup is involved (we don't know the total
// number of connected peers), thus we use a wrapper that PeerGroup can use to register listeners that wont get
@ -87,9 +88,6 @@ public class Peer extends PeerSocketHandler {
// so we can use this to calculate the height of the peers chain, by adding it to the initial height in the version
// message. This method can go wrong if the peer re-orgs onto a shorter (but harder) chain, however, this is rare.
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.
@Nullable 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.
@ -213,7 +211,7 @@ public class Peer extends PeerSocketHandler {
this.isAcked = false;
this.pendingPings = new CopyOnWriteArrayList<PendingPing>();
this.wallets = new CopyOnWriteArrayList<Wallet>();
this.confidenceTable = chain != null ? chain.getContext().getConfidenceTable() : null;
this.context = Context.get();
}
/**
@ -604,11 +602,8 @@ public class Peer extends PeerSocketHandler {
lock.lock();
try {
log.debug("{}: Received tx {}", getAddress(), tx.getHashAsString());
if (confidenceTable != null) {
// We may get back a different transaction object.
tx = confidenceTable.seen(tx, getAddress());
}
fTx = tx;
fTx = context.getConfidenceTable().seen(tx, getAddress());
// Label the transaction as coming in from the P2P network (as opposed to being created by us, direct import,
// etc). This helps the wallet decide how to risk analyze it later.
fTx.getConfidence().setSource(TransactionConfidence.Source.NETWORK);
@ -707,7 +702,6 @@ 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(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());
@ -733,7 +727,6 @@ public class Peer extends PeerSocketHandler {
private ListenableFuture<Object> downloadDependenciesInternal(final Transaction tx,
final Object marker,
final List<Transaction> results) {
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
@ -748,7 +741,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 = confidenceTable.get(hash);
Transaction dep = context.getConfidenceTable().get(hash);
if (dep == null) {
needToRequest.add(hash);
} else {
@ -1064,19 +1057,13 @@ public class Peer extends PeerSocketHandler {
Iterator<InventoryItem> it = transactions.iterator();
while (it.hasNext()) {
InventoryItem item = it.next();
if (confidenceTable == null) {
if (downloadData) {
// If there's no memory pool only download transactions if we're configured to.
getdata.addItem(item);
}
} else {
// Only download the transaction if we are the first peer that saw it be advertised. Other peers will also
// see it be advertised in inv packets asynchronously, they co-ordinate via the memory pool. We could
// potentially download transactions faster by always asking every peer for a tx when advertised, as remote
// 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 (confidenceTable.maybeWasSeen(item.hash)) {
if (context.getConfidenceTable().maybeWasSeen(item.hash)) {
// Some other peer already announced this so don't download.
it.remove();
} else {
@ -1084,8 +1071,7 @@ public class Peer extends PeerSocketHandler {
getdata.addItem(item);
}
// This can trigger transaction confidence listeners.
confidenceTable.seen(item.hash, this.getAddress());
}
context.getConfidenceTable().seen(item.hash, this.getAddress());
}
// If we are requesting filteredblocks we have to send a ping after the getdata so that we have a clear
@ -1557,7 +1543,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, confidenceTable != null || vDownloadData);
setBloomFilter(filter, true);
}
/**

View File

@ -81,6 +81,10 @@ public class PeerGroup implements TransactionBroadcaster {
protected final ReentrantLock lock = Threading.lock("peergroup");
private final NetworkParameters params;
private final Context context;
@Nullable private final AbstractBlockChain chain;
// This executor is used to queue up jobs: it's used when we don't want to use locks for mutual exclusion,
// typically because the job might call in to user provided code that needs/wants the freedom to use the API
// however it wants, or because a job needs to be ordered relative to other jobs like that.
@ -127,8 +131,6 @@ public class PeerGroup implements TransactionBroadcaster {
@GuardedBy("lock") private boolean useLocalhostPeerWhenPossible = true;
@GuardedBy("lock") private boolean ipv6Unreachable = false;
private final NetworkParameters params;
@Nullable private final AbstractBlockChain chain;
@GuardedBy("lock") private long fastCatchupTimeSecs;
private final CopyOnWriteArrayList<Wallet> wallets;
private final CopyOnWriteArrayList<PeerFilterProvider> peerFilterProviders;
@ -307,6 +309,7 @@ public class PeerGroup implements TransactionBroadcaster {
*/
private PeerGroup(NetworkParameters params, @Nullable AbstractBlockChain chain, ClientConnectionManager connectionManager, @Nullable TorClient torClient) {
this.params = checkNotNull(params);
this.context = Context.getOrCreate();
this.chain = chain;
fastCatchupTimeSecs = params.getGenesisBlock().getTimeSeconds();
wallets = new CopyOnWriteArrayList<Wallet>();
@ -353,7 +356,7 @@ public class PeerGroup implements TransactionBroadcaster {
protected ListeningScheduledExecutorService createPrivateExecutor() {
ListeningScheduledExecutorService result = MoreExecutors.listeningDecorator(
new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory("PeerGroup Thread"))
new ScheduledThreadPoolExecutor(1, new ContextPropagatingThreadFactory("PeerGroup Thread"))
);
// Hack: jam the executor so jobs just queue up until the user calls start() on us. For example, adding a wallet
// results in a bloom filter recalc being queued, but we don't want to do that until we're actually started.
@ -520,7 +523,7 @@ public class PeerGroup implements TransactionBroadcaster {
while (it.hasNext()) {
InventoryItem item = it.next();
// Check the confidence pool first.
Transaction tx = chain != null ? chain.getContext().getConfidenceTable().get(item.hash) : null;
Transaction tx = chain != null ? Context.get().getConfidenceTable().get(item.hash) : null;
if (tx != null) {
transactions.add(tx);
it.remove();
@ -1355,14 +1358,10 @@ public class PeerGroup implements TransactionBroadcaster {
}
}
/**
* Use {@link org.bitcoinj.core.Context#getConfidenceTable()} instead, which can be retrieved via
* {@link org.bitcoinj.core.AbstractBlockChain#getContext()}. Can return null if this peer group was
* configured without a block chain object.
*/
/** Use "Context.get().getConfidenceTable()" instead */
@Deprecated @Nullable
public TxConfidenceTable getMemoryPool() {
return chain == null ? null : chain.getContext().getConfidenceTable();
return Context.get().getConfidenceTable();
}
/**
@ -1725,7 +1724,7 @@ public class PeerGroup implements TransactionBroadcaster {
*/
public TransactionBroadcast broadcastTransaction(final Transaction tx, final int minConnections) {
// TODO: Context being owned by BlockChain isn't right w.r.t future intentions so it shouldn't really be optional here.
final TransactionBroadcast broadcast = new TransactionBroadcast(this, chain != null ? chain.getContext() : null, tx);
final TransactionBroadcast broadcast = new TransactionBroadcast(this, tx);
broadcast.setMinConnections(minConnections);
// Send the TX to the wallet once we have a successful broadcast.
Futures.addCallback(broadcast.future(), new FutureCallback<Transaction>() {

View File

@ -41,7 +41,6 @@ public class TransactionBroadcast {
private final SettableFuture<Transaction> future = SettableFuture.create();
private final PeerGroup peerGroup;
private final Transaction tx;
@Nullable private final Context context;
private int minConnections;
private int numWaitingFor;
@ -54,10 +53,8 @@ public class TransactionBroadcast {
// Tracks which nodes sent us a reject message about this broadcast, if any. Useful for debugging.
private Map<Peer, RejectMessage> rejects = Collections.synchronizedMap(new HashMap<Peer, RejectMessage>());
// TODO: Context being owned by BlockChain isn't right w.r.t future intentions so it shouldn't really be optional here.
TransactionBroadcast(PeerGroup peerGroup, @Nullable Context context, Transaction tx) {
TransactionBroadcast(PeerGroup peerGroup, Transaction tx) {
this.peerGroup = peerGroup;
this.context = context;
this.tx = tx;
this.minConnections = Math.max(1, peerGroup.getMinBroadcastConnections());
}
@ -65,7 +62,6 @@ public class TransactionBroadcast {
// Only for mock broadcasts.
private TransactionBroadcast(Transaction tx) {
this.peerGroup = null;
this.context = null;
this.tx = tx;
}
@ -134,7 +130,7 @@ public class TransactionBroadcast {
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).
// TODO: Once confidence state is moved out of Transaction we can kill off this step.
pinnedTx = context != null ? context.getConfidenceTable().intern(tx) : tx;
pinnedTx = Context.get().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

@ -16,22 +16,18 @@
package org.bitcoinj.net;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import org.slf4j.LoggerFactory;
import com.google.common.util.concurrent.*;
import org.bitcoinj.core.*;
import org.slf4j.*;
import javax.annotation.Nullable;
import javax.net.SocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.Set;
import javax.annotation.*;
import javax.net.*;
import java.io.*;
import java.net.*;
import java.nio.*;
import java.util.*;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Preconditions.*;
/**
* <p>Creates a simple connection to a server using a {@link StreamParser} to process data.</p>
@ -71,9 +67,11 @@ public class BlockingClient implements MessageWriteTarget {
dbuf = ByteBuffer.allocateDirect(Math.min(Math.max(parser.getMaxMessageSize(), BUFFER_SIZE_LOWER_BOUND), BUFFER_SIZE_UPPER_BOUND));
parser.setWriteTarget(this);
socket = socketFactory.createSocket();
final Context context = Context.get();
Thread t = new Thread() {
@Override
public void run() {
Context.propagate(context);
if (clientSet != null)
clientSet.add(BlockingClient.this);
try {

View File

@ -18,6 +18,7 @@ package org.bitcoinj.net;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.*;
import org.bitcoinj.utils.*;
import org.slf4j.LoggerFactory;
import java.io.IOException;
@ -188,9 +189,7 @@ public class NioClientManager extends AbstractExecutionThreadService implements
return new Executor() {
@Override
public void execute(Runnable command) {
Thread thread = new Thread(command, "NioClientManager");
thread.setDaemon(true);
thread.start();
new ContextPropagatingThreadFactory("NioClientManager").newThread(command).start();
}
};
}

View File

@ -67,9 +67,9 @@ public class DnsDiscovery extends MultiplexingDiscovery {
// Attempted workaround for reported bugs on Linux in which gethostbyname does not appear to be properly
// thread safe and can cause segfaults on some libc versions.
if (System.getProperty("os.name").toLowerCase().contains("linux"))
return Executors.newSingleThreadExecutor(new DaemonThreadFactory());
return Executors.newSingleThreadExecutor(new ContextPropagatingThreadFactory("DNS seed lookups"));
else
return Executors.newFixedThreadPool(seeds.size(), new DaemonThreadFactory());
return Executors.newFixedThreadPool(seeds.size(), new DaemonThreadFactory("DNS seed lookups"));
}
/** Implements discovery from a single DNS host. */

View File

@ -18,7 +18,7 @@ package org.bitcoinj.net.discovery;
import com.google.common.collect.Lists;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.utils.DaemonThreadFactory;
import org.bitcoinj.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -94,7 +94,7 @@ public class MultiplexingDiscovery implements PeerDiscovery {
}
protected ExecutorService createExecutor() {
return Executors.newFixedThreadPool(seeds.size(), new DaemonThreadFactory());
return Executors.newFixedThreadPool(seeds.size(), new ContextPropagatingThreadFactory("Multiplexing discovery"));
}
@Override

View File

@ -16,44 +16,22 @@
package org.bitcoinj.net.discovery;
import static com.google.common.base.Preconditions.checkArgument;
import org.bitcoinj.core.NetworkParameters;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.subgraph.orchid.Circuit;
import com.subgraph.orchid.RelayCell;
import com.subgraph.orchid.Router;
import com.subgraph.orchid.TorClient;
import com.subgraph.orchid.circuits.path.CircuitPathChooser;
import com.subgraph.orchid.data.HexDigest;
import com.subgraph.orchid.data.exitpolicy.ExitTarget;
import com.google.common.collect.*;
import com.google.common.util.concurrent.*;
import com.subgraph.orchid.*;
import com.subgraph.orchid.circuits.path.*;
import com.subgraph.orchid.data.*;
import com.subgraph.orchid.data.exitpolicy.*;
import org.bitcoinj.core.*;
import org.bitcoinj.utils.*;
import org.slf4j.*;
import org.bitcoinj.utils.DaemonThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static java.util.Collections.singleton;
import static com.google.common.base.Preconditions.*;
import static java.util.Collections.*;
/**
* <p>Supports peer discovery through Tor.</p>
@ -254,7 +232,7 @@ public class TorDiscovery implements PeerDiscovery {
private synchronized void createThreadPool(int size) {
threadPool = MoreExecutors.listeningDecorator(
Executors.newFixedThreadPool(size, new DaemonThreadFactory()));
Executors.newFixedThreadPool(size, new ContextPropagatingThreadFactory("Tor DNS discovery")));
}
private InetAddress lookup(Circuit circuit, String seed) throws UnknownHostException {

View File

@ -81,6 +81,7 @@ public class TestWithNetworkConnections {
BriefLogFormatter.init();
unitTestParams = UnitTestParams.get();
new Context();
Wallet.SendRequest.DEFAULT_FEE_PER_KB = Coin.ZERO;
this.blockStore = blockStore;
// Allow subclasses to override the wallet object with their own.

View File

@ -25,7 +25,7 @@ import org.bitcoinj.params.UnitTestParams;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.MemoryBlockStore;
import com.google.common.base.Preconditions;
import org.bitcoinj.utils.DaemonThreadFactory;
import org.bitcoinj.utils.*;
import java.net.InetSocketAddress;
import java.util.concurrent.*;
@ -98,7 +98,7 @@ public class TestWithPeerGroup extends TestWithNetworkConnections {
return new PeerGroup(unitTestParams, blockChain, manager) {
@Override
protected ListeningScheduledExecutorService createPrivateExecutor() {
return MoreExecutors.listeningDecorator(new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory("PeerGroup test thread")) {
return MoreExecutors.listeningDecorator(new ScheduledThreadPoolExecutor(1, new ContextPropagatingThreadFactory("PeerGroup test thread")) {
@Override
public ScheduledFuture<?> schedule(final Runnable command, final long delay, final TimeUnit unit) {
if (!blockJobs)

View File

@ -0,0 +1,33 @@
package org.bitcoinj.utils;
import org.bitcoinj.core.*;
import java.util.concurrent.*;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A {@link java.util.concurrent.ThreadFactory} that propagates a {@link org.bitcoinj.core.Context} from the creating
* thread into the new thread. This factory creates daemon threads.
*/
public class ContextPropagatingThreadFactory implements ThreadFactory {
private final String name;
public ContextPropagatingThreadFactory(String name) {
this.name = checkNotNull(name);
}
@Override
public Thread newThread(final Runnable r) {
final Context context = Context.get();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Context.propagate(context);
r.run();
}
}, name);
thread.setDaemon(true);
return thread;
}
}

View File

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

@ -78,7 +78,7 @@ public class PeerTest extends TestWithNetworkConnections {
public void setUp() throws Exception {
super.setUp();
confidenceTable = blockChain.getContext().getConfidenceTable();
confidenceTable = Context.get().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);
@ -601,7 +601,6 @@ public class PeerTest extends TestWithNetworkConnections {
assertEquals(t3.getHash(), getdata.getItems().get(1).hash);
assertEquals(someHash, getdata.getItems().get(2).hash);
assertEquals(anotherHash, getdata.getItems().get(3).hash);
long nonce = -1;
// For some random reason, t4 is delivered at this point before it's needed - perhaps it was a Bloom filter
// false positive. We do this to check that the mempool is being checked for seen transactions before
// requesting them.

View File

@ -73,7 +73,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup {
public void fourPeers() throws Exception {
InboundMessageQueuer[] channels = { connectPeer(1), connectPeer(2), connectPeer(3), connectPeer(4) };
Transaction tx = new Transaction(params);
TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, blockChain.getContext(), tx);
TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, tx);
final AtomicDouble lastProgress = new AtomicDouble();
broadcast.setProgressCallback(new TransactionBroadcast.ProgressCallback() {
@Override
@ -102,9 +102,8 @@ public class TransactionBroadcastTest extends TestWithPeerGroup {
assertEquals(0.0, lastProgress.get(), 0.0);
inbound(channels[1], InventoryMessage.with(tx));
pingAndWait(channels[1]);
future.get();
Threading.waitForUserCode();
// FIXME flaky test - future is not handled on user thread
assertTrue(future.isDone());
assertEquals(1.0, lastProgress.get(), 0.0);
}
@ -112,7 +111,7 @@ public class TransactionBroadcastTest extends TestWithPeerGroup {
public void rejectHandling() throws Exception {
InboundMessageQueuer[] channels = { connectPeer(0), connectPeer(1), connectPeer(2), connectPeer(3), connectPeer(4) };
Transaction tx = new Transaction(params);
TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, blockChain.getContext(), tx);
TransactionBroadcast broadcast = new TransactionBroadcast(peerGroup, tx);
ListenableFuture<Transaction> future = broadcast.broadcast();
// 0 and 3 are randomly selected to receive the broadcast.
assertEquals(tx, outbound(channels[1]));