Skip to content

Commit

Permalink
Merge branch 'ETCM-311-fast_sync_improvements' of github.com:input-ou…
Browse files Browse the repository at this point in the history
…tput-hk/mantis into ETCM-311-fast_sync_improvements
  • Loading branch information
1015bit committed Mar 1, 2021
2 parents e2ae8bf + e59e0a0 commit 4d2bd58
Show file tree
Hide file tree
Showing 47 changed files with 799 additions and 268 deletions.
15 changes: 13 additions & 2 deletions .buildkite/pipeline.nix
@@ -1,14 +1,12 @@
{ cfg, pkgs, ... }:

with cfg.steps.commands;

let
commonAttrs = {
retry.automatic = true;
agents.queue = "project42";
};
in

{
steps.commands = {
nixExpr = commonAttrs // {
Expand Down Expand Up @@ -97,6 +95,19 @@ in
];
};

annotate-test-reports = commonAttrs // {
dependsOn = [ test-unit ];
label = "annotate test reports";
command = "junit-annotate";
allowDependencyFailure = true;
plugins = [{
"junit-annotate#1.9.0" = {
artifacts = "target/test-reports/*.xml";
report-slowest = 50;
};
}];
};

test-evm = commonAttrs // {
dependsOn = [ compile ];
label = "EVM tests";
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -24,3 +24,6 @@ metals.sbt
# Nix
result
.nix/

# sonarScan
.scannerwork/
3 changes: 3 additions & 0 deletions build.sbt
Expand Up @@ -255,3 +255,6 @@ addCommandAlias(
|;it:test
|""".stripMargin
)

scapegoatVersion in ThisBuild := "1.4.7"
scapegoatReports := Seq("xml")
11 changes: 11 additions & 0 deletions ci-like/Dockerfile
@@ -0,0 +1,11 @@
FROM nixos/nix

RUN apk --update add git less openssh && \
rm -rf /var/lib/apt/lists/* && \
rm /var/cache/apk/*

RUN nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs
RUN nix-channel --update

RUN nix-build -A pythonFull '<nixpkgs>'

4 changes: 4 additions & 0 deletions ci-like/nix.conf
@@ -0,0 +1,4 @@
sandbox = false
experimental-features = nix-command flakes ca-references
substituters = https://hydra.iohk.io https://cache.nixos.org https://mantis-ops.cachix.org
trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= mantis-ops.cachix.org-1:SornDcX8/9rFrpTjU+mAAb26sF8mUpnxgXNjmKGcglQ=
2 changes: 1 addition & 1 deletion nix/pkgs/mantis.nix
Expand Up @@ -50,7 +50,7 @@ in sbt.mkDerivation {

# This sha represents the change dependencies of mantis.
# Update this sha whenever you change the dependencies
depsSha256 = "07iixw8va4zwpiln2zy2gr245z1ir4jd6pqgmkzhwnhw3mf5j28k";
depsSha256 = "14hx1gxa7505b8jy1vq5gc5p51fn80sj0pafx26awsrl6q67qyld";

# this is the command used to to create the fixed-output-derivation
depsWarmupCommand = "sbt compile --debug -Dnix=true";
Expand Down
10 changes: 7 additions & 3 deletions project/Dependencies.scala
Expand Up @@ -48,7 +48,7 @@ object Dependencies {

val rocksDb = Seq(
// use "5.18.3" for older macOS
"org.rocksdb" % "rocksdbjni" % "6.11.4"
"org.rocksdb" % "rocksdbjni" % "6.15.2"
)

val enumeratum: Seq[ModuleID] = Seq(
Expand All @@ -59,7 +59,7 @@ object Dependencies {

val testing: Seq[ModuleID] = Seq(
"org.scalatest" %% "scalatest" % "3.2.2" % "it,test",
"org.scalamock" %% "scalamock" % "5.0.0" % "test",
"org.scalamock" %% "scalamock" % "5.0.0" % "it,test",
"org.scalatestplus" %% "scalacheck-1-15" % "3.2.3.0" % "test",
"org.scalacheck" %% "scalacheck" % "1.15.1" % "it,test",
"com.softwaremill.diffx" %% "diffx-core" % "0.3.30" % "test",
Expand Down Expand Up @@ -114,7 +114,11 @@ object Dependencies {
"org.scala-sbt.ipcsocket" % "ipcsocket" % "1.1.0",
"org.xerial.snappy" % "snappy-java" % "1.1.7.7",
"org.web3j" % "core" % "5.0.0" % Test,
"io.vavr" % "vavr" % "1.0.0-alpha-3"
"io.vavr" % "vavr" % "1.0.0-alpha-3",
"org.jupnp" % "org.jupnp" % "2.5.2",
"org.jupnp" % "org.jupnp.support" % "2.5.2",
"org.jupnp" % "org.jupnp.tool" % "2.5.2",
"javax.servlet" % "javax.servlet-api" % "4.0.1"
)

val guava: Seq[ModuleID] = {
Expand Down
14 changes: 8 additions & 6 deletions project/plugins.sbt
@@ -1,11 +1,13 @@
logLevel := sbt.Level.Warn
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.7.5")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0")
addSbtPlugin("com.github.mwz" % "sbt-sonar" % "2.2.0")
addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.6")
addSbtPlugin("com.sksamuel.scapegoat" % "sbt-scapegoat" % "1.1.0")
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.0")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.1")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.7.5")
addSbtPlugin("io.kamon" % "sbt-kanela-runner" % "2.0.5")
addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.6")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.2")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")
2 changes: 1 addition & 1 deletion scalastyle-config.xml
@@ -1,7 +1,7 @@
<scalastyle>
<name>Scalastyle standard configuration</name>
<check level="error" class="org.scalastyle.file.FileTabChecker" enabled="true"></check>
<check level="error" class="org.scalastyle.file.FileLengthChecker" enabled="true">
<check level="error" class="org.scalastyle.file.FileLengthChecker" enabled="false">
<parameters>
<parameter name="maxFileLength"><![CDATA[800]]></parameter>
</parameters>
Expand Down
196 changes: 196 additions & 0 deletions src/it/scala/io/iohk/ethereum/ledger/BlockImporterItSpec.scala
@@ -0,0 +1,196 @@
package io.iohk.ethereum.ledger

import akka.testkit.TestProbe
import akka.util.ByteString
import cats.data.NonEmptyList
import io.iohk.ethereum.blockchain.sync.regular.BlockImporter.NewCheckpoint
import io.iohk.ethereum.blockchain.sync.regular.{BlockFetcher, BlockImporter}
import io.iohk.ethereum.checkpointing.CheckpointingTestHelpers
import io.iohk.ethereum.consensus.blocks.CheckpointBlockGenerator
import io.iohk.ethereum.domain._
import io.iohk.ethereum.mpt.MerklePatriciaTrie
import io.iohk.ethereum.utils.Config.SyncConfig
import io.iohk.ethereum.utils.Config
import io.iohk.ethereum.{Fixtures, ObjectGenerators, crypto}
import io.iohk.ethereum.ledger.Ledger.BlockResult
import monix.execution.Scheduler
import org.scalamock.scalatest.MockFactory
import org.scalatest.BeforeAndAfterAll
import org.scalatest.flatspec.AsyncFlatSpecLike
import org.scalatest.matchers.should.Matchers

import scala.concurrent.duration._

class BlockImporterItSpec extends MockFactory with TestSetupWithVmAndValidators with AsyncFlatSpecLike with Matchers with BeforeAndAfterAll {

implicit val testScheduler = Scheduler.fixedPool("test", 32)

override def afterAll(): Unit = {
testScheduler.shutdown()
testScheduler.awaitTermination(60.second)
}

val blockQueue = BlockQueue(blockchain, SyncConfig(Config.config))

val genesis = Block(
Fixtures.Blocks.Genesis.header.copy(stateRoot = ByteString(MerklePatriciaTrie.EmptyRootHash)),
Fixtures.Blocks.Genesis.body
)
val genesisWeight = ChainWeight.zero.increase(genesis.header)

blockchain.save(genesis, Seq(), genesisWeight, saveAsBestBlock = true)

lazy val checkpointBlockGenerator: CheckpointBlockGenerator = new CheckpointBlockGenerator

val fetcherProbe = TestProbe()
val ommersPoolProbe = TestProbe()
val broadcasterProbe = TestProbe()
val pendingTransactionsManagerProbe = TestProbe()
val supervisor = TestProbe()

val emptyWorld: InMemoryWorldStateProxy =
blockchain.getWorldStateProxy(
-1,
UInt256.Zero,
ByteString(MerklePatriciaTrie.EmptyRootHash),
noEmptyAccounts = false,
ethCompatibleStorage = true
)

override lazy val ledger = new TestLedgerImpl(successValidators) {
override private[ledger] lazy val blockExecution = new BlockExecution(blockchain, blockchainConfig, consensus.blockPreparator, blockValidation) {
override def executeAndValidateBlock(block: Block, alreadyValidated: Boolean = false): Either[BlockExecutionError, Seq[Receipt]] =
Right(BlockResult(emptyWorld).receipts)
}
}

val blockImporter = system.actorOf(
BlockImporter.props(
fetcherProbe.ref,
ledger,
blockchain,
syncConfig,
ommersPoolProbe.ref,
broadcasterProbe.ref,
pendingTransactionsManagerProbe.ref,
checkpointBlockGenerator,
supervisor.ref
))

val genesisBlock = blockchain.genesisBlock
val block1: Block = getBlock(genesisBlock.number + 1, parent = genesisBlock.header.hash)
// new chain is shorter but has a higher weight
val newBlock2: Block = getBlock(genesisBlock.number + 2, difficulty = 108, parent = block1.header.hash)
val newBlock3: Block = getBlock(genesisBlock.number + 3, difficulty = 300, parent = newBlock2.header.hash)
val oldBlock2: Block = getBlock(genesisBlock.number + 2, difficulty = 102, parent = block1.header.hash)
val oldBlock3: Block = getBlock(genesisBlock.number + 3, difficulty = 103, parent = oldBlock2.header.hash)
val oldBlock4: Block = getBlock(genesisBlock.number + 4, difficulty = 104, parent = oldBlock3.header.hash)

val weight1 = ChainWeight.totalDifficultyOnly(block1.header.difficulty)
val newWeight2 = weight1.increase(newBlock2.header)
val newWeight3 = newWeight2.increase(newBlock3.header)
val oldWeight2 = weight1.increase(oldBlock2.header)
val oldWeight3 = oldWeight2.increase(oldBlock3.header)
val oldWeight4 = oldWeight3.increase(oldBlock4.header)

//saving initial main chain
blockchain.save(block1, Nil, weight1, saveAsBestBlock = true)
blockchain.save(oldBlock2, Nil, oldWeight2, saveAsBestBlock = true)
blockchain.save(oldBlock3, Nil, oldWeight3, saveAsBestBlock = true)
blockchain.save(oldBlock4, Nil, oldWeight4, saveAsBestBlock = true)

val oldBranch = List(oldBlock2, oldBlock3, oldBlock4)
val newBranch = List(newBlock2, newBlock3)

blockImporter ! BlockImporter.Start

"BlockImporter" should "not discard blocks of the main chain if the reorganisation failed" in {

//ledger with not mocked blockExecution
val ledger = new TestLedgerImpl(successValidators)
val blockImporter = system.actorOf(
BlockImporter.props(
fetcherProbe.ref,
ledger,
blockchain,
syncConfig,
ommersPoolProbe.ref,
broadcasterProbe.ref,
pendingTransactionsManagerProbe.ref,
checkpointBlockGenerator,
supervisor.ref
))

blockImporter ! BlockImporter.Start
blockImporter ! BlockFetcher.PickedBlocks(NonEmptyList.fromListUnsafe(newBranch))

Thread.sleep(1000)
//because the blocks are not valid, we shouldn't reorganise, but at least stay with a current chain, and the best block of the current chain is oldBlock4
blockchain.getBestBlock().get shouldEqual oldBlock4
}

it should "return a correct new best block after reorganising longer chain to a shorter one if its weight is bigger" in {

//returning discarded initial chain
blockchain.save(oldBlock2, Nil, oldWeight2, saveAsBestBlock = true)
blockchain.save(oldBlock3, Nil, oldWeight3, saveAsBestBlock = true)
blockchain.save(oldBlock4, Nil, oldWeight4, saveAsBestBlock = true)

blockImporter ! BlockFetcher.PickedBlocks(NonEmptyList.fromListUnsafe(newBranch))

Thread.sleep(200)
blockchain.getBestBlock().get shouldEqual newBlock3
}


it should "switch to a branch with a checkpoint" in {

val checkpoint = ObjectGenerators.fakeCheckpointGen(3, 3).sample.get
val oldBlock5WithCheckpoint: Block = checkpointBlockGenerator.generate(oldBlock4, checkpoint)
blockchain.save(oldBlock5WithCheckpoint, Nil, oldWeight4, saveAsBestBlock = true)

val newBranch = List(newBlock2, newBlock3)

blockImporter ! BlockFetcher.PickedBlocks(NonEmptyList.fromListUnsafe(newBranch))

Thread.sleep(200)
blockchain.getBestBlock().get shouldEqual oldBlock5WithCheckpoint
blockchain.getLatestCheckpointBlockNumber() shouldEqual oldBlock5WithCheckpoint.header.number
}

it should "switch to a branch with a newer checkpoint" in {

val checkpoint = ObjectGenerators.fakeCheckpointGen(3, 3).sample.get
val newBlock4WithCheckpoint: Block = checkpointBlockGenerator.generate(newBlock3, checkpoint)
blockchain.save(newBlock4WithCheckpoint, Nil, newWeight3, saveAsBestBlock = true)

val newBranch = List(newBlock4WithCheckpoint)

blockImporter ! BlockFetcher.PickedBlocks(NonEmptyList.fromListUnsafe(newBranch))

Thread.sleep(200)
blockchain.getBestBlock().get shouldEqual newBlock4WithCheckpoint
blockchain.getLatestCheckpointBlockNumber() shouldEqual newBlock4WithCheckpoint.header.number
}

it should "return a correct checkpointed block after receiving a request for generating a new checkpoint" in {

val parent = blockchain.getBestBlock().get
val newBlock5: Block = getBlock(genesisBlock.number + 5, difficulty = 104, parent = parent.header.hash)
val newWeight5 = newWeight3.increase(newBlock5.header)

blockchain.save(newBlock5, Nil, newWeight5, saveAsBestBlock = true)

val signatures = CheckpointingTestHelpers.createCheckpointSignatures(
Seq(crypto.generateKeyPair(secureRandom)),
newBlock5.hash
)
blockImporter ! NewCheckpoint(newBlock5.hash, signatures)

val checkpointBlock = checkpointBlockGenerator.generate(newBlock5, Checkpoint(signatures))

Thread.sleep(1000)
blockchain.getBestBlock().get shouldEqual checkpointBlock
blockchain.getLatestCheckpointBlockNumber() shouldEqual newBlock5.header.number + 1
}
}
10 changes: 5 additions & 5 deletions src/it/scala/io/iohk/ethereum/sync/RegularSyncItSpec.scala
Expand Up @@ -37,7 +37,7 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
_ <- peer2.connectToPeers(Set(peer1.node))
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumber)
} yield {
assert(peer1.bl.getBestBlock().hash == peer2.bl.getBestBlock().hash)
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
}
}

Expand All @@ -52,7 +52,7 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
_ <- peer2.connectToPeers(Set(peer1.node))
_ <- peer2.waitForRegularSyncLoadLastBlock(blockHeadersPerRequest + 1)
} yield {
assert(peer1.bl.getBestBlock().hash == peer2.bl.getBestBlock().hash)
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
}
}
}
Expand All @@ -72,7 +72,7 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
_ <- peer1.mineNewBlocks(100.milliseconds, 2)(IdentityUpdate)
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumer + 4)
} yield {
assert(peer1.bl.getBestBlock().hash == peer2.bl.getBestBlock().hash)
assert(peer1.bl.getBestBlock().get.hash == peer2.bl.getBestBlock().get.hash)
}
}

Expand All @@ -94,8 +94,8 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
_ <- peer2.waitForRegularSyncLoadLastBlock(blockNumer + 3)
} yield {
assert(
peer1.bl.getChainWeightByHash(peer1.bl.getBestBlock().hash) == peer2.bl.getChainWeightByHash(
peer2.bl.getBestBlock().hash
peer1.bl.getChainWeightByHash(peer1.bl.getBestBlock().get.hash) == peer2.bl.getChainWeightByHash(
peer2.bl.getBestBlock().get.hash
)
)
(peer1.bl.getBlockByNumber(blockNumer + 1), peer2.bl.getBlockByNumber(blockNumer + 1)) match {
Expand Down
10 changes: 5 additions & 5 deletions src/it/scala/io/iohk/ethereum/sync/util/CommonFakePeer.scala
Expand Up @@ -246,7 +246,7 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
}

def getCurrentState(): BlockchainState = {
val bestBlock = bl.getBestBlock()
val bestBlock = bl.getBestBlock().get
val currentWorldState = getMptForBlock(bestBlock)
val currentWeight = bl.getChainWeightByHash(bestBlock.hash).get
BlockchainState(bestBlock, currentWorldState, currentWeight)
Expand Down Expand Up @@ -301,13 +301,13 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
n: BigInt
)(updateWorldForBlock: (BigInt, InMemoryWorldStateProxy) => InMemoryWorldStateProxy): Task[Unit] = {
Task(bl.getBestBlock()).flatMap { block =>
if (block.number >= n) {
if (block.get.number >= n) {
Task(())
} else {
Task {
val currentWeight = bl.getChainWeightByHash(block.hash).get
val currentWolrd = getMptForBlock(block)
val (newBlock, newWeight, _) = createChildBlock(block, currentWeight, currentWolrd)(updateWorldForBlock)
val currentWeight = bl.getChainWeightByHash(block.get.hash).get
val currentWolrd = getMptForBlock(block.get)
val (newBlock, newWeight, _) = createChildBlock(block.get, currentWeight, currentWolrd)(updateWorldForBlock)
bl.save(newBlock, Seq(), newWeight, saveAsBestBlock = true)
broadcastBlock(newBlock, newWeight)
}.flatMap(_ => importBlocksUntil(n)(updateWorldForBlock))
Expand Down

0 comments on commit 4d2bd58

Please sign in to comment.