Skip to content

Commit 5c5ea0d

Browse files
committed
Networks in Init message
And receive other peer's network, disconnect if incompatible.
1 parent 31a30d2 commit 5c5ea0d

File tree

3 files changed

+31
-15
lines changed

3 files changed

+31
-15
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
101101
transport ! TransportHandler.Listener(self)
102102
context watch transport
103103
val localInit = nodeParams.overrideFeatures.get(remoteNodeId) match {
104-
case Some(f) => wire.Init(f)
104+
case Some(f) => wire.Init(f, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
105105
case None =>
106106
// Eclair-mobile thinks feature bit 15 (payment_secret) is gossip_queries_ex which creates issues, so we mask
107107
// off basic_mpp and payment_secret. As long as they're provided in the invoice it's not an issue.
@@ -116,7 +116,7 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
116116
// ... and leave the others untouched
117117
case (value, _) => value
118118
}).reverse.bytes.dropWhile(_ == 0)
119-
wire.Init(tweakedFeatures)
119+
wire.Init(tweakedFeatures, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
120120
}
121121
log.info(s"using features=${localInit.features.toBin}")
122122
transport ! localInit
@@ -148,9 +148,19 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
148148
case Event(remoteInit: wire.Init, d: InitializingData) =>
149149
d.transport ! TransportHandler.ReadAck(remoteInit)
150150

151-
log.info(s"peer is using features=${remoteInit.features.toBin}")
151+
log.info(s"peer is using features=${remoteInit.features.toBin}, network=${remoteInit.networks}")
152152

153-
if (Features.areSupported(remoteInit.features)) {
153+
if (remoteInit.networks.nonEmpty && !remoteInit.networks.contains(nodeParams.chainHash)) {
154+
log.warning(s"incompatible networks (${remoteInit.networks}), disconnecting")
155+
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible networks")))
156+
d.transport ! PoisonPill
157+
stay
158+
} else if (!Features.areSupported(remoteInit.features)) {
159+
log.warning("incompatible features, disconnecting")
160+
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
161+
d.transport ! PoisonPill
162+
stay
163+
} else {
154164
d.origin_opt.foreach(origin => origin ! "connected")
155165

156166
def localHasFeature(f: Feature): Boolean = Features.hasFeature(d.localInit.features, f)
@@ -181,11 +191,6 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
181191
val rebroadcastDelay = Random.nextInt(nodeParams.routerConf.routerBroadcastInterval.toSeconds.toInt).seconds
182192
log.info(s"rebroadcast will be delayed by $rebroadcastDelay")
183193
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
184-
} else {
185-
log.warning(s"incompatible features, disconnecting")
186-
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
187-
d.transport ! PoisonPill
188-
stay
189194
}
190195

191196
case Event(Authenticator.Authenticated(connection, _, _, _, _, origin_opt), _) =>

eclair-core/src/main/scala/fr/acinq/eclair/wire/InitTlv.scala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ object InitTlvCodecs {
4040

4141
import InitTlv._
4242

43-
// TODO:
44-
// * Send the chainHash from nodeParams when creating Init
45-
// * Add logic to Peer.scala to fail connections to others that don't offer my chainHash
46-
4743
private val networks: Codec[Networks] = variableSizeBytesLong(varintoverflow, list(bytes32)).as[Networks]
4844

4945
val initTlvCodec = TlvCodecs.tlvStream(discriminated[InitTlv].by(varint)

eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import java.net.{Inet4Address, InetAddress, InetSocketAddress, ServerSocket}
2121
import akka.actor.FSM.{CurrentState, SubscribeTransitionCallBack, Transition}
2222
import akka.actor.{ActorRef, PoisonPill}
2323
import akka.testkit.{TestFSMRef, TestProbe}
24+
import fr.acinq.bitcoin.Block
2425
import fr.acinq.bitcoin.Crypto.PublicKey
2526
import fr.acinq.eclair.TestConstants._
2627
import fr.acinq.eclair._
@@ -31,7 +32,7 @@ import fr.acinq.eclair.crypto.TransportHandler
3132
import fr.acinq.eclair.io.Peer._
3233
import fr.acinq.eclair.router.RoutingSyncSpec.makeFakeRoutingInfo
3334
import fr.acinq.eclair.router.{Rebroadcast, RoutingSyncSpec, SendChannelQuery}
34-
import fr.acinq.eclair.wire.{ChannelCodecsSpec, Color, EncodedShortChannelIds, EncodingType, Error, IPv4, LightningMessageCodecs, NodeAddress, NodeAnnouncement, Ping, Pong, QueryShortChannelIds, TlvStream}
35+
import fr.acinq.eclair.wire.{ChannelCodecsSpec, Color, EncodedShortChannelIds, EncodingType, Error, IPv4, InitTlv, LightningMessageCodecs, NodeAddress, NodeAnnouncement, Ping, Pong, QueryShortChannelIds, TlvStream}
3536
import org.scalatest.{Outcome, Tag}
3637
import scodec.bits.{ByteVector, _}
3738

@@ -81,7 +82,8 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
8182
probe.send(peer, Peer.Init(None, channels))
8283
authenticator.send(peer, Authenticator.Authenticated(connection.ref, transport.ref, remoteNodeId, fakeIPAddress.socketAddress, outgoing = true, None))
8384
transport.expectMsgType[TransportHandler.Listener]
84-
transport.expectMsgType[wire.Init]
85+
val localInit = transport.expectMsgType[wire.Init]
86+
assert(localInit.networks === List(Block.RegtestGenesisBlock.hash))
8587
transport.send(peer, remoteInit)
8688
transport.expectMsgType[TransportHandler.ReadAck]
8789
if (expectSync) {
@@ -255,6 +257,19 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
255257
assert(init.features === sentFeatures.bytes)
256258
}
257259
}
260+
261+
test("disconnect if incompatible networks") { f =>
262+
import f._
263+
val probe = TestProbe()
264+
probe.watch(transport.ref)
265+
probe.send(peer, Peer.Init(None, Set.empty))
266+
authenticator.send(peer, Authenticator.Authenticated(connection.ref, transport.ref, remoteNodeId, new InetSocketAddress("1.2.3.4", 42000), outgoing = true, None))
267+
transport.expectMsgType[TransportHandler.Listener]
268+
transport.expectMsgType[wire.Init]
269+
transport.send(peer, wire.Init(Bob.nodeParams.features, TlvStream(InitTlv.Networks(Block.LivenetGenesisBlock.hash :: Block.SegnetGenesisBlock.hash :: Nil))))
270+
transport.expectMsgType[TransportHandler.ReadAck]
271+
probe.expectTerminated(transport.ref)
272+
}
258273

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

0 commit comments

Comments
 (0)