All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.bitcoin.core.PeerGroup Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2013 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.google.bitcoin.core;

import com.google.bitcoin.net.ClientConnectionManager;
import com.google.bitcoin.net.FilterMerger;
import com.google.bitcoin.net.NioClientManager;
import com.google.bitcoin.net.discovery.PeerDiscovery;
import com.google.bitcoin.net.discovery.PeerDiscoveryException;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.utils.ExponentialBackoff;
import com.google.bitcoin.utils.ListenerRegistration;
import com.google.bitcoin.utils.Threading;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.*;
import net.jcip.annotations.GuardedBy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * 

Runs a set of connections to the P2P network, brings up connections to replace disconnected nodes and manages * the interaction between them all. Most applications will want to use one of these.

* *

PeerGroup tries to maintain a constant number of connections to a set of distinct peers. * Each peer runs a network listener in its own thread. When a connection is lost, a new peer * will be tried after a delay as long as the number of connections less than the maximum.

* *

Connections are made to addresses from a provided list. When that list is exhausted, * we start again from the head of the list.

* *

The PeerGroup can broadcast a transaction to the currently connected set of peers. It can * also handle download of the blockchain from peers, restarting the process when peers die.

* *

PeerGroup implements the {@link Service} interface. This means before it will do anything, * you must call the {@link com.google.common.util.concurrent.Service#start()} method (which returns * a future) or {@link com.google.common.util.concurrent.Service#startAndWait()} method, which will block * until peer discovery is completed and some outbound connections have been initiated (it will return * before handshaking is done, however). You should call {@link com.google.common.util.concurrent.Service#stop()} * when finished. Note that not all methods of PeerGroup are safe to call from a UI thread as some may do * network IO, but starting and stopping the service should be fine.

*/ public class PeerGroup extends AbstractExecutionThreadService implements TransactionBroadcaster { private static final int DEFAULT_CONNECTIONS = 4; private static final Logger log = LoggerFactory.getLogger(PeerGroup.class); protected final ReentrantLock lock = Threading.lock("peergroup"); // Addresses to try to connect to, excluding active peers. @GuardedBy("lock") private final PriorityQueue inactives; @GuardedBy("lock") private final Map backoffMap; // Currently active peers. This is an ordered list rather than a set to make unit tests predictable. private final CopyOnWriteArrayList peers; // Currently connecting peers. private final CopyOnWriteArrayList pendingPeers; private final ClientConnectionManager channels; // The peer that has been selected for the purposes of downloading announced data. @GuardedBy("lock") private Peer downloadPeer; // Callback for events related to chain download @Nullable @GuardedBy("lock") private PeerEventListener downloadListener; // Callbacks for events related to peer connection/disconnection private final CopyOnWriteArrayList> peerEventListeners; // Peer discovery sources, will be polled occasionally if there aren't enough inactives. private final CopyOnWriteArraySet peerDiscoverers; // The version message to use for new connections. @GuardedBy("lock") private VersionMessage versionMessage; // A class that tracks recent transactions that have been broadcast across the network, counts how many // peers announced them and updates the transaction confidence data. It is passed to each Peer. private final MemoryPool memoryPool; // How many connections we want to have open at the current time. If we lose connections, we'll try opening more // until we reach this count. @GuardedBy("lock") private int maxConnections; // Minimum protocol version we will allow ourselves to connect to: require Bloom filtering. private volatile int vMinRequiredProtocolVersion = FilteredBlock.MIN_PROTOCOL_VERSION; // Runs a background thread that we use for scheduling pings to our peers, so we can measure their performance // and network latency. We ping peers every pingIntervalMsec milliseconds. private volatile Timer vPingTimer; /** How many milliseconds to wait after receiving a pong before sending another ping. */ public static final long DEFAULT_PING_INTERVAL_MSEC = 2000; private long pingIntervalMsec = DEFAULT_PING_INTERVAL_MSEC; private final NetworkParameters params; private final AbstractBlockChain chain; @GuardedBy("lock") private long fastCatchupTimeSecs; private final CopyOnWriteArrayList wallets; private final CopyOnWriteArrayList peerFilterProviders; // This event listener is added to every peer. It's here so when we announce transactions via an "inv", every // peer can fetch them. private final AbstractPeerEventListener peerListener = new AbstractPeerEventListener() { @Override public List getData(Peer peer, GetDataMessage m) { return handleGetData(m); } @Override public void onBlocksDownloaded(Peer peer, Block block, int blocksLeft) { double rate = checkNotNull(chain).getFalsePositiveRate(); if (rate > bloomFilterMerger.getBloomFilterFPRate() * MAX_FP_RATE_INCREASE) { log.info("Force update Bloom filter due to high false positive rate"); recalculateFastCatchupAndFilter(FilterRecalculateMode.FORCE_SEND); } } }; private int minBroadcastConnections = 0; private Runnable bloomSendIfChanged = new Runnable() { @Override public void run() { recalculateFastCatchupAndFilter(FilterRecalculateMode.SEND_IF_CHANGED); } }; private Runnable bloomDontSend = new Runnable() { @Override public void run() { recalculateFastCatchupAndFilter(FilterRecalculateMode.DONT_SEND); } }; private AbstractWalletEventListener walletEventListener = new AbstractWalletEventListener() { private void queueRecalc(boolean andTransmit) { if (andTransmit) { log.info("Queuing recalc of the Bloom filter due to new keys or scripts becoming available"); Uninterruptibles.putUninterruptibly(jobQueue, bloomSendIfChanged); } else { log.info("Queuing recalc of the Bloom filter due to observing a pay to pubkey output on a relevant tx"); Uninterruptibles.putUninterruptibly(jobQueue, bloomDontSend); } } @Override public void onScriptsAdded(Wallet wallet, List