@@ -92,7 +92,9 @@ class Peer(val nodeParams: NodeParams,
9292 } else {
9393 None
9494 }
95- goto(DISCONNECTED ) using DisconnectedData (channels, activeChannels = Set .empty, PeerStorage (peerStorageData, written = true )) // when we restart, we will attempt to reconnect right away, but then we'll wait
95+ // When we restart, we will attempt to reconnect right away, but then we'll wait.
96+ // We don't fetch our peer's features from the DB: if the connection succeeds, we will get them from their init message, which saves a DB call.
97+ goto(DISCONNECTED ) using DisconnectedData (channels, activeChannels = Set .empty, PeerStorage (peerStorageData, written = true ), remoteFeatures_opt = None )
9698 }
9799
98100 when(DISCONNECTED ) {
@@ -150,7 +152,14 @@ class Peer(val nodeParams: NodeParams,
150152 if (! d.peerStorage.written && ! isTimerActive(WritePeerStorageTimerKey )) {
151153 startSingleTimer(WritePeerStorageTimerKey , WritePeerStorage , nodeParams.peerStorageConfig.writeDelay)
152154 }
153- stay() using d.copy(activeChannels = d.activeChannels + e.channelId)
155+ val remoteFeatures_opt = d.remoteFeatures_opt match {
156+ case Some (remoteFeatures) if ! remoteFeatures.written =>
157+ // We have a channel, so we can write to the DB without any DoS risk.
158+ nodeParams.db.peers.addOrUpdatePeer(remoteNodeId, NodeInfo (remoteFeatures.features, None ))
159+ Some (remoteFeatures.copy(written = true ))
160+ case _ => d.remoteFeatures_opt
161+ }
162+ stay() using d.copy(activeChannels = d.activeChannels + e.channelId, remoteFeatures_opt = remoteFeatures_opt)
154163
155164 case Event (e : LocalChannelDown , d : DisconnectedData ) =>
156165 stay() using d.copy(activeChannels = d.activeChannels - e.channelId)
@@ -447,7 +456,11 @@ class Peer(val nodeParams: NodeParams,
447456 if (! d.peerStorage.written && ! isTimerActive(WritePeerStorageTimerKey )) {
448457 startSingleTimer(WritePeerStorageTimerKey , WritePeerStorage , nodeParams.peerStorageConfig.writeDelay)
449458 }
450- stay() using d.copy(activeChannels = d.activeChannels + e.channelId)
459+ if (! d.remoteFeaturesWritten) {
460+ // We have a channel, so we can write to the DB without any DoS risk.
461+ nodeParams.db.peers.addOrUpdatePeer(remoteNodeId, NodeInfo (d.remoteFeatures, None ))
462+ }
463+ stay() using d.copy(activeChannels = d.activeChannels + e.channelId, remoteFeaturesWritten = true )
451464
452465 case Event (e : LocalChannelDown , d : ConnectedData ) =>
453466 stay() using d.copy(activeChannels = d.activeChannels - e.channelId)
@@ -492,7 +505,8 @@ class Peer(val nodeParams: NodeParams,
492505 stopPeer(d.peerStorage)
493506 } else {
494507 d.channels.values.toSet[ActorRef ].foreach(_ ! INPUT_DISCONNECTED ) // we deduplicate with toSet because there might be two entries per channel (tmp id and final id)
495- goto(DISCONNECTED ) using DisconnectedData (d.channels.collect { case (k : FinalChannelId , v) => (k, v) }, d.activeChannels, d.peerStorage)
508+ val lastRemoteFeatures = LastRemoteFeatures (d.remoteFeatures, d.remoteFeaturesWritten)
509+ goto(DISCONNECTED ) using DisconnectedData (d.channels.collect { case (k : FinalChannelId , v) => (k, v) }, d.activeChannels, d.peerStorage, Some (lastRemoteFeatures))
496510 }
497511
498512 case Event (Terminated (actor), d : ConnectedData ) if d.channels.values.toSet.contains(actor) =>
@@ -587,12 +601,22 @@ class Peer(val nodeParams: NodeParams,
587601
588602 case Event (r : GetPeerInfo , d) =>
589603 val replyTo = r.replyTo.getOrElse(sender().toTyped)
590- val peerInfo = d match {
591- case c : ConnectedData => PeerInfo (self, remoteNodeId, stateName, Some (c.remoteFeatures), Some (c.address), c.channels.values.toSet)
592- case _ => PeerInfo (self, remoteNodeId, stateName, None , None , d.channels.values.toSet)
604+ d match {
605+ case c : ConnectedData =>
606+ replyTo ! PeerInfo (self, remoteNodeId, stateName, Some (c.remoteFeatures), Some (c.address), c.channels.values.toSet)
607+ stay()
608+ case d : DisconnectedData =>
609+ // If we haven't reconnected since our last restart, we fetch the latest remote features from our DB.
610+ val remoteFeatures_opt = d.remoteFeatures_opt match {
611+ case Some (remoteFeatures) => Some (remoteFeatures)
612+ case None => nodeParams.db.peers.getPeer(remoteNodeId).map(nodeInfo => LastRemoteFeatures (nodeInfo.features, written = true ))
613+ }
614+ replyTo ! PeerInfo (self, remoteNodeId, stateName, remoteFeatures_opt.map(_.features), None , d.channels.values.toSet)
615+ stay() using d.copy(remoteFeatures_opt = remoteFeatures_opt)
616+ case _ =>
617+ replyTo ! PeerInfo (self, remoteNodeId, stateName, None , None , d.channels.values.toSet)
618+ stay()
593619 }
594- replyTo ! peerInfo
595- stay()
596620
597621 case Event (r : GetPeerChannels , d) =>
598622 if (d.channels.isEmpty) {
@@ -804,7 +828,13 @@ class Peer(val nodeParams: NodeParams,
804828 // We store the node address and features upon successful outgoing connection, so we can reconnect later.
805829 // The previous address is overwritten: we don't need it since the current one works.
806830 nodeParams.db.peers.addOrUpdatePeer(remoteNodeId, NodeInfo (connectionReady.remoteInit.features, Some (connectionReady.address)))
831+ } else if (channels.nonEmpty) {
832+ // If this is an incoming connection, we only store the peer details in our DB if we have channels with them.
833+ // Otherwise nodes could DoS by simply connecting to us to force us to store data in our DB.
834+ // We don't update the remote address, we don't know if we would successfully connect using the current one.
835+ nodeParams.db.peers.addOrUpdatePeer(remoteNodeId, NodeInfo (connectionReady.remoteInit.features, None ))
807836 }
837+ val remoteFeaturesWritten = connectionReady.outgoing || channels.nonEmpty
808838
809839 // If we have some data stored from our peer, we send it to them before doing anything else.
810840 peerStorage.data.foreach(connectionReady.peerConnection ! PeerStorageRetrieval (_))
@@ -826,7 +856,7 @@ class Peer(val nodeParams: NodeParams,
826856 connectionReady.peerConnection ! CurrentFeeCredit (nodeParams.chainHash, feeCredit.getOrElse(0 msat))
827857 }
828858
829- goto(CONNECTED ) using ConnectedData (connectionReady.address, connectionReady.peerConnection, connectionReady.localInit, connectionReady.remoteInit, channels, activeChannels, feerates, None , peerStorage)
859+ goto(CONNECTED ) using ConnectedData (connectionReady.address, connectionReady.peerConnection, connectionReady.localInit, connectionReady.remoteInit, channels, activeChannels, feerates, None , peerStorage, remoteFeaturesWritten )
830860 }
831861
832862 /**
@@ -967,6 +997,8 @@ object Peer {
967997
968998 case class PeerStorage (data : Option [ByteVector ], written : Boolean )
969999
1000+ case class LastRemoteFeatures (features : Features [InitFeature ], written : Boolean )
1001+
9701002 sealed trait Data {
9711003 def channels : Map [_ <: ChannelId , ActorRef ] // will be overridden by Map[FinalChannelId, ActorRef] or Map[ChannelId, ActorRef]
9721004 def activeChannels : Set [ByteVector32 ] // channels that are available to process payments
@@ -977,8 +1009,8 @@ object Peer {
9771009 override def activeChannels : Set [ByteVector32 ] = Set .empty
9781010 override def peerStorage : PeerStorage = PeerStorage (None , written = true )
9791011 }
980- case class DisconnectedData (channels : Map [FinalChannelId , ActorRef ], activeChannels : Set [ByteVector32 ], peerStorage : PeerStorage ) extends Data
981- case class ConnectedData (address : NodeAddress , peerConnection : ActorRef , localInit : protocol.Init , remoteInit : protocol.Init , channels : Map [ChannelId , ActorRef ], activeChannels : Set [ByteVector32 ], currentFeerates : RecommendedFeerates , previousFeerates_opt : Option [RecommendedFeerates ], peerStorage : PeerStorage ) extends Data {
1012+ case class DisconnectedData (channels : Map [FinalChannelId , ActorRef ], activeChannels : Set [ByteVector32 ], peerStorage : PeerStorage , remoteFeatures_opt : Option [ LastRemoteFeatures ] ) extends Data
1013+ case class ConnectedData (address : NodeAddress , peerConnection : ActorRef , localInit : protocol.Init , remoteInit : protocol.Init , channels : Map [ChannelId , ActorRef ], activeChannels : Set [ByteVector32 ], currentFeerates : RecommendedFeerates , previousFeerates_opt : Option [RecommendedFeerates ], peerStorage : PeerStorage , remoteFeaturesWritten : Boolean ) extends Data {
9821014 val connectionInfo : ConnectionInfo = ConnectionInfo (address, peerConnection, localInit, remoteInit)
9831015 def localFeatures : Features [InitFeature ] = localInit.features
9841016 def remoteFeatures : Features [InitFeature ] = remoteInit.features
0 commit comments