Skip to content

Commit ca2c7f2

Browse files
t-bastthomash-acinq
authored andcommitted
Use existing channel events
Instead of defining new events. We also keep a set of active channels to ensure that duplicate events don't mess up our state (even though this shouldn't happen, it feels safer).
1 parent f708e10 commit ca2c7f2

File tree

4 files changed

+48
-45
lines changed

4 files changed

+48
-45
lines changed

eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,13 +2588,6 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with
25882588
case _: TransientChannelData => None
25892589
}
25902590
context.system.eventStream.publish(ChannelStateChanged(self, nextStateData.channelId, peer, remoteNodeId, state, nextState, commitments_opt))
2591-
2592-
if (state == NORMAL) {
2593-
peer ! Peer.ChannelDeactivated
2594-
}
2595-
if (nextState == NORMAL) {
2596-
peer ! Peer.ChannelActivated
2597-
}
25982591
}
25992592

26002593
if (nextState == CLOSED) {

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

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class Peer(val nodeParams: NodeParams,
7474

7575
context.system.eventStream.subscribe(self, classOf[CurrentFeerates])
7676
context.system.eventStream.subscribe(self, classOf[CurrentBlockHeight])
77+
context.system.eventStream.subscribe(self, classOf[LocalChannelDown])
7778

7879
startWith(INSTANTIATING, Nothing)
7980

@@ -91,7 +92,7 @@ class Peer(val nodeParams: NodeParams,
9192
} else {
9293
None
9394
}
94-
goto(DISCONNECTED) using DisconnectedData(channels, activeChannels = 0, PeerStorage(peerStorageData, written = true)) // when we restart, we will attempt to reconnect right away, but then we'll wait
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
9596
}
9697

9798
when(DISCONNECTED) {
@@ -145,11 +146,11 @@ class Peer(val nodeParams: NodeParams,
145146
d.peerStorage.data.foreach(nodeParams.db.peers.updateStorage(remoteNodeId, _))
146147
stay() using d.copy(peerStorage = d.peerStorage.copy(written = true))
147148

148-
case Event(ChannelActivated, d: DisconnectedData) =>
149-
stay() using d.copy(activeChannels = d.activeChannels + 1)
149+
case Event(e: ChannelReadyForPayments, d: DisconnectedData) =>
150+
stay() using d.copy(activeChannels = d.activeChannels + e.channelId)
150151

151-
case Event(ChannelDeactivated, d: DisconnectedData) =>
152-
stay() using d.copy(activeChannels = d.activeChannels - 1)
152+
case Event(e: LocalChannelDown, d: DisconnectedData) =>
153+
stay() using d.copy(activeChannels = d.activeChannels - e.channelId)
153154
}
154155

155156
when(CONNECTED) {
@@ -424,7 +425,7 @@ class Peer(val nodeParams: NodeParams,
424425
}
425426
stay()
426427

427-
case Event(e: ChannelReadyForPayments, _: ConnectedData) =>
428+
case Event(e: ChannelReadyForPayments, d: ConnectedData) =>
428429
pendingOnTheFlyFunding.foreach {
429430
case (paymentHash, pending) =>
430431
pending.status match {
@@ -440,7 +441,10 @@ class Peer(val nodeParams: NodeParams,
440441
}
441442
}
442443
}
443-
stay()
444+
stay() using d.copy(activeChannels = d.activeChannels + e.channelId)
445+
446+
case Event(e: LocalChannelDown, d: ConnectedData) =>
447+
stay() using d.copy(activeChannels = d.activeChannels - e.channelId)
444448

445449
case Event(msg: HasChannelId, d: ConnectedData) =>
446450
d.channels.get(FinalChannelId(msg.channelId)) match {
@@ -534,26 +538,25 @@ class Peer(val nodeParams: NodeParams,
534538
d.peerConnection forward unknownMsg
535539
stay()
536540

537-
case Event(store: PeerStorageStore, d: ConnectedData) if nodeParams.features.hasFeature(Features.ProvideStorage) && d.activeChannels > 0 =>
538-
// If we don't have any pending write operations, we write the updated peer storage to disk after a delay.
539-
// This ensures that when we receive a burst of peer storage updates, we will rate-limit our IO disk operations.
540-
// If we already have a pending write operation, we must not reset the timer, otherwise we may indefinitely delay
541-
// writing to the DB and may never store our peer's backup.
542-
if (d.peerStorage.written) {
543-
startSingleTimer("peer-storage-write", WritePeerStorage, nodeParams.peerStorageConfig.writeDelay)
541+
case Event(store: PeerStorageStore, d: ConnectedData) =>
542+
if (nodeParams.features.hasFeature(Features.ProvideStorage) && d.activeChannels.nonEmpty) {
543+
// If we don't have any pending write operations, we write the updated peer storage to disk after a delay.
544+
// This ensures that when we receive a burst of peer storage updates, we will rate-limit our IO disk operations.
545+
// If we already have a pending write operation, we must not reset the timer, otherwise we may indefinitely delay
546+
// writing to the DB and may never store our peer's backup.
547+
if (d.peerStorage.written) {
548+
startSingleTimer("peer-storage-write", WritePeerStorage, nodeParams.peerStorageConfig.writeDelay)
549+
}
550+
stay() using d.copy(peerStorage = PeerStorage(Some(store.blob), written = false))
551+
} else {
552+
log.debug("ignoring peer storage (feature={}, channels={})", nodeParams.features.hasFeature(Features.ProvideStorage), d.activeChannels.mkString(","))
553+
stay()
544554
}
545-
stay() using d.copy(peerStorage = PeerStorage(Some(store.blob), written = false))
546555

547556
case Event(WritePeerStorage, d: ConnectedData) =>
548557
d.peerStorage.data.foreach(nodeParams.db.peers.updateStorage(remoteNodeId, _))
549558
stay() using d.copy(peerStorage = d.peerStorage.copy(written = true))
550559

551-
case Event(ChannelActivated, d: ConnectedData) =>
552-
stay() using d.copy(activeChannels = d.activeChannels + 1)
553-
554-
case Event(ChannelDeactivated, d: ConnectedData) =>
555-
stay() using d.copy(activeChannels = d.activeChannels - 1)
556-
557560
case Event(unhandledMsg: LightningMessage, _) =>
558561
log.warning("ignoring message {}", unhandledMsg)
559562
stay()
@@ -785,7 +788,7 @@ class Peer(val nodeParams: NodeParams,
785788
context.system.eventStream.publish(PeerDisconnected(self, remoteNodeId))
786789
}
787790

788-
private def gotoConnected(connectionReady: PeerConnection.ConnectionReady, channels: Map[ChannelId, ActorRef], activeChannels: Int, peerStorage: PeerStorage): State = {
791+
private def gotoConnected(connectionReady: PeerConnection.ConnectionReady, channels: Map[ChannelId, ActorRef], activeChannels: Set[ByteVector32], peerStorage: PeerStorage): State = {
789792
require(remoteNodeId == connectionReady.remoteNodeId, s"invalid nodeId: $remoteNodeId != ${connectionReady.remoteNodeId}")
790793
log.debug("got authenticated connection to address {}", connectionReady.address)
791794

@@ -957,16 +960,16 @@ object Peer {
957960

958961
sealed trait Data {
959962
def channels: Map[_ <: ChannelId, ActorRef] // will be overridden by Map[FinalChannelId, ActorRef] or Map[ChannelId, ActorRef]
960-
def activeChannels: Int
963+
def activeChannels: Set[ByteVector32] // channels that are available to process payments
961964
def peerStorage: PeerStorage
962965
}
963966
case object Nothing extends Data {
964967
override def channels = Map.empty
965-
override def activeChannels: Int = 0
968+
override def activeChannels: Set[ByteVector32] = Set.empty
966969
override def peerStorage: PeerStorage = PeerStorage(None, written = true)
967970
}
968-
case class DisconnectedData(channels: Map[FinalChannelId, ActorRef], activeChannels: Int, peerStorage: PeerStorage) extends Data
969-
case class ConnectedData(address: NodeAddress, peerConnection: ActorRef, localInit: protocol.Init, remoteInit: protocol.Init, channels: Map[ChannelId, ActorRef], activeChannels: Int, currentFeerates: RecommendedFeerates, previousFeerates_opt: Option[RecommendedFeerates], peerStorage: PeerStorage) extends Data {
971+
case class DisconnectedData(channels: Map[FinalChannelId, ActorRef], activeChannels: Set[ByteVector32], peerStorage: PeerStorage) extends Data
972+
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 {
970973
val connectionInfo: ConnectionInfo = ConnectionInfo(address, peerConnection, localInit, remoteInit)
971974
def localFeatures: Features[InitFeature] = localInit.features
972975
def remoteFeatures: Features[InitFeature] = remoteInit.features
@@ -1081,9 +1084,5 @@ object Peer {
10811084
case class RelayUnknownMessage(unknownMessage: UnknownMessage)
10821085

10831086
case object WritePeerStorage
1084-
1085-
case object ChannelActivated
1086-
1087-
case object ChannelDeactivated
10881087
// @formatter:on
10891088
}

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ class PeerSpec extends FixtureSpec {
112112
// let's simulate a connection
113113
if (initializePeer) {
114114
switchboard.send(peer, Peer.Init(channels, Map.empty))
115-
channels.foreach(c => if (c.isInstanceOf[DATA_NORMAL]) peer ! ChannelActivated)
116115
}
117116
val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures())
118117
switchboard.send(peer, PeerConnection.ConnectionReady(peerConnection.ref, remoteNodeId, fakeIPAddress, outgoing = true, localInit, remoteInit))
@@ -759,22 +758,34 @@ class PeerSpec extends FixtureSpec {
759758
test("peer storage") { f =>
760759
import f._
761760

761+
// We connect with a previous backup.
762+
val channel = ChannelCodecsSpec.normal
762763
val peerConnection1 = peerConnection
763-
val peerConnection2 = TestProbe()
764-
val peerConnection3 = TestProbe()
765-
766764
nodeParams.db.peers.updateStorage(remoteNodeId, hex"abcdef")
767-
connect(remoteNodeId, peer, peerConnection1, switchboard, channels = Set(ChannelCodecsSpec.normal), peerStorage = Some(hex"abcdef"))
765+
connect(remoteNodeId, peer, peerConnection1, switchboard, channels = Set(channel), peerStorage = Some(hex"abcdef"))
766+
peer ! ChannelReadyForPayments(ActorRef.noSender, channel.remoteNodeId, channel.channelId, channel.commitments.latest.fundingTxIndex)
768767
peerConnection1.send(peer, PeerStorageStore(hex"deadbeef"))
769768
peerConnection1.send(peer, PeerStorageStore(hex"0123456789"))
769+
770+
// We disconnect and reconnect, sending the last backup we received.
770771
peer ! Peer.Disconnect(f.remoteNodeId)
772+
val peerConnection2 = TestProbe()
771773
connect(remoteNodeId, peer, peerConnection2, switchboard, channels = Set(ChannelCodecsSpec.normal), initializePeer = false, peerStorage = Some(hex"0123456789"))
772774
peerConnection2.send(peer, PeerStorageStore(hex"1111"))
775+
776+
// We reconnect again.
777+
val peerConnection3 = TestProbe()
773778
connect(remoteNodeId, peer, peerConnection3, switchboard, channels = Set(ChannelCodecsSpec.normal), initializePeer = false, peerStorage = Some(hex"1111"))
774779
// Because of the delayed writes, we may not have stored the latest value immediately, but we will eventually store it.
775780
eventually {
776781
assert(nodeParams.db.peers.getStorage(remoteNodeId).contains(hex"1111"))
777782
}
783+
784+
// Our channel closes, so we stop storing backups for that peer.
785+
peer ! LocalChannelDown(ActorRef.noSender, channel.channelId, channel.shortIds, channel.remoteNodeId)
786+
peerConnection3.send(peer, PeerStorageStore(hex"2222"))
787+
peer ! WritePeerStorage
788+
assert(nodeParams.db.peers.getStorage(remoteNodeId).contains(hex"1111"))
778789
}
779790

780791
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class ReconnectionTaskSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
3838
private val recommendedFeerates = RecommendedFeerates(Block.RegtestGenesisBlock.hash, TestConstants.feeratePerKw, TestConstants.anchorOutputsFeeratePerKw)
3939

4040
private val PeerNothingData = Peer.Nothing
41-
private val PeerDisconnectedData = Peer.DisconnectedData(channels, activeChannels = 0, PeerStorage(None, written = true))
42-
private val PeerConnectedData = Peer.ConnectedData(fakeIPAddress, system.deadLetters, null, null, channels.map { case (k: ChannelId, v) => (k, v) }, activeChannels = 0, recommendedFeerates, None, PeerStorage(None, written = true))
41+
private val PeerDisconnectedData = Peer.DisconnectedData(channels, activeChannels = Set.empty, PeerStorage(None, written = true))
42+
private val PeerConnectedData = Peer.ConnectedData(fakeIPAddress, system.deadLetters, null, null, channels.map { case (k: ChannelId, v) => (k, v) }, activeChannels = Set.empty, recommendedFeerates, None, PeerStorage(None, written = true))
4343

4444
case class FixtureParam(nodeParams: NodeParams, remoteNodeId: PublicKey, reconnectionTask: TestFSMRef[ReconnectionTask.State, ReconnectionTask.Data, ReconnectionTask], monitor: TestProbe)
4545

@@ -82,7 +82,7 @@ class ReconnectionTaskSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike
8282
import f._
8383

8484
val peer = TestProbe()
85-
peer.send(reconnectionTask, Peer.Transition(PeerNothingData, Peer.DisconnectedData(Map.empty, activeChannels = 0, PeerStorage(None, written = true))))
85+
peer.send(reconnectionTask, Peer.Transition(PeerNothingData, Peer.DisconnectedData(Map.empty, activeChannels = Set.empty, PeerStorage(None, written = true))))
8686
monitor.expectNoMessage()
8787
}
8888

0 commit comments

Comments
 (0)