Commit ecbd0211 authored by Ross Nicoll's avatar Ross Nicoll Committed by Andreas Schildbach

Refactor listener interfaces.

Refactor listener interfaces into their own package.
Split listener interfaces into smaller interfaces.
Make abstract implementations actually abstract.
Rearrange methods for adding listeners to put executor first.
parent bd080ac5
......@@ -46,7 +46,7 @@ public class BlockChain extends AbstractBlockChain {
* disk serialization (this is rare).</p>
*/
public BlockChain(Context context, Wallet wallet, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<BlockChainListener>(), blockStore);
this(context, new ArrayList<Wallet>(), blockStore);
addWallet(wallet);
}
......@@ -60,24 +60,24 @@ public class BlockChain extends AbstractBlockChain {
* and receiving coins but rather, just want to explore the network data structures.
*/
public BlockChain(Context context, BlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<BlockChainListener>(), blockStore);
this(context, new ArrayList<Wallet>(), blockStore);
}
/** See {@link #BlockChain(Context, BlockStore)} */
public BlockChain(NetworkParameters params, BlockStore blockStore) throws BlockStoreException {
this(params, new ArrayList<BlockChainListener>(), blockStore);
this(params, new ArrayList<Wallet>(), blockStore);
}
/**
* Constructs a BlockChain connected to the given list of listeners and a store.
*/
public BlockChain(Context params, List<BlockChainListener> wallets, BlockStore blockStore) throws BlockStoreException {
public BlockChain(Context params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
super(params, wallets, blockStore);
this.blockStore = blockStore;
}
/** See {@link #BlockChain(Context, List, BlockStore)} */
public BlockChain(NetworkParameters params, List<BlockChainListener> wallets, BlockStore blockStore) throws BlockStoreException {
public BlockChain(NetworkParameters params, List<? extends Wallet> wallets, BlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), wallets, blockStore);
}
......
......@@ -17,6 +17,7 @@
package org.bitcoinj.core;
import org.bitcoinj.core.listeners.TransactionReceivedInBlockListener;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.Script.VerifyFlag;
import org.bitcoinj.store.BlockStoreException;
......@@ -61,7 +62,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
* {@link Wallet#loadFromFile(java.io.File, WalletExtension...)}
*/
public FullPrunedBlockChain(Context context, Wallet wallet, FullPrunedBlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<BlockChainListener>(), blockStore);
this(context, new ArrayList<Wallet>(), blockStore);
addWallet(wallet);
}
......@@ -78,7 +79,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
* Constructs a block chain connected to the given store.
*/
public FullPrunedBlockChain(Context context, FullPrunedBlockStore blockStore) throws BlockStoreException {
this(context, new ArrayList<BlockChainListener>(), blockStore);
this(context, new ArrayList<Wallet>(), blockStore);
}
/**
......@@ -91,7 +92,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
/**
* Constructs a block chain connected to the given list of wallets and a store.
*/
public FullPrunedBlockChain(Context context, List<BlockChainListener> listeners, FullPrunedBlockStore blockStore) throws BlockStoreException {
public FullPrunedBlockChain(Context context, List<Wallet> listeners, FullPrunedBlockStore blockStore) throws BlockStoreException {
super(context, listeners, blockStore);
this.blockStore = blockStore;
// Ignore upgrading for now
......@@ -101,7 +102,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
/**
* See {@link #FullPrunedBlockChain(Context, List, FullPrunedBlockStore)}
*/
public FullPrunedBlockChain(NetworkParameters params, List<BlockChainListener> listeners,
public FullPrunedBlockChain(NetworkParameters params, List<Wallet> listeners,
FullPrunedBlockStore blockStore) throws BlockStoreException {
this(Context.getOrCreate(params), listeners, blockStore);
}
......
......@@ -19,6 +19,8 @@ package org.bitcoinj.core;
import com.google.common.annotations.*;
import com.google.common.base.*;
import com.google.common.util.concurrent.*;
import org.bitcoinj.core.listeners.AbstractPeerDataEventListener;
import org.bitcoinj.core.listeners.PeerDataEventListener;
import org.bitcoinj.utils.*;
import org.slf4j.*;
......@@ -86,7 +88,7 @@ public class TransactionBroadcast {
this.minConnections = minConnections;
}
private PeerEventListener rejectionListener = new AbstractPeerEventListener() {
private PeerDataEventListener rejectionListener = new AbstractPeerDataEventListener() {
@Override
public Message onPreMessageReceived(Peer peer, Message m) {
if (m instanceof RejectMessage) {
......@@ -98,7 +100,7 @@ public class TransactionBroadcast {
if (size > threshold) {
log.warn("Threshold for considering broadcast rejected has been reached ({}/{})", size, threshold);
future.setException(new RejectedTransactionException(tx, rejectMessage));
peerGroup.removeEventListener(this);
peerGroup.removeDataEventListener(this);
}
}
}
......@@ -107,7 +109,7 @@ public class TransactionBroadcast {
};
public ListenableFuture<Transaction> broadcast() {
peerGroup.addEventListener(rejectionListener, Threading.SAME_THREAD);
peerGroup.addDataEventListener(Threading.SAME_THREAD, rejectionListener);
log.info("Waiting for {} peers required for broadcast, we have {} ...", minConnections, peerGroup.getConnectedPeers().size());
peerGroup.waitForPeers(minConnections).addListener(new EnoughAvailablePeers(), Threading.SAME_THREAD);
return future;
......@@ -159,7 +161,7 @@ public class TransactionBroadcast {
// So we just have to assume we're done, at that point. This happens when we're not given
// any peer discovery source and the user just calls connectTo() once.
if (minConnections == 1) {
peerGroup.removeEventListener(rejectionListener);
peerGroup.removeDataEventListener(rejectionListener);
future.set(tx);
}
}
......@@ -195,7 +197,7 @@ public class TransactionBroadcast {
// We're done! It's important that the PeerGroup lock is not held (by this thread) at this
// point to avoid triggering inversions when the Future completes.
log.info("broadcastTransaction: {} complete", tx.getHash());
peerGroup.removeEventListener(rejectionListener);
peerGroup.removeDataEventListener(rejectionListener);
conf.removeEventListener(this);
future.set(tx); // RE-ENTRANCY POINT
}
......
......@@ -193,9 +193,9 @@ public class TransactionConfidence {
* the best chain). If you want to know when the transaction gets buried under another block, consider using
* a future from {@link #getDepthFuture(int)}.</p>
*/
public void addEventListener(Listener listener, Executor executor) {
public void addEventListener(Executor executor, Listener listener) {
checkNotNull(listener);
listeners.addIfAbsent(new ListenerRegistration<Listener>(listener, executor));
listeners.addIfAbsent(new ListenerRegistration<Listener>(executor, listener));
pinnedConfidenceObjects.add(this);
}
......@@ -210,7 +210,7 @@ public class TransactionConfidence {
* confidence object to determine the new depth.</p>
*/
public void addEventListener(Listener listener) {
addEventListener(listener, Threading.USER_THREAD);
addEventListener(Threading.USER_THREAD, listener);
}
public boolean removeEventListener(Listener listener) {
......@@ -457,14 +457,14 @@ public class TransactionConfidence {
if (getDepthInBlocks() >= depth) {
result.set(this);
}
addEventListener(new Listener() {
addEventListener(executor, new Listener() {
@Override public void onConfidenceChanged(TransactionConfidence confidence, ChangeReason reason) {
if (getDepthInBlocks() >= depth) {
removeEventListener(this);
result.set(confidence);
}
}
}, executor);
});
return result;
}
......
/**
* Copyright 2013 Google Inc.
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -14,30 +14,29 @@
* limitations under the License.
*/
package org.bitcoinj.core;
package org.bitcoinj.core.listeners;
import java.util.List;
import org.bitcoinj.core.Peer;
import java.util.Set;
import org.bitcoinj.core.PeerAddress;
/**
* Default no-op implementation of {@link BlockChainListener}.
* Convenience implementation of {@link PeerEventListener}.
*/
public class AbstractBlockChainListener implements BlockChainListener {
@Override
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
}
public abstract class AbstractPeerConnectionEventListener implements PeerConnectionEventListener {
@Override
public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks) throws VerificationException {
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
// Do nothing
}
@Override
public void receiveFromBlock(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType,
int relativityOffset) throws VerificationException {
public void onPeerConnected(Peer peer, int peerCount) {
// Do nothing
}
@Override
public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, BlockChain.NewBlockType blockType,
int relativityOffset) throws VerificationException {
return false;
public void onPeerDisconnected(Peer peer, int peerCount) {
// Do nothing
}
}
/**
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core.listeners;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.FilteredBlock;
import org.bitcoinj.core.GetDataMessage;
import org.bitcoinj.core.Message;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.Transaction;
import javax.annotation.*;
import java.util.List;
/**
* Convenience implementation of {@link PeerEventListener}.
*/
public abstract class AbstractPeerDataEventListener implements PeerDataEventListener {
@Override
public void onBlocksDownloaded(Peer peer, Block block, @Nullable FilteredBlock filteredBlock, int blocksLeft) {
}
@Override
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
}
@Override
public Message onPreMessageReceived(Peer peer, Message m) {
// Just pass the message right through for further processing.
return m;
}
@Override
public void onTransaction(Peer peer, Transaction t) {
}
@Override
public List<Message> getData(Peer peer, GetDataMessage m) {
return null;
}
}
......@@ -14,8 +14,15 @@
* limitations under the License.
*/
package org.bitcoinj.core;
package org.bitcoinj.core.listeners;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.FilteredBlock;
import org.bitcoinj.core.GetDataMessage;
import org.bitcoinj.core.Message;
import org.bitcoinj.core.PeerAddress;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.Transaction;
import javax.annotation.*;
import java.util.List;
import java.util.Set;
......@@ -23,39 +30,39 @@ import java.util.Set;
/**
* Convenience implementation of {@link PeerEventListener}.
*/
public class AbstractPeerEventListener implements PeerEventListener {
public abstract class AbstractPeerEventListener extends AbstractPeerDataEventListener implements PeerConnectionEventListener {
@Override
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
public void onBlocksDownloaded(Peer peer, Block block, @Nullable FilteredBlock filteredBlock, int blocksLeft) {
}
@Override
public void onBlocksDownloaded(Peer peer, Block block, @Nullable FilteredBlock filteredBlock, int blocksLeft) {
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
}
@Override
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
public Message onPreMessageReceived(Peer peer, Message m) {
// Just pass the message right through for further processing.
return m;
}
@Override
public void onPeerConnected(Peer peer, int peerCount) {
public void onTransaction(Peer peer, Transaction t) {
}
@Override
public void onPeerDisconnected(Peer peer, int peerCount) {
public List<Message> getData(Peer peer, GetDataMessage m) {
return null;
}
@Override
public Message onPreMessageReceived(Peer peer, Message m) {
// Just pass the message right through for further processing.
return m;
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
}
@Override
public void onTransaction(Peer peer, Transaction t) {
public void onPeerConnected(Peer peer, int peerCount) {
}
@Override
public List<Message> getData(Peer peer, GetDataMessage m) {
return null;
public void onPeerDisconnected(Peer peer, int peerCount) {
}
}
......@@ -14,8 +14,12 @@
* limitations under the License.
*/
package org.bitcoinj.core;
package org.bitcoinj.core.listeners;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Wallet;
import org.bitcoinj.script.Script;
import org.bitcoinj.wallet.AbstractKeyChainEventListener;
......
......@@ -15,8 +15,12 @@
* limitations under the License.
*/
package org.bitcoinj.core;
package org.bitcoinj.core.listeners;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.FilteredBlock;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.Utils;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import org.slf4j.Logger;
......@@ -27,11 +31,11 @@ import java.util.Date;
import java.util.concurrent.ExecutionException;
/**
* <p>An implementation of {@link AbstractPeerEventListener} that listens to chain download events and tracks progress
* <p>An implementation of {@link AbstractPeerDataEventListener} that listens to chain download events and tracks progress
* as a percentage. The default implementation prints progress to stdout, but you can subclass it and override the
* progress method to update a GUI instead.</p>
*/
public class DownloadProgressTracker extends AbstractPeerEventListener {
public class DownloadProgressTracker extends AbstractPeerDataEventListener {
private static final Logger log = LoggerFactory.getLogger(DownloadProgressTracker.class);
private int originalBlocksLeft = -1;
private int lastPercent = 0;
......
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core.listeners;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.VerificationException;
/**
* Listener interface for when a new block on the best chain is seen.
*/
public interface NewBestBlockListener {
/**
* Called when a new block on the best chain is seen, after relevant
* transactions are extracted and sent to us via either
* {@link ReceiveFromBlockListener#receiveFromBlock(Transaction, StoredBlock, org.bitcoinj.core.BlockChain.NewBlockType, int)}
* or {@link TransactionIsInBlockListener#notifyTransactionIsInBlock(Sha256Hash, StoredBlock, org.bitcoinj.core.BlockChain.NewBlockType, int)}.
* If this block is causing a re-organise to a new chain, this method is NOT
* called even though the block may be the new best block: your reorganize
* implementation is expected to do whatever would normally be done do for a
* new best block in this case.
*/
void notifyNewBestBlock(final StoredBlock block) throws VerificationException;
}
/**
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core.listeners;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.PeerAddress;
import java.util.Set;
/**
* <p>Implementors can listen to events like blocks being downloaded/transactions being broadcast/connect/disconnects,
* they can pre-filter messages before they are procesesed by a {@link Peer} or {@link PeerGroup}, and they can
* provide transactions to remote peers when they ask for them.</p>
*/
public interface PeerConnectionEventListener {
/**
* <p>Called when peers are discovered, this happens at startup of {@link PeerGroup} or if we run out of
* suitable {@link Peer}s to connect to.</p>
*
* @param peerAddresses the set of discovered {@link PeerAddress}es
*/
void onPeersDiscovered(Set<PeerAddress> peerAddresses);
/**
* Called when a peer is connected. If this listener is registered to a {@link Peer} instead of a {@link PeerGroup},
* peerCount will always be 1.
*
* @param peer
* @param peerCount the total number of connected peers
*/
void onPeerConnected(Peer peer, int peerCount);
/**
* Called when a peer is disconnected. Note that this won't be called if the listener is registered on a
* {@link PeerGroup} and the group is in the process of shutting down. If this listener is registered to a
* {@link Peer} instead of a {@link PeerGroup}, peerCount will always be 0. This handler can be called without
* a corresponding invocation of onPeerConnected if the initial connection is never successful.
*
* @param peer
* @param peerCount the total number of connected peers
*/
void onPeerDisconnected(Peer peer, int peerCount);
}
......@@ -14,24 +14,23 @@
* limitations under the License.
*/
package org.bitcoinj.core;
package org.bitcoinj.core.listeners;
import javax.annotation.*;
import java.util.*;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.FilteredBlock;
import org.bitcoinj.core.GetDataMessage;
import org.bitcoinj.core.Message;
import org.bitcoinj.core.Peer;
import org.bitcoinj.core.Transaction;
import javax.annotation.Nullable;
import java.util.List;
/**
* <p>Implementors can listen to events like blocks being downloaded/transactions being broadcast/connect/disconnects,
* they can pre-filter messages before they are procesesed by a {@link Peer} or {@link PeerGroup}, and they can
* provide transactions to remote peers when they ask for them.</p>
*/
public interface PeerEventListener {
/**
* <p>Called when peers are discovered, this happens at startup of {@link PeerGroup} or if we run out of
* suitable {@link Peer}s to connect to.</p>
*
* @param peerAddresses the set of discovered {@link PeerAddress}es
*/
void onPeersDiscovered(Set<PeerAddress> peerAddresses);
public interface PeerDataEventListener {
// TODO: Fix the Block/FilteredBlock type hierarchy so we can avoid the stupid typeless API here.
/**
......@@ -55,26 +54,6 @@ public interface PeerEventListener {
*/
void onChainDownloadStarted(Peer peer, int blocksLeft);
/**
* Called when a peer is connected. If this listener is registered to a {@link Peer} instead of a {@link PeerGroup},
* peerCount will always be 1.
*
* @param peer
* @param peerCount the total number of connected peers
*/
void onPeerConnected(Peer peer, int peerCount);
/**
* Called when a peer is disconnected. Note that this won't be called if the listener is registered on a
* {@link PeerGroup} and the group is in the process of shutting down. If this listener is registered to a
* {@link Peer} instead of a {@link PeerGroup}, peerCount will always be 0. This handler can be called without
* a corresponding invocation of onPeerConnected if the initial connection is never successful.
*
* @param peer
* @param peerCount the total number of connected peers
*/
void onPeerDisconnected(Peer peer, int peerCount);
/**
* <p>Called when a message is received by a peer, before the message is processed. The returned message is
* processed instead. Returning null will cause the message to be ignored by the Peer returning the same message
......
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core.listeners;
import java.util.List;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.VerificationException;
/**
* Listener interface for when the best chain has changed.
*/
public interface ReorganizeListener {
/**
* Called by the {@link BlockChain} when the best chain (representing total work done) has changed. In this case,
* we need to go through our transactions and find out if any have become invalid. It's possible for our balance
* to go down in this case: money we thought we had can suddenly vanish if the rest of the network agrees it
* should be so.<p>
*
* The oldBlocks/newBlocks lists are ordered height-wise from top first to bottom last (i.e. newest blocks first).
*/
void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks,
List<StoredBlock> newBlocks) throws VerificationException;
}
/**
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
......@@ -13,38 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.bitcoinj.core.listeners;
package org.bitcoinj.core;
import java.util.List;
import org.bitcoinj.core.BlockChain;
import org.bitcoinj.core.ScriptException;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.VerificationException;
/**
* Implementors can be connected to a {@link BlockChain} and have its methods called when various things
* happen that modify the state of the chain, for example: new blocks being received, a re-org occurring, or the
* best chain head changing.
* Listener interface for when we receive a new block that contains a relevant
* transaction.
*/
public interface BlockChainListener {
/**
* Called when a new block on the best chain is seen, after relevant transactions are extracted and sent to
* us via either {@link #receiveFromBlock(Transaction, StoredBlock, org.bitcoinj.core.BlockChain.NewBlockType, int)}
* or {@link #notifyTransactionIsInBlock(Sha256Hash, StoredBlock, org.bitcoinj.core.BlockChain.NewBlockType, int)}.
* If this block is causing a re-organise to a new chain, this method is NOT called even though the block may be
* the new best block: your reorganize implementation is expected to do whatever would normally be done do for a new
* best block in this case.
*/
void notifyNewBestBlock(StoredBlock block) throws VerificationException;
/**
* Called by the {@link BlockChain} when the best chain (representing total work done) has changed. In this case,
* we need to go through our transactions and find out if any have become invalid. It's possible for our balance
* to go down in this case: money we thought we had can suddenly vanish if the rest of the network agrees it
* should be so.<p>
*
* The oldBlocks/newBlocks lists are ordered height-wise from top first to bottom last (i.e. newest blocks first).
*/
void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks,
List<StoredBlock> newBlocks) throws VerificationException;
public interface TransactionReceivedInBlockListener {
/**
* <p>Called by the {@link BlockChain} when we receive a new block that contains a relevant transaction.</p>
*
......@@ -61,7 +43,6 @@ public interface BlockChainListener {
void receiveFromBlock(Transaction tx, StoredBlock block,
BlockChain.NewBlockType blockType,
int relativityOffset) throws VerificationException;
/**
* <p>Called by the {@link BlockChain} when we receive a new {@link FilteredBlock} that contains the given
* transaction hash in its merkle tree.</p>
......
......@@ -14,8 +14,10 @@
* limitations under the License.
*/
package org.bitcoinj.core;
package org.bitcoinj.core.listeners;
import org.bitcoinj.core.Transaction;