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

ETCM-168: Discovery part4 #770

Merged
merged 42 commits into from
Nov 17, 2020
Merged
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
722ba3e
ETCM-168: Use the DiscoveryService. Fix codecs based on mordor trials.
aakoshh Nov 3, 2020
a41dd6d
ETCM-168: Mute logs from scalanet and netty.
aakoshh Nov 3, 2020
f032e45
ETCM-168: Updated the discovery configuration.
aakoshh Nov 3, 2020
60d38a1
ETCM-168: Remove original Packet and Message types.
aakoshh Nov 3, 2020
03116dd
ETCM-168: Take the TCP port from config rather than wait for the status.
aakoshh Nov 3, 2020
a806e7c
ETCM-168: Add kademlia-alpha to config.
aakoshh Nov 3, 2020
6ad90ca
ETCM-168: Use Predefined from Scalanet.
aakoshh Nov 4, 2020
ef83ddc
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 4, 2020
b432147
ETCM-168: Fix scalastyle issues.
aakoshh Nov 4, 2020
907da9f
ETCM-168: Moved service building to a separate file.
aakoshh Nov 4, 2020
2464423
ETCM-168: Keep using local Predefined because of nix.
aakoshh Nov 4, 2020
3bd993e
ETCM-168: Fix config key.
aakoshh Nov 4, 2020
119826a
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 5, 2020
3074e7d
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 5, 2020
d2889c1
ETCM-168: Updated nix patch with scalanet changes.
aakoshh Nov 5, 2020
8a4b75b
ETCM-168: Try again after nix patch.
aakoshh Nov 5, 2020
d880927
ETCM-168: Change to dir in mantis-launcher
aakoshh Nov 5, 2020
333385e
ETCM-168: Log start/stop errors in the PeerDiscoveryManager.
aakoshh Nov 5, 2020
c2aab40
ETCM-168: Use null for unset host.
aakoshh Nov 5, 2020
3348c6b
ETCM-168: Fix argument order in ActorLogging.error calls.
aakoshh Nov 6, 2020
11b7adc
ETCM-168: Trying a more basic approach to make sure future errors are…
aakoshh Nov 6, 2020
2b77e45
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 6, 2020
79484fb
ETCM-168: Update Scalanet version.
aakoshh Nov 6, 2020
ed7853c
ETCM-168: Update to Scalanet 0.4.1
aakoshh Nov 6, 2020
17e653c
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 9, 2020
401c5d5
ETCM-168: Scalafmt on EthService.
aakoshh Nov 9, 2020
6ac2cd8
ETCM-168: Enroll discovery in the background.
aakoshh Nov 9, 2020
8925f66
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 9, 2020
3ff108f
ETCM-168: Fix nix.
aakoshh Nov 9, 2020
0b7a349
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 10, 2020
b18a3b8
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 11, 2020
8fccfb7
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 11, 2020
1b3556e
ETCM-168: Use ConfigUtils for optional value.
aakoshh Nov 12, 2020
39c0923
ETCM-168: Log fatal errors too.
aakoshh Nov 12, 2020
527e0fa
ETCM-168: Be strict by default with the RLPList item numbers.
aakoshh Nov 12, 2020
8c2d6b6
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 12, 2020
53ac35d
ETCM-168: Flip parameter order so it resembles the regular call.
aakoshh Nov 12, 2020
cd01dd3
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 13, 2020
38359df
ETCM-168: Update comments about background enrollment.
aakoshh Nov 13, 2020
aa120cd
ETCM-168: Added unit tests to PeerDiscoveryManager.
aakoshh Nov 17, 2020
fbd7a4d
ETCM-168: Use a flag to avoid having to sleep.
aakoshh Nov 17, 2020
02f3469
Merge remote-tracking branch 'origin/develop' into ETCM-168-discovery…
aakoshh Nov 17, 2020
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package io.iohk.ethereum.network.discovery

import akka.pattern.ask
import akka.actor.ActorSystem
import akka.testkit.{TestActorRef, TestKit}
import akka.util.{ByteString, Timeout}
import cats.effect.Resource
import io.iohk.ethereum.utils.Config
import io.iohk.ethereum.db.storage.KnownNodesStorage
import io.iohk.scalanet.discovery.crypto.PublicKey
import io.iohk.scalanet.discovery.ethereum.v4.DiscoveryService
import io.iohk.scalanet.discovery.ethereum.{Node => ENode}
import monix.eval.Task
import monix.execution.Scheduler
import org.scalatest.concurrent.Eventually
import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.should.Matchers
import org.scalatest.concurrent.ScalaFutures
import org.scalamock.scalatest.MockFactory
import scala.concurrent.duration._
import scala.concurrent.Await
import scodec.bits.BitVector
import scala.util.control.NoStackTrace
import io.iohk.ethereum.NormalPatience

class PeerDiscoveryManagerSpec
extends TestKit(ActorSystem("PeerDiscoveryManagerSpec_System"))
with AnyFlatSpecLike
with Matchers
with Eventually
with MockFactory
with ScalaFutures
with NormalPatience {

implicit val scheduler = Scheduler.Implicits.global
implicit val timeout: Timeout = 1.second

val defaultConfig = DiscoveryConfig(Config.config, Set.empty)

val sampleKnownUris = Set(
"enode://a59e33ccd2b3e52d578f1fbd70c6f9babda2650f0760d6ff3b37742fdcdfdb3defba5d56d315b40c46b70198c7621e63ffa3f987389c7118634b0fefbbdfa7fd@51.158.191.43:38556?discport=38556",
"enode://651b484b652c07c72adebfaaf8bc2bd95b420b16952ef3de76a9c00ef63f07cca02a20bd2363426f9e6fe372cef96a42b0fec3c747d118f79fd5e02f2a4ebd4e@51.158.190.99:45678?discport=45678",
"enode://9b1bf9613d859ac2071d88509ab40a111b75c1cfc51f4ad78a1fdbb429ff2405de0dc5ea8ae75e6ac88e03e51a465f0b27b517e78517f7220ae163a2e0692991@51.158.190.99:30426?discport=30426"
).map(new java.net.URI(_))

val sampleBootstrapNodes = Set(
"enode://111bd28d5b2c1378d748383fd83ff59572967c317c3063a9f475a26ad3f1517642a164338fb5268d4e32ea1cc48e663bd627dec572f1d201c7198518e5a506b1@88.99.216.30:45834?discport=45834",
"enode://2b69a3926f36a7748c9021c34050be5e0b64346225e477fe7377070f6289bd363b2be73a06010fd516e6ea3ee90778dd0399bc007bb1281923a79374f842675a@51.15.116.226:30303?discport=30303"
).map(new java.net.URI(_)).map(Node.fromUri)

trait Fixture {
lazy val discoveryConfig = defaultConfig
lazy val knownNodesStorage = mock[KnownNodesStorage]
lazy val discoveryService = mock[DiscoveryService]
lazy val discoveryServiceResource = Resource.pure[Task, DiscoveryService](discoveryService)

lazy val peerDiscoveryManager =
TestActorRef[PeerDiscoveryManager](
PeerDiscoveryManager.props(
localNodeId = ByteString.fromString("test-node"),
discoveryConfig = discoveryConfig,
knownNodesStorage = knownNodesStorage,
discoveryServiceResource = discoveryServiceResource
)
)

def getPeers =
(peerDiscoveryManager ? PeerDiscoveryManager.GetDiscoveredNodesInfo)
.mapTo[PeerDiscoveryManager.DiscoveredNodesInfo]

def test(): Unit
}

def test(fixture: Fixture): Unit = {
try {
fixture.test()
} finally {
system.stop(fixture.peerDiscoveryManager)
}
}

def toENode(node: Node): ENode =
ENode(
id = PublicKey(BitVector(node.id.toArray[Byte])),
address = ENode.Address(ip = node.addr, udpPort = node.udpPort, tcpPort = node.tcpPort)
)

behavior of "PeerDiscoveryManager"

it should "serve no peers if discovery is disabled and known peers are disabled and the manager isn't started" in test {
new Fixture {
override lazy val discoveryConfig =
defaultConfig.copy(discoveryEnabled = false, reuseKnownNodes = false, bootstrapNodes = Set.empty)

override def test(): Unit = {
getPeers.futureValue.nodes shouldBe empty
}
}
}

it should "serve the bootstrap nodes if known peers are reused even discovery isn't enabled and the manager isn't started" in test {
new Fixture {
override lazy val discoveryConfig =
defaultConfig.copy(discoveryEnabled = false, reuseKnownNodes = true, bootstrapNodes = sampleBootstrapNodes)

override def test(): Unit = {
getPeers.futureValue.nodes should contain theSameElementsAs (sampleBootstrapNodes)
}
}
}

it should "serve the known peers if discovery is enabled and the manager isn't started" in test {
new Fixture {
override lazy val discoveryConfig =
defaultConfig.copy(discoveryEnabled = true, reuseKnownNodes = true, bootstrapNodes = Set.empty)

(knownNodesStorage.getKnownNodes _)
.expects()
.returning(sampleKnownUris)
.once()

override def test(): Unit = {
getPeers.futureValue.nodes.map(_.toUri) should contain theSameElementsAs (sampleKnownUris)
}
}
}

it should "merge the known peers with the service if it's started" in test {
new Fixture {
override lazy val discoveryConfig =
defaultConfig.copy(discoveryEnabled = true, reuseKnownNodes = true, bootstrapNodes = Set.empty)

val sampleNodes = sampleBootstrapNodes

(knownNodesStorage.getKnownNodes _)
.expects()
.returning(sampleKnownUris)
.once()

(discoveryService.getNodes _)
.expects()
.returning(Task(sampleNodes.map(toENode)))
.once()

val expected = sampleKnownUris ++ sampleNodes.map(_.toUri)

override def test(): Unit = {
peerDiscoveryManager ! PeerDiscoveryManager.Start
eventually {
getPeers.futureValue.nodes.map(_.toUri) should contain theSameElementsAs (expected)
}
}
}
}

it should "keep serving the known peers if the service fails to start" in test {
new Fixture {
override lazy val discoveryConfig =
defaultConfig.copy(discoveryEnabled = true, reuseKnownNodes = true, bootstrapNodes = Set.empty)

override lazy val discoveryServiceResource: Resource[Task, DiscoveryService] =
Resource.liftF(Task.raiseError[DiscoveryService](new RuntimeException("Oh no!") with NoStackTrace))

(knownNodesStorage.getKnownNodes _)
.expects()
.returning(sampleKnownUris)
.once()

override def test(): Unit = {
peerDiscoveryManager ! PeerDiscoveryManager.Start
Thread.sleep(100)
getPeers.futureValue.nodes should have size (sampleKnownUris.size)
aakoshh marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

it should "stop using the service after it is stopped" in test {
new Fixture {
override lazy val discoveryConfig =
defaultConfig.copy(discoveryEnabled = true, reuseKnownNodes = true, bootstrapNodes = Set.empty)

(knownNodesStorage.getKnownNodes _)
.expects()
.returning(sampleKnownUris)
.once()

(discoveryService.getNodes _)
.expects()
.returning(Task(sampleBootstrapNodes.map(toENode)))
.once()

override def test(): Unit = {
peerDiscoveryManager ! PeerDiscoveryManager.Start
eventually {
getPeers.futureValue.nodes should have size (sampleKnownUris.size + sampleBootstrapNodes.size)
}
peerDiscoveryManager ! PeerDiscoveryManager.Stop
eventually {
getPeers.futureValue.nodes should have size (sampleKnownUris.size)
}
}
}
}

it should "propagate any error from the service to the caller" in test {
new Fixture {
override lazy val discoveryConfig =
defaultConfig.copy(discoveryEnabled = true, reuseKnownNodes = false, bootstrapNodes = Set.empty)

(discoveryService.getNodes _)
.expects()
.returning(Task.raiseError(new RuntimeException("Oh no!") with NoStackTrace))
.atLeastOnce()

override def test(): Unit = {
peerDiscoveryManager ! PeerDiscoveryManager.Start
eventually {
a[RuntimeException] shouldBe thrownBy(Await.result(getPeers, 50.millis))
}
}
}
}
}