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 Jan 13, 2020
1 parent 31a30d2 commit 5c5ea0d
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 15 deletions.
23 changes: 14 additions & 9 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,7 +101,7 @@ 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(f)
case Some(f) => wire.Init(f, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
case None =>
// Eclair-mobile thinks feature bit 15 (payment_secret) is gossip_queries_ex which creates issues, so we mask
// off basic_mpp and payment_secret. As long as they're provided in the invoice it's not an issue.
Expand All @@ -116,7 +116,7 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
// ... and leave the others untouched
case (value, _) => value
}).reverse.bytes.dropWhile(_ == 0)
wire.Init(tweakedFeatures)
wire.Init(tweakedFeatures, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
}
log.info(s"using features=${localInit.features.toBin}")
transport ! localInit
Expand Down Expand Up @@ -148,9 +148,19 @@ 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 features=${remoteInit.features.toBin}")
log.info(s"peer is using features=${remoteInit.features.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")

def localHasFeature(f: Feature): Boolean = Features.hasFeature(d.localInit.features, f)
Expand Down Expand Up @@ -181,11 +191,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, LightningMessageCodecs, NodeAddress, NodeAnnouncement, Ping, Pong, QueryShortChannelIds, TlvStream}
import fr.acinq.eclair.wire.{ChannelCodecsSpec, Color, EncodedShortChannelIds, EncodingType, Error, IPv4, InitTlv, LightningMessageCodecs, 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 @@ -255,6 +257,19 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
assert(init.features === sentFeatures.bytes)
}
}

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(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 5c5ea0d

Please sign in to comment.