imToken v2.9.3版本的 MD5 值为:42af1a08a3f01f69d2f3782d81007b3c

以下内容为反编译后的 PeerGroup.java 源代码,内容仅作参考


package org.bitcoinj.core;

import cn.jiguang.internal.JConstants;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.net.InetAddresses;
import com.google.common.primitives.Ints;
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.ListenableScheduledFuture;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Runnables;
import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.Uninterruptibles;
import com.squareup.okhttp.OkHttpClient;
import com.subgraph.orchid.TorClient;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NoRouteToHostException;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.Nullable;
import net.jcip.annotations.GuardedBy;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.listeners.AbstractPeerEventListener;
import org.bitcoinj.core.listeners.BlocksDownloadedEventListener;
import org.bitcoinj.core.listeners.ChainDownloadStartedEventListener;
import org.bitcoinj.core.listeners.DownloadProgressTracker;
import org.bitcoinj.core.listeners.GetDataEventListener;
import org.bitcoinj.core.listeners.OnTransactionBroadcastListener;
import org.bitcoinj.core.listeners.PeerConnectedEventListener;
import org.bitcoinj.core.listeners.PeerDataEventListener;
import org.bitcoinj.core.listeners.PeerDisconnectedEventListener;
import org.bitcoinj.core.listeners.PeerDiscoveredEventListener;
import org.bitcoinj.core.listeners.PreMessageReceivedEventListener;
import org.bitcoinj.crypto.DRMWorkaround;
import org.bitcoinj.net.BlockingClientManager;
import org.bitcoinj.net.ClientConnectionManager;
import org.bitcoinj.net.FilterMerger;
import org.bitcoinj.net.NioClientManager;
import org.bitcoinj.net.discovery.HttpDiscovery;
import org.bitcoinj.net.discovery.MultiplexingDiscovery;
import org.bitcoinj.net.discovery.PeerDiscovery;
import org.bitcoinj.net.discovery.PeerDiscoveryException;
import org.bitcoinj.net.discovery.TorDiscovery;
import org.bitcoinj.script.Script;
import org.bitcoinj.utils.ContextPropagatingThreadFactory;
import org.bitcoinj.utils.ExponentialBackoff;
import org.bitcoinj.utils.ListenerRegistration;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.listeners.KeyChainEventListener;
import org.bitcoinj.wallet.listeners.ScriptsChangeEventListener;
import org.bitcoinj.wallet.listeners.WalletCoinsReceivedEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PeerGroup implements TransactionBroadcaster {
    public static final double DEFAULT_BLOOM_FILTER_FP_RATE = 1.0E-5d;
    public static final int DEFAULT_CONNECTIONS = 12;
    public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 5000;
    private static final long DEFAULT_PEER_DISCOVERY_TIMEOUT_MILLIS = 5000;
    public static final long DEFAULT_PING_INTERVAL_MSEC = 2000;
    public static final double MAX_FP_RATE_INCREASE = 10.0d;
    private static final int TOR_TIMEOUT_SECONDS = 60;
    private static final Logger log = LoggerFactory.getLogger((Class<?>) PeerGroup.class);

    @GuardedBy("lock")
    private final Map<PeerAddress, ExponentialBackoff> backoffMap;
    private final FilterMerger bloomFilterMerger;

    @Nullable
    protected final AbstractBlockChain chain;

    @Nullable
    private ChainDownloadSpeedCalculator chainDownloadSpeedCalculator;
    private final ClientConnectionManager channels;

    @GuardedBy("lock")
    @Nullable
    private PeerDataEventListener downloadListener;

    @GuardedBy("lock")
    private Peer downloadPeer;

    @GuardedBy("lock")
    private int downloadTxDependencyDepth;
    protected final ListeningScheduledExecutorService executor;
    private CountDownLatch executorStartupLatch;

    @GuardedBy("lock")
    private long fastCatchupTimeSecs;

    @GuardedBy("lock")
    private ExponentialBackoff groupBackoff;
    private final Map<FilterRecalculateMode, SettableFuture<BloomFilter>> inFlightRecalculations;

    @GuardedBy("lock")
    private final PriorityQueue<PeerAddress> inactives;

    @GuardedBy("lock")
    private boolean ipv6Unreachable;
    private LocalhostCheckState localhostCheckState;
    protected final ReentrantLock lock;

    @GuardedBy("lock")
    private int maxConnections;
    private int minBroadcastConnections;
    protected final NetworkParameters params;
    private final ExponentialBackoff.Params peerBackoffParams;
    protected final CopyOnWriteArrayList<ListenerRegistration<PeerConnectedEventListener>> peerConnectedEventListeners;
    protected final CopyOnWriteArrayList<ListenerRegistration<PeerDisconnectedEventListener>> peerDisconnectedEventListeners;
    protected final CopyOnWriteArrayList<ListenerRegistration<PeerDiscoveredEventListener>> peerDiscoveredEventListeners;
    private final CopyOnWriteArraySet<PeerDiscovery> peerDiscoverers;
    private final CopyOnWriteArrayList<PeerFilterProvider> peerFilterProviders;
    private final CopyOnWriteArrayList<ListenerRegistration<GetDataEventListener>> peerGetDataEventListeners;
    private final PeerListener peerListener;
    private final CopyOnWriteArrayList<Peer> peers;
    private final CopyOnWriteArrayList<ListenerRegistration<BlocksDownloadedEventListener>> peersBlocksDownloadedEventListeners;
    private final CopyOnWriteArrayList<ListenerRegistration<ChainDownloadStartedEventListener>> peersChainDownloadStartedEventListeners;
    private final CopyOnWriteArrayList<ListenerRegistration<PreMessageReceivedEventListener>> peersPreMessageReceivedEventListeners;
    protected final CopyOnWriteArrayList<ListenerRegistration<OnTransactionBroadcastListener>> peersTransactionBroadastEventListeners;
    private final CopyOnWriteArrayList<Peer> pendingPeers;

    @GuardedBy("lock")
    private long pingIntervalMsec;
    private long requiredServices;
    private final Set<TransactionBroadcast> runningBroadcasts;

    @GuardedBy("lock")
    private int stallMinSpeedBytesSec;

    @GuardedBy("lock")
    private int stallPeriodSeconds;
    private final PeerStartupListener startupListener;

    @Nullable
    private final TorClient torClient;
    private Runnable triggerConnectionsJob;

    @GuardedBy("lock")
    private boolean useLocalhostPeerWhenPossible;
    private volatile boolean vBloomFilteringEnabled;
    private volatile int vConnectTimeoutMillis;
    private volatile int vMaxPeersToDiscoverCount;
    private volatile int vMinRequiredProtocolVersion;
    private volatile long vPeerDiscoveryTimeoutMillis;

    @Nullable
    private volatile ListenableScheduledFuture<?> vPingTask;
    private volatile boolean vRunning;
    private volatile boolean vUsedUp;

    @GuardedBy("lock")
    private VersionMessage versionMessage;
    private final WalletCoinsReceivedEventListener walletCoinsReceivedEventListener;
    private final KeyChainEventListener walletKeyEventListener;
    private final ScriptsChangeEventListener walletScriptEventListener;
    private final CopyOnWriteArrayList<Wallet> wallets;

    public enum FilterRecalculateMode {
        SEND_IF_CHANGED,
        FORCE_SEND_FOR_REFRESH,
        DONT_SEND
    }

    public enum LocalhostCheckState {
        NOT_TRIED,
        FOUND,
        FOUND_AND_CONNECTED,
        NOT_THERE
    }

    public class PeerListener implements GetDataEventListener, BlocksDownloadedEventListener {
        public PeerListener() {
        }

        @Override
        public List<Message> getData(Peer peer, GetDataMessage getDataMessage) {
            return PeerGroup.this.handleGetData(getDataMessage);
        }

        @Override
        public void onBlocksDownloaded(Peer peer, Block block, @Nullable FilteredBlock filteredBlock, int i) {
            if (PeerGroup.this.chain == null) {
                return;
            }
            double falsePositiveRate = PeerGroup.this.chain.getFalsePositiveRate();
            double bloomFilterFPRate = PeerGroup.this.bloomFilterMerger.getBloomFilterFPRate() * 10.0d;
            if (falsePositiveRate > bloomFilterFPRate) {
                if (PeerGroup.log.isDebugEnabled()) {
                    PeerGroup.log.debug("Force update Bloom filter due to high false positive rate ({} vs {})", Double.valueOf(falsePositiveRate), Double.valueOf(bloomFilterFPRate));
                }
                PeerGroup.this.recalculateFastCatchupAndFilter(FilterRecalculateMode.FORCE_SEND_FOR_REFRESH);
            }
        }
    }

    public class PeerStartupListener implements PeerConnectedEventListener, PeerDisconnectedEventListener {
        private PeerStartupListener() {
        }

        @Override
        public void onPeerConnected(Peer peer, int i) {
            PeerGroup.this.handleNewPeer(peer);
        }

        @Override
        public void onPeerDisconnected(Peer peer, int i) {
            PeerGroup.this.handlePeerDeath(peer, null);
        }
    }

    public PeerGroup(NetworkParameters networkParameters) {
        this(networkParameters, (AbstractBlockChain) null);
    }

    public PeerGroup(Context context) {
        this(context, (AbstractBlockChain) null);
    }

    public PeerGroup(NetworkParameters networkParameters, @Nullable AbstractBlockChain abstractBlockChain) {
        this(Context.getOrCreate(networkParameters), abstractBlockChain, new NioClientManager());
    }

    public PeerGroup(Context context, @Nullable AbstractBlockChain abstractBlockChain) {
        this(context, abstractBlockChain, new NioClientManager());
    }

    public static PeerGroup newWithTor(NetworkParameters networkParameters, @Nullable AbstractBlockChain abstractBlockChain, TorClient torClient) throws TimeoutException {
        return newWithTor(Context.getOrCreate(networkParameters), abstractBlockChain, torClient);
    }

    public static PeerGroup newWithTor(Context context, @Nullable AbstractBlockChain abstractBlockChain, TorClient torClient) throws TimeoutException {
        return newWithTor(context, abstractBlockChain, torClient, true);
    }

    public static PeerGroup newWithTor(Context context, @Nullable AbstractBlockChain abstractBlockChain, TorClient torClient, boolean z) throws TimeoutException {
        Preconditions.checkNotNull(torClient);
        DRMWorkaround.maybeDisableExportControls();
        BlockingClientManager blockingClientManager = new BlockingClientManager(torClient.getSocketFactory());
        blockingClientManager.setConnectTimeoutMillis(60000);
        PeerGroup peerGroup = new PeerGroup(context, abstractBlockChain, blockingClientManager, torClient);
        peerGroup.setConnectTimeoutMillis(60000);
        if (z) {
            NetworkParameters params = context.getParams();
            HttpDiscovery.Details[] httpSeeds = params.getHttpSeeds();
            if (httpSeeds.length > 0) {
                OkHttpClient okHttpClient = new OkHttpClient();
                okHttpClient.setSocketFactory(torClient.getSocketFactory());
                ArrayList newArrayList = Lists.newArrayList();
                for (HttpDiscovery.Details details : httpSeeds) {
                    newArrayList.add(new HttpDiscovery(params, details, okHttpClient));
                }
                peerGroup.addPeerDiscovery(new MultiplexingDiscovery(params, newArrayList));
            } else {
                peerGroup.addPeerDiscovery(new TorDiscovery(params, torClient));
            }
        }
        return peerGroup;
    }

    public PeerGroup(NetworkParameters networkParameters, @Nullable AbstractBlockChain abstractBlockChain, ClientConnectionManager clientConnectionManager) {
        this(Context.getOrCreate(networkParameters), abstractBlockChain, clientConnectionManager, null);
    }

    public PeerGroup(Context context, @Nullable AbstractBlockChain abstractBlockChain, ClientConnectionManager clientConnectionManager) {
        this(context, abstractBlockChain, clientConnectionManager, null);
    }

    private PeerGroup(Context context, @Nullable AbstractBlockChain abstractBlockChain, ClientConnectionManager clientConnectionManager, @Nullable TorClient torClient) {
        this.requiredServices = 0L;
        this.vMaxPeersToDiscoverCount = 100;
        this.vPeerDiscoveryTimeoutMillis = 5000L;
        this.lock = Threading.lock("peergroup");
        this.peersBlocksDownloadedEventListeners = new CopyOnWriteArrayList<>();
        this.peersChainDownloadStartedEventListeners = new CopyOnWriteArrayList<>();
        this.peerConnectedEventListeners = new CopyOnWriteArrayList<>();
        this.peerDiscoveredEventListeners = new CopyOnWriteArrayList<>();
        this.peerDisconnectedEventListeners = new CopyOnWriteArrayList<>();
        this.peerGetDataEventListeners = new CopyOnWriteArrayList<>();
        this.peersPreMessageReceivedEventListeners = new CopyOnWriteArrayList<>();
        this.peersTransactionBroadastEventListeners = new CopyOnWriteArrayList<>();
        this.pingIntervalMsec = 2000L;
        this.useLocalhostPeerWhenPossible = true;
        this.ipv6Unreachable = false;
        this.peerListener = new PeerListener();
        this.minBroadcastConnections = 0;
        this.walletScriptEventListener = new ScriptsChangeEventListener() {
            @Override
            public void onScriptsChanged(Wallet wallet, List<Script> list, boolean z) {
                PeerGroup.this.recalculateFastCatchupAndFilter(FilterRecalculateMode.SEND_IF_CHANGED);
            }
        };
        this.walletKeyEventListener = new KeyChainEventListener() {
            @Override
            public void onKeysAdded(List<ECKey> list) {
                PeerGroup.this.recalculateFastCatchupAndFilter(FilterRecalculateMode.SEND_IF_CHANGED);
            }
        };
        this.walletCoinsReceivedEventListener = new WalletCoinsReceivedEventListener() {
            @Override
            public void onCoinsReceived(Wallet wallet, Transaction transaction, Coin coin, Coin coin2) {
                for (TransactionOutput transactionOutput : transaction.getOutputs()) {
                    if (transactionOutput.getScriptPubKey().isSentToRawPubKey() && transactionOutput.isMine(wallet)) {
                        if (transaction.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
                            PeerGroup.this.recalculateFastCatchupAndFilter(FilterRecalculateMode.SEND_IF_CHANGED);
                            return;
                        } else {
                            PeerGroup.this.recalculateFastCatchupAndFilter(FilterRecalculateMode.DONT_SEND);
                            return;
                        }
                    }
                }
            }
        };
        this.peerBackoffParams = new ExponentialBackoff.Params(1000L, 1.5f, 600000L);
        this.groupBackoff = new ExponentialBackoff(new ExponentialBackoff.Params(1000L, 1.5f, 10000L));
        this.startupListener = new PeerStartupListener();
        this.vConnectTimeoutMillis = 5000;
        this.vBloomFilteringEnabled = true;
        this.executorStartupLatch = new CountDownLatch(1);
        this.triggerConnectionsJob = new Runnable() {
            private static final long MIN_PEER_DISCOVERY_INTERVAL = 1000;
            private boolean firstRun = true;

            @Override
            public void run() {
                try {
                    go();
                } catch (Throwable th) {
                    PeerGroup.log.error("Exception when trying to build connections", th);
                }
            }

            public void go() {
                PeerGroup peerGroup;
                ReentrantLock reentrantLock;
                PeerAddress peerAddress;
                String str;
                if (PeerGroup.this.vRunning) {
                    long currentTimeMillis = Utils.currentTimeMillis();
                    PeerGroup.this.lock.lock();
                    try {
                        if (!Utils.isAndroidRuntime() && PeerGroup.this.useLocalhostPeerWhenPossible && PeerGroup.this.maybeCheckForLocalhostPeer() && this.firstRun) {
                            PeerGroup.log.info("Localhost peer detected, trying to use it instead of P2P discovery");
                            PeerGroup.this.maxConnections = 0;
                            PeerGroup.this.connectToLocalHost();
                            return;
                        }
                        boolean z = true;
                        boolean z2 = !(!PeerGroup.this.inactives.isEmpty() && ((ExponentialBackoff) PeerGroup.this.backoffMap.get(PeerGroup.this.inactives.peek())).getRetryTime() <= currentTimeMillis);
                        if (z2) {
                            try {
                            } catch (PeerDiscoveryException e) {
                                PeerGroup.log.error("Peer discovery failure", (Throwable) e);
                            }
                        }
                        z = false;
                        PeerGroup.this.lock.lock();
                        if (z2) {
                            if (z) {
                                try {
                                    if (PeerGroup.this.countConnectedAndPendingPeers() >= PeerGroup.this.getMaxConnections()) {
                                        PeerGroup.this.groupBackoff.trackSuccess();
                                    }
                                } finally {
                                    PeerGroup.this.lock.unlock();
                                }
                            }
                            PeerGroup.this.groupBackoff.trackFailure();
                        }
                        if (PeerGroup.this.inactives.isEmpty()) {
                            if (PeerGroup.this.countConnectedAndPendingPeers() < PeerGroup.this.getMaxConnections()) {
                                long max = Math.max(PeerGroup.this.groupBackoff.getRetryTime() - currentTimeMillis, 1000L);
                                PeerGroup.log.info("Peer discovery didn't provide us any more peers, will try again in " + max + "ms.");
                                PeerGroup.this.executor.schedule((Runnable) this, max, TimeUnit.MILLISECONDS);
                            }
                            return;
                        }
                        do {
                            peerAddress = (PeerAddress) PeerGroup.this.inactives.poll();
                            if (!PeerGroup.this.ipv6Unreachable) {
                                break;
                            }
                        } while (peerAddress.getAddr() instanceof Inet6Address);
                        long max2 = Math.max(((ExponentialBackoff) PeerGroup.this.backoffMap.get(peerAddress)).getRetryTime(), PeerGroup.this.groupBackoff.getRetryTime());
                        if (max2 <= currentTimeMillis) {
                            PeerGroup.this.connectTo(peerAddress, false, PeerGroup.this.vConnectTimeoutMillis);
                            PeerGroup.this.lock.unlock();
                            if (PeerGroup.this.countConnectedAndPendingPeers() < PeerGroup.this.getMaxConnections()) {
                                PeerGroup.this.executor.execute(this);
                                return;
                            }
                            return;
                        }
                        long j = max2 - currentTimeMillis;
                        Logger logger = PeerGroup.log;
                        Long valueOf = Long.valueOf(j);
                        if (peerAddress == null) {
                            str = "";
                        } else {
                            str = "to " + peerAddress;
                        }
                        logger.info("Waiting {} msec before next connect attempt {}", valueOf, str);
                        PeerGroup.this.inactives.add(peerAddress);
                        PeerGroup.this.executor.schedule((Runnable) this, j, TimeUnit.MILLISECONDS);
                    } finally {
                        this.firstRun = false;
                    }
                }
            }
        };
        this.localhostCheckState = LocalhostCheckState.NOT_TRIED;
        this.inFlightRecalculations = Maps.newHashMap();
        this.stallPeriodSeconds = 10;
        this.stallMinSpeedBytesSec = 1600;
        Preconditions.checkNotNull(context);
        NetworkParameters params = context.getParams();
        this.params = params;
        this.chain = abstractBlockChain;
        this.fastCatchupTimeSecs = params.getGenesisBlock().getTimeSeconds();
        this.wallets = new CopyOnWriteArrayList<>();
        this.peerFilterProviders = new CopyOnWriteArrayList<>();
        this.torClient = torClient;
        this.executor = createPrivateExecutor();
        this.maxConnections = 0;
        VersionMessage versionMessage = new VersionMessage(this.params, abstractBlockChain != null ? abstractBlockChain.getBestChainHeight() : 0);
        this.versionMessage = versionMessage;
        versionMessage.relayTxesBeforeFilter = true;
        this.downloadTxDependencyDepth = Integer.MAX_VALUE;
        this.inactives = new PriorityQueue<>(1, new Comparator<PeerAddress>() {
            @Override
            public int compare(PeerAddress peerAddress, PeerAddress peerAddress2) {
                Preconditions.checkState(PeerGroup.this.lock.isHeldByCurrentThread());
                int compareTo = ((ExponentialBackoff) PeerGroup.this.backoffMap.get(peerAddress)).compareTo((ExponentialBackoff) PeerGroup.this.backoffMap.get(peerAddress2));
                return compareTo == 0 ? Ints.compare(peerAddress.getPort(), peerAddress2.getPort()) : compareTo;
            }
        });
        this.backoffMap = new HashMap();
        this.peers = new CopyOnWriteArrayList<>();
        this.pendingPeers = new CopyOnWriteArrayList<>();
        this.channels = clientConnectionManager;
        this.peerDiscoverers = new CopyOnWriteArraySet<>();
        this.runningBroadcasts = Collections.synchronizedSet(new HashSet());
        this.bloomFilterMerger = new FilterMerger(1.0E-5d);
        this.vMinRequiredProtocolVersion = this.params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.BLOOM_FILTER);
    }

    protected ListeningScheduledExecutorService createPrivateExecutor() {
        ListeningScheduledExecutorService listeningDecorator = MoreExecutors.listeningDecorator((ScheduledExecutorService) new ScheduledThreadPoolExecutor(1, new ContextPropagatingThreadFactory("PeerGroup Thread")));
        listeningDecorator.execute(new Runnable() {
            @Override
            public void run() {
                Uninterruptibles.awaitUninterruptibly(PeerGroup.this.executorStartupLatch);
            }
        });
        return listeningDecorator;
    }

    public void setPeerDiscoveryTimeoutMillis(long j) {
        this.vPeerDiscoveryTimeoutMillis = j;
    }

    public void setMaxConnections(int i) {
        this.lock.lock();
        try {
            this.maxConnections = i;
            if (isRunning()) {
                this.lock.unlock();
                int connectedClientCount = i - this.channels.getConnectedClientCount();
                if (connectedClientCount > 0) {
                    triggerConnections();
                }
                if (connectedClientCount < 0) {
                    this.channels.closeConnections(-connectedClientCount);
                }
            }
        } finally {
            this.lock.unlock();
        }
    }

    public void setDownloadTxDependencies(int i) {
        this.lock.lock();
        try {
            this.downloadTxDependencyDepth = i;
        } finally {
            this.lock.unlock();
        }
    }

    public void triggerConnections() {
        if (this.executor.isShutdown()) {
            return;
        }
        this.executor.execute(this.triggerConnectionsJob);
    }

    public int getMaxConnections() {
        this.lock.lock();
        try {
            return this.maxConnections;
        } finally {
            this.lock.unlock();
        }
    }

    public List<Message> handleGetData(GetDataMessage getDataMessage) {
        this.lock.lock();
        try {
            LinkedList linkedList = new LinkedList();
            Iterator it = new LinkedList(getDataMessage.getItems()).iterator();
            while (it.hasNext()) {
                InventoryItem inventoryItem = (InventoryItem) it.next();
                Iterator<Wallet> it2 = this.wallets.iterator();
                while (true) {
                    if (!it2.hasNext()) {
                        break;
                    }
                    Transaction transaction = it2.next().getTransaction(inventoryItem.hash);
                    if (transaction != null) {
                        linkedList.add(transaction);
                        it.remove();
                        break;
                    }
                }
            }
            return linkedList;
        } finally {
            this.lock.unlock();
        }
    }

    public void setVersionMessage(VersionMessage versionMessage) {
        this.lock.lock();
        try {
            this.versionMessage = versionMessage;
        } finally {
            this.lock.unlock();
        }
    }

    public VersionMessage getVersionMessage() {
        this.lock.lock();
        try {
            return this.versionMessage;
        } finally {
            this.lock.unlock();
        }
    }

    public void setUserAgent(String str, String str2, @Nullable String str3) {
        AbstractBlockChain abstractBlockChain = this.chain;
        VersionMessage versionMessage = new VersionMessage(this.params, abstractBlockChain == null ? 0 : abstractBlockChain.getBestChainHeight());
        versionMessage.relayTxesBeforeFilter = false;
        updateVersionMessageRelayTxesBeforeFilter(versionMessage);
        versionMessage.appendToSubVer(str, str2, str3);
        setVersionMessage(versionMessage);
    }

    private void updateVersionMessageRelayTxesBeforeFilter(VersionMessage versionMessage) {
        this.lock.lock();
        try {
            boolean z = true;
            if ((this.chain != null && !this.chain.shouldVerifyTransactions()) && this.peerFilterProviders.size() > 0 && this.vBloomFilteringEnabled) {
                z = false;
            }
            versionMessage.relayTxesBeforeFilter = z;
        } finally {
            this.lock.unlock();
        }
    }

    public void setUserAgent(String str, String str2) {
        setUserAgent(str, str2, null);
    }

    @Deprecated
    public void addEventListener(AbstractPeerEventListener abstractPeerEventListener, Executor executor) {
        addBlocksDownloadedEventListener(Threading.USER_THREAD, abstractPeerEventListener);
        addChainDownloadStartedEventListener(Threading.USER_THREAD, abstractPeerEventListener);
        addConnectedEventListener(Threading.USER_THREAD, abstractPeerEventListener);
        addDisconnectedEventListener(Threading.USER_THREAD, abstractPeerEventListener);
        addDiscoveredEventListener(Threading.USER_THREAD, abstractPeerEventListener);
        addGetDataEventListener(Threading.USER_THREAD, abstractPeerEventListener);
        addOnTransactionBroadcastListener(Threading.USER_THREAD, abstractPeerEventListener);
        addPreMessageReceivedEventListener(Threading.USER_THREAD, abstractPeerEventListener);
    }

    @Deprecated
    public void addEventListener(AbstractPeerEventListener abstractPeerEventListener) {
        addBlocksDownloadedEventListener(this.executor, abstractPeerEventListener);
        addChainDownloadStartedEventListener(this.executor, abstractPeerEventListener);
        addConnectedEventListener(this.executor, abstractPeerEventListener);
        addDisconnectedEventListener(this.executor, abstractPeerEventListener);
        addDiscoveredEventListener(this.executor, abstractPeerEventListener);
        addGetDataEventListener(this.executor, abstractPeerEventListener);
        addOnTransactionBroadcastListener(this.executor, abstractPeerEventListener);
        addPreMessageReceivedEventListener(this.executor, abstractPeerEventListener);
    }

    public void addBlocksDownloadedEventListener(BlocksDownloadedEventListener blocksDownloadedEventListener) {
        addBlocksDownloadedEventListener(Threading.USER_THREAD, blocksDownloadedEventListener);
    }

    public void addBlocksDownloadedEventListener(Executor executor, BlocksDownloadedEventListener blocksDownloadedEventListener) {
        this.peersBlocksDownloadedEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(blocksDownloadedEventListener), executor));
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().addBlocksDownloadedEventListener(executor, blocksDownloadedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().addBlocksDownloadedEventListener(executor, blocksDownloadedEventListener);
        }
    }

    public void addChainDownloadStartedEventListener(ChainDownloadStartedEventListener chainDownloadStartedEventListener) {
        addChainDownloadStartedEventListener(Threading.USER_THREAD, chainDownloadStartedEventListener);
    }

    public void addChainDownloadStartedEventListener(Executor executor, ChainDownloadStartedEventListener chainDownloadStartedEventListener) {
        this.peersChainDownloadStartedEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(chainDownloadStartedEventListener), executor));
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().addChainDownloadStartedEventListener(executor, chainDownloadStartedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().addChainDownloadStartedEventListener(executor, chainDownloadStartedEventListener);
        }
    }

    public void addConnectedEventListener(PeerConnectedEventListener peerConnectedEventListener) {
        addConnectedEventListener(Threading.USER_THREAD, peerConnectedEventListener);
    }

    public void addConnectedEventListener(Executor executor, PeerConnectedEventListener peerConnectedEventListener) {
        this.peerConnectedEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(peerConnectedEventListener), executor));
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().addConnectedEventListener(executor, peerConnectedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().addConnectedEventListener(executor, peerConnectedEventListener);
        }
    }

    public void addDisconnectedEventListener(PeerDisconnectedEventListener peerDisconnectedEventListener) {
        addDisconnectedEventListener(Threading.USER_THREAD, peerDisconnectedEventListener);
    }

    public void addDisconnectedEventListener(Executor executor, PeerDisconnectedEventListener peerDisconnectedEventListener) {
        this.peerDisconnectedEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(peerDisconnectedEventListener), executor));
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().addDisconnectedEventListener(executor, peerDisconnectedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().addDisconnectedEventListener(executor, peerDisconnectedEventListener);
        }
    }

    public void addDiscoveredEventListener(PeerDiscoveredEventListener peerDiscoveredEventListener) {
        addDiscoveredEventListener(Threading.USER_THREAD, peerDiscoveredEventListener);
    }

    public void addDiscoveredEventListener(Executor executor, PeerDiscoveredEventListener peerDiscoveredEventListener) {
        this.peerDiscoveredEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(peerDiscoveredEventListener), executor));
    }

    public void addGetDataEventListener(GetDataEventListener getDataEventListener) {
        addGetDataEventListener(Threading.USER_THREAD, getDataEventListener);
    }

    public void addGetDataEventListener(Executor executor, GetDataEventListener getDataEventListener) {
        this.peerGetDataEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(getDataEventListener), executor));
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().addGetDataEventListener(executor, getDataEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().addGetDataEventListener(executor, getDataEventListener);
        }
    }

    public void addOnTransactionBroadcastListener(OnTransactionBroadcastListener onTransactionBroadcastListener) {
        addOnTransactionBroadcastListener(Threading.USER_THREAD, onTransactionBroadcastListener);
    }

    public void addOnTransactionBroadcastListener(Executor executor, OnTransactionBroadcastListener onTransactionBroadcastListener) {
        this.peersTransactionBroadastEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(onTransactionBroadcastListener), executor));
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().addOnTransactionBroadcastListener(executor, onTransactionBroadcastListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().addOnTransactionBroadcastListener(executor, onTransactionBroadcastListener);
        }
    }

    public void addPreMessageReceivedEventListener(PreMessageReceivedEventListener preMessageReceivedEventListener) {
        addPreMessageReceivedEventListener(Threading.USER_THREAD, preMessageReceivedEventListener);
    }

    public void addPreMessageReceivedEventListener(Executor executor, PreMessageReceivedEventListener preMessageReceivedEventListener) {
        this.peersPreMessageReceivedEventListeners.add(new ListenerRegistration<>(Preconditions.checkNotNull(preMessageReceivedEventListener), executor));
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().addPreMessageReceivedEventListener(executor, preMessageReceivedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().addPreMessageReceivedEventListener(executor, preMessageReceivedEventListener);
        }
    }

    @Deprecated
    public void removeEventListener(AbstractPeerEventListener abstractPeerEventListener) {
        removeBlocksDownloadedEventListener(abstractPeerEventListener);
        removeChainDownloadStartedEventListener(abstractPeerEventListener);
        removeConnectedEventListener(abstractPeerEventListener);
        removeDisconnectedEventListener(abstractPeerEventListener);
        removeDiscoveredEventListener(abstractPeerEventListener);
        removeGetDataEventListener(abstractPeerEventListener);
        removeOnTransactionBroadcastListener(abstractPeerEventListener);
        removePreMessageReceivedEventListener(abstractPeerEventListener);
    }

    public boolean removeBlocksDownloadedEventListener(BlocksDownloadedEventListener blocksDownloadedEventListener) {
        boolean removeFromList = ListenerRegistration.removeFromList(blocksDownloadedEventListener, this.peersBlocksDownloadedEventListeners);
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().removeBlocksDownloadedEventListener(blocksDownloadedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().removeBlocksDownloadedEventListener(blocksDownloadedEventListener);
        }
        return removeFromList;
    }

    public boolean removeChainDownloadStartedEventListener(ChainDownloadStartedEventListener chainDownloadStartedEventListener) {
        boolean removeFromList = ListenerRegistration.removeFromList(chainDownloadStartedEventListener, this.peersChainDownloadStartedEventListeners);
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().removeChainDownloadStartedEventListener(chainDownloadStartedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().removeChainDownloadStartedEventListener(chainDownloadStartedEventListener);
        }
        return removeFromList;
    }

    public boolean removeConnectedEventListener(PeerConnectedEventListener peerConnectedEventListener) {
        boolean removeFromList = ListenerRegistration.removeFromList(peerConnectedEventListener, this.peerConnectedEventListeners);
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().removeConnectedEventListener(peerConnectedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().removeConnectedEventListener(peerConnectedEventListener);
        }
        return removeFromList;
    }

    public boolean removeDisconnectedEventListener(PeerDisconnectedEventListener peerDisconnectedEventListener) {
        boolean removeFromList = ListenerRegistration.removeFromList(peerDisconnectedEventListener, this.peerDisconnectedEventListeners);
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().removeDisconnectedEventListener(peerDisconnectedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().removeDisconnectedEventListener(peerDisconnectedEventListener);
        }
        return removeFromList;
    }

    public boolean removeDiscoveredEventListener(PeerDiscoveredEventListener peerDiscoveredEventListener) {
        return ListenerRegistration.removeFromList(peerDiscoveredEventListener, this.peerDiscoveredEventListeners);
    }

    public boolean removeGetDataEventListener(GetDataEventListener getDataEventListener) {
        boolean removeFromList = ListenerRegistration.removeFromList(getDataEventListener, this.peerGetDataEventListeners);
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().removeGetDataEventListener(getDataEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().removeGetDataEventListener(getDataEventListener);
        }
        return removeFromList;
    }

    public boolean removeOnTransactionBroadcastListener(OnTransactionBroadcastListener onTransactionBroadcastListener) {
        boolean removeFromList = ListenerRegistration.removeFromList(onTransactionBroadcastListener, this.peersTransactionBroadastEventListeners);
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().removeOnTransactionBroadcastListener(onTransactionBroadcastListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().removeOnTransactionBroadcastListener(onTransactionBroadcastListener);
        }
        return removeFromList;
    }

    public boolean removePreMessageReceivedEventListener(PreMessageReceivedEventListener preMessageReceivedEventListener) {
        boolean removeFromList = ListenerRegistration.removeFromList(preMessageReceivedEventListener, this.peersPreMessageReceivedEventListeners);
        Iterator<Peer> it = getConnectedPeers().iterator();
        while (it.hasNext()) {
            it.next().removePreMessageReceivedEventListener(preMessageReceivedEventListener);
        }
        Iterator<Peer> it2 = getPendingPeers().iterator();
        while (it2.hasNext()) {
            it2.next().removePreMessageReceivedEventListener(preMessageReceivedEventListener);
        }
        return removeFromList;
    }

    public List<Peer> getConnectedPeers() {
        this.lock.lock();
        try {
            return new ArrayList(this.peers);
        } finally {
            this.lock.unlock();
        }
    }

    public List<Peer> getPendingPeers() {
        this.lock.lock();
        try {
            return new ArrayList(this.pendingPeers);
        } finally {
            this.lock.unlock();
        }
    }

    public void addAddress(PeerAddress peerAddress) {
        this.lock.lock();
        try {
            addInactive(peerAddress);
            int maxConnections = getMaxConnections() + 1;
            this.lock.unlock();
            setMaxConnections(maxConnections);
        } catch (Throwable th) {
            this.lock.unlock();
            throw th;
        }
    }

    private void addInactive(PeerAddress peerAddress) {
        this.lock.lock();
        try {
            if (this.backoffMap.containsKey(peerAddress)) {
                return;
            }
            this.backoffMap.put(peerAddress, new ExponentialBackoff(this.peerBackoffParams));
            this.inactives.offer(peerAddress);
        } finally {
            this.lock.unlock();
        }
    }

    public void setRequiredServices(long j) {
        this.lock.lock();
        try {
            this.requiredServices = j;
            this.peerDiscoverers.clear();
            addPeerDiscovery(MultiplexingDiscovery.forServices(this.params, j));
        } finally {
            this.lock.unlock();
        }
    }

    public void addAddress(InetAddress inetAddress) {
        NetworkParameters networkParameters = this.params;
        addAddress(new PeerAddress(networkParameters, inetAddress, networkParameters.getPort()));
    }

    public void addPeerDiscovery(PeerDiscovery peerDiscovery) {
        this.lock.lock();
        try {
            if (getMaxConnections() == 0) {
                setMaxConnections(12);
            }
            this.peerDiscoverers.add(peerDiscovery);
        } finally {
            this.lock.unlock();
        }
    }

    protected int discoverPeers() throws PeerDiscoveryException {
        Preconditions.checkState(!this.lock.isHeldByCurrentThread());
        int i = this.vMaxPeersToDiscoverCount;
        long j = this.vPeerDiscoveryTimeoutMillis;
        Stopwatch createStarted = Stopwatch.createStarted();
        LinkedList newLinkedList = Lists.newLinkedList();
        Iterator<PeerDiscovery> it = this.peerDiscoverers.iterator();
        while (it.hasNext()) {
            for (InetSocketAddress inetSocketAddress : it.next().getPeers(this.requiredServices, j, TimeUnit.MILLISECONDS)) {
                newLinkedList.add(new PeerAddress(this.params, inetSocketAddress));
            }
            if (newLinkedList.size() >= i) {
                break;
            }
        }
        if (!newLinkedList.isEmpty()) {
            Iterator it2 = newLinkedList.iterator();
            while (it2.hasNext()) {
                addInactive((PeerAddress) it2.next());
            }
            final ImmutableSet copyOf = ImmutableSet.copyOf((Collection) newLinkedList);
            Iterator<ListenerRegistration<PeerDiscoveredEventListener>> it3 = this.peerDiscoveredEventListeners.iterator();
            while (it3.hasNext()) {
                final ListenerRegistration<PeerDiscoveredEventListener> next = it3.next();
                next.executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        ((PeerDiscoveredEventListener) next.listener).onPeersDiscovered(copyOf);
                    }
                });
            }
        }
        createStarted.stop();
        log.info("Peer discovery took {} and returned {} items", createStarted, Integer.valueOf(newLinkedList.size()));
        return newLinkedList.size();
    }

    void waitForJobQueue() {
        Futures.getUnchecked(this.executor.submit(Runnables.doNothing()));
    }

    public int countConnectedAndPendingPeers() {
        this.lock.lock();
        try {
            return this.peers.size() + this.pendingPeers.size();
        } finally {
            this.lock.unlock();
        }
    }

    public boolean maybeCheckForLocalhostPeer() {
        Socket socket;
        Throwable th;
        Preconditions.checkState(this.lock.isHeldByCurrentThread());
        if (this.localhostCheckState != LocalhostCheckState.NOT_TRIED) {
            return false;
        }
        Socket socket2 = null;
        try {
            try {
                socket = new Socket();
            } catch (Throwable th2) {
                socket = null;
                th = th2;
            }
        } catch (IOException unused) {
        }
        try {
            socket.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"), this.params.getPort()), this.vConnectTimeoutMillis);
            this.localhostCheckState = LocalhostCheckState.FOUND;
            try {
                socket.close();
            } catch (IOException unused2) {
            }
            return true;
        } catch (IOException unused3) {
            socket2 = socket;
            log.info("Localhost peer not detected.");
            this.localhostCheckState = LocalhostCheckState.NOT_THERE;
            if (socket2 == null) {
                return false;
            }
            try {
                socket2.close();
                return false;
            } catch (IOException unused4) {
                return false;
            }
        } catch (Throwable th3) {
            th = th3;
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException unused5) {
                }
            }
            throw th;
        }
    }

    public ListenableFuture startAsync() {
        if (this.chain == null) {
            log.warn("Starting up with no attached block chain. Did you forget to pass one to the constructor?");
        }
        Preconditions.checkState(!this.vUsedUp, "Cannot start a peer group twice");
        this.vRunning = true;
        this.vUsedUp = true;
        this.executorStartupLatch.countDown();
        return this.executor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    PeerGroup.log.info("Starting ...");
                    if (PeerGroup.this.torClient != null) {
                        PeerGroup.log.info("Starting Tor/Orchid ...");
                        PeerGroup.this.torClient.start();
                        try {
                            PeerGroup.this.torClient.waitUntilReady(JConstants.MIN);
                            PeerGroup.log.info("Tor ready");
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                    PeerGroup.this.channels.startAsync();
                    PeerGroup.this.channels.awaitRunning();
                    PeerGroup.this.triggerConnections();
                    PeerGroup.this.setupPinging();
                } catch (Throwable th) {
                    PeerGroup.log.error("Exception when starting up", th);
                }
            }
        });
    }

    public void start() {
        Futures.getUnchecked(startAsync());
    }

    @Deprecated
    public void awaitRunning() {
        waitForJobQueue();
    }

    public ListenableFuture stopAsync() {
        Preconditions.checkState(this.vRunning);
        this.vRunning = false;
        ListenableFuture<?> submit = this.executor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    PeerGroup.log.info("Stopping ...");
                    PeerGroup.this.channels.stopAsync();
                    PeerGroup.this.channels.awaitTerminated();
                    Iterator it = PeerGroup.this.peerDiscoverers.iterator();
                    while (it.hasNext()) {
                        ((PeerDiscovery) it.next()).shutdown();
                    }
                    if (PeerGroup.this.torClient != null) {
                        PeerGroup.this.torClient.stop();
                    }
                    PeerGroup.this.vRunning = false;
                    PeerGroup.log.info("Stopped.");
                } catch (Throwable th) {
                    PeerGroup.log.error("Exception when shutting down", th);
                }
            }
        });
        this.executor.shutdown();
        return submit;
    }

    public void stop() {
        try {
            stopAsync();
            log.info("Awaiting PeerGroup shutdown ...");
            this.executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Deprecated
    public void awaitTerminated() {
        try {
            this.executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public void addWallet(Wallet wallet) {
        this.lock.lock();
        try {
            Preconditions.checkNotNull(wallet);
            Preconditions.checkState(!this.wallets.contains(wallet));
            this.wallets.add(wallet);
            wallet.setTransactionBroadcaster(this);
            wallet.addCoinsReceivedEventListener(Threading.SAME_THREAD, this.walletCoinsReceivedEventListener);
            wallet.addKeyChainEventListener(Threading.SAME_THREAD, this.walletKeyEventListener);
            wallet.addScriptChangeEventListener(Threading.SAME_THREAD, this.walletScriptEventListener);
            addPeerFilterProvider(wallet);
            Iterator<Peer> it = this.peers.iterator();
            while (it.hasNext()) {
                it.next().addWallet(wallet);
            }
        } finally {
            this.lock.unlock();
        }
    }

    public ListenableFuture<BloomFilter> addPeerFilterProvider(PeerFilterProvider peerFilterProvider) {
        this.lock.lock();
        try {
            Preconditions.checkNotNull(peerFilterProvider);
            Preconditions.checkState(!this.peerFilterProviders.contains(peerFilterProvider));
            this.peerFilterProviders.add(0, peerFilterProvider);
            ListenableFuture<BloomFilter> recalculateFastCatchupAndFilter = recalculateFastCatchupAndFilter(FilterRecalculateMode.SEND_IF_CHANGED);
            updateVersionMessageRelayTxesBeforeFilter(getVersionMessage());
            return recalculateFastCatchupAndFilter;
        } finally {
            this.lock.unlock();
        }
    }

    public void removePeerFilterProvider(PeerFilterProvider peerFilterProvider) {
        this.lock.lock();
        try {
            Preconditions.checkNotNull(peerFilterProvider);
            Preconditions.checkArgument(this.peerFilterProviders.remove(peerFilterProvider));
        } finally {
            this.lock.unlock();
        }
    }

    public void removeWallet(Wallet wallet) {
        this.wallets.remove(Preconditions.checkNotNull(wallet));
        this.peerFilterProviders.remove(wallet);
        wallet.removeCoinsReceivedEventListener(this.walletCoinsReceivedEventListener);
        wallet.removeKeyChainEventListener(this.walletKeyEventListener);
        wallet.removeScriptChangeEventListener(this.walletScriptEventListener);
        wallet.setTransactionBroadcaster(null);
        Iterator<Peer> it = this.peers.iterator();
        while (it.hasNext()) {
            it.next().removeWallet(wallet);
        }
    }

    public ListenableFuture<BloomFilter> recalculateFastCatchupAndFilter(final FilterRecalculateMode filterRecalculateMode) {
        final SettableFuture<BloomFilter> create = SettableFuture.create();
        synchronized (this.inFlightRecalculations) {
            if (this.inFlightRecalculations.get(filterRecalculateMode) != null) {
                return this.inFlightRecalculations.get(filterRecalculateMode);
            }
            this.inFlightRecalculations.put(filterRecalculateMode, create);
            try {
                this.executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            go();
                        } catch (Throwable th) {
                            PeerGroup.log.error("Exception when trying to recalculate Bloom filter", th);
                        }
                    }

                    public void go() {
                        boolean z;
                        Preconditions.checkState(!PeerGroup.this.lock.isHeldByCurrentThread());
                        if ((PeerGroup.this.chain == null || !PeerGroup.this.chain.shouldVerifyTransactions()) && PeerGroup.this.vBloomFilteringEnabled) {
                            FilterMerger.Result calculate = PeerGroup.this.bloomFilterMerger.calculate(ImmutableList.copyOf((Collection) PeerGroup.this.peerFilterProviders));
                            int i = AnonymousClass17.$SwitchMap$org$bitcoinj$core$PeerGroup$FilterRecalculateMode[filterRecalculateMode.ordinal()];
                            if (i == 1) {
                                z = calculate.changed;
                            } else if (i == 2) {
                                z = false;
                            } else {
                                if (i != 3) {
                                    throw new UnsupportedOperationException();
                                }
                                z = true;
                            }
                            if (z) {
                                Iterator it = PeerGroup.this.peers.iterator();
                                while (it.hasNext()) {
                                    ((Peer) it.next()).setBloomFilter(calculate.filter, filterRecalculateMode != FilterRecalculateMode.FORCE_SEND_FOR_REFRESH);
                                }
                                if (PeerGroup.this.chain != null) {
                                    PeerGroup.this.chain.resetFalsePositiveEstimate();
                                }
                            }
                            PeerGroup.this.setFastCatchupTimeSecs(calculate.earliestKeyTimeSecs);
                            synchronized (PeerGroup.this.inFlightRecalculations) {
                                PeerGroup.this.inFlightRecalculations.put(filterRecalculateMode, null);
                            }
                            create.set(calculate.filter);
                        }
                    }
                });
            } catch (RejectedExecutionException unused) {
            }
            return create;
        }
    }

    public static class AnonymousClass17 {
        static final int[] $SwitchMap$org$bitcoinj$core$PeerGroup$FilterRecalculateMode;

        static {
            int[] iArr = new int[FilterRecalculateMode.values().length];
            $SwitchMap$org$bitcoinj$core$PeerGroup$FilterRecalculateMode = iArr;
            try {
                iArr[FilterRecalculateMode.SEND_IF_CHANGED.ordinal()] = 1;
            } catch (NoSuchFieldError unused) {
            }
            try {
                $SwitchMap$org$bitcoinj$core$PeerGroup$FilterRecalculateMode[FilterRecalculateMode.DONT_SEND.ordinal()] = 2;
            } catch (NoSuchFieldError unused2) {
            }
            try {
                $SwitchMap$org$bitcoinj$core$PeerGroup$FilterRecalculateMode[FilterRecalculateMode.FORCE_SEND_FOR_REFRESH.ordinal()] = 3;
            } catch (NoSuchFieldError unused3) {
            }
        }
    }

    public void setBloomFilterFalsePositiveRate(double d) {
        this.lock.lock();
        try {
            this.bloomFilterMerger.setBloomFilterFPRate(d);
            recalculateFastCatchupAndFilter(FilterRecalculateMode.SEND_IF_CHANGED);
        } finally {
            this.lock.unlock();
        }
    }

    public int numConnectedPeers() {
        return this.peers.size();
    }

    @Nullable
    public Peer connectTo(InetSocketAddress inetSocketAddress) {
        this.lock.lock();
        try {
            PeerAddress peerAddress = new PeerAddress(this.params, inetSocketAddress);
            this.backoffMap.put(peerAddress, new ExponentialBackoff(this.peerBackoffParams));
            return connectTo(peerAddress, true, this.vConnectTimeoutMillis);
        } finally {
            this.lock.unlock();
        }
    }

    @Nullable
    public Peer connectToLocalHost() {
        this.lock.lock();
        try {
            PeerAddress localhost = PeerAddress.localhost(this.params);
            this.backoffMap.put(localhost, new ExponentialBackoff(this.peerBackoffParams));
            return connectTo(localhost, true, this.vConnectTimeoutMillis);
        } finally {
            this.lock.unlock();
        }
    }

    @GuardedBy("lock")
    @Nullable
    protected Peer connectTo(PeerAddress peerAddress, boolean z, int i) {
        Preconditions.checkState(this.lock.isHeldByCurrentThread());
        VersionMessage duplicate = getVersionMessage().duplicate();
        duplicate.bestHeight = this.chain == null ? 0L : r1.getBestChainHeight();
        duplicate.time = Utils.currentTimeSeconds();
        Peer createPeer = createPeer(peerAddress, duplicate);
        createPeer.addConnectedEventListener(Threading.SAME_THREAD, this.startupListener);
        createPeer.addDisconnectedEventListener(Threading.SAME_THREAD, this.startupListener);
        createPeer.setMinProtocolVersion(this.vMinRequiredProtocolVersion);
        this.pendingPeers.add(createPeer);
        try {
            log.info("Attempting connection to {}     ({} connected, {} pending, {} max)", peerAddress, Integer.valueOf(this.peers.size()), Integer.valueOf(this.pendingPeers.size()), Integer.valueOf(this.maxConnections));
            ListenableFuture<SocketAddress> openConnection = this.channels.openConnection(peerAddress.toSocketAddress(), createPeer);
            if (openConnection.isDone()) {
                Uninterruptibles.getUninterruptibly(openConnection);
            }
            createPeer.setSocketTimeout(i);
            if (z) {
                this.maxConnections++;
            }
            return createPeer;
        } catch (ExecutionException e) {
            Throwable rootCause = Throwables.getRootCause(e);
            log.warn("Failed to connect to " + peerAddress + ": " + rootCause.getMessage());
            handlePeerDeath(createPeer, rootCause);
            return null;
        }
    }

    @GuardedBy("lock")
    protected Peer createPeer(PeerAddress peerAddress, VersionMessage versionMessage) {
        return new Peer(this.params, versionMessage, peerAddress, this.chain, this.downloadTxDependencyDepth);
    }

    public void setConnectTimeoutMillis(int i) {
        this.vConnectTimeoutMillis = i;
    }

    public void startBlockChainDownload(PeerDataEventListener peerDataEventListener) {
        this.lock.lock();
        try {
            if (this.downloadPeer != null) {
                if (this.downloadListener != null) {
                    removeDataEventListenerFromPeer(this.downloadPeer, this.downloadListener);
                }
                if (peerDataEventListener != null) {
                    addDataEventListenerToPeer(Threading.USER_THREAD, this.downloadPeer, peerDataEventListener);
                }
            }
            this.downloadListener = peerDataEventListener;
            if (!this.peers.isEmpty()) {
                startBlockChainDownloadFromPeer(this.peers.iterator().next());
            }
        } finally {
            this.lock.unlock();
        }
    }

    private static void addDataEventListenerToPeer(Executor executor, Peer peer, PeerDataEventListener peerDataEventListener) {
        peer.addBlocksDownloadedEventListener(executor, peerDataEventListener);
        peer.addChainDownloadStartedEventListener(executor, peerDataEventListener);
        peer.addGetDataEventListener(executor, peerDataEventListener);
        peer.addPreMessageReceivedEventListener(executor, peerDataEventListener);
    }

    private static void removeDataEventListenerFromPeer(Peer peer, PeerDataEventListener peerDataEventListener) {
        peer.removeBlocksDownloadedEventListener(peerDataEventListener);
        peer.removeChainDownloadStartedEventListener(peerDataEventListener);
        peer.removeGetDataEventListener(peerDataEventListener);
        peer.removePreMessageReceivedEventListener(peerDataEventListener);
    }

    public void downloadBlockChain() {
        DownloadProgressTracker downloadProgressTracker = new DownloadProgressTracker();
        startBlockChainDownload(downloadProgressTracker);
        try {
            downloadProgressTracker.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    protected void handleNewPeer(final Peer peer) {
        this.lock.lock();
        try {
            this.groupBackoff.trackSuccess();
            this.backoffMap.get(peer.getAddress()).trackSuccess();
            this.pendingPeers.remove(peer);
            this.peers.add(peer);
            final int size = this.peers.size();
            boolean z = false;
            log.info("{}: New peer      ({} connected, {} pending, {} max)", peer, Integer.valueOf(size), Integer.valueOf(this.pendingPeers.size()), Integer.valueOf(this.maxConnections));
            if (this.bloomFilterMerger.getLastFilter() != null) {
                peer.setBloomFilter(this.bloomFilterMerger.getLastFilter());
            }
            peer.setDownloadData(false);
            Iterator<Wallet> it = this.wallets.iterator();
            while (it.hasNext()) {
                peer.addWallet(it.next());
            }
            if (this.downloadPeer == null) {
                setDownloadPeer(selectDownloadPeer(this.peers));
                if (this.downloadListener != null && this.chain != null) {
                    z = true;
                }
                if (z) {
                    startBlockChainDownloadFromPeer(this.downloadPeer);
                }
            }
            peer.addBlocksDownloadedEventListener(Threading.SAME_THREAD, this.peerListener);
            peer.addGetDataEventListener(Threading.SAME_THREAD, this.peerListener);
            Iterator<ListenerRegistration<BlocksDownloadedEventListener>> it2 = this.peersBlocksDownloadedEventListeners.iterator();
            while (it2.hasNext()) {
                ListenerRegistration<BlocksDownloadedEventListener> next = it2.next();
                peer.addBlocksDownloadedEventListener(next.executor, next.listener);
            }
            Iterator<ListenerRegistration<ChainDownloadStartedEventListener>> it3 = this.peersChainDownloadStartedEventListeners.iterator();
            while (it3.hasNext()) {
                ListenerRegistration<ChainDownloadStartedEventListener> next2 = it3.next();
                peer.addChainDownloadStartedEventListener(next2.executor, next2.listener);
            }
            Iterator<ListenerRegistration<PeerConnectedEventListener>> it4 = this.peerConnectedEventListeners.iterator();
            while (it4.hasNext()) {
                ListenerRegistration<PeerConnectedEventListener> next3 = it4.next();
                peer.addConnectedEventListener(next3.executor, next3.listener);
            }
            Iterator<ListenerRegistration<GetDataEventListener>> it5 = this.peerGetDataEventListeners.iterator();
            while (it5.hasNext()) {
                ListenerRegistration<GetDataEventListener> next4 = it5.next();
                peer.addGetDataEventListener(next4.executor, next4.listener);
            }
            Iterator<ListenerRegistration<OnTransactionBroadcastListener>> it6 = this.peersTransactionBroadastEventListeners.iterator();
            while (it6.hasNext()) {
                ListenerRegistration<OnTransactionBroadcastListener> next5 = it6.next();
                peer.addOnTransactionBroadcastListener(next5.executor, next5.listener);
            }
            Iterator<ListenerRegistration<PreMessageReceivedEventListener>> it7 = this.peersPreMessageReceivedEventListeners.iterator();
            while (it7.hasNext()) {
                ListenerRegistration<PreMessageReceivedEventListener> next6 = it7.next();
                peer.addPreMessageReceivedEventListener(next6.executor, next6.listener);
            }
            this.lock.unlock();
            Iterator<ListenerRegistration<PeerConnectedEventListener>> it8 = this.peerConnectedEventListeners.iterator();
            while (it8.hasNext()) {
                final ListenerRegistration<PeerConnectedEventListener> next7 = it8.next();
                next7.executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        ((PeerConnectedEventListener) next7.listener).onPeerConnected(peer, size);
                    }
                });
            }
        } catch (Throwable th) {
            this.lock.unlock();
            throw th;
        }
    }

    public void setupPinging() {
        if (getPingIntervalMsec() <= 0) {
            return;
        }
        this.vPingTask = this.executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    if (PeerGroup.this.getPingIntervalMsec() <= 0) {
                        ListenableScheduledFuture listenableScheduledFuture = PeerGroup.this.vPingTask;
                        if (listenableScheduledFuture != null) {
                            listenableScheduledFuture.cancel(false);
                            PeerGroup.this.vPingTask = null;
                            return;
                        }
                        return;
                    }
                    for (Peer peer : PeerGroup.this.getConnectedPeers()) {
                        if (peer.getPeerVersionMessage().clientVersion >= PeerGroup.this.params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.PONG)) {
                            peer.ping();
                        }
                    }
                } catch (Throwable th) {
                    PeerGroup.log.error("Exception in ping loop", th);
                }
            }
        }, getPingIntervalMsec(), getPingIntervalMsec(), TimeUnit.MILLISECONDS);
    }

    private void setDownloadPeer(@Nullable Peer peer) {
        this.lock.lock();
        try {
            if (this.downloadPeer == peer) {
                return;
            }
            if (this.downloadPeer != null) {
                log.info("Unsetting download peer: {}", this.downloadPeer);
                if (this.downloadListener != null) {
                    removeDataEventListenerFromPeer(this.downloadPeer, this.downloadListener);
                }
                this.downloadPeer.setDownloadData(false);
            }
            this.downloadPeer = peer;
            if (peer != null) {
                log.info("Setting download peer: {}", peer);
                if (this.downloadListener != null) {
                    addDataEventListenerToPeer(Threading.SAME_THREAD, peer, this.downloadListener);
                }
                this.downloadPeer.setDownloadData(true);
                if (this.chain != null) {
                    this.downloadPeer.setDownloadParameters(this.fastCatchupTimeSecs, this.bloomFilterMerger.getLastFilter() != null);
                }
            }
        } finally {
            this.lock.unlock();
        }
    }

    @Nullable
    @Deprecated
    public TxConfidenceTable getMemoryPool() {
        return Context.get().getConfidenceTable();
    }

    public void setFastCatchupTimeSecs(long j) {
        boolean z;
        this.lock.lock();
        try {
            if (this.chain != null && this.chain.shouldVerifyTransactions()) {
                z = false;
                Preconditions.checkState(z, "Fast catchup is incompatible with fully verifying");
                this.fastCatchupTimeSecs = j;
                if (this.downloadPeer != null) {
                    this.downloadPeer.setDownloadParameters(j, this.bloomFilterMerger.getLastFilter() != null);
                }
            }
            z = true;
            Preconditions.checkState(z, "Fast catchup is incompatible with fully verifying");
            this.fastCatchupTimeSecs = j;
            if (this.downloadPeer != null) {
            }
        } finally {
            this.lock.unlock();
        }
    }

    public long getFastCatchupTimeSecs() {
        this.lock.lock();
        try {
            return this.fastCatchupTimeSecs;
        } finally {
            this.lock.unlock();
        }
    }

    protected void handlePeerDeath(final Peer peer, @Nullable Throwable th) {
        if (isRunning()) {
            this.lock.lock();
            try {
                this.pendingPeers.remove(peer);
                this.peers.remove(peer);
                PeerAddress address = peer.getAddress();
                log.info("{}: Peer died      ({} connected, {} pending, {} max)", address, Integer.valueOf(this.peers.size()), Integer.valueOf(this.pendingPeers.size()), Integer.valueOf(this.maxConnections));
                if (peer == this.downloadPeer) {
                    log.info("Download peer died. Picking a new one.");
                    setDownloadPeer(null);
                    Peer selectDownloadPeer = selectDownloadPeer(this.peers);
                    if (selectDownloadPeer != null) {
                        setDownloadPeer(selectDownloadPeer);
                        if (this.downloadListener != null) {
                            startBlockChainDownloadFromPeer(selectDownloadPeer);
                        }
                    }
                }
                int size = this.peers.size() + this.pendingPeers.size();
                final int size2 = this.peers.size();
                this.groupBackoff.trackFailure();
                if (th instanceof NoRouteToHostException) {
                    if ((address.getAddr() instanceof Inet6Address) && !this.ipv6Unreachable) {
                        this.ipv6Unreachable = true;
                        log.warn("IPv6 peer connect failed due to routing failure, ignoring IPv6 addresses from now on");
                    }
                } else {
                    this.backoffMap.get(address).trackFailure();
                    this.inactives.offer(address);
                }
                if (size < getMaxConnections()) {
                    triggerConnections();
                }
                this.lock.unlock();
                peer.removeBlocksDownloadedEventListener(this.peerListener);
                peer.removeGetDataEventListener(this.peerListener);
                Iterator<Wallet> it = this.wallets.iterator();
                while (it.hasNext()) {
                    peer.removeWallet(it.next());
                }
                Iterator<ListenerRegistration<BlocksDownloadedEventListener>> it2 = this.peersBlocksDownloadedEventListeners.iterator();
                while (it2.hasNext()) {
                    peer.removeBlocksDownloadedEventListener(it2.next().listener);
                }
                Iterator<ListenerRegistration<ChainDownloadStartedEventListener>> it3 = this.peersChainDownloadStartedEventListeners.iterator();
                while (it3.hasNext()) {
                    peer.removeChainDownloadStartedEventListener(it3.next().listener);
                }
                Iterator<ListenerRegistration<GetDataEventListener>> it4 = this.peerGetDataEventListeners.iterator();
                while (it4.hasNext()) {
                    peer.removeGetDataEventListener(it4.next().listener);
                }
                Iterator<ListenerRegistration<PreMessageReceivedEventListener>> it5 = this.peersPreMessageReceivedEventListeners.iterator();
                while (it5.hasNext()) {
                    peer.removePreMessageReceivedEventListener(it5.next().listener);
                }
                Iterator<ListenerRegistration<OnTransactionBroadcastListener>> it6 = this.peersTransactionBroadastEventListeners.iterator();
                while (it6.hasNext()) {
                    peer.removeOnTransactionBroadcastListener(it6.next().listener);
                }
                Iterator<ListenerRegistration<PeerDisconnectedEventListener>> it7 = this.peerDisconnectedEventListeners.iterator();
                while (it7.hasNext()) {
                    final ListenerRegistration<PeerDisconnectedEventListener> next = it7.next();
                    next.executor.execute(new Runnable() {
                        @Override
                        public void run() {
                            ((PeerDisconnectedEventListener) next.listener).onPeerDisconnected(peer, size2);
                        }
                    });
                    peer.removeDisconnectedEventListener(next.listener);
                }
            } catch (Throwable th2) {
                this.lock.unlock();
                throw th2;
            }
        }
    }

    public void setStallThreshold(int i, int i2) {
        this.lock.lock();
        try {
            this.stallPeriodSeconds = i;
            this.stallMinSpeedBytesSec = i2;
        } finally {
            this.lock.unlock();
        }
    }

    public class ChainDownloadSpeedCalculator implements BlocksDownloadedEventListener, Runnable {
        private int blocksInLastSecond;
        private long bytesInLastSecond;
        private int cursor;
        private int maxStalls;
        private int origTxnsInLastSecond;
        private long[] samples;
        private boolean syncDone;
        private int txnsInLastSecond;
        private int warmupSeconds;

        private ChainDownloadSpeedCalculator() {
            this.maxStalls = 3;
            this.warmupSeconds = -1;
        }

        @Override
        public synchronized void onBlocksDownloaded(Peer peer, Block block, @Nullable FilteredBlock filteredBlock, int i) {
            this.blocksInLastSecond++;
            this.bytesInLastSecond += 80;
            List<Transaction> transactions = block.getTransactions();
            this.txnsInLastSecond += (transactions != null ? countAndMeasureSize(transactions) : 0) + (filteredBlock != null ? countAndMeasureSize(filteredBlock.getAssociatedTransactions().values()) : 0);
            if (filteredBlock != null) {
                this.origTxnsInLastSecond += filteredBlock.getTransactionCount();
            }
        }

        private int countAndMeasureSize(Collection<Transaction> collection) {
            Iterator<Transaction> it = collection.iterator();
            while (it.hasNext()) {
                this.bytesInLastSecond += it.next().getMessageSize();
            }
            return collection.size();
        }

        @Override
        public void run() {
            try {
                calculate();
            } catch (Throwable th) {
                PeerGroup.log.error("Error in speed calculator", th);
            }
        }

        private void calculate() {
            PeerGroup.this.lock.lock();
            try {
                int i = PeerGroup.this.stallMinSpeedBytesSec;
                int i2 = PeerGroup.this.stallPeriodSeconds;
                synchronized (this) {
                    if (this.samples == null || this.samples.length != i2) {
                        long[] jArr = new long[i2];
                        this.samples = jArr;
                        Arrays.fill(jArr, i * 2);
                        this.warmupSeconds = 15;
                    }
                    if (!(PeerGroup.this.chain != null && PeerGroup.this.chain.getBestChainHeight() < PeerGroup.this.getMostCommonChainHeight())) {
                        this.syncDone = true;
                    }
                    if (!this.syncDone) {
                        if (this.warmupSeconds < 0) {
                            long[] jArr2 = this.samples;
                            int i3 = this.cursor;
                            int i4 = i3 + 1;
                            this.cursor = i4;
                            jArr2[i3] = this.bytesInLastSecond;
                            if (i4 == this.samples.length) {
                                this.cursor = 0;
                            }
                            long j = 0;
                            for (long j2 : this.samples) {
                                j += j2;
                            }
                            long length = j / this.samples.length;
                            double d = length / 1024.0d;
                            double d2 = i / 1024.0d;
                            PeerGroup.log.info(String.format(Locale.US, "%d blocks/sec, %d tx/sec, %d pre-filtered tx/sec, avg/last %.2f/%.2f kilobytes per sec (stall threshold <%.2f KB/sec for %d seconds)", Integer.valueOf(this.blocksInLastSecond), Integer.valueOf(this.txnsInLastSecond), Integer.valueOf(this.origTxnsInLastSecond), Double.valueOf(d), Double.valueOf(this.bytesInLastSecond / 1024.0d), Double.valueOf(d2), Integer.valueOf(this.samples.length)));
                            if (length < i && this.maxStalls > 0) {
                                int i5 = this.maxStalls - 1;
                                this.maxStalls = i5;
                                if (i5 == 0) {
                                    PeerGroup.log.warn("This network seems to be slower than the requested stall threshold - won't do stall disconnects any more.");
                                } else {
                                    Peer downloadPeer = PeerGroup.this.getDownloadPeer();
                                    PeerGroup.log.warn(String.format(Locale.US, "Chain download stalled: received %.2f KB/sec for %d seconds, require average of %.2f KB/sec, disconnecting %s", Double.valueOf(d), Integer.valueOf(this.samples.length), Double.valueOf(d2), downloadPeer));
                                    downloadPeer.close();
                                    this.samples = null;
                                    this.warmupSeconds = i2;
                                }
                            }
                        } else {
                            this.warmupSeconds--;
                            if (this.bytesInLastSecond > 0) {
                                PeerGroup.log.info(String.format(Locale.US, "%d blocks/sec, %d tx/sec, %d pre-filtered tx/sec, last %.2f kilobytes per sec", Integer.valueOf(this.blocksInLastSecond), Integer.valueOf(this.txnsInLastSecond), Integer.valueOf(this.origTxnsInLastSecond), Double.valueOf(this.bytesInLastSecond / 1024.0d)));
                            }
                        }
                    }
                    this.blocksInLastSecond = 0;
                    this.txnsInLastSecond = 0;
                    this.origTxnsInLastSecond = 0;
                    this.bytesInLastSecond = 0L;
                }
            } finally {
                PeerGroup.this.lock.unlock();
            }
        }
    }

    private void startBlockChainDownloadFromPeer(Peer peer) {
        this.lock.lock();
        try {
            setDownloadPeer(peer);
            if (this.chainDownloadSpeedCalculator == null) {
                ChainDownloadSpeedCalculator chainDownloadSpeedCalculator = new ChainDownloadSpeedCalculator();
                this.chainDownloadSpeedCalculator = chainDownloadSpeedCalculator;
                this.executor.scheduleAtFixedRate((Runnable) chainDownloadSpeedCalculator, 1L, 1L, TimeUnit.SECONDS);
            }
            peer.addBlocksDownloadedEventListener(Threading.SAME_THREAD, this.chainDownloadSpeedCalculator);
            peer.startBlockChainDownload();
        } finally {
            this.lock.unlock();
        }
    }

    public ListenableFuture<List<Peer>> waitForPeers(int i) {
        return waitForPeersOfVersion(i, 0L);
    }

    public ListenableFuture<List<Peer>> waitForPeersOfVersion(final int i, final long j) {
        List<Peer> findPeersOfAtLeastVersion = findPeersOfAtLeastVersion(j);
        if (findPeersOfAtLeastVersion.size() >= i) {
            return Futures.immediateFuture(findPeersOfAtLeastVersion);
        }
        final SettableFuture create = SettableFuture.create();
        addConnectedEventListener(new PeerConnectedEventListener() {
            @Override
            public void onPeerConnected(Peer peer, int i2) {
                List<Peer> findPeersOfAtLeastVersion2 = PeerGroup.this.findPeersOfAtLeastVersion(j);
                if (findPeersOfAtLeastVersion2.size() >= i) {
                    create.set(findPeersOfAtLeastVersion2);
                    PeerGroup.this.removeConnectedEventListener(this);
                }
            }
        });
        return create;
    }

    public List<Peer> findPeersOfAtLeastVersion(long j) {
        this.lock.lock();
        try {
            ArrayList arrayList = new ArrayList(this.peers.size());
            Iterator<Peer> it = this.peers.iterator();
            while (it.hasNext()) {
                Peer next = it.next();
                if (next.getPeerVersionMessage().clientVersion >= j) {
                    arrayList.add(next);
                }
            }
            return arrayList;
        } finally {
            this.lock.unlock();
        }
    }

    public ListenableFuture<List<Peer>> waitForPeersWithServiceMask(final int i, final int i2) {
        this.lock.lock();
        try {
            List<Peer> findPeersWithServiceMask = findPeersWithServiceMask(i2);
            if (findPeersWithServiceMask.size() >= i) {
                return Futures.immediateFuture(findPeersWithServiceMask);
            }
            final SettableFuture create = SettableFuture.create();
            addConnectedEventListener(new PeerConnectedEventListener() {
                @Override
                public void onPeerConnected(Peer peer, int i3) {
                    List<Peer> findPeersWithServiceMask2 = PeerGroup.this.findPeersWithServiceMask(i2);
                    if (findPeersWithServiceMask2.size() >= i) {
                        create.set(findPeersWithServiceMask2);
                        PeerGroup.this.removeConnectedEventListener(this);
                    }
                }
            });
            return create;
        } finally {
            this.lock.unlock();
        }
    }

    public List<Peer> findPeersWithServiceMask(int i) {
        this.lock.lock();
        try {
            ArrayList arrayList = new ArrayList(this.peers.size());
            Iterator<Peer> it = this.peers.iterator();
            while (it.hasNext()) {
                Peer next = it.next();
                long j = i;
                if ((next.getPeerVersionMessage().localServices & j) == j) {
                    arrayList.add(next);
                }
            }
            return arrayList;
        } finally {
            this.lock.unlock();
        }
    }

    public int getMinBroadcastConnections() {
        int i;
        this.lock.lock();
        try {
            if (this.minBroadcastConnections == 0) {
                i = getMaxConnections();
                if (i > 1) {
                    return (int) Math.round(getMaxConnections() * 0.8d);
                }
            } else {
                i = this.minBroadcastConnections;
            }
            return i;
        } finally {
            this.lock.unlock();
        }
    }

    public void setMinBroadcastConnections(int i) {
        this.lock.lock();
        try {
            this.minBroadcastConnections = i;
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public TransactionBroadcast broadcastTransaction(Transaction transaction) {
        return broadcastTransaction(transaction, Math.max(1, getMinBroadcastConnections()));
    }

    public TransactionBroadcast broadcastTransaction(Transaction transaction, int i) {
        if (transaction.getConfidence().getSource().equals(TransactionConfidence.Source.UNKNOWN)) {
            log.info("Transaction source unknown, setting to SELF: {}", transaction.getHashAsString());
            transaction.getConfidence().setSource(TransactionConfidence.Source.SELF);
        }
        final TransactionBroadcast transactionBroadcast = new TransactionBroadcast(this, transaction);
        transactionBroadcast.setMinConnections(i);
        Futures.addCallback(transactionBroadcast.future(), new FutureCallback<Transaction>() {
            @Override
            public void onSuccess(Transaction transaction2) {
                PeerGroup.this.runningBroadcasts.remove(transactionBroadcast);
                Iterator it = PeerGroup.this.wallets.iterator();
                while (it.hasNext()) {
                    try {
                        ((Wallet) it.next()).receivePending(transaction2, null);
                    } catch (VerificationException e) {
                        throw new RuntimeException(e);
                    }
                }
            }

            @Override
            public void onFailure(Throwable th) {
                PeerGroup.this.runningBroadcasts.remove(transactionBroadcast);
            }
        });
        this.runningBroadcasts.add(transactionBroadcast);
        transactionBroadcast.broadcast();
        return transactionBroadcast;
    }

    public long getPingIntervalMsec() {
        this.lock.lock();
        try {
            return this.pingIntervalMsec;
        } finally {
            this.lock.unlock();
        }
    }

    public void setPingIntervalMsec(long j) {
        this.lock.lock();
        try {
            this.pingIntervalMsec = j;
            ListenableScheduledFuture<?> listenableScheduledFuture = this.vPingTask;
            if (listenableScheduledFuture != null) {
                listenableScheduledFuture.cancel(false);
            }
            setupPinging();
        } finally {
            this.lock.unlock();
        }
    }

    public void setMinRequiredProtocolVersion(int i) {
        this.vMinRequiredProtocolVersion = i;
    }

    public int getMinRequiredProtocolVersion() {
        return this.vMinRequiredProtocolVersion;
    }

    public int getMostCommonChainHeight() {
        this.lock.lock();
        try {
            return getMostCommonChainHeight(this.peers);
        } finally {
            this.lock.unlock();
        }
    }

    public static int getMostCommonChainHeight(List<Peer> list) {
        if (list.isEmpty()) {
            return 0;
        }
        ArrayList arrayList = new ArrayList(list.size());
        Iterator<Peer> it = list.iterator();
        while (it.hasNext()) {
            arrayList.add(Integer.valueOf((int) it.next().getBestHeight()));
        }
        return Utils.maxOfMostFreq(arrayList);
    }

    @Nullable
    protected Peer selectDownloadPeer(List<Peer> list) {
        if (list.isEmpty()) {
            return null;
        }
        int mostCommonChainHeight = getMostCommonChainHeight(list);
        ArrayList<Peer> arrayList = new ArrayList();
        for (Peer peer : list) {
            if (peer.getBestHeight() == mostCommonChainHeight) {
                arrayList.add(peer);
            }
        }
        int protocolVersionNum = this.params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.BLOOM_FILTER);
        Iterator it = arrayList.iterator();
        int i = 0;
        int i2 = 0;
        while (it.hasNext()) {
            i = Math.max(((Peer) it.next()).getPeerVersionMessage().clientVersion, i);
            i2 = Math.min(i, protocolVersionNum);
        }
        ArrayList arrayList2 = new ArrayList(arrayList.size());
        for (Peer peer2 : arrayList) {
            if (peer2.getPeerVersionMessage().clientVersion >= i2) {
                arrayList2.add(peer2);
            }
        }
        return (Peer) arrayList2.get((int) (Math.random() * arrayList2.size()));
    }

    public Peer getDownloadPeer() {
        this.lock.lock();
        try {
            return this.downloadPeer;
        } finally {
            this.lock.unlock();
        }
    }

    @Nullable
    public TorClient getTorClient() {
        return this.torClient;
    }

    public int getMaxPeersToDiscoverCount() {
        return this.vMaxPeersToDiscoverCount;
    }

    public void setMaxPeersToDiscoverCount(int i) {
        this.vMaxPeersToDiscoverCount = i;
    }

    public boolean getUseLocalhostPeerWhenPossible() {
        this.lock.lock();
        try {
            return this.useLocalhostPeerWhenPossible;
        } finally {
            this.lock.unlock();
        }
    }

    public void setUseLocalhostPeerWhenPossible(boolean z) {
        this.lock.lock();
        try {
            this.useLocalhostPeerWhenPossible = z;
        } finally {
            this.lock.unlock();
        }
    }

    public boolean isRunning() {
        return this.vRunning;
    }

    public void setBloomFilteringEnabled(boolean z) {
        this.vBloomFilteringEnabled = z;
    }

    public boolean isBloomFilteringEnabled() {
        return this.vBloomFilteringEnabled;
    }
}