@@ -35,13 +35,19 @@ class PivotBlockSelector(
35
35
import PivotBlockSelector ._
36
36
import syncConfig ._
37
37
38
+ private var pivotBlockRetryCount = 0
39
+
38
40
override def receive : Receive = idle
39
41
40
42
private def idle : Receive = handlePeerListMessages orElse { case SelectPivotBlock =>
41
- val election @ ElectionDetails (correctPeers, expectedPivotBlock) = collectVoters
43
+ val electionDetails = collectVoters()
44
+ startPivotBlockSelection(electionDetails)
45
+ }
42
46
43
- if (election.isEnoughVoters(minPeersToChoosePivotBlock)) {
47
+ private def startPivotBlockSelection (election : ElectionDetails ): Unit = {
48
+ val ElectionDetails (correctPeers, currentBestBlockNumber, expectedPivotBlock) = election
44
49
50
+ if (election.hasEnoughVoters(minPeersToChoosePivotBlock)) {
45
51
val (peersToAsk, waitingPeers) = correctPeers.splitAt(minPeersToChoosePivotBlock + peersToChoosePivotBlockMargin)
46
52
47
53
log.info(
@@ -64,17 +70,35 @@ class PivotBlockSelector(
64
70
)
65
71
} else {
66
72
log.info(
67
- " Cannot pick pivot block. Need at least {} peers, but there are only {} which meet the criteria ({} all available at the moment). Retrying in {}" ,
73
+ " Cannot pick pivot block. Need at least {} peers, but there are only {} which meet the criteria " +
74
+ " ({} all available at the moment)." ,
68
75
minPeersToChoosePivotBlock,
69
76
correctPeers.size,
70
77
peersToDownloadFrom.size,
78
+ currentBestBlockNumber
79
+ )
80
+ retryPivotBlockSelection(currentBestBlockNumber)
81
+ }
82
+ }
83
+
84
+ // Voters are collected until minimum peers to choose pivot block is obtained.
85
+ private def retryPivotBlockSelection (pivotBlockNumber : BigInt ): Unit = {
86
+ pivotBlockRetryCount += 1
87
+ if (pivotBlockRetryCount <= maxPivotBlockFailuresCount && pivotBlockNumber > 0 ) {
88
+ val electionDetails = collectVoters(Some (pivotBlockNumber))
89
+ startPivotBlockSelection(electionDetails)
90
+ } else {
91
+ log.debug(
92
+ " Cannot pick pivot block. Current best block number [{}]. Retrying in [{}]" ,
93
+ pivotBlockNumber,
71
94
startRetryInterval
72
95
)
96
+ // Restart the whole process.
73
97
scheduleRetry(startRetryInterval)
74
98
}
75
99
}
76
100
77
- def runningPivotBlockElection (
101
+ private def runningPivotBlockElection (
78
102
peersToAsk : Set [PeerId ],
79
103
waitingPeers : List [PeerId ],
80
104
pivotBlockNumber : BigInt ,
@@ -85,10 +109,7 @@ class PivotBlockSelector(
85
109
case MessageFromPeer (blockHeaders : BlockHeaders , peerId) =>
86
110
peerEventBus ! Unsubscribe (MessageClassifier (Set (Codes .BlockHeadersCode ), PeerSelector .WithId (peerId)))
87
111
val updatedPeersToAsk = peersToAsk - peerId
88
- val targetBlockHeaderOpt =
89
- if (blockHeaders.headers.size != 1 ) None
90
- else
91
- blockHeaders.headers.find(header => header.number == pivotBlockNumber)
112
+ val targetBlockHeaderOpt = blockHeaders.headers.find(header => header.number == pivotBlockNumber)
92
113
targetBlockHeaderOpt match {
93
114
case Some (targetBlockHeader) =>
94
115
val newValue =
@@ -159,6 +180,7 @@ class PivotBlockSelector(
159
180
peersLeft + bestHeaderVotes >= minPeersToChoosePivotBlock
160
181
161
182
private def scheduleRetry (interval : FiniteDuration ): Unit = {
183
+ pivotBlockRetryCount = 0
162
184
scheduler.scheduleOnce(interval, self, SelectPivotBlock )
163
185
context become idle
164
186
}
@@ -178,7 +200,7 @@ class PivotBlockSelector(
178
200
)
179
201
}
180
202
181
- private def collectVoters : ElectionDetails = {
203
+ private def collectVoters ( previousBestBlockNumber : Option [ BigInt ] = None ) : ElectionDetails = {
182
204
val peersUsedToChooseTarget = peersToDownloadFrom.collect {
183
205
case (_, PeerWithInfo (peer, PeerInfo (_, _, true , maxBlockNumber, _))) =>
184
206
(peer, maxBlockNumber)
@@ -188,12 +210,20 @@ class PivotBlockSelector(
188
210
val bestPeerBestBlockNumber = peersSortedByBestNumber.headOption
189
211
.map { case (_, bestPeerBestBlockNumber) => bestPeerBestBlockNumber }
190
212
.getOrElse(BigInt (0 ))
191
- val expectedPivotBlock = (bestPeerBestBlockNumber - syncConfig.pivotBlockOffset).max(0 )
213
+
214
+ // The current best block number is pushed back by `pivotBlockNumberResetDelta`
215
+ // if this request is issued by the retry logic.
216
+ val currentBestBlockNumber : BigInt =
217
+ previousBestBlockNumber
218
+ .map(_ - pivotBlockNumberResetDelta.max(0 ))
219
+ .getOrElse(bestPeerBestBlockNumber)
220
+
221
+ val expectedPivotBlock = (currentBestBlockNumber - syncConfig.pivotBlockOffset).max(0 )
192
222
val correctPeers = peersSortedByBestNumber
193
223
.takeWhile { case (_, number) => number >= expectedPivotBlock }
194
224
.map { case (peer, _) => peer }
195
225
196
- ElectionDetails (correctPeers, expectedPivotBlock)
226
+ ElectionDetails (correctPeers, currentBestBlockNumber, expectedPivotBlock)
197
227
}
198
228
}
199
229
@@ -209,7 +239,7 @@ object PivotBlockSelector {
209
239
Props (new PivotBlockSelector (etcPeerManager : ActorRef , peerEventBus, syncConfig, scheduler, fastSync, blacklist))
210
240
211
241
case object SelectPivotBlock
212
- case class Result (targetBlockHeader : BlockHeader )
242
+ final case class Result (targetBlockHeader : BlockHeader )
213
243
214
244
case object ElectionPivotBlockTimeout
215
245
@@ -223,7 +253,11 @@ object PivotBlockSelector {
223
253
}
224
254
}
225
255
226
- case class ElectionDetails (participants : List [Peer ], expectedPivotBlock : BigInt ) {
227
- def isEnoughVoters (minNumberOfVoters : Int ): Boolean = participants.size >= minNumberOfVoters
256
+ final case class ElectionDetails (
257
+ participants : List [Peer ],
258
+ currentBestBlockNumber : BigInt ,
259
+ expectedPivotBlock : BigInt
260
+ ) {
261
+ def hasEnoughVoters (minNumberOfVoters : Int ): Boolean = participants.size >= minNumberOfVoters
228
262
}
229
263
}
0 commit comments