Coverage Summary for Class: ChannelManagerImpl (org.ethereum.net.server)

Class Class, % Method, % Line, %
ChannelManagerImpl 0% (0/1) 0% (0/38) 0% (0/136)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2017 RSK Labs Ltd. 4  * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>) 5  * 6  * This program is free software: you can redistribute it and/or modify 7  * it under the terms of the GNU Lesser General Public License as published by 8  * the Free Software Foundation, either version 3 of the License, or 9  * (at your option) any later version. 10  * 11  * This program is distributed in the hope that it will be useful, 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14  * GNU Lesser General Public License for more details. 15  * 16  * You should have received a copy of the GNU Lesser General Public License 17  * along with this program. If not, see <http://www.gnu.org/licenses/>. 18  */ 19  20 package org.ethereum.net.server; 21  22 import co.rsk.config.RskSystemProperties; 23 import co.rsk.net.Peer; 24 import co.rsk.net.NodeID; 25 import co.rsk.net.Status; 26 import co.rsk.net.messages.*; 27 import co.rsk.scoring.InetAddressBlock; 28 import com.google.common.annotations.VisibleForTesting; 29 import org.ethereum.config.NodeFilter; 30 import org.ethereum.core.Block; 31 import org.ethereum.core.BlockIdentifier; 32 import org.ethereum.core.Transaction; 33 import org.ethereum.net.message.ReasonCode; 34 import org.ethereum.sync.SyncPool; 35 import org.slf4j.Logger; 36 import org.slf4j.LoggerFactory; 37  38 import javax.annotation.Nonnull; 39 import java.net.InetAddress; 40 import java.time.Duration; 41 import java.time.Instant; 42 import java.util.*; 43 import java.util.concurrent.*; 44 import java.util.stream.Collectors; 45  46 /** 47  * @author Roman Mandeleil 48  * @since 11.11.2014 49  */ 50 public class ChannelManagerImpl implements ChannelManager { 51  52  private static final Logger logger = LoggerFactory.getLogger("net"); 53  54  // If the inbound peer connection was dropped by us with a reason message 55  // then we ban that peer IP on any connections for some time to protect from 56  // too active peers 57  private static final Duration INBOUND_CONNECTION_BAN_TIMEOUT = Duration.ofSeconds(10); 58  private final Object activePeersLock = new Object(); 59  private final Map<NodeID, Channel> activePeers; 60  61  // Using a concurrent list 62  // (the add and remove methods copy an internal array, 63  // but the iterator directly use the internal array) 64  private final List<Channel> newPeers; 65  66  private final ScheduledExecutorService mainWorker; 67  private final Map<InetAddress, Instant> disconnectionsTimeouts; 68  private final Object disconnectionTimeoutsLock = new Object(); 69  70  private final SyncPool syncPool; 71  private final NodeFilter trustedPeers; 72  private final int maxActivePeers; 73  private final int maxConnectionsAllowed; 74  private final int networkCIDR; 75  76  public ChannelManagerImpl(RskSystemProperties config, SyncPool syncPool) { 77  this.mainWorker = Executors.newSingleThreadScheduledExecutor(target -> new Thread(target, "newPeersProcessor")); 78  this.syncPool = syncPool; 79  this.maxActivePeers = config.maxActivePeers(); 80  this.trustedPeers = config.trustedPeers(); 81  this.disconnectionsTimeouts = new HashMap<>(); 82  this.activePeers = new ConcurrentHashMap<>(); 83  this.newPeers = new CopyOnWriteArrayList<>(); 84  this.maxConnectionsAllowed = config.maxConnectionsAllowed(); 85  this.networkCIDR = config.networkCIDR(); 86  } 87  88  @Override 89  public void start() { 90  mainWorker.scheduleWithFixedDelay(this::handleNewPeersAndDisconnections, 0, 1, TimeUnit.SECONDS); 91  } 92  93  @Override 94  public void stop() { 95  mainWorker.shutdown(); 96  } 97  98  private void handleNewPeersAndDisconnections() { 99  this.tryProcessNewPeers(); 100  this.cleanDisconnections(); 101  } 102  103  @VisibleForTesting 104  public void tryProcessNewPeers() { 105  if (newPeers.isEmpty()) { 106  return; 107  } 108  109  try { 110  processNewPeers(); 111  } catch (Exception e) { 112  logger.error("Error", e); 113  } 114  } 115  116  private void cleanDisconnections() { 117  synchronized (disconnectionTimeoutsLock) { 118  Instant now = Instant.now(); 119  disconnectionsTimeouts.values().removeIf(v -> !isRecent(v, now)); 120  } 121  } 122  123  private void processNewPeers() { 124  synchronized (newPeers) { 125  List<Channel> processedChannels = new ArrayList<>(); 126  newPeers.stream().filter(Channel::isProtocolsInitialized).forEach(c -> processNewPeer(c, processedChannels)); 127  newPeers.removeAll(processedChannels); 128  } 129  } 130  131  private void processNewPeer(Channel channel, List<Channel> processedChannels) { 132  ReasonCode reason = getNewPeerDisconnectionReason(channel); 133  if (reason != null) { 134  disconnect(channel, reason); 135  } else { 136  addToActives(channel); 137  } 138  processedChannels.add(channel); 139  } 140  141  private ReasonCode getNewPeerDisconnectionReason(Channel channel) { 142  if (activePeers.containsKey(channel.getNodeId())) { 143  return ReasonCode.DUPLICATE_PEER; 144  } 145  146  if (!channel.isActive() && activePeers.size() >= maxActivePeers && !trustedPeers.accept(channel.getNode())) { 147  return ReasonCode.TOO_MANY_PEERS; 148  } 149  150  return null; 151  } 152  153  private void disconnect(Channel peer, ReasonCode reason) { 154  logger.debug("Disconnecting peer with reason {} : {}", reason, peer); 155  peer.disconnect(reason); 156  synchronized (disconnectionTimeoutsLock) { 157  disconnectionsTimeouts.put(peer.getInetSocketAddress().getAddress(), 158  Instant.now().plus(INBOUND_CONNECTION_BAN_TIMEOUT)); 159  } 160  } 161  162  public boolean isRecentlyDisconnected(InetAddress peerAddress) { 163  synchronized (disconnectionTimeoutsLock) { 164  return isRecent(disconnectionsTimeouts.getOrDefault(peerAddress, Instant.EPOCH), Instant.now()); 165  } 166  } 167  168  private boolean isRecent(Instant disconnectionTimeout, Instant currentTime) { 169  return currentTime.isBefore(disconnectionTimeout); 170  } 171  172  private void addToActives(Channel peer) { 173  if (peer.isUsingNewProtocol() || peer.hasEthStatusSucceeded()) { 174  syncPool.add(peer); 175  synchronized (activePeersLock) { 176  activePeers.put(peer.getNodeId(), peer); 177  } 178  } 179  } 180  181  /** 182  * broadcastBlock Propagates a block message across active peers 183  * 184  * @param block new Block to be sent 185  * @return a set containing the ids of the peers that received the block. 186  */ 187  @Nonnull 188  public Set<NodeID> broadcastBlock(@Nonnull final Block block) { 189  190  final Set<NodeID> nodesIdsBroadcastedTo = new HashSet<>(); 191  final BlockIdentifier bi = new BlockIdentifier(block.getHash().getBytes(), block.getNumber()); 192  final Message newBlock = new BlockMessage(block); 193  final Message newBlockHashes = new NewBlockHashesMessage(Arrays.asList(bi)); 194  synchronized (activePeersLock) { 195  // Get a randomized list with all the peers that don't have the block yet. 196  activePeers.values().forEach(c -> logger.trace("RSK activePeers: {}", c)); 197  List<Channel> peers = new ArrayList<>(activePeers.values()); 198  Collections.shuffle(peers); 199  200  int sqrt = (int) Math.floor(Math.sqrt(peers.size())); 201  for (int i = 0; i < sqrt; i++) { 202  Channel peer = peers.get(i); 203  nodesIdsBroadcastedTo.add(peer.getNodeId()); 204  logger.trace("RSK propagate: {}", peer); 205  peer.sendMessage(newBlock); 206  } 207  for (int i = sqrt; i < peers.size(); i++) { 208  Channel peer = peers.get(i); 209  logger.trace("RSK announce: {}", peer); 210  peer.sendMessage(newBlockHashes); 211  } 212  } 213  214  return nodesIdsBroadcastedTo; 215  } 216  217  @Nonnull 218  public Set<NodeID> broadcastBlockHash(@Nonnull final List<BlockIdentifier> identifiers, final Set<NodeID> targets) { 219  final Set<NodeID> nodesIdsBroadcastedTo = new HashSet<>(); 220  final Message newBlockHash = new NewBlockHashesMessage(identifiers); 221  222  synchronized (activePeersLock) { 223  activePeers.values().forEach(c -> logger.trace("RSK activePeers: {}", c)); 224  225  activePeers.values().stream() 226  .filter(p -> targets.contains(p.getNodeId())) 227  .forEach(peer -> { 228  logger.trace("RSK announce hash: {}", peer); 229  peer.sendMessage(newBlockHash); 230  }); 231  } 232  233  return nodesIdsBroadcastedTo; 234  } 235  236  @Override 237  public int broadcastStatus(Status status) { 238  final Message message = new StatusMessage(status); 239  synchronized (activePeersLock) { 240  if (activePeers.isEmpty()) { 241  return 0; 242  } 243  244  int numberOfPeersToSendStatusTo = getNumberOfPeersToSendStatusTo(activePeers.size()); 245  List<Channel> shuffledPeers = new ArrayList<>(activePeers.values()); 246  Collections.shuffle(shuffledPeers); 247  shuffledPeers.stream() 248  .limit(numberOfPeersToSendStatusTo) 249  .forEach(c -> c.sendMessage(message)); 250  return numberOfPeersToSendStatusTo; 251  } 252  } 253  254  @VisibleForTesting 255  int getNumberOfPeersToSendStatusTo(int peerCount) { 256  // Send to the sqrt of number of peers. 257  // Make sure the number is between 3 and 10 (unless there are less than 3 peers). 258  int peerCountSqrt = (int) Math.sqrt(peerCount); 259  return Math.min(10, Math.min(Math.max(3, peerCountSqrt), peerCount)); 260  } 261  262  public void add(Channel peer) { 263  newPeers.add(peer); 264  } 265  266  public void notifyDisconnect(Channel channel) { 267  logger.debug("Peer {}: notifies about disconnect", channel.getPeerId()); 268  channel.onDisconnect(); 269  synchronized (newPeers) { 270  if(newPeers.remove(channel)) { 271  logger.info("Peer removed from new peers list: {}", channel.getPeerId()); 272  } 273  synchronized (activePeersLock){ 274  if(activePeers.values().remove(channel)) { 275  logger.info("Peer removed from active peers list: {}", channel.getPeerId()); 276  } 277  } 278  syncPool.onDisconnect(channel); 279  } 280  } 281  282  public Collection<Peer> getActivePeers() { 283  // from the docs: it is imperative to synchronize when iterating 284  synchronized (activePeersLock) { 285  return new ArrayList<>(activePeers.values()); 286  } 287  } 288  289  public boolean isAddressBlockAvailable(InetAddress inetAddress) { 290  synchronized (activePeersLock) { 291  //TODO(lsebrie): save block address in a data structure and keep updated on each channel add/remove 292  //TODO(lsebrie): check if we need to use a different networkCIDR for ipv6 293  return activePeers.values().stream() 294  .map(ch -> new InetAddressBlock(ch.getInetSocketAddress().getAddress(), networkCIDR)) 295  .filter(block -> block.contains(inetAddress)) 296  .count() < maxConnectionsAllowed; 297  } 298  } 299  300  /** 301  * broadcastTransaction Propagates a transaction message across active peers with exclusion of 302  * the peers with an id belonging to the skip set. 303  * 304  * @param transaction new Transaction to be sent 305  * @param skip the set of peers to avoid sending the message. 306  * @return a set containing the ids of the peers that received the transaction. 307  */ 308  @Nonnull 309  public Set<NodeID> broadcastTransaction(@Nonnull final Transaction transaction, @Nonnull final Set<NodeID> skip) { 310  List<Transaction> transactions = Collections.singletonList(transaction); 311  312  return internalBroadcastTransactions(skip, transactions); 313  } 314  315  /** 316  * broadcastTransaction Propagates a transaction message across active peers with exclusion of 317  * the peers with an id belonging to the skip set. 318  * 319  * @param transactions List of Transactions to be sent 320  * @param skip the set of peers to avoid sending the message. 321  * @return a set containing the ids of the peers that received the transaction. 322  */ 323  @Override 324  public Set<NodeID> broadcastTransactions(@Nonnull final List<Transaction> transactions, @Nonnull final Set<NodeID> skip) { 325  return internalBroadcastTransactions(skip, transactions); 326  } 327  328  private Set<NodeID> internalBroadcastTransactions(Set<NodeID> skip, List<Transaction> transactions) { 329  final Set<NodeID> nodesIdsBroadcastedTo = new HashSet<>(); 330  final Message newTransactions = new TransactionsMessage(transactions); 331  final List<Channel> peersToBroadcast = activePeers.values().stream(). 332  filter(p -> !skip.contains(p.getNodeId())).collect(Collectors.toList()); 333  334  peersToBroadcast.forEach(peer -> { 335  peer.sendMessage(newTransactions); 336  nodesIdsBroadcastedTo.add(peer.getNodeId()); 337  }); 338  339  return nodesIdsBroadcastedTo; 340  } 341  342  @VisibleForTesting 343  public void setActivePeers(Map<NodeID, Channel> newActivePeers) { 344  this.activePeers.clear(); 345  this.activePeers.putAll(newActivePeers); 346  } 347  348 }