diff --git a/core/src/main/java/com/google/bitcoin/core/Peer.java b/core/src/main/java/com/google/bitcoin/core/Peer.java index 9b24fafb..bcf95332 100644 --- a/core/src/main/java/com/google/bitcoin/core/Peer.java +++ b/core/src/main/java/com/google/bitcoin/core/Peer.java @@ -53,6 +53,7 @@ public class Peer { private final NetworkParameters params; private final AbstractBlockChain blockChain; private PeerAddress address; + // TODO: Make the types here explicit and remove synchronization on adders/removers. private List eventListeners; private List lifecycleListeners; // Whether to try and download blocks and transactions from this peer. Set to false by PeerGroup if not the @@ -69,6 +70,8 @@ public class Peer { // 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 MemoryPool memoryPool; + // Each wallet added to the peer will be notified of downloaded transaction data. + private CopyOnWriteArrayList wallets; // A time before which we only download block headers, after that point we download block bodies. private long fastCatchupTimeSecs; // Whether we are currently downloading headers only or block bodies. Starts at true. If the fast catchup time is @@ -123,6 +126,7 @@ public class Peer { this.handler = new PeerHandler(); this.pendingPings = new CopyOnWriteArrayList(); this.lastPingTimes = null; + this.wallets = new CopyOnWriteArrayList(); } /** @@ -413,9 +417,18 @@ public class Peer { } if (maybeHandleRequestedData(tx)) return; - // Tell all listeners (like wallets) about this tx so they can decide whether to keep it or not. If no - // listener keeps a reference around then the memory pool will forget about it after a while too because - // it uses weak references. + // Tell all wallets about this tx so they can check if it's relevant or not. + for (ListIterator it = wallets.listIterator(); it.hasNext();) { + Wallet wallet = it.next(); + try { + wallet.receivePending(tx); + } catch (VerificationException e) { + log.error("Wallet failed to verify tx", e); + // Carry on, listeners may still want to know. + } + } + // Tell all listeners about this tx so they can decide whether to keep it or not. If no listener keeps a + // reference around then the memory pool will forget about it after a while too because it uses weak references. final Transaction ftx = tx; EventListenerInvoker.invoke(eventListeners, new EventListenerInvoker() { @Override @@ -863,12 +876,12 @@ public class Peer { * independently, otherwise the wallet will receive duplicate notifications. */ public void addWallet(Wallet wallet) { - addEventListener(wallet.getPeerEventListener()); + wallets.add(wallet); } /** Unlinks the given wallet from peer. See {@link Peer#addWallet(Wallet)}. */ public void removeWallet(Wallet wallet) { - removeEventListener(wallet.getPeerEventListener()); + wallets.remove(wallet); } /** diff --git a/core/src/main/java/com/google/bitcoin/core/PeerGroup.java b/core/src/main/java/com/google/bitcoin/core/PeerGroup.java index 85ddd963..51e8d54b 100644 --- a/core/src/main/java/com/google/bitcoin/core/PeerGroup.java +++ b/core/src/main/java/com/google/bitcoin/core/PeerGroup.java @@ -542,7 +542,6 @@ public class PeerGroup extends AbstractIdleService { public synchronized void addWallet(Wallet wallet) { Preconditions.checkNotNull(wallet); wallets.add(wallet); - addEventListener(wallet.getPeerEventListener()); announcePendingWalletTransactions(Collections.singletonList(wallet), peers); // Don't bother downloading block bodies before the oldest keys in all our wallets. Make sure we recalculate @@ -609,7 +608,6 @@ public class PeerGroup extends AbstractIdleService { if (wallet == null) throw new IllegalArgumentException("wallet is null"); wallets.remove(wallet); - removeEventListener(wallet.getPeerEventListener()); } /** @@ -854,13 +852,17 @@ public class PeerGroup extends AbstractIdleService { if (downloadPeer != null) { log.info("Unsetting download peer: {}", downloadPeer); downloadPeer.setDownloadData(false); + for (Wallet wallet : wallets) + downloadPeer.removeWallet(wallet); } downloadPeer = peer; if (downloadPeer != null) { log.info("Setting download peer: {}", downloadPeer); downloadPeer.setDownloadData(true); - if (chain != null) - downloadPeer.setDownloadParameters(fastCatchupTimeSecs, bloomFilter != null); + downloadPeer.setDownloadParameters(fastCatchupTimeSecs, bloomFilter != null); + // TODO: The peer should calculate the fast catchup time from the added wallets here. + for (Wallet wallet : wallets) + downloadPeer.addWallet(wallet); } } diff --git a/core/src/main/java/com/google/bitcoin/core/Wallet.java b/core/src/main/java/com/google/bitcoin/core/Wallet.java index 5b1bf688..9d6de798 100644 --- a/core/src/main/java/com/google/bitcoin/core/Wallet.java +++ b/core/src/main/java/com/google/bitcoin/core/Wallet.java @@ -2201,34 +2201,6 @@ public class Wallet implements Serializable, BlockChainListener { } return earliestTime; } - - // This object is used to receive events from a Peer or PeerGroup. Currently it is only used to receive - // transactions. Note that it does NOT pay attention to block message because they will be received from the - // BlockChain object along with extra data we need for correct handling of re-orgs. - private transient PeerEventListener peerEventListener; - - /** - * The returned object can be used to connect the wallet to a {@link Peer} or {@link PeerGroup} in order to - * receive and process blocks and transactions. - */ - synchronized PeerEventListener getPeerEventListener() { - if (peerEventListener == null) { - // Instantiate here to avoid issues with wallets resurrected from serialized copies. - peerEventListener = new AbstractPeerEventListener() { - @Override - public void onTransaction(Peer peer, Transaction t) { - // Runs locked on a peer thread. - try { - receivePending(t); - } catch (VerificationException e) { - log.warn("Received broadcast transaction that does not validate: {}", t); - log.warn("VerificationException caught", e); - } - } - }; - } - return peerEventListener; - } public Sha256Hash getLastBlockSeenHash() { return lastBlockSeenHash;