Skip to content

Commit 635f002

Browse files
author
Michał Mrożek
authored
Merge pull request #761 from input-output-hk/etcm-198-fast-sync-stuck-issue
[ETCM-198] Update fastsync to request headers only from peers with be…
2 parents f0befc1 + 038c1d9 commit 635f002

File tree

3 files changed

+52
-9
lines changed

3 files changed

+52
-9
lines changed

src/it/scala/io/iohk/ethereum/sync/FastSyncItSpec.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,34 @@ class FastSyncItSpec extends FlatSpecBase with Matchers with BeforeAndAfterAll {
148148
assert(peer1.bl.getBestBlockNumber() == peer2.bl.getBestBlockNumber() - peer2.testSyncConfig.pivotBlockOffset)
149149
}
150150
}
151+
152+
it should "follow the longest chains" in customTestCaseResourceM(
153+
FakePeer.start4FakePeersRes()
154+
) { case (peer1, peer2, peer3, peer4) =>
155+
for {
156+
_ <- peer2.importBlocksUntil(1000)(IdentityUpdate)
157+
_ <- peer3.importBlocksUntil(1000)(IdentityUpdate)
158+
_ <- peer4.importBlocksUntil(1000)(IdentityUpdate)
159+
160+
_ <- peer2.importBlocksUntil(2000)(IdentityUpdate)
161+
_ <- peer3.importBlocksUntil(3000)(updateStateAtBlock(1001, endAccount = 3000))
162+
_ <- peer4.importBlocksUntil(3000)(updateStateAtBlock(1001, endAccount = 3000))
163+
164+
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node, peer4.node))
165+
_ <- peer1.startFastSync().delayExecution(50.milliseconds)
166+
_ <- peer1.waitForFastSyncFinish()
167+
} yield {
168+
val trie = peer1.getBestBlockTrie()
169+
val synchronizingPeerHaveAllData = peer1.containsExpectedDataUpToAccountAtBlock(3000, 1001)
170+
// due to the fact that function generating state is deterministic both peer3 and peer4 ends up with exactly same
171+
// state, so peer1 can get whole trie from both of them.
172+
assert(peer1.bl.getBestBlockNumber() == peer3.bl.getBestBlockNumber() - peer3.testSyncConfig.pivotBlockOffset)
173+
assert(peer1.bl.getBestBlockNumber() == peer4.bl.getBestBlockNumber() - peer4.testSyncConfig.pivotBlockOffset)
174+
assert(trie.isDefined)
175+
assert(synchronizingPeerHaveAllData)
176+
}
177+
}
178+
151179
}
152180

153181
object FastSyncItSpec {

src/it/scala/io/iohk/ethereum/sync/util/FastSyncItSpecUtils.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ object FastSyncItSpecUtils {
144144
peer3 <- start1FakePeerRes(fakePeerCustomConfig3, "Peer3")
145145
} yield (peer1, peer2, peer3)
146146
}
147-
}
148147

148+
def start4FakePeersRes(
149+
fakePeerCustomConfig1: FakePeerCustomConfig = defaultConfig,
150+
fakePeerCustomConfig2: FakePeerCustomConfig = defaultConfig,
151+
fakePeerCustomConfig3: FakePeerCustomConfig = defaultConfig,
152+
fakePeerCustomConfig4: FakePeerCustomConfig = defaultConfig
153+
): Resource[Task, (FakePeer, FakePeer, FakePeer, FakePeer)] = {
154+
for {
155+
peer1 <- start1FakePeerRes(fakePeerCustomConfig1, "Peer1")
156+
peer2 <- start1FakePeerRes(fakePeerCustomConfig2, "Peer2")
157+
peer3 <- start1FakePeerRes(fakePeerCustomConfig3, "Peer3")
158+
peer4 <- start1FakePeerRes(fakePeerCustomConfig4, "Peer3")
159+
} yield (peer1, peer2, peer3, peer4)
160+
}
161+
}
149162
}

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.iohk.ethereum.blockchain.sync
22

33
import java.time.Instant
4-
54
import akka.actor._
65
import akka.util.ByteString
76
import cats.data.NonEmptyList
@@ -25,7 +24,6 @@ import io.iohk.ethereum.network.p2p.messages.PV63._
2524
import io.iohk.ethereum.utils.ByteStringUtils
2625
import io.iohk.ethereum.utils.Config.SyncConfig
2726
import org.bouncycastle.util.encoders.Hex
28-
2927
import scala.annotation.tailrec
3028
import scala.concurrent.ExecutionContext.Implicits.global
3129
import scala.concurrent.duration.{FiniteDuration, _}
@@ -647,24 +645,25 @@ class FastSync(
647645
} else {
648646
val now = Instant.now()
649647
val peers = unassignedPeers
650-
.filter(p => peerRequestsTime.get(p).forall(d => d.plusMillis(fastSyncThrottle.toMillis).isBefore(now)))
648+
.filter(p => peerRequestsTime.get(p.peer).forall(d => d.plusMillis(fastSyncThrottle.toMillis).isBefore(now)))
651649
peers
652650
.take(maxConcurrentRequests - assignedHandlers.size)
653-
.toSeq
654-
.sortBy(_.ref.toString())
651+
.sortBy(_.info.maxBlockNumber)(Ordering[BigInt].reverse)
655652
.foreach(assignBlockchainWork)
656653
}
657654
}
658655

659-
def assignBlockchainWork(peer: Peer): Unit = {
656+
def assignBlockchainWork(peerWithInfo: PeerWithInfo): Unit = {
657+
val PeerWithInfo(peer, peerInfo) = peerWithInfo
660658
if (syncState.receiptsQueue.nonEmpty) {
661659
requestReceipts(peer)
662660
} else if (syncState.blockBodiesQueue.nonEmpty) {
663661
requestBlockBodies(peer)
664662
} else if (
665663
requestedHeaders.isEmpty &&
666664
context.child(BlockHeadersHandlerName).isEmpty &&
667-
syncState.bestBlockHeaderNumber < syncState.safeDownloadTarget
665+
syncState.bestBlockHeaderNumber < syncState.safeDownloadTarget &&
666+
peerInfo.maxBlockNumber >= syncState.pivotBlock.number
668667
) {
669668
requestBlockHeaders(peer)
670669
}
@@ -737,7 +736,8 @@ class FastSync(
737736
peerRequestsTime += (peer -> Instant.now())
738737
}
739738

740-
def unassignedPeers: Set[Peer] = peersToDownloadFrom.keySet diff assignedHandlers.values.toSet
739+
def unassignedPeers: List[PeerWithInfo] =
740+
(peersToDownloadFrom -- assignedHandlers.values).map(PeerWithInfo.tupled).toList
741741

742742
def blockchainDataToDownload: Boolean =
743743
syncState.blockChainWorkQueued || syncState.bestBlockHeaderNumber < syncState.safeDownloadTarget
@@ -768,6 +768,8 @@ class FastSync(
768768

769769
object FastSync {
770770

771+
case class PeerWithInfo(peer: Peer, info: PeerInfo)
772+
771773
// scalastyle:off parameter.number
772774
def props(
773775
fastSyncStateStorage: FastSyncStateStorage,

0 commit comments

Comments
 (0)