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:
parent
aa137b7da6
commit
518e56f462
2 changed files with 53 additions and 24 deletions
|
@ -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)
|
||||
|
||||
/**
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue