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 }