Skip to content

Commit 595c95f

Browse files
authored
[Etcm 521] Fast sync integration tests (#944)
* [ETCM-521] Add integration tests for fast sync - Apply fix for branch resolving not being started * [ETCM-521] Fix failing test, cleanup * [ETCM-521] Fix failing test * [ETCM-521] Address review comments * Add comments, rename helper methods
1 parent 9893aa0 commit 595c95f

File tree

3 files changed

+141
-9
lines changed

3 files changed

+141
-9
lines changed

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package io.iohk.ethereum.sync
22

33
import akka.util.ByteString
44
import io.iohk.ethereum.FlatSpecBase
5+
import io.iohk.ethereum.blockchain.sync.Blacklist.BlacklistReason.BlacklistReasonType
56
import io.iohk.ethereum.domain._
67
import io.iohk.ethereum.ledger.InMemoryWorldStateProxy
8+
import io.iohk.ethereum.network.PeerId
79
import io.iohk.ethereum.sync.FastSyncItSpec._
810
import io.iohk.ethereum.sync.util.FastSyncItSpecUtils.FakePeer
911
import io.iohk.ethereum.sync.util.SyncCommonItSpec._
@@ -180,6 +182,65 @@ class FastSyncItSpec extends FlatSpecBase with Matchers with BeforeAndAfterAll {
180182
}
181183
}
182184

185+
it should "switch to regular sync once `safeDownloadTarget` is reached" in customTestCaseResourceM(
186+
FakePeer.start3FakePeersRes()
187+
) { case (peer1, peer2, peer3) =>
188+
for {
189+
_ <- peer2.importBlocksUntil(1200)(IdentityUpdate)
190+
_ <- peer3.importBlocksUntil(1200)(IdentityUpdate)
191+
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node))
192+
_ <- peer1.startFastSync().delayExecution(50.milliseconds)
193+
_ <- peer1.waitForFastSyncFinish()
194+
} yield {
195+
assert(peer1.bl.getBestBlockNumber() == peer3.bl.getBestBlockNumber() - peer3.testSyncConfig.pivotBlockOffset)
196+
}
197+
}
198+
199+
it should "blacklist peer on Invalid batch last header number" in customTestCaseResourceM(
200+
FakePeer.start3FakePeersRes()
201+
) { case (peer1, peer2, peer3) =>
202+
for {
203+
_ <- peer2.importBlocksUntil(1000)(IdentityUpdate)
204+
_ <- peer3.importInvalidBlockNumbers(201, 1200)(IdentityUpdate)
205+
206+
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node))
207+
_ <- peer1.startFastSync().delayExecution(50.milliseconds)
208+
_ <- peer1.waitForFastSyncFinish()
209+
} yield {
210+
// Peer3 is blacklisted
211+
val blacklistedPeer = PeerId(s"${peer3.node.addr.getHostAddress}:${peer3.node.tcpPort}")
212+
val blacklistReason = peer1.blacklist.cache.getIfPresent(blacklistedPeer)
213+
214+
assert(peer1.blacklist.isBlacklisted(blacklistedPeer))
215+
assert(blacklistReason.get == BlacklistReasonType.BlockHeaderValidationFailedType)
216+
}
217+
}
218+
219+
it should "sync blockchain when peer responds with invalid batch last header hash" in customTestCaseResourceM(
220+
FakePeer.start4FakePeersRes()
221+
) { case (peer1, peer2, peer3, peer4) =>
222+
for {
223+
_ <- peer1.importBlocksUntil(400)(IdentityUpdate)
224+
_ <- peer2.importBlocksUntil(1000)(IdentityUpdate)
225+
226+
_ <- peer3.importInvalidBlocks(600, 800)(IdentityUpdate)
227+
_ <- peer3.importBlocksUntil(1200)(updateStateAtBlock(1000))
228+
229+
_ <- peer4.importBlocksUntil(1100)(IdentityUpdate)
230+
231+
_ <- peer1.connectToPeers(Set(peer2.node, peer3.node, peer4.node))
232+
_ <- peer1.startFastSync().delayExecution(50.milliseconds)
233+
_ <- peer2.importBlocksUntil(1200)(IdentityUpdate).startAndForget
234+
_ <- peer1.waitForFastSyncFinish()
235+
} yield {
236+
// Peer3 is blacklisted
237+
val blacklistedPeer = PeerId(s"${peer3.node.addr.getHostAddress}:${peer3.node.tcpPort}")
238+
val blacklistReason = peer1.blacklist.cache.getIfPresent(blacklistedPeer)
239+
240+
assert(peer1.blacklist.isBlacklisted(blacklistedPeer))
241+
assert(blacklistReason.get == BlacklistReasonType.BlockHeaderValidationFailedType)
242+
}
243+
}
183244
}
184245

185246
object FastSyncItSpec {

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

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
223223
nodesPerRequest = 200,
224224
maxTargetDifference = 1,
225225
syncRetryInterval = 50.milliseconds,
226-
blacklistDuration = 100.seconds
226+
blacklistDuration = 100.seconds,
227+
fastSyncMaxBatchRetries = 2,
228+
fastSyncBlockValidationN = 200
227229
)
228230

229231
lazy val broadcaster = new BlockBroadcast(etcPeerManager)
@@ -298,21 +300,90 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
298300
(newBlock, newWeight, parentWorld)
299301
}
300302

303+
private def generateInvalidBlock(
304+
currentBestBlock: Block
305+
)(updateWorldForBlock: (BigInt, InMemoryWorldStateProxy) => InMemoryWorldStateProxy): Task[Unit] = {
306+
Task {
307+
val currentWorld = getMptForBlock(currentBestBlock)
308+
309+
val newBlockNumber = currentBestBlock.header.number + 1
310+
val newWorld = updateWorldForBlock(newBlockNumber, currentWorld)
311+
312+
// The child block is made invalid by not properly updating its parent hash.
313+
val childBlock =
314+
currentBestBlock.copy(header =
315+
currentBestBlock.header.copy(
316+
number = newBlockNumber,
317+
stateRoot = newWorld.stateRootHash
318+
)
319+
)
320+
val newWeight = ChainWeight.totalDifficultyOnly(1)
321+
322+
broadcastBlock(childBlock, newWeight)
323+
bl.save(childBlock, Seq(), newWeight, saveAsBestBlock = true)
324+
}
325+
}
326+
327+
private def generateValidBlock(
328+
currentBestBlock: Block
329+
)(updateWorldForBlock: (BigInt, InMemoryWorldStateProxy) => InMemoryWorldStateProxy): Task[Unit] = {
330+
Task {
331+
val currentWeight = bl.getChainWeightByHash(currentBestBlock.hash).get
332+
val currentWorld = getMptForBlock(currentBestBlock)
333+
val (newBlock, newWeight, _) =
334+
createChildBlock(currentBestBlock, currentWeight, currentWorld)(updateWorldForBlock)
335+
bl.save(newBlock, Seq(), newWeight, saveAsBestBlock = true)
336+
broadcastBlock(newBlock, newWeight)
337+
}
338+
}
339+
301340
def importBlocksUntil(
302341
n: BigInt
303342
)(updateWorldForBlock: (BigInt, InMemoryWorldStateProxy) => InMemoryWorldStateProxy): Task[Unit] = {
304343
Task(bl.getBestBlock()).flatMap { block =>
305344
if (block.get.number >= n) {
306345
Task(())
307346
} else {
308-
Task {
309-
val currentWeight = bl.getChainWeightByHash(block.get.hash).get
310-
val currentWolrd = getMptForBlock(block.get)
311-
val (newBlock, newWeight, _) = createChildBlock(block.get, currentWeight, currentWolrd)(updateWorldForBlock)
312-
bl.save(newBlock, Seq(), newWeight, saveAsBestBlock = true)
313-
broadcastBlock(newBlock, newWeight)
314-
}.flatMap(_ => importBlocksUntil(n)(updateWorldForBlock))
347+
generateValidBlock(block.get)(updateWorldForBlock).flatMap(_ => importBlocksUntil(n)(updateWorldForBlock))
348+
}
349+
}
350+
}
351+
352+
def importInvalidBlocks(
353+
from: BigInt,
354+
to: BigInt
355+
)(updateWorldForBlock: (BigInt, InMemoryWorldStateProxy) => InMemoryWorldStateProxy): Task[Unit] = {
356+
Task(bl.getBestBlock()).flatMap { block =>
357+
if (block.get.number >= to) {
358+
Task(())
359+
} else if (block.get.number >= from) {
360+
generateInvalidBlock(block.get)(updateWorldForBlock).flatMap(_ =>
361+
importInvalidBlocks(from, to)(updateWorldForBlock)
362+
)
363+
} else {
364+
generateValidBlock(block.get)(updateWorldForBlock).flatMap(_ =>
365+
importInvalidBlocks(from, to)(updateWorldForBlock)
366+
)
315367
}
368+
369+
}
370+
}
371+
372+
def importInvalidBlockNumbers(
373+
from: BigInt,
374+
to: BigInt
375+
)(updateWorldForBlock: (BigInt, InMemoryWorldStateProxy) => InMemoryWorldStateProxy): Task[Unit] = {
376+
Task(bl.getBestBlock()).flatMap { block =>
377+
if (block.get.number >= to) {
378+
Task(())
379+
} else if (block.get.number >= from) {
380+
generateInvalidBlock(block.get)(updateWorldForBlock).flatMap(_ =>
381+
importInvalidBlockNumbers(from, to)(updateWorldForBlock)
382+
)
383+
} else {
384+
importBlocksUntil(from)(updateWorldForBlock)
385+
}
386+
316387
}
317388
}
318389

src/main/scala/io/iohk/ethereum/blockchain/sync/fast/FastSyncBranchResolverActor.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class FastSyncBranchResolverActor(
7979
handlePeerListMessages orElse {
8080
case ResponseReceived(peer, BlockHeaders(headers), durationMs) if peer == searchState.masterPeer =>
8181
context.unwatch(requestHandler)
82-
headers match {
82+
headers.toList match {
8383
case childHeader :: Nil if childHeader.number == blockHeaderNumberToSearch =>
8484
log.debug(ReceivedBlockHeaderLog, blockHeaderNumberToSearch, peer.id, durationMs)
8585
handleBinarySearchBlockHeaderResponse(searchState, childHeader)

0 commit comments

Comments
 (0)