1
0
Fork 0
mirror of https://github.com/ACINQ/eclair.git synced 2025-02-23 22:46:44 +01:00

Broadcast gossip regardless of timestamp filters (#1284)

This is needed to make sure we broadcast our own gossip.
Otherwise we will try to gossip at the beginning of the connection,
when the peer hasn't set any timestamp, so our gossip will be dropped.

See https://github.com/lightningnetwork/lightning-rfc/pull/684
This commit is contained in:
Bastien Teinturier 2020-01-20 15:32:43 +01:00 committed by GitHub
parent aa137b7da6
commit 518e56f462
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 24 deletions

View file

@ -376,7 +376,7 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
case (count, (_, origins)) if origins.contains(self) =>
// the announcement came from this peer, we don't send it back
count
case (count, (msg, _)) if !timestampInRange(msg, d.gossipTimestampFilter) =>
case (count, (msg, origins)) if !timestampInRange(msg, origins, d.gossipTimestampFilter) =>
// the peer has set up a filter on timestamp and this message is out of range
count
case (count, (msg, _)) =>
@ -587,6 +587,31 @@ class Peer(val nodeParams: NodeParams, remoteNodeId: PublicKey, authenticator: A
nodeParams.db.network.getNode(remoteNodeId).flatMap(_.addresses.headOption.map(_.socketAddress))
}
/**
* Peer may want to filter announcements based on timestamp.
*
* @param gossipTimestampFilter_opt optional gossip timestamp range
* @return
* - true if this is our own gossip
* - true if there is a filter and msg has no timestamp, or has one that matches the filter
* - false otherwise
*/
def timestampInRange(msg: RoutingMessage, origins: Set[ActorRef], gossipTimestampFilter_opt: Option[GossipTimestampFilter]): Boolean = {
// For our own gossip, we should ignore the peer's timestamp filter.
val isOurGossip = msg match {
case n: NodeAnnouncement if n.nodeId == nodeParams.nodeId => true
case _ if origins.isEmpty || origins == Set(router) => true // if gossip doesn't come from another peer, it's ours
case _ => false
}
// Otherwise we check if this message has a timestamp that matches the timestamp filter.
val matchesFilter = (msg, gossipTimestampFilter_opt) match {
case (_, None) => false // BOLT 7: A node which wants any gossip messages would have to send this, otherwise [...] no gossip messages would be received.
case (hasTs: HasTimestamp, Some(GossipTimestampFilter(_, firstTimestamp, timestampRange))) => hasTs.timestamp >= firstTimestamp && hasTs.timestamp <= firstTimestamp + timestampRange
case _ => true // if there is a filter and message doesn't have a timestamp (e.g. channel_announcement), then we send it
}
isOurGossip || matchesFilter
}
// a failing channel won't be restarted, it should handle its states
override val supervisorStrategy = OneForOneStrategy(loggingEnabled = true) { case _ => SupervisorStrategy.Stop }
@ -697,23 +722,6 @@ object Peer {
features = nodeParams.features)
}
/**
* Peer may want to filter announcements based on timestamp
*
* @param gossipTimestampFilter_opt optional gossip timestamp range
* @return
* - true if there is a filter and msg has no timestamp, or has one that matches the filter
* - false otherwise
*/
def timestampInRange(msg: RoutingMessage, gossipTimestampFilter_opt: Option[GossipTimestampFilter]): Boolean = {
// check if this message has a timestamp that matches our timestamp filter
(msg, gossipTimestampFilter_opt) match {
case (_, None) => false // BOLT 7: A node which wants any gossip messages would have to send this, otherwise [...] no gossip messages would be received.
case (hasTs: HasTimestamp, Some(GossipTimestampFilter(_, firstTimestamp, timestampRange))) => hasTs.timestamp >= firstTimestamp && hasTs.timestamp <= firstTimestamp + timestampRange
case _ => true // if there is a filter and message doesn't have a timestamp (e.g. channel_announcement), then we send it
}
}
def hostAndPort2InetSocketAddress(hostAndPort: HostAndPort): InetSocketAddress = new InetSocketAddress(hostAndPort.getHost, hostAndPort.getPort)
/**

View file

@ -353,21 +353,23 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
test("filter gossip message (no filtering)") { f =>
import f._
val probe = TestProbe()
val gossipOrigin = Set(TestProbe().ref)
connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)
val rebroadcast = Rebroadcast(channels.map(_ -> Set.empty[ActorRef]).toMap, updates.map(_ -> Set.empty[ActorRef]).toMap, nodes.map(_ -> Set.empty[ActorRef]).toMap)
val rebroadcast = Rebroadcast(channels.map(_ -> gossipOrigin).toMap, updates.map(_ -> gossipOrigin).toMap, nodes.map(_ -> gossipOrigin).toMap)
probe.send(peer, rebroadcast)
transport.expectNoMsg(2 seconds)
transport.expectNoMsg(10 seconds)
}
test("filter gossip message (filtered by origin)") { f =>
import f._
val probe = TestProbe()
connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)
val gossipOrigin = Set(TestProbe().ref)
val peerActor: ActorRef = peer
val rebroadcast = Rebroadcast(
channels.map(_ -> Set.empty[ActorRef]).toMap + (channels(5) -> Set(peerActor)),
updates.map(_ -> Set.empty[ActorRef]).toMap + (updates(6) -> Set(peerActor)) + (updates(10) -> Set(peerActor)),
nodes.map(_ -> Set.empty[ActorRef]).toMap + (nodes(4) -> Set(peerActor)))
channels.map(_ -> gossipOrigin).toMap + (channels(5) -> Set(peerActor)),
updates.map(_ -> gossipOrigin).toMap + (updates(6) -> Set(peerActor)) + (updates(10) -> Set(peerActor)),
nodes.map(_ -> gossipOrigin).toMap + (nodes(4) -> Set(peerActor)))
val filter = wire.GossipTimestampFilter(Alice.nodeParams.chainHash, 0, Long.MaxValue) // no filtering on timestamps
probe.send(peer, filter)
probe.send(peer, rebroadcast)
@ -381,7 +383,8 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
import f._
val probe = TestProbe()
connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)
val rebroadcast = Rebroadcast(channels.map(_ -> Set.empty[ActorRef]).toMap, updates.map(_ -> Set.empty[ActorRef]).toMap, nodes.map(_ -> Set.empty[ActorRef]).toMap)
val gossipOrigin = Set(TestProbe().ref)
val rebroadcast = Rebroadcast(channels.map(_ -> gossipOrigin).toMap, updates.map(_ -> gossipOrigin).toMap, nodes.map(_ -> gossipOrigin).toMap)
val timestamps = updates.map(_.timestamp).sorted.slice(10, 30)
val filter = wire.GossipTimestampFilter(Alice.nodeParams.chainHash, timestamps.head, timestamps.last - timestamps.head)
probe.send(peer, filter)
@ -393,6 +396,24 @@ class PeerSpec extends TestkitBaseClass with StateTestsHelperMethods {
nodes.filter(u => timestamps.contains(u.timestamp)).foreach(transport.expectMsg(_))
}
test("does not filter our own gossip message") { f =>
import f._
val probe = TestProbe()
connect(remoteNodeId, authenticator, watcher, router, relayer, connection, transport, peer)
val gossipOrigin = Set(TestProbe().ref)
val rebroadcast = Rebroadcast(
channels.map(_ -> gossipOrigin).toMap + (channels(5) -> Set(router.ref)),
updates.map(_ -> gossipOrigin).toMap + (updates(6) -> Set(router.ref)) + (updates(10) -> Set(router.ref)),
nodes.map(_ -> gossipOrigin).toMap + (nodes(4) -> Set(router.ref)))
// No timestamp filter set -> the only gossip we should broadcast is our own.
probe.send(peer, rebroadcast)
transport.expectMsg(channels(5))
transport.expectMsg(updates(6))
transport.expectMsg(updates(10))
transport.expectMsg(nodes(4))
transport.expectNoMsg(10 seconds)
}
test("react to peer's bad behavior") { f =>
import f._
val probe = TestProbe()