Coverage Summary for Class: DownloadingBackwardsBodiesSyncState (co.rsk.net.sync)

Class Class, % Method, % Line, %
DownloadingBackwardsBodiesSyncState 0% (0/1) 0% (0/7) 0% (0/61)


1 package co.rsk.net.sync; 2  3 import co.rsk.core.BlockDifficulty; 4 import co.rsk.net.Peer; 5 import co.rsk.net.messages.BodyResponseMessage; 6 import co.rsk.scoring.EventType; 7 import org.ethereum.core.Block; 8 import org.ethereum.core.BlockFactory; 9 import org.ethereum.core.BlockHeader; 10 import org.ethereum.core.Genesis; 11 import org.ethereum.db.BlockStore; 12 import org.slf4j.Logger; 13 import org.slf4j.LoggerFactory; 14  15 import java.util.*; 16  17 /** 18  * Given a sequential list of headers to request and an already stored child of the newest header 19  * DownloadingBackwardsBodiesSyncState downloads the bodies of the requested headers and connects them to the child. 20  * <p> 21  * If the child is block number 1 or the requested list contains block number 1 it is connected to genesis. 22  * <p> 23  * Assuming that child is valid, the previous block can be validated by comparing it's hash to the child's parent. 24  */ 25 public class DownloadingBackwardsBodiesSyncState extends BaseSyncState { 26  27  private static final Logger logger = LoggerFactory.getLogger("syncprocessor"); 28  29  private final PeersInformation peersInformation; 30  private final Genesis genesis; 31  private final BlockFactory blockFactory; 32  private final BlockStore blockStore; 33  private final Queue<BlockHeader> toRequest; 34  private final Peer selectedPeer; 35  36  private final PriorityQueue<Block> responses; 37  private final Map<Long, BlockHeader> inTransit; 38  private Block child; 39  40  public DownloadingBackwardsBodiesSyncState( 41  SyncConfiguration syncConfiguration, 42  SyncEventsHandler syncEventsHandler, 43  PeersInformation peersInformation, 44  Genesis genesis, 45  BlockFactory blockFactory, 46  BlockStore blockStore, 47  Block child, 48  List<BlockHeader> toRequest, 49  Peer peer) { 50  51  super(syncEventsHandler, syncConfiguration); 52  this.peersInformation = peersInformation; 53  this.genesis = genesis; 54  this.blockFactory = blockFactory; 55  this.blockStore = blockStore; 56  this.toRequest = new LinkedList<>(toRequest); 57  this.child = child; 58  this.selectedPeer = peer; 59  this.responses = new PriorityQueue<>(Comparator.comparingLong(Block::getNumber).reversed()); 60  this.inTransit = new HashMap<>(); 61  } 62  63  /** 64  * Validates and connects the bodies in order. 65  * <p> 66  * As the responses may come in any order, they are stored in a priority queue by block number. 67  * This allows connecting blocks sequentially with the current child. 68  * 69  * @param body The requested message containing the body 70  * @param peer The peer sending the message. 71  */ 72  @Override 73  public void newBody(BodyResponseMessage body, Peer peer) { 74  BlockHeader requestedHeader = inTransit.get(body.getId()); 75  if (requestedHeader == null) { 76  peersInformation.reportEvent(peer.getPeerNodeID(), EventType.INVALID_MESSAGE); 77  return; 78  } 79  80  Block block = blockFactory.newBlock(requestedHeader, body.getTransactions(), body.getUncles()); 81  block.seal(); 82  83  if (!block.getHash().equals(requestedHeader.getHash())) { 84  peersInformation.reportEvent(peer.getPeerNodeID(), EventType.INVALID_MESSAGE); 85  return; 86  } 87  88  resetTimeElapsed(); 89  inTransit.remove(body.getId()); 90  responses.add(block); 91  92  while (!responses.isEmpty() && responses.peek().isParentOf(child)) { 93  Block connectedBlock = responses.poll(); 94  BlockDifficulty blockchainDifficulty = blockStore.getTotalDifficultyForHash(child.getHash().getBytes()); 95  blockStore.saveBlock( 96  connectedBlock, 97  blockchainDifficulty.subtract(child.getCumulativeDifficulty()), 98  true); 99  child = connectedBlock; 100  } 101  102  if (child.getNumber() == 1) { 103  connectGenesis(child); 104  blockStore.flush(); 105  logger.info("Backward syncing complete"); 106  syncEventsHandler.stopSyncing(); 107  return; 108  } 109  110  while (!toRequest.isEmpty() && inTransit.size() < syncConfiguration.getMaxRequestedBodies()) { 111  requestBodies(toRequest.remove()); 112  } 113  114  if (toRequest.isEmpty() && inTransit.isEmpty()) { 115  blockStore.flush(); 116  logger.info("Backward syncing phase complete {}", block.getNumber()); 117  syncEventsHandler.stopSyncing(); 118  } 119  } 120  121  @Override 122  public void onEnter() { 123  if (child.getNumber() == 1L) { 124  connectGenesis(child); 125  blockStore.flush(); 126  syncEventsHandler.stopSyncing(); 127  return; 128  } 129  130  if (toRequest.isEmpty()) { 131  syncEventsHandler.stopSyncing(); 132  return; 133  } 134  135  while (!toRequest.isEmpty() && inTransit.size() < syncConfiguration.getMaxRequestedBodies()) { 136  BlockHeader headerToRequest = toRequest.remove(); 137  requestBodies(headerToRequest); 138  } 139  } 140  141  private void requestBodies(BlockHeader headerToRequest) { 142  long requestNumber = syncEventsHandler.sendBodyRequest(selectedPeer, headerToRequest); 143  inTransit.put(requestNumber, headerToRequest); 144  } 145  146  private void connectGenesis(Block child) { 147  if (!genesis.isParentOf(child)) { 148  throw new IllegalStateException("Genesis does not connect to the current chain."); 149  } 150  151  BlockDifficulty blockchainDifficulty = blockStore.getTotalDifficultyForHash(this.child.getHash().getBytes()); 152  if (!blockchainDifficulty.subtract(this.child.getCumulativeDifficulty()).equals(genesis.getCumulativeDifficulty())) { 153  throw new IllegalStateException("Blockchain difficulty does not match genesis."); 154  } 155  156  blockStore.saveBlock(genesis, genesis.getCumulativeDifficulty(), true); 157  } 158  159  @Override 160  protected void onMessageTimeOut() { 161  syncEventsHandler.onErrorSyncing( 162  selectedPeer.getPeerNodeID(), 163  "Timeout waiting requests {}", 164  EventType.TIMEOUT_MESSAGE, 165  this.getClass(), 166  selectedPeer.getPeerNodeID()); 167  } 168 }