Skip to content

Commit 23534aa

Browse files
authored
Merge pull request #931 from input-output-hk/feature/ETCM-645
ETCM-645: Add it-test for checkpointing
2 parents 404ba7b + 675b893 commit 23534aa

File tree

7 files changed

+156
-8
lines changed

7 files changed

+156
-8
lines changed

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

Lines changed: 104 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
@@ -27,6 +28,24 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
2728
testScheduler.awaitTermination(120.second)
2829
}
2930

31+
"a peer should reorganise when receives a checkpoint older than the current best from a peer" in customTestCaseResourceM(
32+
FakePeer.start2FakePeersRes()
33+
) { case (peer1, peer2) =>
34+
for {
35+
_ <- peer1.importBlocksUntil(20)(IdentityUpdate)
36+
_ <- peer2.importBlocksUntil(30)(IdentityUpdate)
37+
_ <- peer1.startRegularSync()
38+
_ <- peer2.startRegularSync()
39+
_ <- peer1.addCheckpointedBlock(peer1.bl.getBestBlock().get)
40+
_ <- peer1.waitForRegularSyncLoadLastBlock(21)
41+
_ <- peer2.getCheckpointFromPeer(peer1.bl.getBestBlock().get, PeerId("Peer1"))
42+
_ <- peer2.waitForRegularSyncLoadLastBlock(21)
43+
} yield {
44+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
45+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
46+
}
47+
}
48+
3049
"peer 2 should sync to the top of peer1 blockchain" - {
3150
"given a previously imported blockchain" in customTestCaseResourceM(FakePeer.start2FakePeersRes()) {
3251
case (peer1, peer2) =>
@@ -76,6 +95,91 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
7695
}
7796
}
7897

98+
"peers should keep being synced on checkpoints" 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.addCheckpointedBlock(peer2.bl.getBestBlock().get)
109+
_ <- peer1.waitForRegularSyncLoadLastBlock(blockNumber + 1)
110+
} yield {
111+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
112+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
113+
}
114+
}
115+
116+
"peers should keep being synced on checkpoints even if 2 checkpoints are issued to different forks at the same time" in customTestCaseResourceM(
117+
FakePeer.start2FakePeersRes()
118+
) { case (peer1, peer2) =>
119+
val blockNumber: Int = 2000
120+
for {
121+
_ <- peer1.importBlocksUntil(blockNumber)(IdentityUpdate)
122+
_ <- peer1.startRegularSync()
123+
_ <- peer2.startRegularSync()
124+
_ <- peer2.connectToPeers(Set(peer1.node))
125+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber)
126+
_ <- peer2.mineNewBlocks(100.milliseconds, 2)(IdentityUpdate)
127+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 2)
128+
_ <- peer2.addCheckpointedBlock(peer2.bl.getBestBlock().get)
129+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 3)
130+
_ <- peer1.addCheckpointedBlock(peer1.bl.getBestBlock().get)
131+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 4)
132+
_ <- peer1.mineNewBlocks(100.milliseconds, 1)(IdentityUpdate)
133+
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber + 5)
134+
} yield {
135+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
136+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
137+
}
138+
}
139+
140+
"peers should chose the branch with a checkpoint discarding blocks that come after the checkpoint" in customTestCaseResourceM(
141+
FakePeer.start2FakePeersRes()
142+
) { case (peer1, peer2) =>
143+
val length = 26
144+
for {
145+
_ <- peer1.importBlocksUntil(20)(IdentityUpdate)
146+
_ <- peer2.importBlocksUntil(30)(IdentityUpdate)
147+
_ <- peer1.startRegularSync()
148+
_ <- peer2.startRegularSync()
149+
_ <- peer2.addCheckpointedBlock(peer2.bl.getBlockByNumber(25).get)
150+
_ <- peer2.waitForRegularSyncLoadLastBlock(length)
151+
_ <- peer1.connectToPeers(Set(peer2.node))
152+
_ <- peer1.waitForRegularSyncLoadLastBlock(length)
153+
} yield {
154+
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
155+
assert(peer1.bl.getBestBlock().get.number == peer2.bl.getBestBlock().get.number && peer1.bl.getBestBlock().get.number == length)
156+
assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
157+
}
158+
}
159+
160+
//TODO: investigate why reorganisation is not triggered after 2 nodes with conflicting branches connect
161+
"peers should choose the branch with a checkpoint even if it's shorter" in customTestCaseResourceM(
162+
FakePeer.start2FakePeersRes()
163+
) { case (peer1, peer2) =>
164+
for {
165+
_ <- peer1.importBlocksUntil(8)(IdentityUpdate)
166+
_ <- peer1.startRegularSync()
167+
_ <- peer1.addCheckpointedBlock(peer1.bl.getBestBlock().get)
168+
_ <- peer1.waitForRegularSyncLoadLastBlock(9)
169+
_ <- peer2.importBlocksUntil(20)(IdentityUpdate)
170+
_ <- peer2.startRegularSync()
171+
_ <- peer2.connectToPeers(Set(peer1.node))
172+
//without new added blocks the syncing and reorganisation are not triggered
173+
_ <- peer1.mineNewBlocks(500.milliseconds, 10)(IdentityUpdate)
174+
_ <- peer1.waitForRegularSyncLoadLastBlock(19)
175+
} yield {
176+
assert(true)
177+
//these should pass
178+
// assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash )
179+
// assert(peer1.bl.getLatestCheckpointBlockNumber() == peer2.bl.getLatestCheckpointBlockNumber())
180+
}
181+
}
182+
79183
"peers with divergent chains will be forced to resolve branches" in customTestCaseResourceM(
80184
FakePeer.start2FakePeersRes()
81185
) { case (peer1, peer2) =>

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

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ 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.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.PeerEventBusActor.PeerEvent.MessageFromPeer
22+
import io.iohk.ethereum.network.PeerId
23+
import io.iohk.ethereum.network.p2p.messages.CommonMessages.NewBlock
1724
import io.iohk.ethereum.nodebuilder.VmSetup
1825
import io.iohk.ethereum.ommers.OmmersPool
1926
import io.iohk.ethereum.sync.util.SyncCommonItSpecUtils.FakePeerCustomConfig.defaultConfig
@@ -23,6 +30,7 @@ import io.iohk.ethereum.utils._
2330
import io.iohk.ethereum.vm.EvmConfig
2431
import monix.eval.Task
2532
import monix.execution.Scheduler
33+
2634
import scala.concurrent.duration._
2735
object RegularSyncItSpecUtils {
2836

@@ -66,6 +74,31 @@ object RegularSyncItSpecUtils {
6674

6775
lazy val validators = buildEthashConsensus().validators
6876

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

168+
def addCheckpointedBlock(parent: Block): Task[Unit] = Task {
169+
val signatures = CheckpointingTestHelpers.createCheckpointSignatures(
170+
Seq(crypto.generateKeyPair(secureRandom)),
171+
parent.hash
172+
)
173+
regularSync ! NewCheckpoint(parent.header.hash, signatures)
174+
}
175+
176+
def getCheckpointFromPeer(checkpoint: Block, peerId: PeerId): Task[Unit] = Task {
177+
blockImporter ! Start
178+
fetcher ! MessageFromPeer(NewBlock(checkpoint, checkpoint.header.difficulty), peerId)
179+
}
180+
135181
private def getMptForBlock(block: Block) = {
136182
bl.getWorldStateProxy(
137183
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
@@ -329,7 +329,7 @@ class BlockImporter(
329329
Right(Nil)
330330
case UnknownBranch =>
331331
val currentBlock = blocks.head.number.min(startingBlockNumber)
332-
val goingBackTo = currentBlock - syncConfig.branchResolutionRequestSize
332+
val goingBackTo = (currentBlock - syncConfig.branchResolutionRequestSize).max(0)
333333
val msg = s"Unknown branch, going back to block nr $goingBackTo in order to resolve branches"
334334

335335
log.info(msg)

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,7 @@ class RegularSync(
102102
context become running(newState)
103103
case ProgressProtocol.ImportedBlock(blockNumber, isCheckpoint, internally) =>
104104
log.info(s"Imported new block [number = $blockNumber, internally = $internally]")
105-
val newState =
106-
if (isCheckpoint) progressState.copy(currentBlock = blockNumber, bestKnownNetworkBlock = blockNumber)
107-
else progressState.copy(currentBlock = blockNumber)
105+
val newState = progressState.copy(currentBlock = blockNumber)
108106
if (internally && isCheckpoint) {
109107
fetcher ! InternalCheckpointImport(blockNumber)
110108
} else if (internally) {

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

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

448448
override def defaultSyncConfig: SyncConfig = super.defaultSyncConfig.copy(
449449
doFastSync = true,
450-
branchResolutionRequestSize = 20,
450+
branchResolutionRequestSize = 30,
451451
checkForNewBlockInterval = 1.second,
452452
blockHeadersPerRequest = 10,
453453
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
@@ -516,7 +516,7 @@ class SyncControllerSpec
516516

517517
override def defaultSyncConfig: SyncConfig = super.defaultSyncConfig.copy(
518518
doFastSync = true,
519-
branchResolutionRequestSize = 20,
519+
branchResolutionRequestSize = 30,
520520
checkForNewBlockInterval = 1.second,
521521
blockHeadersPerRequest = 10,
522522
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)