Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EC-749 #370

Open
wants to merge 57 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
7c24305
frame
Sep 2, 2019
3554ad9
add test draft: Late node should sync with chain
Sep 2, 2019
4775c79
add test draft: Late node should sync with chain
Sep 2, 2019
736394b
add test Late node should sync with one node
Sep 3, 2019
74edce8
maxTries -> implicit duration
Sep 3, 2019
0346c3e
add test draft "Node should sync after change offlineGeneration and p…
Sep 3, 2019
363ae2c
add /peers/connect api route
Sep 3, 2019
3fcf2c9
add /node/shutdown api route
Sep 4, 2019
16f127e
add node api shutdown
Sep 4, 2019
b0f3672
add Config wrappers
Sep 4, 2019
baab41f
passthrough volume param
Sep 4, 2019
6efc443
add stopNode
Sep 4, 2019
a5f9630
add test "Nodes should sync after restart with new offlineGeneration …
Sep 4, 2019
b5ff812
add test "Nodes should sync after restart with new offlineGeneration …
Sep 4, 2019
2014d59
fix remove container
Sep 4, 2019
8d700d4
general refactoring of tests
Sep 5, 2019
39e49a5
cleanup debug info
Sep 5, 2019
b3f9325
constant class name should be passed through application.conf
Sep 5, 2019
e65b4ee
MainConstants rename to Constants
Sep 6, 2019
58d65c5
add SlowMiningConstants
Sep 6, 2019
abc87eb
add constantsClass wrapper
Sep 6, 2019
26a5ff9
add fixed network port
Sep 6, 2019
8f56ecb
add SyncThreeNodesKnowAboutEachOtherTest
Sep 6, 2019
63e07f4
add SyncThreeNodesСonnectedWithFirstTest
Sep 6, 2019
415d329
refactoring
Sep 6, 2019
eb5d14b
refactor to implicits
Sep 6, 2019
508fd20
cleanup
Sep 6, 2019
a0b0a99
fix bug with two setting instance
Sep 6, 2019
a6a0fae
link to EncryApp.settings replaced
Sep 6, 2019
7256360
EncryApp.settings to local param settings replaced
Sep 6, 2019
12b130f
fix null cmdArgs
Sep 6, 2019
434883e
refactor load settings
Sep 9, 2019
36c6279
refactoring EncryApp.settings global links to params
Sep 9, 2019
650bd0c
more settings refactoring
Sep 10, 2019
7ae12ea
replace DummyEncryAppSettingsReader with Settings trait
Sep 10, 2019
ddd0fce
more settings refactoring
Sep 10, 2019
d16cca1
replace val settings with Settings trait
Sep 10, 2019
d0b477a
replace DummyEncryAppSettingsReader with TestSettings trait
Sep 10, 2019
97b4040
replace MainTestSettings.conf with MainTestSettings trait
Sep 10, 2019
b27c856
remove unused constants
Sep 10, 2019
eeeb2d2
more refactoring
Sep 10, 2019
71bcca3
fix args
Sep 10, 2019
25e41b0
benchmark mix settings
Sep 10, 2019
17f9543
PR fixes
Sep 13, 2019
dd0e82c
rename MainTestSettings
Sep 16, 2019
53f3164
fix AdditionalTestSettings
Sep 16, 2019
34a4741
fix settings
Sep 16, 2019
5b9a64c
Merge branch 'SunFrost-EC-751'
Sep 16, 2019
caeb564
merge branch
Sep 17, 2019
6214ce3
merge branch
Sep 17, 2019
14f5438
merge branch
Sep 17, 2019
efc53e4
Merge remote-tracking branch 'origin/master' into EC-749
Sep 24, 2019
ae30aa6
Merge remote-tracking branch 'origin/master' into EC-749
Sep 24, 2019
fc460f7
PR fixes
Oct 1, 2019
a942218
Merge remote-tracking branch 'origin/master' into EC-749
Oct 1, 2019
f98e344
rename
Oct 2, 2019
fa270cc
Merge branch 'master' into EC-749
CapDev12 Oct 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions it/src/main/scala/encry/it/api/HttpApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ trait HttpApi { // scalastyle:ignore
)
}

def bestFullHeaderId: Future[String] = get("/info") flatMap { r =>
val response = jsonAnswerAs[Json](r.getResponseBody)
val eitherId = response.hcursor.downField("bestFullHeaderId").as[Option[String]]
eitherId.fold[Future[String]](
e => Future.failed(new Exception(s"Error getting `bestFullHeaderId` from /info response: $e\n$response", e)),
maybeId => Future.successful(maybeId.getOrElse(""))
)
}

def balances: Future[Map[String, Long]] = get("/wallet/info") flatMap { r =>
val response = jsonAnswerAs[Json](r.getResponseBody)
val eitherBalance = response.hcursor.downField("balances").as[Map[String, String]]
Expand Down Expand Up @@ -148,13 +157,23 @@ trait HttpApi { // scalastyle:ignore
waitFor[Int](_.headersHeight, h => h >= expectedHeight, retryingInterval)
}

def waitForBestFullHeaderId(expectedId: String, retryingInterval: FiniteDuration = 1.minute): Future[String] = {
waitFor[String](_.bestFullHeaderId, id => id == expectedId, retryingInterval)
}

def waitFor[A](f: this.type => Future[A], cond: A => Boolean, retryInterval: FiniteDuration): Future[A] = {
timer.retryUntil(f(this), cond, retryInterval)
}

def connect(addressAndPort: String): Future[Unit] = post("/peers/connect", addressAndPort).map(_ => ())

def shutdown: Future[Unit] = post("/node/shutdown", "").map(_ => ())

def postJson[A: Encoder](path: String, body: A): Future[Response] =
post(path, body.asJson.toString())

def error(): Unit = {

}

}
25 changes: 25 additions & 0 deletions it/src/main/scala/encry/it/configs/Configs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ object Configs {
""".stripMargin
)

def connectOnlyWithKnownPeers(connectOnlyWithKnownPeersEnable: Boolean): Config = ConfigFactory.parseString(
s"""
|encry.network.connectOnlyWithKnownPeers=$connectOnlyWithKnownPeersEnable
""".stripMargin
)

def miningDelay(miningDelay: Int): Config = ConfigFactory.parseString(
s"""
|encry.node.miningDelay=${miningDelay}s
Expand All @@ -44,4 +50,23 @@ object Configs {
|encry.wallet.seed="$key"
""".stripMargin
)

def networkAddress(address: String): Config = ConfigFactory.parseString(
s"""
|encry.network.bindAddress = "$address"
""".stripMargin
)

def apiAddress(address: String): Config = ConfigFactory.parseString(
s"""
|encry.restApi.bindAddress = "$address"
""".stripMargin
)

def constantsClass(name: String): Config = ConfigFactory.parseString(
s"""
|encry.node.constantsClass="$name"
""".stripMargin
)

}
19 changes: 14 additions & 5 deletions it/src/main/scala/encry/it/docker/Docker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import scala.collection.JavaConverters._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{Await, Future, blocking}
import scala.util.Random
import scala.util.{Random, Try}
import scala.util.control.NonFatal

case class Docker(suiteConfig: Config = empty,
Expand Down Expand Up @@ -210,13 +210,13 @@ case class Docker(suiteConfig: Config = empty,
.build()
}

def startNodeInternal(nodeConfig: Config): Node =
def startNodeInternal(nodeConfig: Config, specialVolumeOpt: Option[(String, String)] = None): Node =
try {
val settings = EncryAppSettings.fromConfig(nodeConfig.withFallback(configTemplate))
val nodeNumber = settings.network.nodeName.map(_.replace("node", "").toInt).getOrElse(0)
val ip = ipForNode(nodeNumber)

val containerConfig = buildPeerContainerConfig(nodeConfig, EncryAppSettings.fromConfig(nodeConfig), ip)
val containerConfig = buildPeerContainerConfig(nodeConfig, EncryAppSettings.fromConfig(nodeConfig), ip, specialVolumeOpt)

val containerId = {
val containerName = networkName + "-" + settings.network.nodeName.getOrElse("NodeWithoutName") + "-" + uuidShort
Expand All @@ -233,8 +233,8 @@ case class Docker(suiteConfig: Config = empty,
client.startContainer(containerId)
val containerInfo = client.inspectContainer(containerId)
val ports = containerInfo.networkSettings().ports()
val hostPort = extractHostPort(ports, 9001)
val hostRestApiPort = extractHostPort(ports, 9051) //get port from settings
val hostPort = extractHostPort(ports, extractNetworkPortFromConfig(nodeConfig).getOrElse(9001))
val hostRestApiPort = extractHostPort(ports, extractApiPortFromConfig(nodeConfig).getOrElse(9051))
val node = new Node(nodeConfig, hostRestApiPort, containerId, attachedNetwork.ipAddress(), hostPort, http)
nodes.add(node)
logger.debug(s"Started $containerId -> ${node.name}")
Expand All @@ -246,10 +246,19 @@ case class Docker(suiteConfig: Config = empty,
throw e
}

def stopNode(node: Node, secondsToWaitBeforeKilling: Int = 0) {
client.stopContainer(node.containerId, secondsToWaitBeforeKilling)
}

def extractHostPort(portBindingMap: JMap[String, JList[PortBinding]], containerPort: Int): Int =
portBindingMap.get(s"$containerPort/tcp").get(0).hostPort().toInt

def extractNetworkPortFromConfig(config: Config): Option[Int] =
Try(config.getString("encry.network.bindAddress").split(":")(1).toInt).toOption

def extractApiPortFromConfig(config: Config): Option[Int] =
Try(config.getString("encry.restApi.bindAddress").split(":")(1).toInt).toOption

private def saveNodeLogs(): Unit = {
val logDir = Paths.get(System.getProperty("user.dir"), "target", "logs")
Files.createDirectories(logDir)
Expand Down
22 changes: 22 additions & 0 deletions it/src/main/scala/encry/it/util/WaitUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package encry.it.util

import scala.annotation.tailrec
import scala.concurrent.duration.Duration

object WaitUtils {

def waitForEqualsId(id1Func: => String, id2Func: => String)(implicit duration: Duration): (String, String) = {
@tailrec
def loop(id1Func: => String, id2Func: => String, maxTries: Long): (String, String) = {
val id1: String = id1Func
val id2: String = id2Func
if (id1 != id2 && maxTries > 0) {
Thread.sleep(1000)
loop(id1Func, id2Func, maxTries - 1)
} else (id1, id2)
}

loop(id1Func, id2Func, duration.toSeconds)
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package encry.it
package encry.it.miner

import com.typesafe.config.Config
import encry.consensus.EncrySupplyController
Expand Down
78 changes: 78 additions & 0 deletions it/src/test/scala/encry/it/net/NodeRestartTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package encry.it.net

import java.nio.file.Paths

import encry.it.configs.Configs
import encry.it.docker.Docker.defaultConf
import encry.it.docker.DockerAfterAll
import encry.it.util.WaitUtils._
import encry.it.utils.FutureAwait._
import org.encryfoundation.common.utils.Algos
import org.scalatest.{AsyncFunSuite, Matchers}
import scorex.utils.Random

import scala.concurrent.duration._

class NodeRestartTest extends AsyncFunSuite with Matchers with DockerAfterAll {

implicit val futureDuration: FiniteDuration = 10 minutes

test("Nodes should sync after restart with new offlineGeneration and port") {
val node1 = docker
.startNodeInternal(Configs.mining(true)
.withFallback(Configs.nodeName("node1"))
.withFallback(Configs.offlineGeneration(true))
.withFallback(Configs.knownPeers(Seq()))
.withFallback(defaultConf)
)

val userDir = Paths.get(System.getProperty("user.dir"))
val volumeName = Algos.encode(Random.randomBytes(32))
val containerMountPath = userDir + "/encry/data"

val node21 = docker
.startNodeInternal(Configs.mining(true)
.withFallback(Configs.nodeName("node21"))
.withFallback(Configs.offlineGeneration(false))
.withFallback(Configs.knownPeers(Seq()))
.withFallback(defaultConf),
Some(volumeName, containerMountPath)
)

node1.connect(s"${node21.nodeIp}:9001").await
node21.connect(s"${node1.nodeIp}:9001").await

node1.waitForFullHeight(5).await

node21.shutdown
Thread.sleep(5000)
docker.stopNode(node21, 5)
Thread.sleep(7000)

node1.waitForFullHeight(10).await

val node22 = docker
.startNodeInternal(Configs.mining(true)
.withFallback(Configs.nodeName("node22"))
.withFallback(Configs.offlineGeneration(true))
.withFallback(Configs.knownPeers(Seq()))
.withFallback(Configs.networkAddress("0.0.0.0:9002"))
.withFallback(Configs.apiAddress("0.0.0.0:9052"))
.withFallback(defaultConf),
Some(volumeName, containerMountPath)
)

node1.waitForFullHeight(15).await

node1.connect(s"${node22.nodeIp}:9002").await
node22.connect(s"${node1.nodeIp}:9001").await

node1.waitForFullHeight(20).await

val (bestFullHeaderId1, bestFullHeaderId2) =
waitForEqualsId(node1.bestFullHeaderId.await, node22.bestFullHeaderId.await)

bestFullHeaderId2 shouldEqual bestFullHeaderId1
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package encry.it.net

import encry.it.configs.Configs
import encry.it.docker.Docker.defaultConf
import encry.it.docker.DockerAfterAll
import encry.it.util.WaitUtils._
import encry.it.utils.FutureAwait._
import org.scalatest.{FunSuite, Matchers}

import scala.concurrent.duration._

class ThreeNodesKnowAboutEachOtherTest extends FunSuite with Matchers with DockerAfterAll {

implicit val futureDuration: FiniteDuration = 10 minutes
val heightSeparation = 10 //blocks

test("nodes know about each other should sync") {

val node1 = docker
.startNodeInternal(Configs.nodeName("node1")
.withFallback(Configs.mining(true))
.withFallback(Configs.offlineGeneration(true))
.withFallback(Configs.networkAddress("0.0.0.0:9001"))
.withFallback(Configs.knownPeers(Seq()))
.withFallback(defaultConf)
)

node1.waitForFullHeight(heightSeparation).await

val node2 = docker
.startNodeInternal(Configs.nodeName("node2")
.withFallback(Configs.mining(true))
.withFallback(Configs.offlineGeneration(false))
.withFallback(Configs.networkAddress("0.0.0.0:9001"))
.withFallback(Configs.knownPeers(Seq((node1.nodeIp, 9001))))
.withFallback(defaultConf)
)

node1.connect(s"${node2.nodeIp}:9001").await

node1.waitForFullHeight(heightSeparation * 2).await

val node3 = docker
.startNodeInternal(Configs.nodeName("node3")
.withFallback(Configs.mining(true))
.withFallback(Configs.offlineGeneration(false))
.withFallback(Configs.networkAddress("0.0.0.0:9001"))
.withFallback(Configs.knownPeers(Seq((node1.nodeIp, 9001), (node2.nodeIp, 9001))))
.withFallback(defaultConf)
)

waitForEqualsId(node1.bestFullHeaderId.await, node3.bestFullHeaderId.await)

val (bestFullHeaderId2, bestFullHeaderId3) =
waitForEqualsId(node2.bestFullHeaderId.await, node3.bestFullHeaderId.await)

bestFullHeaderId2 shouldEqual bestFullHeaderId3
}
}
44 changes: 44 additions & 0 deletions it/src/test/scala/encry/it/net/TwoNodesTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package encry.it.net

import encry.it.configs.Configs
import encry.it.docker.Docker.defaultConf
import encry.it.docker.{Docker, DockerAfterAll}
import encry.it.util.WaitUtils._
import encry.it.utils.FutureAwait._
import org.scalatest.{FunSuite, Matchers}

import scala.concurrent.duration._

class TwoNodesTest extends FunSuite with Matchers with DockerAfterAll {

implicit val futureDuration: FiniteDuration = 10 minutes
val heightSeparation = 10 //blocks

test("Late node should sync with one node") {

val miningNodeConfig = Configs.mining(true)
.withFallback(Configs.offlineGeneration(true))
.withFallback(Configs.knownPeers(Seq()))
.withFallback(Configs.networkAddress("0.0.0.0:9001"))
.withFallback(Docker.defaultConf)

val node1 = docker
.startNodeInternal(miningNodeConfig.withFallback(Configs.nodeName("node1")))

node1.waitForFullHeight(heightSeparation).await

val node2 = docker
.startNodeInternal(
Configs.nodeName("node2")
.withFallback(Configs.mining(false))
.withFallback(Configs.knownPeers(Seq((node1.nodeIp, 9001))))
.withFallback(defaultConf)
)

val (bestFullHeaderId1, bestFullHeaderId2) =
waitForEqualsId(node1.bestFullHeaderId.await, node2.bestFullHeaderId.await)

bestFullHeaderId2 shouldEqual bestFullHeaderId1
}

}
Loading