Coverage Summary for Class: DownloadingHeadersSyncState (co.rsk.net.sync)
Class |
Class, %
|
Method, %
|
Line, %
|
DownloadingHeadersSyncState |
0%
(0/1)
|
0%
(0/7)
|
0%
(0/53)
|
1 package co.rsk.net.sync;
2
3 import co.rsk.core.bc.ConsensusValidationMainchainView;
4 import co.rsk.crypto.Keccak256;
5 import co.rsk.net.Peer;
6 import co.rsk.scoring.EventType;
7 import co.rsk.validators.BlockHeaderValidationRule;
8 import com.google.common.annotations.VisibleForTesting;
9 import org.ethereum.core.BlockHeader;
10 import org.ethereum.core.BlockIdentifier;
11 import org.ethereum.rpc.TypeConverter;
12 import org.ethereum.util.ByteUtil;
13 import org.ethereum.validator.DependentBlockHeaderRule;
14
15 import java.util.*;
16 import java.util.concurrent.ConcurrentHashMap;
17
18 public class DownloadingHeadersSyncState extends BaseSyncState {
19
20 private final Map<Peer, List<BlockIdentifier>> skeletons;
21 private final List<Deque<BlockHeader>> pendingHeaders;
22 private final ChunksDownloadHelper chunksDownloadHelper;
23 private final DependentBlockHeaderRule blockParentValidationRule;
24 private final BlockHeaderValidationRule blockHeaderValidationRule;
25 private final Peer selectedPeer;
26 private Map<Keccak256, BlockHeader> pendingHeadersByHash;
27
28 public DownloadingHeadersSyncState(
29 SyncConfiguration syncConfiguration,
30 SyncEventsHandler syncEventsHandler,
31 ConsensusValidationMainchainView mainchainView,
32 DependentBlockHeaderRule blockParentValidationRule,
33 BlockHeaderValidationRule blockHeaderValidationRule,
34 Peer peer,
35 Map<Peer, List<BlockIdentifier>> skeletons,
36 long connectionPoint) {
37 super(syncEventsHandler, syncConfiguration);
38 this.blockParentValidationRule = blockParentValidationRule;
39 this.blockHeaderValidationRule = blockHeaderValidationRule;
40 this.selectedPeer = peer;
41 this.pendingHeaders = new ArrayList<>();
42 this.skeletons = skeletons;
43 this.chunksDownloadHelper = new ChunksDownloadHelper(
44 syncConfiguration,
45 skeletons.get(selectedPeer),
46 connectionPoint);
47 this.pendingHeadersByHash = new ConcurrentHashMap<>();
48 mainchainView.setPendingHeaders(pendingHeadersByHash);
49 }
50
51 @Override
52 public void newBlockHeaders(List<BlockHeader> chunk) {
53 Optional<ChunkDescriptor> currentChunkOpt = chunksDownloadHelper.getCurrentChunk();
54 if (!currentChunkOpt.isPresent()) {
55 syncEventsHandler.onSyncIssue(
56 "Current chunk not present. Node {}",
57 selectedPeer.getPeerNodeID());
58 return;
59 }
60 ChunkDescriptor currentChunk = currentChunkOpt.get();
61 if (chunk.size() != currentChunk.getCount()
62 || !ByteUtil.fastEquals(chunk.get(0).getHash().getBytes(), currentChunk.getHash())) {
63 syncEventsHandler.onErrorSyncing(
64 selectedPeer.getPeerNodeID(),
65 "Invalid chunk received from node {} {}", EventType.INVALID_MESSAGE,
66 selectedPeer.getPeerNodeID(),
67 TypeConverter.toUnformattedJsonHex(currentChunk.getHash()));
68 return;
69 }
70
71 Deque<BlockHeader> headers = new ArrayDeque<>();
72 // the headers come ordered by block number desc
73 // we start adding the first parent header
74 BlockHeader headerToAdd = chunk.get(chunk.size() - 1);
75 headers.add(headerToAdd);
76 pendingHeadersByHash.put(headerToAdd.getHash(), headerToAdd);
77
78 for (int k = 1; k < chunk.size(); ++k) {
79 BlockHeader parentHeader = chunk.get(chunk.size() - k);
80 BlockHeader header = chunk.get(chunk.size() - k - 1);
81
82 if (!blockHeaderIsValid(header, parentHeader)) {
83 syncEventsHandler.onErrorSyncing(
84 selectedPeer.getPeerNodeID(),
85 "Invalid header received from node {} {} {}", EventType.INVALID_HEADER,
86 header.getNumber(), header.getPrintableHash());
87 return;
88 }
89
90 headers.add(header);
91 pendingHeadersByHash.put(header.getHash(), header);
92 }
93
94 pendingHeaders.add(headers);
95
96 if (!chunksDownloadHelper.hasNextChunk()) {
97 // Finished verifying headers
98 syncEventsHandler.startDownloadingBodies(pendingHeaders, skeletons, selectedPeer);
99 return;
100 }
101
102 resetTimeElapsed();
103 trySendRequest();
104 }
105
106 @Override
107 public void onEnter() {
108 trySendRequest();
109 }
110
111 @VisibleForTesting
112 public List<BlockIdentifier> getSkeleton() {
113 return chunksDownloadHelper.getSkeleton();
114 }
115
116 private void trySendRequest() {
117 syncEventsHandler.sendBlockHeadersRequest(selectedPeer, chunksDownloadHelper.getNextChunk());
118 }
119
120 @Override
121 protected void onMessageTimeOut() {
122 syncEventsHandler.onErrorSyncing(
123 selectedPeer.getPeerNodeID(),
124 "Timeout waiting requests {}",
125 EventType.TIMEOUT_MESSAGE,
126 this.getClass(),
127 selectedPeer.getPeerNodeID());
128 }
129
130 private boolean blockHeaderIsValid(BlockHeader header, BlockHeader parentHeader) {
131 if (!parentHeader.getHash().equals(header.getParentHash())) {
132 return false;
133 }
134
135 if (header.getNumber() != parentHeader.getNumber() + 1) {
136 return false;
137 }
138
139 if (!blockHeaderValidationRule.isValid(header)) {
140 return false;
141 }
142
143 return blockParentValidationRule.validate(header, parentHeader);
144 }
145 }