Skip to content

Commit 1944bc3

Browse files
committed
ETCM-645: Add it-test for checkpointing
1 parent e52683f commit 1944bc3

File tree

6 files changed

+152
-5
lines changed

6 files changed

+152
-5
lines changed

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

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.iohk.ethereum.sync
33
import com.typesafe.config.ConfigValueFactory
44
import io.iohk.ethereum.FreeSpecBase
55
import io.iohk.ethereum.metrics.{Metrics, MetricsConfig}
6+
import io.iohk.ethereum.network.PeerId
67
import io.iohk.ethereum.sync.util.RegularSyncItSpecUtils.FakePeer
78
import io.iohk.ethereum.sync.util.SyncCommonItSpec._
89
import io.iohk.ethereum.utils.Config
@@ -76,6 +77,108 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
7677
}
7778
}
7879

80+
"peers should keep being synced on checkpoints" in customTestCaseResourceM(
81+
FakePeer.start2FakePeersRes()
82+
) { case (peer1, peer2) =>
83+
val blockNumber: Int = 2000
84+
for {
85+
_ <- peer1.importBlocksUntil(blockNumber)(IdentityUpdate)
86+
_ <- peer1.startRegularSync()
87+
_ <- peer2.startRegularSync()
88+
_ <- peer2.connectToPeers(Set(peer1.node))
89+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber)
90+
_ <- peer2.addCheckpointedBlock(peer2.bl.getBestBlock().get)
91+
_ <- peer1.waitForRegularSyncLoadLastBlock(blockNumber + 1)
92+
} yield {
93+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
94+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
95+
}
96+
}
97+
98+
"peers should keep being synced on checkpoints even if 2 checkpoints are issued to different forks at the same time" in customTestCaseResourceM(
99+
FakePeer.start2FakePeersRes()
100+
) { case (peer1, peer2) =>
101+
val blockNumber: Int = 2000
102+
for {
103+
_ <- peer1.importBlocksUntil(blockNumber)(IdentityUpdate)
104+
_ <- peer1.startRegularSync()
105+
_ <- peer2.startRegularSync()
106+
_ <- peer2.connectToPeers(Set(peer1.node))
107+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber)
108+
_ <- peer2.mineNewBlocks(100.milliseconds, 2)(IdentityUpdate)
109+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 2)
110+
_ <- peer2.addCheckpointedBlock(peer2.bl.getBestBlock().get)
111+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 3)
112+
_ <- peer1.addCheckpointedBlock(peer1.bl.getBestBlock().get)
113+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 4)
114+
_ <- peer1.mineNewBlocks(100.milliseconds, 1)(IdentityUpdate)
115+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 5)
116+
} yield {
117+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
118+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
119+
}
120+
}
121+
122+
"peers should chose the branch with a checkpoint discarding blocks that come after the checkpoint" in customTestCaseResourceM(
123+
FakePeer.start2FakePeersRes()
124+
) { case (peer1, peer2) =>
125+
val lenght = 26
126+
for {
127+
_ <- peer1.importBlocksUntil(20)(IdentityUpdate)
128+
_ <- peer2.importBlocksUntil(30)(IdentityUpdate)
129+
_ <- peer1.startRegularSync()
130+
_ <- peer2.startRegularSync()
131+
_ <- peer2.addCheckpointedBlock(peer2.bl.getBlockByNumber(25).get)
132+
_ <- peer2.waitForRegularSyncLoadLastBlock(lenght)
133+
_ <- peer1.connectToPeers(Set(peer2.node))
134+
_ <- peer1.waitForRegularSyncLoadLastBlock(lenght)
135+
} yield {
136+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
137+
assert(peer1.bl.getBestBlock().get.number == peer2.bl.getBestBlock().get.number && peer1.bl.getBestBlock().get.number == lenght)
138+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
139+
}
140+
}
141+
142+
"peers should choose the branch with a checkpoint even if it's shorter" in customTestCaseResourceM(
143+
FakePeer.start2FakePeersRes()
144+
) { case (peer1, peer2) =>
145+
for {
146+
_ <- peer1.importBlocksUntil(4)(IdentityUpdate)
147+
_ <- peer2.importBlocksUntil(8)(IdentityUpdate)
148+
_ <- peer1.startRegularSync()
149+
_ <- peer2.startRegularSync()
150+
_ <- peer1.addCheckpointedBlock(peer1.bl.getBestBlock().get)
151+
_ = Thread.sleep(5000)
152+
_ <- peer1.waitForRegularSyncLoadLastBlock(5)
153+
_ <- peer1.connectToPeers(Set(peer2.node))
154+
//TODO: remove the following line and the test should still work. Currently reorganisation is only triggered when the checkpoint is explicitly propagated,
155+
// but it should also work when 2 new peers with conflicting branches connect
156+
_ <- peer2.getCheckpointFromPeer(peer1.bl.getBestBlock().get, PeerId("Peer1"))
157+
_ <- peer2.waitForRegularSyncLoadLastBlock(5)
158+
} yield {
159+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
160+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
161+
}
162+
}
163+
164+
"a peer should reorganise when receives a checkpoint older than the current best from a peer" in customTestCaseResourceM(
165+
FakePeer.start2FakePeersRes()
166+
) { case (peer1, peer2) =>
167+
for {
168+
_ <- peer1.importBlocksUntil(20)(IdentityUpdate)
169+
_ <- peer2.importBlocksUntil(30)(IdentityUpdate)
170+
_ <- peer1.startRegularSync()
171+
_ <- peer2.startRegularSync()
172+
_ <- peer1.addCheckpointedBlock(peer1.bl.getBestBlock().get)
173+
_ <- peer1.waitForRegularSyncLoadLastBlock(21)
174+
_ <- peer2.getCheckpointFromPeer(peer1.bl.getBestBlock().get, PeerId("Peer1"))
175+
_ <- peer2.waitForRegularSyncLoadLastBlock(21)
176+
} yield {
177+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
178+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
179+
}
180+
}
181+
79182
"peers with divergent chains will be forced to resolve branches" in customTestCaseResourceM(
80183
FakePeer.start2FakePeersRes()
81184
) { case (peer1, peer2) =>

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ import cats.effect.Resource
66
import io.iohk.ethereum.Mocks.MockValidatorsAlwaysSucceed
77
import io.iohk.ethereum.blockchain.sync.regular.BlockBroadcast.BlockToBroadcast
88
import io.iohk.ethereum.blockchain.sync.regular.BlockBroadcasterActor.BroadcastBlock
9-
import io.iohk.ethereum.blockchain.sync.regular.RegularSync
9+
import io.iohk.ethereum.blockchain.sync.regular.BlockImporter.{NewCheckpointBlock, Start}
10+
import io.iohk.ethereum.blockchain.sync.regular.{BlockBroadcast, BlockBroadcasterActor, BlockFetcher, BlockImporter, RegularSync}
11+
import io.iohk.ethereum.blockchain.sync.regular.RegularSync.NewCheckpoint
1012
import io.iohk.ethereum.blockchain.sync.{PeersClient, SyncProtocol}
13+
import io.iohk.ethereum.checkpointing.CheckpointingTestHelpers
1114
import io.iohk.ethereum.consensus.Protocol.NoAdditionalEthashData
1215
import io.iohk.ethereum.consensus.blocks.CheckpointBlockGenerator
1316
import io.iohk.ethereum.consensus.ethash.{EthashConfig, EthashConsensus}
1417
import io.iohk.ethereum.consensus.{ConsensusConfig, FullConsensusConfig, ethash}
18+
import io.iohk.ethereum.crypto
1519
import io.iohk.ethereum.domain._
1620
import io.iohk.ethereum.ledger._
21+
import io.iohk.ethereum.network.PeerId
1722
import io.iohk.ethereum.nodebuilder.VmSetup
1823
import io.iohk.ethereum.ommers.OmmersPool
1924
import io.iohk.ethereum.sync.util.SyncCommonItSpecUtils.FakePeerCustomConfig.defaultConfig
@@ -23,6 +28,7 @@ import io.iohk.ethereum.utils._
2328
import io.iohk.ethereum.vm.EvmConfig
2429
import monix.eval.Task
2530
import monix.execution.Scheduler
31+
2632
import scala.concurrent.duration._
2733
object RegularSyncItSpecUtils {
2834

@@ -66,6 +72,31 @@ object RegularSyncItSpecUtils {
6672

6773
lazy val validators = buildEthashConsensus().validators
6874

75+
val broadcasterRef: ActorRef = system.actorOf(
76+
BlockBroadcasterActor
77+
.props(new BlockBroadcast(etcPeerManager), peerEventBus, etcPeerManager, syncConfig, system.scheduler),
78+
"block-broadcaster"
79+
)
80+
81+
val fetcher: ActorRef =
82+
system.actorOf(
83+
BlockFetcher.props(peersClient, peerEventBus, regularSync, syncConfig, validators.blockValidator),
84+
"block-fetcher"
85+
)
86+
87+
lazy val blockImporter = system.actorOf(
88+
BlockImporter.props(
89+
fetcher,
90+
ledger,
91+
bl,
92+
syncConfig,
93+
ommersPool,
94+
broadcasterRef,
95+
pendingTransactionsManager,
96+
checkpointBlockGenerator,
97+
regularSync
98+
))
99+
69100
lazy val regularSync = system.actorOf(
70101
RegularSync.props(
71102
peersClient,
@@ -132,6 +163,19 @@ object RegularSyncItSpecUtils {
132163
} else Task(())
133164
}
134165

166+
def addCheckpointedBlock(parent: Block): Task[Unit] = Task {
167+
val signatures = CheckpointingTestHelpers.createCheckpointSignatures(
168+
Seq(crypto.generateKeyPair(secureRandom)),
169+
parent.hash
170+
)
171+
regularSync ! NewCheckpoint(parent.header.hash, signatures)
172+
}
173+
174+
def getCheckpointFromPeer(checkpoint: Block, peerId: PeerId): Task[Unit] = Task {
175+
blockImporter ! Start
176+
blockImporter ! NewCheckpointBlock(checkpoint, peerId)
177+
}
178+
135179
private def getMptForBlock(block: Block) = {
136180
bl.getWorldStateProxy(
137181
blockNumber = block.number,

src/main/scala/io/iohk/ethereum/blockchain/sync/regular/BlockImporter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ class BlockImporter(
327327
Right(Nil)
328328
case UnknownBranch =>
329329
val currentBlock = blocks.head.number.min(startingBlockNumber)
330-
val goingBackTo = currentBlock - syncConfig.branchResolutionRequestSize
330+
val goingBackTo = (currentBlock - syncConfig.branchResolutionRequestSize).max(0)
331331
val msg = s"Unknown branch, going back to block nr $goingBackTo in order to resolve branches"
332332

333333
log.info(msg)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ class PivotBlockSelectorSpec
445445

446446
override def defaultSyncConfig: SyncConfig = super.defaultSyncConfig.copy(
447447
doFastSync = true,
448-
branchResolutionRequestSize = 20,
448+
branchResolutionRequestSize = 30,
449449
checkForNewBlockInterval = 1.second,
450450
blockHeadersPerRequest = 10,
451451
blockBodiesPerRequest = 10,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ class SyncControllerSpec
481481

482482
override def defaultSyncConfig: SyncConfig = super.defaultSyncConfig.copy(
483483
doFastSync = true,
484-
branchResolutionRequestSize = 20,
484+
branchResolutionRequestSize = 30,
485485
checkForNewBlockInterval = 1.second,
486486
blockHeadersPerRequest = 10,
487487
blockBodiesPerRequest = 10,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ trait TestSyncConfig extends SyncConfigBuilder {
1010
printStatusInterval = 1.second,
1111
persistStateSnapshotInterval = 2.seconds,
1212
pivotBlockOffset = 500,
13-
branchResolutionRequestSize = 2,
13+
branchResolutionRequestSize = 30,
1414
blacklistDuration = 5.seconds,
1515
criticalBlacklistDuration = 10.seconds,
1616
syncRetryInterval = 1.second,

0 commit comments

Comments
 (0)