Skip to content

Commit e9833d1

Browse files
committed
Merge remote-tracking branch 'origin/develop' into etcm-213/relad-bloom-after-restart
2 parents 54e75c1 + effa2d1 commit e9833d1

File tree

2 files changed

+41
-8
lines changed

2 files changed

+41
-8
lines changed

src/main/scala/io/iohk/ethereum/blockchain/sync/PivotBlockSelector.scala

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,15 @@ class PivotBlockSelector(
109109
timeout: Cancellable,
110110
headers: Map[ByteString, BlockHeaderWithVotes]
111111
): Unit = {
112-
val BlockHeaderWithVotes(mostPopularBlockHeader, updatedVotes) = headers.mostVotedHeader
112+
// most voted header can return empty if we asked one peer and it returned us non expected block. Then headers map is empty
113+
// so there is no most voted header
114+
val maybeBlockHeaderWithVotes = headers.mostVotedHeader
113115
// All peers responded - consensus reached
114-
if (peersToAsk.isEmpty && updatedVotes >= minPeersToChoosePivotBlock) {
116+
if (peersToAsk.isEmpty && maybeBlockHeaderWithVotes.exists(hWv => hWv.votes >= minPeersToChoosePivotBlock)) {
115117
timeout.cancel()
116-
sendResponseAndCleanup(mostPopularBlockHeader)
118+
sendResponseAndCleanup(maybeBlockHeaderWithVotes.get.header)
117119
// Consensus could not be reached - ask additional peer if available
118-
} else if (!isPossibleToReachConsensus(peersToAsk.size, updatedVotes)) {
120+
} else if (!isPossibleToReachConsensus(peersToAsk.size, maybeBlockHeaderWithVotes.map(_.votes).getOrElse(0))) {
119121
timeout.cancel()
120122
if (waitingPeers.nonEmpty) { // There are more peers to ask
121123
val newTimeout = scheduler.scheduleOnce(peerResponseTimeout, self, ElectionPivotBlockTimeout)
@@ -206,11 +208,11 @@ object PivotBlockSelector {
206208
case class BlockHeaderWithVotes(header: BlockHeader, votes: Int = 1) {
207209
def vote: BlockHeaderWithVotes = copy(votes = votes + 1)
208210
}
209-
211+
import cats.implicits._
210212
implicit class SortableHeadersMap(headers: Map[ByteString, BlockHeaderWithVotes]) {
211-
def mostVotedHeader: BlockHeaderWithVotes = headers.maxBy { case (_, headerWithVotes) =>
212-
headerWithVotes.votes
213-
}._2
213+
def mostVotedHeader: Option[BlockHeaderWithVotes] = {
214+
headers.toList.maximumByOption { case (_, headerWithVotes) => headerWithVotes.votes }.map(_._2)
215+
}
214216
}
215217

216218
case class ElectionDetails(participants: List[Peer], expectedPivotBlock: BigInt) {

src/test/scala/io/iohk/ethereum/blockchain/sync/PivotBlockSelectorSpec.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,37 @@ class PivotBlockSelectorSpec
188188
)
189189
}
190190

191+
it should "handle case when one peer responded with wrong block header" in new TestSetup {
192+
override def minPeersToChoosePivotBlock: Int = 1
193+
194+
updateHandshakedPeers(HandshakedPeers(singlePeer))
195+
196+
pivotBlockSelector ! SelectPivotBlock
197+
198+
peerMessageBus.expectMsgAllOf(
199+
Subscribe(MessageClassifier(Set(BlockHeaders.code), PeerSelector.WithId(peer1.id)))
200+
)
201+
202+
etcPeerManager.expectMsgAllOf(
203+
EtcPeerManagerActor.SendMessage(GetBlockHeaders(Left(expectedPivotBlock), 1, 0, reverse = false), peer1.id)
204+
)
205+
206+
// peer responds with block header number
207+
pivotBlockSelector ! MessageFromPeer(
208+
BlockHeaders(Seq(pivotBlockHeader.copy(number = expectedPivotBlock + 1))),
209+
peer1.id
210+
)
211+
212+
peerMessageBus.expectMsgAllOf(
213+
Unsubscribe(MessageClassifier(Set(BlockHeaders.code), PeerSelector.WithId(peer1.id))),
214+
Unsubscribe()
215+
)
216+
time.advance(syncConfig.syncRetryInterval)
217+
218+
fastSync.expectNoMessage() // consensus not reached - process have to be repeated
219+
peerMessageBus.expectNoMessage()
220+
}
221+
191222
it should "not ask additional peers if not needed" in new TestSetup {
192223
override val minPeersToChoosePivotBlock = 2
193224
override val peersToChoosePivotBlockMargin = 1

0 commit comments

Comments
 (0)