Skip to content

Commit

Permalink
Add switch to disable download of pending transaction dependencies. T…
Browse files Browse the repository at this point in the history
…his becomes necessary because the current recursive download strategy is often triggering a stack overflow on Android devices.
  • Loading branch information
schildbach authored and mikehearn committed Apr 22, 2014
1 parent b0fa543 commit 53147fa
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 20 deletions.
61 changes: 42 additions & 19 deletions core/src/main/java/com/google/bitcoin/core/Peer.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public PeerListenerRegistration(PeerEventListener listener, Executor executor, b
// The version data to announce to the other side of the connections we make: useful for setting our "user agent"
// equivalent and other things.
private final VersionMessage versionMessage;
// Switch for enabling download of pending transaction dependencies.
private final boolean downloadTxDependencies;
// How many block messages the peer has announced to us. Peers only announce blocks that attach to their best chain
// 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.
Expand Down Expand Up @@ -173,10 +175,30 @@ public Peer(NetworkParameters params, VersionMessage ver, @Nullable AbstractBloc
* 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 MemoryPool mempool) {
this(params, ver, remoteAddress, chain, mempool, true);
}

/**
* <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
* 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
* connection. If you want to create a one-off connection, create a Peer and pass it to
* {@link com.google.bitcoin.net.NioClientManager#openConnection(java.net.SocketAddress, com.google.bitcoin.net.StreamParser)}
* or
* {@link com.google.bitcoin.net.NioClient#NioClient(java.net.SocketAddress, com.google.bitcoin.net.StreamParser, int)}.</p>
*
* <p>The remoteAddress provided should match the remote address of the peer which is being connected to, and is
* 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) {
super(params, remoteAddress);
this.params = Preconditions.checkNotNull(params);
this.versionMessage = Preconditions.checkNotNull(ver);
this.downloadTxDependencies = downloadTxDependencies;
this.blockChain = chain; // Allowed to be null.
this.vDownloadData = chain != null;
this.getDataFutures = new CopyOnWriteArrayList<GetDataRequest>();
Expand Down Expand Up @@ -577,26 +599,27 @@ private void processTransaction(Transaction tx) throws VerificationException {
// the chain, in case the sender goes away and the network starts to forget.
// TODO: Not all the above things are implemented.

Futures.addCallback(downloadDependencies(fTx), new FutureCallback<List<Transaction>>() {
public void onSuccess(List<Transaction> dependencies) {
try {
log.info("{}: Dependency download complete!", getAddress());
wallet.receivePending(fTx, dependencies);
} catch (VerificationException e) {
log.error("{}: Wallet failed to process pending transaction {}",
getAddress(), fTx.getHashAsString());
log.error("Error was: ", e);
// Not much more we can do at this point.
if (downloadTxDependencies) {
Futures.addCallback(downloadDependencies(fTx), new FutureCallback<List<Transaction>>() {
public void onSuccess(List<Transaction> dependencies) {
try {
log.info("{}: Dependency download complete!", getAddress());
wallet.receivePending(fTx, dependencies);
} catch (VerificationException e) {
log.error("{}: Wallet failed to process pending transaction {}", getAddress(),
fTx.getHashAsString());
log.error("Error was: ", e);
// Not much more we can do at this point.
}
}
}

public void onFailure(Throwable throwable) {
log.error("Could not download dependencies of tx {}", fTx.getHashAsString());
log.error("Error was: ", throwable);
// Not much more we can do at this point.
}
});

public void onFailure(Throwable throwable) {
log.error("Could not download dependencies of tx {}", fTx.getHashAsString());
log.error("Error was: ", throwable);
// Not much more we can do at this point.
}
});
}
}
} catch (VerificationException e) {
log.error("Wallet failed to verify tx", e);
Expand Down
17 changes: 16 additions & 1 deletion core/src/main/java/com/google/bitcoin/core/PeerGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
private final CopyOnWriteArraySet<PeerDiscovery> peerDiscoverers;
// The version message to use for new connections.
@GuardedBy("lock") private VersionMessage versionMessage;
// Switch for enabling download of pending transaction dependencies.
@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;
Expand Down Expand Up @@ -290,6 +292,7 @@ public PeerGroup(NetworkParameters params, @Nullable AbstractBlockChain chain, C
int height = chain == null ? 0 : chain.getBestChainHeight();
// We never request that the remote node wait for a bloom filter yet, as we have no wallets
this.versionMessage = new VersionMessage(params, height, true);
this.downloadTxDependencies = true;

memoryPool = new MemoryPool();

Expand Down Expand Up @@ -335,6 +338,18 @@ public void setMaxConnections(int maxConnections) {
channels.closeConnections(-adjustment);
}

/**
* Switch for enabling download of pending transaction dependencies. A change of value only takes effect for newly
* connected peers.
*/
public void setDownloadTxDependencies(boolean downloadTxDependencies) {
lock.lock();
try {
this.downloadTxDependencies = downloadTxDependencies;
} finally {
lock.unlock();
}
}

private Runnable triggerConnectionsJob = new Runnable() {
@Override
Expand Down Expand Up @@ -895,7 +910,7 @@ protected Peer connectTo(PeerAddress address, boolean incrementMaxConnections) {
ver.bestHeight = chain == null ? 0 : chain.getBestChainHeight();
ver.time = Utils.currentTimeSeconds();

Peer peer = new Peer(params, ver, address, chain, memoryPool);
Peer peer = new Peer(params, ver, address, chain, memoryPool, downloadTxDependencies);
peer.addEventListener(startupListener, Threading.SAME_THREAD);
peer.setMinProtocolVersion(vMinRequiredProtocolVersion);
pendingPeers.add(peer);
Expand Down

0 comments on commit 53147fa

Please sign in to comment.