Coverage Summary for Class: RskWireProtocol (co.rsk.net.eth)

Class Method, % Line, %
RskWireProtocol 0% (0/18) 0% (0/120)
RskWireProtocol$1 0% (0/1) 0% (0/2)
RskWireProtocol$EthState 0% (0/1) 0% (0/5)
Total 0% (0/20) 0% (0/127)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2017 RSK Labs Ltd. 4  * 5  * This program is free software: you can redistribute it and/or modify 6  * it under the terms of the GNU Lesser General Public License as published by 7  * the Free Software Foundation, either version 3 of the License, or 8  * (at your option) any later version. 9  * 10  * This program is distributed in the hope that it will be useful, 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13  * GNU Lesser General Public License for more details. 14  * 15  * You should have received a copy of the GNU Lesser General Public License 16  * along with this program. If not, see <http://www.gnu.org/licenses/>. 17  */ 18  19 package co.rsk.net.eth; 20  21 import co.rsk.config.RskSystemProperties; 22 import co.rsk.crypto.Keccak256; 23 import co.rsk.net.MessageHandler; 24 import co.rsk.net.NodeID; 25 import co.rsk.net.Status; 26 import co.rsk.net.StatusResolver; 27 import co.rsk.net.messages.BlockMessage; 28 import co.rsk.net.messages.GetBlockMessage; 29 import co.rsk.net.messages.Message; 30 import co.rsk.net.messages.StatusMessage; 31 import co.rsk.scoring.EventType; 32 import co.rsk.scoring.PeerScoringManager; 33 import io.netty.channel.ChannelHandlerContext; 34 import io.netty.channel.SimpleChannelInboundHandler; 35 import org.ethereum.core.Genesis; 36 import org.ethereum.listener.CompositeEthereumListener; 37 import org.ethereum.net.MessageQueue; 38 import org.ethereum.net.eth.EthVersion; 39 import org.ethereum.net.eth.handler.Eth; 40 import org.ethereum.net.eth.message.EthMessage; 41 import org.ethereum.net.eth.message.EthMessageCodes; 42 import org.ethereum.net.message.ReasonCode; 43 import org.ethereum.net.server.Channel; 44 import org.ethereum.sync.SyncStatistics; 45 import org.ethereum.util.ByteUtil; 46 import org.slf4j.Logger; 47 import org.slf4j.LoggerFactory; 48  49 import java.net.InetAddress; 50 import java.net.InetSocketAddress; 51 import java.net.SocketAddress; 52 import java.util.NoSuchElementException; 53  54 import static org.ethereum.net.eth.EthVersion.V62; 55 import static org.ethereum.net.message.ReasonCode.USELESS_PEER; 56  57 public class RskWireProtocol extends SimpleChannelInboundHandler<EthMessage> implements Eth { 58  59  private static final Logger logger = LoggerFactory.getLogger("sync"); 60  private static final Logger loggerNet = LoggerFactory.getLogger("net"); 61  private final CompositeEthereumListener ethereumListener; 62  /** 63  * Header list sent in GET_BLOCK_BODIES message, 64  * used to create blocks from headers and bodies 65  * also, is useful when returned BLOCK_BODIES msg doesn't cover all sent hashes 66  * or in case when peer is disconnected 67  */ 68  private final PeerScoringManager peerScoringManager; 69  private final SyncStatistics syncStats = new SyncStatistics(); 70  private final Channel channel; 71  private final EthVersion version; 72  private EthState ethState = EthState.INIT; 73  74  private final RskSystemProperties config; 75  private final StatusResolver statusResolver; 76  private final MessageHandler messageHandler; 77  private final MessageRecorder messageRecorder; 78  private final Genesis genesis; 79  private final MessageQueue msgQueue; 80  81  public RskWireProtocol(RskSystemProperties config, 82  PeerScoringManager peerScoringManager, 83  MessageHandler messageHandler, 84  CompositeEthereumListener ethereumListener, 85  Genesis genesis, 86  MessageRecorder messageRecorder, 87  StatusResolver statusResolver, 88  MessageQueue msgQueue, 89  Channel channel) { 90  this.ethereumListener = ethereumListener; 91  this.version = V62; 92  93  this.msgQueue = msgQueue; 94  this.channel = channel; 95  this.peerScoringManager = peerScoringManager; 96  this.messageHandler = messageHandler; 97  this.config = config; 98  this.statusResolver = statusResolver; 99  this.messageRecorder = messageRecorder; 100  this.genesis = genesis; 101  } 102  103  @Override 104  public void channelRead0(final ChannelHandlerContext ctx, EthMessage msg) throws InterruptedException { 105  loggerNet.debug("Read message: {}", msg); 106  107  if (EthMessageCodes.inRange(msg.getCommand().asByte(), version)) { 108  loggerNet.trace("EthHandler invoke: [{}]", msg.getCommand()); 109  } 110  111  ethereumListener.trace(String.format("EthHandler invoke: [%s]", msg.getCommand())); 112  113  channel.getNodeStatistics().getEthInbound().add(); 114  115  msgQueue.receivedMessage(msg); 116  117  if (this.messageRecorder != null) { 118  this.messageRecorder.recordMessage(channel.getPeerNodeID(), msg); 119  } 120  121  if (!hasGoodReputation(ctx)) { 122  ctx.disconnect(); 123  return; 124  } 125  126  switch (msg.getCommand()) { 127  case STATUS: 128  processStatus((org.ethereum.net.eth.message.StatusMessage) msg, ctx); 129  break; 130  case RSK_MESSAGE: 131  RskMessage rskmessage = (RskMessage)msg; 132  Message message = rskmessage.getMessage(); 133  134  switch (message.getMessageType()) { 135  case BLOCK_MESSAGE: 136  loggerNet.trace("RSK Block Message: Block {} {} from {}", ((BlockMessage)message).getBlock().getNumber(), ((BlockMessage)message).getBlock().getPrintableHash(), channel.getPeerNodeID()); 137  syncStats.addBlocks(1); 138  break; 139  case GET_BLOCK_MESSAGE: 140  loggerNet.trace("RSK Get Block Message: Block {} from {}", ByteUtil.toHexString(((GetBlockMessage)message).getBlockHash()), channel.getPeerNodeID()); 141  syncStats.getBlock(); 142  break; 143  case STATUS_MESSAGE: 144  loggerNet.trace("RSK Status Message: Block {} {} from {}", ((StatusMessage)message).getStatus().getBestBlockNumber(), ByteUtil.toHexString(((StatusMessage)message).getStatus().getBestBlockHash()), channel.getPeerNodeID()); 145  syncStats.addStatus(); 146  break; 147  } 148  149  if (this.messageHandler != null) { 150  this.messageHandler.postMessage(channel, rskmessage.getMessage()); 151  } 152  break; 153  default: 154  break; 155  } 156  } 157  158  /************************* 159  * Message Processing * 160  *************************/ 161  162  protected void processStatus(org.ethereum.net.eth.message.StatusMessage msg, ChannelHandlerContext ctx) { 163  try { 164  byte protocolVersion = msg.getProtocolVersion(); 165  byte versionCode = version.getCode(); 166  if (protocolVersion != versionCode) { 167  loggerNet.info("Removing EthHandler for {} due to protocol incompatibility", ctx.channel().remoteAddress()); 168  loggerNet.info("Protocol version {} - message protocol version {}", 169  versionCode, 170  protocolVersion); 171  ethState = EthState.STATUS_FAILED; 172  recordEvent(EventType.INCOMPATIBLE_PROTOCOL); 173  disconnect(ReasonCode.INCOMPATIBLE_PROTOCOL); 174  ctx.pipeline().remove(this); // Peer is not compatible for the 'eth' sub-protocol 175  return; 176  } 177  178  int networkId = config.networkId(); 179  int msgNetworkId = msg.getNetworkId(); 180  if (msgNetworkId != networkId) { 181  loggerNet.info("Removing EthHandler for {} due to invalid network", ctx.channel().remoteAddress()); 182  loggerNet.info("Different network received: config network ID {} - message network ID {}", 183  networkId, msgNetworkId); 184  ethState = EthState.STATUS_FAILED; 185  recordEvent(EventType.INVALID_NETWORK); 186  disconnect(ReasonCode.NULL_IDENTITY); 187  ctx.pipeline().remove(this); 188  return; 189  } 190  191  Keccak256 genesisHash = genesis.getHash(); 192  Keccak256 msgGenesisHash = new Keccak256(msg.getGenesisHash()); 193  if (!msgGenesisHash.equals(genesisHash)) { 194  loggerNet.info("Removing EthHandler for {} due to unexpected genesis", ctx.channel().remoteAddress()); 195  loggerNet.info("Config genesis hash {} - message genesis hash {}", 196  genesisHash, msgGenesisHash); 197  ethState = EthState.STATUS_FAILED; 198  recordEvent(EventType.UNEXPECTED_GENESIS); 199  disconnect(ReasonCode.UNEXPECTED_GENESIS); 200  ctx.pipeline().remove(this); 201  return; 202  } 203  204  // basic checks passed, update statistics 205  channel.getNodeStatistics().ethHandshake(msg); 206  ethereumListener.onEthStatusUpdated(channel, msg); 207  } catch (NoSuchElementException e) { 208  loggerNet.debug("EthHandler already removed"); 209  } 210  } 211  212  private boolean hasGoodReputation(ChannelHandlerContext ctx) { 213  SocketAddress socketAddress = ctx.channel().remoteAddress(); 214  215  //TODO(mmarquez): and if not ??? 216  if (socketAddress instanceof InetSocketAddress) { 217  218  InetAddress address = ((InetSocketAddress)socketAddress).getAddress(); 219  220  if (!peerScoringManager.hasGoodReputation(address)) { 221  return false; 222  } 223  224  NodeID nodeID = channel.getNodeId(); 225  226  if (nodeID != null && !peerScoringManager.hasGoodReputation(nodeID)) { 227  return false; 228  } 229  230  } 231  232  return true; //TODO(mmarquez): ugly 233  } 234  235  private void recordEvent(EventType event) { 236  peerScoringManager.recordEvent( 237  channel.getPeerNodeID(), 238  channel.getAddress(), 239  event); 240  } 241  242  243  /************************* 244  * Message Sending * 245  *************************/ 246  247  @Override 248  public void sendStatus() { 249  byte protocolVersion = version.getCode(); 250  int networkId = config.networkId(); 251  252  Status status = statusResolver.currentStatus(); 253  254  // Original status 255  org.ethereum.net.eth.message.StatusMessage msg = new org.ethereum.net.eth.message.StatusMessage( 256  protocolVersion, 257  networkId, 258  ByteUtil.bigIntegerToBytes(status.getTotalDifficulty().asBigInteger()), 259  status.getBestBlockHash(), 260  genesis.getHash().getBytes()); 261  sendMessage(msg); 262  263  // RSK new protocol send status 264  RskMessage rskmessage = new RskMessage(new StatusMessage(status)); 265  loggerNet.trace("Sending status best block {} to {}", 266  status.getBestBlockNumber(), 267  channel.getPeerNodeID()); 268  sendMessage(rskmessage); 269  270  ethState = EthState.STATUS_SENT; 271  } 272  273  274  @Override 275  public boolean hasStatusPassed() { 276  return ethState.ordinal() > EthState.STATUS_SENT.ordinal(); 277  } 278  279  @Override 280  public boolean hasStatusSucceeded() { 281  return ethState == EthState.STATUS_SUCCEEDED; 282  } 283  284  @Override 285  public SyncStatistics getStats() { 286  return syncStats; 287  } 288  289  @Override 290  public EthVersion getVersion() { 291  return version; 292  } 293  294  @Override 295  public void dropConnection() { 296  297  // todo: reduce reputation 298  299  logger.info("Peer {}: is a bad one, drop", channel.getPeerId()); 300  disconnect(USELESS_PEER); 301  } 302  303  @Override 304  public boolean isUsingNewProtocol() { 305  return true; 306  } 307  308  @Override 309  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 310  loggerNet.error("Eth handling failed", cause); 311  ctx.close(); 312  } 313  314  @Override 315  public void handlerRemoved(ChannelHandlerContext ctx) { 316  loggerNet.debug("handlerRemoved: kill timers in EthHandler"); 317  } 318  319  public void activate() { 320  loggerNet.info("RSK protocol activated"); 321  ethereumListener.trace("RSK protocol activated"); 322  sendStatus(); 323  } 324  325  protected void disconnect(ReasonCode reason) { 326  msgQueue.disconnect(reason); 327  channel.getNodeStatistics().nodeDisconnectedLocal(reason); 328  } 329  330  @Override 331  public void sendMessage(EthMessage message) { 332  loggerNet.debug("Send message: {}", message); 333  334  msgQueue.sendMessage(message); 335  channel.getNodeStatistics().getEthOutbound().add(); 336  } 337  338  private enum EthState { 339  INIT, 340  STATUS_SENT, 341  STATUS_SUCCEEDED, 342  STATUS_FAILED 343  } 344  345  public interface Factory { 346  RskWireProtocol newInstance(MessageQueue messageQueue, Channel channel); 347  } 348 }