Skip to content

Commit 2e9c752

Browse files
committed
Networks in Init message
And receive other peer's network, disconnect if incompatible.
1 parent 09a642b commit 2e9c752

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,8 +101,8 @@ 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)
105-
case None => wire.Init(nodeParams.features)
104+
case Some(f) => wire.Init(f, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
105+
case None => wire.Init(nodeParams.features, TlvStream(InitTlv.Networks(nodeParams.chainHash :: Nil)))
106106
}
107107
log.info(s"using features=${localInit.features.toBin}")
108108
transport ! localInit
@@ -134,9 +134,19 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
134134
case Event(remoteInit: wire.Init, d: InitializingData) =>
135135
d.transport ! TransportHandler.ReadAck(remoteInit)
136136

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

139-
if (Features.areSupported(remoteInit.features)) {
139+
if (remoteInit.networks.nonEmpty && !remoteInit.networks.contains(nodeParams.chainHash)) {
140+
log.warning(s"incompatible networks (${remoteInit.networks}), disconnecting")
141+
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible networks")))
142+
d.transport ! PoisonPill
143+
stay
144+
} else if (!Features.areSupported(remoteInit.features)) {
145+
log.warning("incompatible features, disconnecting")
146+
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
147+
d.transport ! PoisonPill
148+
stay
149+
} else {
140150
d.origin_opt.foreach(origin => origin ! "connected")
141151

142152
def localHasFeature(f: Feature): Boolean = Features.hasFeature(d.localInit.features, f, None)
@@ -167,11 +177,6 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
167177
val rebroadcastDelay = Random.nextInt(nodeParams.routerConf.routerBroadcastInterval.toSeconds.toInt).seconds
168178
log.info(s"rebroadcast will be delayed by $rebroadcastDelay")
169179
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
170-
} else {
171-
log.warning(s"incompatible features, disconnecting")
172-
d.origin_opt.foreach(origin => origin ! Status.Failure(new RuntimeException("incompatible features")))
173-
d.transport ! PoisonPill
174-
stay
175180
}
176181

177182
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) {
@@ -231,6 +233,19 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
231233
probe.expectTerminated(transport.ref)
232234
}
233235

236+
test("disconnect if incompatible networks") { f =>
237+
import f._
238+
val probe = TestProbe()
239+
probe.watch(transport.ref)
240+
probe.send(peer, Peer.Init(None, Set.empty))
241+
authenticator.send(peer, Authenticator.Authenticated(connection.ref, transport.ref, remoteNodeId, new InetSocketAddress("1.2.3.4", 42000), outgoing = true, None))
242+
transport.expectMsgType[TransportHandler.Listener]
243+
transport.expectMsgType[wire.Init]
244+
transport.send(peer, wire.Init(Bob.nodeParams.features, TlvStream(InitTlv.Networks(Block.LivenetGenesisBlock.hash :: Block.SegnetGenesisBlock.hash :: Nil))))
245+
transport.expectMsgType[TransportHandler.ReadAck]
246+
probe.expectTerminated(transport.ref)
247+
}
248+
234249
test("handle disconnect in status INITIALIZING") { f =>
235250
import f._
236251

0 commit comments

Comments
 (0)