Coverage Summary for Class: SyncProcessor (co.rsk.net)

Class Method, % Line, %
SyncProcessor 0% (0/35) 0% (0/158)
SyncProcessor$1 0% (0/2) 0% (0/5)
Total 0% (0/37) 0% (0/163)


1 package co.rsk.net; 2  3 import co.rsk.core.DifficultyCalculator; 4 import co.rsk.core.bc.BlockChainStatus; 5 import co.rsk.core.bc.ConsensusValidationMainchainView; 6 import co.rsk.net.messages.*; 7 import co.rsk.net.sync.*; 8 import co.rsk.scoring.EventType; 9 import co.rsk.validators.BlockHeaderValidationRule; 10 import co.rsk.validators.SyncBlockValidatorRule; 11 import com.google.common.annotations.VisibleForTesting; 12 import org.ethereum.core.*; 13 import org.ethereum.crypto.HashUtil; 14 import org.ethereum.db.BlockStore; 15 import org.ethereum.validator.DifficultyRule; 16 import org.slf4j.Logger; 17 import org.slf4j.LoggerFactory; 18  19 import javax.annotation.Nonnull; 20 import java.time.Duration; 21 import java.util.*; 22  23 /** 24  * This class' methods are executed one at a time because NodeMessageHandler is synchronized. 25  */ 26 public class SyncProcessor implements SyncEventsHandler { 27  private static final int MAX_PENDING_MESSAGES = 100_000; 28  private static final Logger logger = LoggerFactory.getLogger("syncprocessor"); 29  30  private final SyncConfiguration syncConfiguration; 31  private final Blockchain blockchain; 32  private final BlockStore blockStore; 33  private final ConsensusValidationMainchainView consensusValidationMainchainView; 34  private final BlockSyncService blockSyncService; 35  private final BlockFactory blockFactory; 36  private final BlockHeaderValidationRule blockHeaderValidationRule; 37  private final SyncBlockValidatorRule blockValidationRule; 38  private final DifficultyRule difficultyRule; 39  private final Genesis genesis; 40  41  private final PeersInformation peersInformation; 42  private final Map<Long, MessageType> pendingMessages; 43  44  private SyncState syncState; 45  private long lastRequestId; 46  47  public SyncProcessor(Blockchain blockchain, 48  BlockStore blockStore, 49  ConsensusValidationMainchainView consensusValidationMainchainView, 50  BlockSyncService blockSyncService, 51  SyncConfiguration syncConfiguration, 52  BlockFactory blockFactory, 53  BlockHeaderValidationRule blockHeaderValidationRule, 54  SyncBlockValidatorRule syncBlockValidatorRule, 55  DifficultyCalculator difficultyCalculator, 56  PeersInformation peersInformation, 57  Genesis genesis) { 58  this.blockchain = blockchain; 59  this.blockStore = blockStore; 60  this.consensusValidationMainchainView = consensusValidationMainchainView; 61  this.blockSyncService = blockSyncService; 62  this.syncConfiguration = syncConfiguration; 63  this.blockFactory = blockFactory; 64  this.blockHeaderValidationRule = blockHeaderValidationRule; 65  this.blockValidationRule = syncBlockValidatorRule; 66  this.difficultyRule = new DifficultyRule(difficultyCalculator); 67  this.genesis = genesis; 68  this.pendingMessages = new LinkedHashMap<Long, MessageType>() { 69  @Override 70  protected boolean removeEldestEntry(Map.Entry<Long, MessageType> eldest) { 71  boolean shouldDiscard = size() > MAX_PENDING_MESSAGES; 72  if (shouldDiscard) { 73  logger.trace("Pending {}@{} DISCARDED", eldest.getValue(), eldest.getKey()); 74  } 75  return shouldDiscard; 76  } 77  }; 78  79  this.peersInformation = peersInformation; 80  setSyncState(new DecidingSyncState(syncConfiguration, this, peersInformation, blockStore)); 81  } 82  83  public void processStatus(Peer sender, Status status) { 84  logger.debug("Receiving syncState from node {} block {} {}", sender.getPeerNodeID(), status.getBestBlockNumber(), HashUtil.toPrintableHash(status.getBestBlockHash())); 85  peersInformation.registerPeer(sender).setStatus(status); 86  syncState.newPeerStatus(); 87  } 88  89  public void processSkeletonResponse(Peer peer, SkeletonResponseMessage message) { 90  logger.debug("Process skeleton response from node {}", peer.getPeerNodeID()); 91  peersInformation.getOrRegisterPeer(peer); 92  93  long messageId = message.getId(); 94  MessageType messageType = message.getMessageType(); 95  if (isPending(messageId, messageType)) { 96  removePendingMessage(messageId, messageType); 97  syncState.newSkeleton(message.getBlockIdentifiers(), peer); 98  } else { 99  peersInformation.reportEvent(peer.getPeerNodeID(), EventType.UNEXPECTED_MESSAGE); 100  } 101  } 102  103  public void processBlockHashResponse(Peer peer, BlockHashResponseMessage message) { 104  NodeID nodeID = peer.getPeerNodeID(); 105  logger.debug("Process block hash response from node {} hash {}", nodeID, HashUtil.toPrintableHash(message.getHash())); 106  peersInformation.getOrRegisterPeer(peer); 107  108  long messageId = message.getId(); 109  MessageType messageType = message.getMessageType(); 110  if (isPending(messageId, messageType)) { 111  removePendingMessage(messageId, messageType); 112  syncState.newConnectionPointData(message.getHash()); 113  } else { 114  peersInformation.reportEvent(peer.getPeerNodeID(), EventType.UNEXPECTED_MESSAGE); 115  } 116  } 117  118  public void processBlockHeadersResponse(Peer peer, BlockHeadersResponseMessage message) { 119  logger.debug("Process block headers response from node {}", peer.getPeerNodeID()); 120  peersInformation.getOrRegisterPeer(peer); 121  122  long messageId = message.getId(); 123  MessageType messageType = message.getMessageType(); 124  if (isPending(messageId, messageType)) { 125  removePendingMessage(messageId, messageType); 126  syncState.newBlockHeaders(message.getBlockHeaders()); 127  } else { 128  peersInformation.reportEvent(peer.getPeerNodeID(), EventType.UNEXPECTED_MESSAGE); 129  } 130  } 131  132  public void processBodyResponse(Peer peer, BodyResponseMessage message) { 133  logger.debug("Process body response from node {}", peer.getPeerNodeID()); 134  peersInformation.getOrRegisterPeer(peer); 135  136  long messageId = message.getId(); 137  MessageType messageType = message.getMessageType(); 138  if (isPending(messageId, messageType)) { 139  removePendingMessage(messageId, messageType); 140  syncState.newBody(message, peer); 141  } else { 142  peersInformation.reportEvent(peer.getPeerNodeID(), EventType.UNEXPECTED_MESSAGE); 143  } 144  } 145  146  public void processNewBlockHash(Peer peer, NewBlockHashMessage message) { 147  NodeID nodeID = peer.getPeerNodeID(); 148  logger.debug("Process new block hash from node {} hash {}", nodeID, HashUtil.toPrintableHash(message.getBlockHash())); 149  byte[] hash = message.getBlockHash(); 150  151  if (syncState instanceof DecidingSyncState && blockSyncService.getBlockFromStoreOrBlockchain(hash) == null) { 152  peersInformation.getOrRegisterPeer(peer); 153  sendMessage(peer, new BlockRequestMessage(++lastRequestId, hash)); 154  } 155  } 156  157  public void processBlockResponse(Peer peer, BlockResponseMessage message) { 158  NodeID nodeID = peer.getPeerNodeID(); 159  logger.debug("Process block response from node {} block {} {}", nodeID, message.getBlock().getNumber(), message.getBlock().getPrintableHash()); 160  peersInformation.getOrRegisterPeer(peer); 161  162  long messageId = message.getId(); 163  MessageType messageType = message.getMessageType(); 164  if (isPending(messageId, messageType)) { 165  removePendingMessage(messageId, messageType); 166  blockSyncService.processBlock(message.getBlock(), peer, false); 167  } else { 168  peersInformation.reportEvent(peer.getPeerNodeID(), EventType.UNEXPECTED_MESSAGE); 169  } 170  } 171  172  @Override 173  public void sendSkeletonRequest(Peer peer, long height) { 174  logger.debug("Send skeleton request to node {} height {}", peer.getPeerNodeID(), height); 175  MessageWithId message = new SkeletonRequestMessage(++lastRequestId, height); 176  sendMessage(peer, message); 177  } 178  179  @Override 180  public void sendBlockHashRequest(Peer peer, long height) { 181  logger.debug("Send hash request to node {} height {}", peer.getPeerNodeID(), height); 182  BlockHashRequestMessage message = new BlockHashRequestMessage(++lastRequestId, height); 183  sendMessage(peer, message); 184  } 185  186  @Override 187  public void sendBlockHeadersRequest(Peer peer, ChunkDescriptor chunk) { 188  logger.debug("Send headers request to node {}", peer.getPeerNodeID()); 189  190  BlockHeadersRequestMessage message = 191  new BlockHeadersRequestMessage(++lastRequestId, chunk.getHash(), chunk.getCount()); 192  sendMessage(peer, message); 193  } 194  195  @Override 196  public long sendBodyRequest(Peer peer, @Nonnull BlockHeader header) { 197  logger.debug("Send body request block {} hash {} to peer {}", header.getNumber(), 198  HashUtil.toPrintableHash(header.getHash().getBytes()), peer.getPeerNodeID()); 199  200  BodyRequestMessage message = new BodyRequestMessage(++lastRequestId, header.getHash().getBytes()); 201  sendMessage(peer, message); 202  return message.getId(); 203  } 204  205  public Set<NodeID> getKnownPeersNodeIDs() { 206  return this.peersInformation.knownNodeIds(); 207  } 208  209  public void onTimePassed(Duration timePassed) { 210  this.syncState.tick(timePassed); 211  } 212  213  @Override 214  public void startSyncing(Peer peer) { 215  NodeID nodeID = peer.getPeerNodeID(); 216  logger.info("Start syncing with node {}", nodeID); 217  byte[] bestBlockHash = peersInformation.getPeer(peer).getStatus().getBestBlockHash(); 218  setSyncState(new CheckingBestHeaderSyncState( 219  syncConfiguration, 220  this, 221  blockHeaderValidationRule, peer, bestBlockHash)); 222  } 223  224  @Override 225  public void startDownloadingBodies( 226  List<Deque<BlockHeader>> pendingHeaders, Map<Peer, List<BlockIdentifier>> skeletons, Peer peer) { 227  // we keep track of best known block and we start to trust it when all headers are validated 228  List<BlockIdentifier> selectedSkeleton = skeletons.get(peer); 229  final long peerBestBlockNumber = selectedSkeleton.get(selectedSkeleton.size() - 1).getNumber(); 230  231  if (peerBestBlockNumber > blockSyncService.getLastKnownBlockNumber()) { 232  blockSyncService.setLastKnownBlockNumber(peerBestBlockNumber); 233  } 234  235  setSyncState(new DownloadingBodiesSyncState(syncConfiguration, 236  this, 237  peersInformation, 238  blockchain, 239  blockFactory, 240  blockSyncService, 241  blockValidationRule, 242  pendingHeaders, 243  skeletons)); 244  } 245  246  @Override 247  public void startDownloadingHeaders(Map<Peer, List<BlockIdentifier>> skeletons, long connectionPoint, Peer peer) { 248  setSyncState(new DownloadingHeadersSyncState( 249  syncConfiguration, 250  this, 251  consensusValidationMainchainView, 252  difficultyRule, 253  blockHeaderValidationRule, 254  peer, 255  skeletons, 256  connectionPoint)); 257  } 258  259  @Override 260  public void startDownloadingSkeleton(long connectionPoint, Peer peer) { 261  setSyncState(new DownloadingSkeletonSyncState( 262  syncConfiguration, 263  this, 264  peersInformation, 265  peer, 266  connectionPoint)); 267  } 268  269  @Override 270  public void startFindingConnectionPoint(Peer peer) { 271  NodeID peerId = peer.getPeerNodeID(); 272  logger.debug("Find connection point with node {}", peerId); 273  long bestBlockNumber = peersInformation.getPeer(peer).getStatus().getBestBlockNumber(); 274  setSyncState(new FindingConnectionPointSyncState( 275  syncConfiguration, this, blockStore, peer, bestBlockNumber)); 276  } 277  278  @Override 279  public void backwardSyncing(Peer peer) { 280  NodeID peerId = peer.getPeerNodeID(); 281  logger.debug("Starting backwards synchronization with node {}", peerId); 282  setSyncState(new DownloadingBackwardsHeadersSyncState( 283  syncConfiguration, 284  this, 285  blockStore, 286  peer 287  )); 288  } 289  290  @Override 291  public void backwardDownloadBodies(Block child, List<BlockHeader> toRequest, Peer peer) { 292  logger.debug("Starting backwards body download with node {}", peer.getPeerNodeID()); 293  setSyncState(new DownloadingBackwardsBodiesSyncState( 294  syncConfiguration, 295  this, 296  peersInformation, 297  genesis, 298  blockFactory, 299  blockStore, 300  child, 301  toRequest, 302  peer 303  )); 304  } 305  306  @Override 307  public void stopSyncing() { 308  int pendingMessagesCount = pendingMessages.size(); 309  pendingMessages.clear(); 310  logger.trace("Pending {} CLEAR", pendingMessagesCount); 311  // always that a syncing process ends unexpectedly the best block number is reset 312  blockSyncService.setLastKnownBlockNumber(blockchain.getBestBlock().getNumber()); 313  peersInformation.clearOldFailedPeers(); 314  setSyncState(new DecidingSyncState(syncConfiguration, 315  this, 316  peersInformation, 317  blockStore)); 318  } 319  320  @Override 321  public void onErrorSyncing(NodeID peerId, String message, EventType eventType, Object... arguments) { 322  peersInformation.reportErrorEvent(peerId, message, eventType, arguments); 323  stopSyncing(); 324  } 325  326  @Override 327  public void onSyncIssue(String message, Object... arguments) { 328  logger.trace(message, arguments); 329  stopSyncing(); 330  } 331  332  private void sendMessage(Peer peer, MessageWithId message) { 333  MessageType messageType = message.getResponseMessageType(); 334  long messageId = message.getId(); 335  pendingMessages.put(messageId, messageType); 336  logger.trace("Pending {}@{} ADDED for {}", messageType, messageId, peer.getPeerNodeID()); 337  peer.sendMessage(message); 338  } 339  340  private void setSyncState(SyncState syncState) { 341  this.syncState = syncState; 342  this.syncState.onEnter(); 343  } 344  345  @VisibleForTesting 346  int getPeersCount() { 347  return this.peersInformation.count(); 348  } 349  350  @VisibleForTesting 351  int getNoAdvancedPeers() { 352  BlockChainStatus chainStatus = this.blockchain.getStatus(); 353  354  if (chainStatus == null) { 355  return this.peersInformation.count(); 356  } 357  358  return this.peersInformation.countIf(s -> chainStatus.hasLowerTotalDifficultyThan(s.getStatus())); 359  } 360  361  @VisibleForTesting 362  public void registerExpectedMessage(MessageWithId message) { 363  pendingMessages.put(message.getId(), message.getMessageType()); 364  } 365  366  @VisibleForTesting 367  public SyncState getSyncState() { 368  return this.syncState; 369  } 370  371  @VisibleForTesting 372  public Map<Long, MessageType> getExpectedResponses() { 373  return pendingMessages; 374  } 375  376  private boolean isPending(long messageId, MessageType messageType) { 377  return pendingMessages.containsKey(messageId) && pendingMessages.get(messageId) == messageType; 378  } 379  380  private void removePendingMessage(long messageId, MessageType messageType) { 381  pendingMessages.remove(messageId); 382  logger.trace("Pending {}@{} REMOVED", messageType, messageId); 383  } 384 }