Skip to content

Commit

Permalink
Networks in Init message
Browse files Browse the repository at this point in the history
And receive other peer's network, disconnect if incompatible.
  • Loading branch information
t-bast committed Dec 16, 2019
1 parent 5875fd4 commit 4bffe45
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 19 deletions.
27 changes: 14 additions & 13 deletions eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
transport ! TransportHandler.Listener(self)
context watch transport
val localInit = nodeParams.overrideFeatures.get(remoteNodeId) match {
case Some(f) => wire.Init(ByteVector.empty, f)
case None => wire.Init(ByteVector.empty, nodeParams.features)
case Some(f) => wire.Init(ByteVector.empty, f, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
case None => wire.Init(ByteVector.empty, nodeParams.features, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
}
log.info(s"using globalFeatures=${localInit.globalFeatures.toBin} and localFeatures=${localInit.localFeatures.toBin}")
transport ! localInit
Expand Down Expand Up @@ -134,21 +134,27 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
case Event(remoteInit: wire.Init, d: InitializingData) =>
d.transport ! TransportHandler.ReadAck(remoteInit)

log.info(s"peer is using globalFeatures=${remoteInit.globalFeatures.toBin} and localFeatures=${remoteInit.localFeatures.toBin}")
log.info(s"peer is using globalFeatures=${remoteInit.globalFeatures.toBin}, localFeatures=${remoteInit.localFeatures.toBin}, network=${remoteInit.networks}")

if (Features.areSupported(remoteInit.features)) {
if (remoteInit.networks.nonEmpty && !remoteInit.networks.contains(nodeParams.chainHash)) {
log.warning(s"incompatible networks (${remoteInit.networks}), disconnecting")
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible networks")))
d.transport ! PoisonPill
stay
} else if (!Features.areSupported(remoteInit.features)) {
log.warning("incompatible features, disconnecting")
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
d.transport ! PoisonPill
stay
} else {
d.origin_opt.foreach(origin => origin ! "connected")

import Features._

def hasLocalFeature(bit: Int) = Features.hasFeature(d.localInit.features, bit)

def hasRemoteFeature(bit: Int) = Features.hasFeature(remoteInit.features, bit)

val canUseChannelRangeQueries = (hasLocalFeature(CHANNEL_RANGE_QUERIES_BIT_OPTIONAL) || hasLocalFeature(CHANNEL_RANGE_QUERIES_BIT_MANDATORY)) && (hasRemoteFeature(CHANNEL_RANGE_QUERIES_BIT_OPTIONAL) || hasRemoteFeature(CHANNEL_RANGE_QUERIES_BIT_MANDATORY))

val canUseChannelRangeQueriesEx = (hasLocalFeature(CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL) || hasLocalFeature(CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY)) && (hasRemoteFeature(CHANNEL_RANGE_QUERIES_EX_BIT_OPTIONAL) || hasRemoteFeature(CHANNEL_RANGE_QUERIES_EX_BIT_MANDATORY))

if (canUseChannelRangeQueries || canUseChannelRangeQueriesEx) {
// if they support channel queries we don't send routing info yet, if they want it they will query us
// we will query them, using extended queries if supported
Expand All @@ -171,11 +177,6 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
val rebroadcastDelay = Random.nextInt(nodeParams.routerConf.routerBroadcastInterval.toSeconds.toInt).seconds
log.info(s"rebroadcast will be delayed by $rebroadcastDelay")
goto(CONNECTED) using ConnectedData(d.address_opt, d.transport, d.localInit, remoteInit, d.channels.map { case (k: ChannelId, v) => (k, v) }, rebroadcastDelay) forMax (30 seconds) // forMax will trigger a StateTimeout
} else {
log.warning(s"incompatible features, disconnecting")
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
d.transport ! PoisonPill
stay
}

case Event(Authenticator.Authenticated(connection, _, _, _, _, origin_opt), _) =>
Expand Down
4 changes: 0 additions & 4 deletions eclair-core/src/main/scala/fr/acinq/eclair/wire/InitTlv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ object InitTlvCodecs {

import InitTlv._

// TODO:
// * Send the chainHash from nodeParams when creating Init
// * Add logic to Peer.scala to fail connections to others that don't offer my chainHash

private val networks: Codec[Networks] = variableSizeBytesLong(varintoverflow, list(bytes32)).as[Networks]

val initTlvCodec = TlvCodecs.tlvStream(discriminated[InitTlv].by(varint)
Expand Down
19 changes: 17 additions & 2 deletions eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import java.net.{Inet4Address, InetAddress, InetSocketAddress, ServerSocket}
import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition}
import akka.actor.{ActorRef, PoisonPill}
import akka.testkit.{TestFSMRef, TestProbe}
import fr.acinq.bitcoin.Block
import fr.acinq.bitcoin.Crypto.PublicKey
import fr.acinq.eclair.TestConstants._
import fr.acinq.eclair._
Expand All @@ -31,7 +32,7 @@ import fr.acinq.eclair.crypto.TransportHandler
import fr.acinq.eclair.io.Peer._
import fr.acinq.eclair.router.RoutingSyncSpec.makeFakeRoutingInfo
import fr.acinq.eclair.router.{Rebroadcast, RoutingSyncSpec, SendChannelQuery}
import fr.acinq.eclair.wire.{ChannelCodecsSpec, Color, EncodedShortChannelIds, EncodingType, Error, IPv4, NodeAddress, NodeAnnouncement, Ping, Pong, QueryShortChannelIds, TlvStream}
import fr.acinq.eclair.wire.{ChannelCodecsSpec, Color, EncodedShortChannelIds, EncodingType, Error, IPv4, InitTlv, NodeAddress, NodeAnnouncement, Ping, Pong, QueryShortChannelIds, TlvStream}
import org.scalatest.{Outcome, Tag}
import scodec.bits.{ByteVector, _}

Expand Down Expand Up @@ -81,7 +82,8 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
probe.send(peer, Peer.Init(None, channels))
authenticator.send(peer, Authenticator.Authenticated(connection.ref, transport.ref, remoteNodeId, fakeIPAddress.socketAddress, outgoing = true, None))
transport.expectMsgType[TransportHandler.Listener]
transport.expectMsgType[wire.Init]
val localInit = transport.expectMsgType[wire.Init]
assert(localInit.networks === List(Block.RegtestGenesisBlock.hash))
transport.send(peer, remoteInit)
transport.expectMsgType[TransportHandler.ReadAck]
if (expectSync) {
Expand Down Expand Up @@ -231,6 +233,19 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
probe.expectTerminated(transport.ref)
}

test("disconnect if incompatible networks") { f =>
import f._
val probe = TestProbe()
probe.watch(transport.ref)
probe.send(peer, Peer.Init(None, Set.empty))
authenticator.send(peer, Authenticator.Authenticated(connection.ref, transport.ref, remoteNodeId, new InetSocketAddress("1.2.3.4", 42000), outgoing = true, None))
transport.expectMsgType[TransportHandler.Listener]
transport.expectMsgType[wire.Init]
transport.send(peer, wire.Init(ByteVector.empty, Bob.nodeParams.features, TlvStream(InitTlv.Networks(Block.LivenetGenesisBlock.hash :: Block.SegnetGenesisBlock.hash :: Nil))))
transport.expectMsgType[TransportHandler.ReadAck]
probe.expectTerminated(transport.ref)
}

test("handle disconnect in status INITIALIZING") { f =>
import f._

Expand Down

0 comments on commit 4bffe45

Please sign in to comment.