diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala index a18712b594..216170d918 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala @@ -2024,7 +2024,19 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with case Event(e: Error, d: DATA_CLOSING) => handleRemoteError(e, d) - case Event(INPUT_DISCONNECTED | INPUT_RECONNECTED(_, _, _), _) => stay() // we don't really care at this point + case Event(_: Shutdown, _) => + log.debug("ignoring shutdown, closing transaction already published") + stay() + + case Event(_: ClosingSigned, _) => + log.debug("ignoring closing_signed, closing transaction already published") + stay() + + case Event(_: AnnouncementSignatures, _) => + log.debug("ignoring announcement_signatures, channel is closing") + stay() + + case Event(INPUT_DISCONNECTED | _: INPUT_RECONNECTED, _) => stay() // we don't really care at this point }) when(CLOSED)(handleExceptions { @@ -2045,10 +2057,22 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with stay() case Event(w: WatchTriggered, _) => - log.warning("ignoring watch event, channel is closed (event={})", w) + log.debug("ignoring watch event, channel is closed (event={})", w) + stay() + + case Event(_: Shutdown, _) => + log.debug("ignoring shutdown, channel is closed") + stay() + + case Event(_: ClosingSigned, _) => + log.debug("ignoring closing_signed, channel is closed") stay() - case Event(INPUT_DISCONNECTED, _) => stay() // we are disconnected, but it doesn't matter anymore + case Event(_: AnnouncementSignatures, _) => + log.debug("ignoring announcement_signatures, channel is closed") + stay() + + case Event(INPUT_DISCONNECTED | _: INPUT_RECONNECTED, _) => stay() // we are disconnected, but it doesn't matter anymore }) when(OFFLINE)(handleExceptions { @@ -2118,6 +2142,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) => handleAddDisconnected(c, d) + case Event(e: HtlcSettlementCommand, _) => + log.debug("cannot settle htlc_id={}, channel is offline", e.id) + stay() + + case Event(_: CMD_SIGN, _) => stay() + + case Event(_: CMD_UPDATE_FEE, _) => stay() + case Event(c: CMD_UPDATE_RELAY_FEE, d: DATA_NORMAL) => handleUpdateRelayFeeDisconnected(c, d) case Event(getTxResponse: GetTxWithMetaResponse, d: DATA_WAIT_FOR_FUNDING_CONFIRMED) if getTxResponse.txid == d.commitments.latest.fundingTxId => handleGetFundingTx(getTxResponse, d.waitingSince, d.fundingTx_opt) @@ -2325,6 +2357,14 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with case Event(c: CMD_ADD_HTLC, d: DATA_NORMAL) => handleAddDisconnected(c, d) + case Event(e: HtlcSettlementCommand, _) => + log.debug("cannot settle htlc_id={}, channel is syncing", e.id) + stay() + + case Event(_: CMD_SIGN, _) => stay() + + case Event(_: CMD_UPDATE_FEE, _) => stay() + case Event(c: CMD_UPDATE_RELAY_FEE, d: DATA_NORMAL) => handleUpdateRelayFeeDisconnected(c, d) case Event(channelReestablish: ChannelReestablish, d: DATA_SHUTDOWN) => @@ -2469,7 +2509,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with case Event(_: BroadcastChannelUpdate, _) => stay() // we receive this when we tell the peer to disconnect - case Event("disconnecting", _) => stay() + case Event(_: Peer.DisconnectResponse, _) => stay() // funding tx was confirmed in time, let's just ignore this case Event(BITCOIN_FUNDING_TIMEOUT, _: PersistentChannelData) => stay() diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala index 947da39cce..ddb8f5d5d1 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Peer.scala @@ -18,7 +18,7 @@ package fr.acinq.eclair.io import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.adapter.{ClassicActorContextOps, ClassicActorRefOps} -import akka.actor.{Actor, ActorContext, ActorRef, ExtendedActorSystem, FSM, OneForOneStrategy, PossiblyHarmful, Props, Status, SupervisorStrategy, Terminated, typed} +import akka.actor.{Actor, ActorContext, ActorRef, ExtendedActorSystem, FSM, OneForOneStrategy, PossiblyHarmful, Props, Status, SupervisorStrategy, typed} import akka.event.Logging.MDC import akka.event.{BusLogging, DiagnosticLoggingAdapter} import akka.util.Timeout @@ -103,11 +103,15 @@ class Peer(val nodeParams: NodeParams, case Event(connectionReady: PeerConnection.ConnectionReady, d: DisconnectedData) => gotoConnected(connectionReady, d.channels.map { case (k: ChannelId, v) => (k, v) }, d.activeChannels, d.peerStorage) - case Event(Terminated(actor), d: DisconnectedData) if d.channels.values.toSet.contains(actor) => - // we have at most 2 ids: a TemporaryChannelId and a FinalChannelId - val channelIds = d.channels.filter(_._2 == actor).keys - log.info(s"channel closed: channelId=${channelIds.mkString("/")}") - val channels1 = d.channels -- channelIds + case Event(ChannelTerminated(actor), d: DisconnectedData) => + val channels1 = if (d.channels.values.toSet.contains(actor)) { + // we have at most 2 ids: a TemporaryChannelId and a FinalChannelId + val channelIds = d.channels.filter(_._2 == actor).keys + log.info(s"channel closed: channelId=${channelIds.mkString("/")}") + d.channels -- channelIds + } else { + d.channels + } if (channels1.isEmpty && canForgetPendingOnTheFlyFunding()) { log.info("that was the last open channel") context.system.eventStream.publish(LastChannelClosed(self, remoteNodeId)) @@ -495,17 +499,21 @@ class Peer(val nodeParams: NodeParams, goto(DISCONNECTED) using DisconnectedData(d.channels.collect { case (k: FinalChannelId, v) => (k, v) }, d.activeChannels, d.peerStorage) } - case Event(Terminated(actor), d: ConnectedData) if d.channels.values.toSet.contains(actor) => - // we have at most 2 ids: a TemporaryChannelId and a FinalChannelId - val channelIds = d.channels.filter(_._2 == actor).keys - log.info(s"channel closed: channelId=${channelIds.mkString("/")}") - val channels1 = d.channels -- channelIds - if (channels1.isEmpty) { - log.info("that was the last open channel, closing the connection") - context.system.eventStream.publish(LastChannelClosed(self, remoteNodeId)) - d.peerConnection ! PeerConnection.Kill(KillReason.NoRemainingChannel) + case Event(ChannelTerminated(actor), d: ConnectedData) => + if (d.channels.values.toSet.contains(actor)) { + // we have at most 2 ids: a TemporaryChannelId and a FinalChannelId + val channelIds = d.channels.filter(_._2 == actor).keys + log.info(s"channel closed: channelId=${channelIds.mkString("/")}") + val channels1 = d.channels -- channelIds + if (channels1.isEmpty) { + log.info("that was the last open channel, closing the connection") + context.system.eventStream.publish(LastChannelClosed(self, remoteNodeId)) + d.peerConnection ! PeerConnection.Kill(KillReason.NoRemainingChannel) + } + stay() using d.copy(channels = channels1) + } else { + stay() } - stay() using d.copy(channels = channels1) case Event(connectionReady: PeerConnection.ConnectionReady, d: ConnectedData) => log.debug(s"got new connection, killing current one and switching") @@ -843,7 +851,7 @@ class Peer(val nodeParams: NodeParams, private def spawnChannel(): ActorRef = { val channel = channelFactory.spawn(context, remoteNodeId) - context watch channel + context.watchWith(channel, ChannelTerminated(channel.ref)) channel } @@ -1088,6 +1096,9 @@ object Peer { */ case class ConnectionDown(peerConnection: ActorRef) extends RemoteTypes + /** A child channel actor has been terminated. */ + case class ChannelTerminated(channel: ActorRef) extends RemoteTypes + case class RelayOnionMessage(messageId: ByteVector32, msg: OnionMessage, replyTo_opt: Option[typed.ActorRef[Status]]) case class RelayUnknownMessage(unknownMessage: UnknownMessage)