Coverage Summary for Class: P2pHandler (org.ethereum.net.p2p)

Class Method, % Line, %
P2pHandler 0% (0/14) 0% (0/54)
P2pHandler$1 0% (0/2) 0% (0/5)
P2pHandler$2 0% (0/1) 0% (0/1)
Total 0% (0/17) 0% (0/60)


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.p2p; 21  22 import io.netty.channel.ChannelHandlerContext; 23 import io.netty.channel.SimpleChannelInboundHandler; 24 import org.ethereum.listener.EthereumListener; 25 import org.ethereum.net.MessageQueue; 26 import org.ethereum.net.message.ReasonCode; 27 import org.ethereum.net.server.Channel; 28 import org.slf4j.Logger; 29 import org.slf4j.LoggerFactory; 30  31 import java.util.concurrent.*; 32  33 import static org.ethereum.net.message.StaticMessages.PING_MESSAGE; 34 import static org.ethereum.net.message.StaticMessages.PONG_MESSAGE; 35  36 /** 37  * Process the basic protocol messages between every peer on the network. 38  * 39  * Peers can send/receive 40  * <ul> 41  * <li>HELLO : Announce themselves to the network</li> 42  * <li>DISCONNECT : Disconnect themselves from the network</li> 43  * <li>GET_PEERS : Request a list of other knows peers</li> 44  * <li>PEERS : Send a list of known peers</li> 45  * <li>PING : Check if another peer is still alive</li> 46  * <li>PONG : Confirm that they themselves are still alive</li> 47  * </ul> 48  */ 49 public class P2pHandler extends SimpleChannelInboundHandler<P2pMessage> { 50  51  public static final byte VERSION = 4; 52  53  private static final byte[] SUPPORTED_VERSIONS = {4, 5}; 54  55  private static final Logger logger = LoggerFactory.getLogger("net"); 56  57  private static ScheduledExecutorService pingTimer = 58  Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "P2pPingTimer")); 59  60  private int ethInbound; 61  private int ethOutbound; 62  63  private final EthereumListener ethereumListener; 64  private final MessageQueue msgQueue; 65  private final int pingInterval; 66  67  public P2pHandler( 68  EthereumListener ethereumListener, 69  MessageQueue msgQueue, 70  int pingInterval) { 71  this.ethereumListener = ethereumListener; 72  this.msgQueue = msgQueue; 73  this.pingInterval = pingInterval; 74  } 75  76  private Channel channel; 77  private ScheduledFuture<?> pingTask; 78  79  80  @Override 81  public void handlerAdded(ChannelHandlerContext ctx) throws Exception { 82  logger.info("P2P protocol activated"); 83  msgQueue.activate(ctx); 84  ethereumListener.trace("P2P protocol activated"); 85  startTimers(); 86  } 87  88  89  @Override 90  public void channelRead0(final ChannelHandlerContext ctx, P2pMessage msg) throws InterruptedException { 91  92  if (P2pMessageCodes.inRange(msg.getCommand().asByte())) { 93  logger.trace("P2PHandler invoke: [{}]", msg.getCommand()); 94  } 95  96  ethereumListener.trace(String.format("P2PHandler invoke: [%s]", msg.getCommand())); 97  98  switch (msg.getCommand()) { 99  case HELLO: 100  logger.trace("Received unexpected HELLO message, channel {}", channel); 101  msgQueue.receivedMessage(msg); 102  sendDisconnect(); 103  break; 104  case DISCONNECT: 105  msgQueue.receivedMessage(msg); 106  channel.getNodeStatistics().nodeDisconnectedRemote(((DisconnectMessage) msg).getReason()); 107  processDisconnect((DisconnectMessage) msg); 108  break; 109  case PING: 110  logger.trace("Receive PING message, channel {}", channel); 111  msgQueue.receivedMessage(msg); 112  ctx.writeAndFlush(PONG_MESSAGE); 113  break; 114  case PONG: 115  logger.trace("Receive PONG message, channel {}", channel); 116  msgQueue.receivedMessage(msg); 117  break; 118  default: 119  ctx.fireChannelRead(msg); 120  break; 121  } 122  } 123  124  @Override 125  public void channelInactive(ChannelHandlerContext ctx) throws Exception { 126  logger.info("channel inactive: ", ctx); 127  this.killTimers(); 128  } 129  130  @Override 131  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 132  logger.error("P2p handling failed", cause); 133  ctx.close(); 134  killTimers(); 135  } 136  137  private void processDisconnect(DisconnectMessage msg) { 138  139  if (!logger.isInfoEnabled() || msg.getReason() != ReasonCode.USELESS_PEER) { 140  return; 141  } 142  143  if (channel.getNodeStatistics().getEthInbound().get() - ethInbound > 1 || 144  channel.getNodeStatistics().getEthOutbound().get() - ethOutbound > 1) { 145  146  // it means that we've been disconnected 147  // after some incorrect action from our peer 148  // need to log this moment 149  logger.info("From: \t{}\t [DISCONNECT reason=BAD_PEER_ACTION]", channel); 150  } 151  } 152  153  public void setHandshake(HelloMessage msg) { 154  155  channel.getNodeStatistics().setClientId(msg.getClientId()); 156  157  this.ethInbound = channel.getNodeStatistics().getEthInbound().get(); 158  this.ethOutbound = channel.getNodeStatistics().getEthOutbound().get(); 159  160  ethereumListener.onHandShakePeer(channel, msg); 161  } 162  163  public void sendDisconnect() { 164  msgQueue.disconnect(); 165  } 166  167  private void startTimers() { 168  // sample for pinging in background 169  pingTask = pingTimer.scheduleAtFixedRate(new Runnable() { 170  @Override 171  public void run() { 172  try { 173  msgQueue.sendMessage(PING_MESSAGE); 174  } catch (Throwable t) { 175  logger.error("Unhandled exception", t); 176  } 177  } 178  }, 2, pingInterval, TimeUnit.SECONDS); 179  } 180  181  public void killTimers() { 182  pingTask.cancel(false); 183  msgQueue.close(); 184  } 185  186  public void setChannel(Channel channel) { 187  this.channel = channel; 188  } 189  190  public static boolean isProtocolVersionSupported(byte ver) { 191  for (byte v : SUPPORTED_VERSIONS) { 192  if (v == ver) { 193  return true; 194  } 195  } 196  return false; 197  } 198  199 }