From 20793e1d8221e2b7158bb32572f3e9b7ef17c079 Mon Sep 17 00:00:00 2001 From: sstone Date: Thu, 30 Apr 2026 11:45:28 +0200 Subject: [PATCH 1/2] Compute channel keys once on on startup We compute channel keys when we load and verify channels from our database, and pass them along instead of re-computing them later (which is expensive). --- .../scala/fr/acinq/eclair/DBChecker.scala | 20 +++++------ .../main/scala/fr/acinq/eclair/Setup.scala | 4 +-- .../fr/acinq/eclair/channel/ChannelData.scala | 15 +++++++- .../main/scala/fr/acinq/eclair/io/Peer.scala | 17 ++++++--- .../fr/acinq/eclair/io/Switchboard.scala | 20 ++++++----- .../relay/PostRestartHtlcCleaner.scala | 21 ++++++----- .../acinq/eclair/payment/relay/Relayer.scala | 1 + .../basic/fixtures/MinimalNodeFixture.scala | 2 +- .../scala/fr/acinq/eclair/io/PeerSpec.scala | 24 ++++++------- .../fr/acinq/eclair/io/SwitchboardSpec.scala | 32 ++++++++--------- .../payment/PostRestartHtlcCleanerSpec.scala | 36 +++++++++---------- .../payment/relay/OnTheFlyFundingSpec.scala | 6 ++-- 12 files changed, 112 insertions(+), 86 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala b/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala index bacd948555..8b309131da 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair -import fr.acinq.eclair.channel.{ChannelDataWithCommitments, PersistentChannelData} +import fr.acinq.eclair.channel.{ChannelDataWithCommitments, ChannelDataWithoutCommitments, PersistentChannelData, PersistentChannelDataAndChannelKeys} import fr.acinq.eclair.router.Router import fr.acinq.eclair.wire.protocol.NodeAnnouncement import grizzled.slf4j.Logging @@ -31,18 +31,16 @@ object DBChecker extends Logging { * - it is compatible with the current version of eclair * - channel keys can be re-generated from the channel seed */ - def checkChannelsDB(nodeParams: NodeParams): Seq[PersistentChannelData] = { + def checkChannelsDB(nodeParams: NodeParams): Seq[PersistentChannelDataAndChannelKeys] = { Try(nodeParams.db.channels.listLocalChannels()) match { case Success(channels) => - channels.foreach { - case data: ChannelDataWithCommitments => - val channelKeys = nodeParams.channelKeyManager.channelKeys(data.channelParams.channelConfig, data.channelParams.localParams.fundingKeyPath) - if (!data.commitments.validateSeed(channelKeys)) { - throw InvalidChannelSeedException(data.channelId) - } - case _ => () - } - channels + channels.map(channel => { + val channelWithKeys = channel.withChannelKeys(nodeParams) + if (!channelWithKeys.validateSeed()) { + throw InvalidChannelSeedException(channel.channelId) + } + channelWithKeys + }) case Failure(t) => throw IncompatibleDBException(t) } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala index c21f718541..b5440fc7ba 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala @@ -389,7 +389,7 @@ class Setup(val datadir: File, txPublisherFactory = Channel.SimpleTxPublisherFactory(nodeParams, bitcoinClient) channelFactory = Peer.SimpleChannelFactory(nodeParams, watcher, relayer, bitcoinClient, txPublisherFactory) - pendingChannelsRateLimiter = system.spawn(Behaviors.supervise(PendingChannelsRateLimiter(nodeParams, router.toTyped, channels)).onFailure(typed.SupervisorStrategy.resume), name = "pending-channels-rate-limiter") + pendingChannelsRateLimiter = system.spawn(Behaviors.supervise(PendingChannelsRateLimiter(nodeParams, router.toTyped, channels.map(_.channelData))).onFailure(typed.SupervisorStrategy.resume), name = "pending-channels-rate-limiter") peerFactory = Switchboard.SimplePeerFactory(nodeParams, bitcoinClient, channelFactory, pendingChannelsRateLimiter, register, router.toTyped) switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, peerFactory), "switchboard", SupervisorStrategy.Resume)) @@ -403,7 +403,7 @@ class Setup(val datadir: File, balanceActor = system.spawn(BalanceActor(bitcoinClient, nodeParams.channelConf.minDepth, channelsListener, nodeParams.balanceCheckInterval), name = "balance-actor") postman = system.spawn(Behaviors.supervise(Postman(nodeParams, switchboard, router.toTyped, register, offerManager)).onFailure(typed.SupervisorStrategy.restart), name = "postman") peerScorer_opt = if (nodeParams.peerScoringConfig.enabled) { - val statsTracker = system.spawn(Behaviors.supervise(PeerStatsTracker(nodeParams.peerStatsTrackerConfig, nodeParams.db.audit, channels)).onFailure(typed.SupervisorStrategy.restart), name = "peer-stats-tracker") + val statsTracker = system.spawn(Behaviors.supervise(PeerStatsTracker(nodeParams.peerStatsTrackerConfig, nodeParams.db.audit, channels.map(_.channelData))).onFailure(typed.SupervisorStrategy.restart), name = "peer-stats-tracker") Some(system.spawn(Behaviors.supervise(PeerScorer(nodeParams, bitcoinClient, statsTracker, register)).onFailure(typed.SupervisorStrategy.restart), name = "peer-scorer")) } else None diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala index 548ae9497f..5262ea93ae 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala @@ -23,12 +23,13 @@ import fr.acinq.eclair.blockchain.fee.{ConfirmationTarget, FeeratePerKw} import fr.acinq.eclair.channel.Helpers.Closing import fr.acinq.eclair.channel.fund.InteractiveTxBuilder._ import fr.acinq.eclair.channel.fund.{InteractiveTxBuilder, InteractiveTxSigningSession} +import fr.acinq.eclair.crypto.keymanager.ChannelKeys import fr.acinq.eclair.io.Peer import fr.acinq.eclair.reputation.Reputation import fr.acinq.eclair.transactions.CommitmentSpec import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelReady, ChannelReestablish, ChannelUpdate, ClosingSigned, CommitSig, FailureReason, FundingCreated, FundingSigned, Init, LiquidityAds, OnionRoutingPacket, OpenChannel, OpenDualFundedChannel, Shutdown, SpliceInit, Stfu, TxInitRbf, TxSignatures, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFulfillHtlc} -import fr.acinq.eclair.{Alias, BlockHeight, CltvExpiry, CltvExpiryDelta, Features, InitFeature, MilliSatoshi, MilliSatoshiLong, RealShortChannelId, TimestampMilli, UInt64} +import fr.acinq.eclair.{Alias, BlockHeight, CltvExpiry, CltvExpiryDelta, Features, InitFeature, MilliSatoshi, MilliSatoshiLong, NodeParams, RealShortChannelId, TimestampMilli, UInt64} import scodec.bits.ByteVector import java.util.UUID @@ -549,7 +550,19 @@ case object Nothing extends TransientChannelData { sealed trait PersistentChannelData extends ChannelData { def remoteNodeId: PublicKey def channelParams: ChannelParams + def withChannelKeys(nodeParams: NodeParams): PersistentChannelDataAndChannelKeys = { + val channelKeys = nodeParams.channelKeyManager.channelKeys(channelParams.channelConfig, channelParams.localParams.fundingKeyPath) + PersistentChannelDataAndChannelKeys(this, channelKeys) + } } + +case class PersistentChannelDataAndChannelKeys(channelData: PersistentChannelData, channelKeys: ChannelKeys) { + def validateSeed(): Boolean = channelData match { + case c: ChannelDataWithCommitments => c.commitments.validateSeed(channelKeys) + case _: DATA_WAIT_FOR_DUAL_FUNDING_SIGNED => true + } +} + sealed trait ChannelDataWithoutCommitments extends PersistentChannelData { val channelId: ByteVector32 = channelParams.channelId val remoteNodeId: PublicKey = channelParams.remoteNodeId 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 63b373fb79..5333415139 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 @@ -80,16 +80,19 @@ class Peer(val nodeParams: NodeParams, startWith(INSTANTIATING, Nothing) when(INSTANTIATING) { + case Event(init: InitWithoutKeys, _) => + self.forward(Init(init.storedChannels.map(_.withChannelKeys(nodeParams)), pendingOnTheFlyFunding)) + stay() case Event(init: Init, _) => pendingOnTheFlyFunding = init.pendingOnTheFlyFunding val channels = init.storedChannels.map { state => - val channelKeys = nodeParams.channelKeyManager.channelKeys(state.channelParams.channelConfig, state.channelParams.localParams.fundingKeyPath) + val channelKeys = state.channelKeys val channel = spawnChannel(channelKeys) - channel ! INPUT_RESTORED(state) - FinalChannelId(state.channelId) -> channel + channel ! INPUT_RESTORED(state.channelData) + FinalChannelId(state.channelData.channelId) -> channel }.toMap // We only connect to nodes with whom we have a channel, which aren't mobile wallets. - val autoReconnect = init.storedChannels.exists(c => !c.channelParams.remoteParams.initFeatures.hasFeature(Features.WakeUpNotificationClient)) + val autoReconnect = init.storedChannels.exists(c => !c.channelData.channelParams.remoteParams.initFeatures.hasFeature(Features.WakeUpNotificationClient)) context.system.eventStream.publish(PeerCreated(self, remoteNodeId)) // When we restart, we will attempt to reconnect right away, but then we'll wait. // 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. @@ -1121,7 +1124,11 @@ object Peer { case object DISCONNECTED extends State case object CONNECTED extends State - case class Init(storedChannels: Set[PersistentChannelData], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]) + case class Init(storedChannels: Set[PersistentChannelDataAndChannelKeys], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]) + // should be used in tests only as computing channels keys is expensive + case class InitWithoutKeys(storedChannels: Set[PersistentChannelData], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]) { + def init(nodeParams: NodeParams) = Init(storedChannels.map(_.withChannelKeys(nodeParams)), pendingOnTheFlyFunding) + } case class Connect(nodeId: PublicKey, address_opt: Option[NodeAddress], replyTo: ActorRef, isPersistent: Boolean) { def uri: Option[NodeURI] = address_opt.map(NodeURI(nodeId, _)) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala index c455056c66..f9f3287b00 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala @@ -50,20 +50,22 @@ class Switchboard(nodeParams: NodeParams, peerFactory: Switchboard.PeerFactory) context.system.toTyped.receptionist ! Receptionist.Register(SwitchboardServiceKey, context.self.toTyped[GetPeerInfo]) def receive: Receive = { + case init: InitWithoutKeys => + self.forward(Init(init.channels.map(_.withChannelKeys(nodeParams)))) case init: Init => // Check if channels that are still in CLOSING state have actually been closed. This can happen when the app is stopped // just after a channel state has transitioned to CLOSED and before it has effectively been removed. // Closed channels will be removed, other channels will be restored. - val (channels, closedChannels) = init.channels.partition(c => Closing.isClosed(c, None).isEmpty) + val (channels, closedChannels) = init.channels.partition(c => Closing.isClosed(c.channelData, None).isEmpty) closedChannels.foreach(c => { - log.info("channel {} was closed before restarting, updating the DB", c.channelId) - val closingData_opt = (c, Closing.isClosed(c, None)) match { + log.info("channel {} was closed before restarting, updating the DB", c.channelData.channelId) + val closingData_opt = (c.channelData, Closing.isClosed(c.channelData, None)) match { case (c: DATA_CLOSING, Some(closingType)) => Some(DATA_CLOSED(c, closingType)) case _ => None } - nodeParams.db.channels.removeChannel(c.channelId, closingData_opt) + nodeParams.db.channels.removeChannel(c.channelData.channelId, closingData_opt) }) - val peersWithChannels = channels.groupBy(_.remoteNodeId) + val peersWithChannels = channels.groupBy(_.channelData.remoteNodeId) val peersWithOnTheFlyFunding = nodeParams.db.liquidity.listPendingOnTheFlyFunding() peersWithChannels.foreach { case (remoteNodeId, states) => createOrGetPeer(remoteNodeId, offlineChannels = states.toSet, peersWithOnTheFlyFunding.getOrElse(remoteNodeId, Map.empty)) } // We must re-create peers that have a funded on-the-fly payment, even if they don't have a channel yet. @@ -71,7 +73,7 @@ class Switchboard(nodeParams: NodeParams, peerFactory: Switchboard.PeerFactory) (peersWithOnTheFlyFunding -- peersWithChannels.keySet).foreach { case (remoteNodeId, pending) => createOrGetPeer(remoteNodeId, Set.empty, pending) } - val peerCapacities = channels.map { + val peerCapacities = channels.map(_.channelData).map { case channelData: ChannelDataWithoutCommitments => (channelData.remoteNodeId, 0L) case channelData: ChannelDataWithCommitments => (channelData.remoteNodeId, channelData.commitments.capacity.toLong) }.groupMapReduce[PublicKey, Long](_._1)(_._2)(_ + _) @@ -154,7 +156,7 @@ class Switchboard(nodeParams: NodeParams, peerFactory: Switchboard.PeerFactory) def createPeer(remoteNodeId: PublicKey): ActorRef = peerFactory.spawn(context, remoteNodeId) - def createOrGetPeer(remoteNodeId: PublicKey, offlineChannels: Set[PersistentChannelData], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]): ActorRef = { + def createOrGetPeer(remoteNodeId: PublicKey, offlineChannels: Set[PersistentChannelDataAndChannelKeys], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]): ActorRef = { getPeer(remoteNodeId) match { case Some(peer) => peer case None => @@ -190,7 +192,9 @@ object Switchboard { def peerActorName(remoteNodeId: PublicKey): String = s"peer-$remoteNodeId" // @formatter:off - case class Init(channels: Seq[PersistentChannelData]) + case class Init(channels: Seq[PersistentChannelDataAndChannelKeys]) + // should be used in tests only as computing channels keys is expensive + case class InitWithoutKeys(channels: Seq[PersistentChannelData]) case object GetPeers case class GetPeerInfo(replyTo: typed.ActorRef[PeerInfoResponse], remoteNodeId: PublicKey) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala index 9236f0430b..df26ccccfb 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala @@ -60,6 +60,8 @@ class PostRestartHtlcCleaner(nodeParams: NodeParams, register: ActorRef, initial context.system.eventStream.subscribe(self, classOf[ChannelStateChanged]) override def receive: Receive = { + case init: InitWithoutKeys => + self.forward(Init(init.channels.map(_.withChannelKeys(nodeParams)))) case init: Init => // If we do nothing after a restart, incoming HTLCs that were committed upstream but not relayed will eventually // expire and we won't lose money, but the channel will get closed, which is a major inconvenience. We want to detect @@ -71,7 +73,7 @@ class PostRestartHtlcCleaner(nodeParams: NodeParams, register: ActorRef, initial val channels = listLocalChannels(init.channels) val onTheFlyPayments = nodeParams.db.liquidity.listPendingOnTheFlyPayments().values.flatten.toSet val nonStandardIncomingHtlcs: Seq[IncomingHtlc] = nodeParams.pluginParams.collect { case p: CustomCommitmentsPlugin => p.getIncomingHtlcs(nodeParams, log) }.flatten - val htlcsIn: Seq[IncomingHtlc] = getIncomingHtlcs(channels, nodeParams.db.payments, nodeParams.privateKey, nodeParams.features) ++ nonStandardIncomingHtlcs + val htlcsIn: Seq[IncomingHtlc] = getIncomingHtlcs(channels.map(_.channelData), nodeParams.db.payments, nodeParams.privateKey, nodeParams.features) ++ nonStandardIncomingHtlcs val nonStandardRelayedOutHtlcs: Map[Origin.Cold, Set[(ByteVector32, Long)]] = nodeParams.pluginParams.collect { case p: CustomCommitmentsPlugin => p.getHtlcsRelayedOut(htlcsIn, nodeParams, log) }.flatten.toMap val relayedOut: Map[Origin.Cold, Set[(ByteVector32, Long)]] = getHtlcsRelayedOut(nodeParams, channels, htlcsIn) ++ nonStandardRelayedOutHtlcs @@ -310,7 +312,10 @@ object PostRestartHtlcCleaner { def props(nodeParams: NodeParams, register: ActorRef, initialized: Option[Promise[Done]] = None): Props = Props(new PostRestartHtlcCleaner(nodeParams, register, initialized)) - case class Init(channels: Seq[PersistentChannelData]) + case class Init(channels: Seq[PersistentChannelDataAndChannelKeys]) + + // should be used in tests only, as computing channel keys is expensive + case class InitWithoutKeys(channels: Seq[PersistentChannelData]) case object GetBrokenHtlcs @@ -405,10 +410,9 @@ object PostRestartHtlcCleaner { .toMap /** @return pending outgoing HTLCs, grouped by their upstream origin. */ - private def getHtlcsRelayedOut(nodeParams: NodeParams, channels: Seq[PersistentChannelData], htlcsIn: Seq[IncomingHtlc])(implicit log: LoggingAdapter): Map[Origin.Cold, Set[(ByteVector32, Long)]] = { + private def getHtlcsRelayedOut(nodeParams: NodeParams, channels: Seq[PersistentChannelDataAndChannelKeys], htlcsIn: Seq[IncomingHtlc])(implicit log: LoggingAdapter): Map[Origin.Cold, Set[(ByteVector32, Long)]] = { val htlcsOut = channels - .collect { case c: ChannelDataWithCommitments => c } - .flatMap { c => + .collect { case PersistentChannelDataAndChannelKeys(c: ChannelDataWithCommitments, channelKeys) => // Filter out HTLCs that will never reach the blockchain or have already been settled on-chain. val htlcsToIgnore: Set[Long] = c match { case d: DATA_CLOSING => @@ -429,7 +433,6 @@ object PostRestartHtlcCleaner { case Some(_: Closing.MutualClose) => Set.empty case None => Set.empty } - val channelKeys = nodeParams.channelKeyManager.channelKeys(d.commitments.channelParams.channelConfig, d.commitments.localChannelParams.fundingKeyPath) val timedOutHtlcs: Set[Long] = (closingType_opt match { case Some(c: Closing.LocalClose) => confirmedTxs.flatMap(tx => Closing.trimmedOrTimedOutHtlcs(channelKeys, d.commitments.latest, c.localCommit, tx)) case Some(c: Closing.RemoteClose) => confirmedTxs.flatMap(tx => Closing.trimmedOrTimedOutHtlcs(channelKeys, d.commitments.latest, c.remoteCommit, tx)) @@ -444,7 +447,7 @@ object PostRestartHtlcCleaner { c.commitments.originChannels.collect { case (outgoingHtlcId, origin: Origin.Cold) if !htlcsToIgnore.contains(outgoingHtlcId) => (origin, c.channelId, outgoingHtlcId) } - } + }.flatten groupByOrigin(htlcsOut, htlcsIn) } @@ -454,8 +457,8 @@ object PostRestartHtlcCleaner { * and before it has effectively been removed. Such closed channels will automatically be removed once the channel is * restored. */ - private def listLocalChannels(channels: Seq[PersistentChannelData]): Seq[PersistentChannelData] = - channels.filterNot(c => Closing.isClosed(c, None).isDefined) + private def listLocalChannels(channels: Seq[PersistentChannelDataAndChannelKeys]): Seq[PersistentChannelDataAndChannelKeys] = + channels.filterNot(c => Closing.isClosed(c.channelData, None).isDefined) /** * We store [[CMD_FULFILL_HTLC]]/[[CMD_FAIL_HTLC]]/[[CMD_FAIL_MALFORMED_HTLC]] in a database diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala index 06dec87653..e8f6a4bb68 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala @@ -64,6 +64,7 @@ class Relayer(nodeParams: NodeParams, router: ActorRef, register: ActorRef, paym def receive: Receive = { case init: PostRestartHtlcCleaner.Init => postRestartCleaner forward init + case init: PostRestartHtlcCleaner.InitWithoutKeys => postRestartCleaner forward init case RelayForward(add, originNode, incomingChannelOccupancy) => log.debug(s"received forwarding request for htlc #${add.id} from channelId=${add.channelId}") IncomingPaymentPacket.decrypt(add, nodeParams.privateKey, nodeParams.features) match { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala index 5a93b17dbc..18c6242efb 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala @@ -107,7 +107,7 @@ object MinimalNodeFixture extends Assertions with Eventually with IntegrationPat val switchboard = system.actorOf(Switchboard.props(nodeParams, peerFactory), "switchboard") val paymentFactory = PaymentInitiator.SimplePaymentFactory(nodeParams, router, register) val paymentInitiator = system.actorOf(PaymentInitiator.props(nodeParams, paymentFactory), "payment-initiator") - val channels = nodeParams.db.channels.listLocalChannels() + val channels = nodeParams.db.channels.listLocalChannels().map(_.withChannelKeys(nodeParams)) val postman = system.spawn(Behaviors.supervise(Postman(nodeParams, switchboard, router.toTyped, register, offerManager)).onFailure(typed.SupervisorStrategy.restart), name = "postman") switchboard ! Switchboard.Init(channels) relayer ! PostRestartHtlcCleaner.Init(channels) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala index ff3d2f2172..9c23ad34d1 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala @@ -116,7 +116,7 @@ class PeerSpec extends FixtureSpec { def connect(remoteNodeId: PublicKey, peer: TestFSMRef[Peer.State, Peer.Data, Peer], peerConnection: TestProbe, switchboard: TestProbe, channels: Set[PersistentChannelData] = Set.empty, remoteInit: protocol.Init = protocol.Init(Bob.nodeParams.features.initFeatures()), initializePeer: Boolean = true, peerStorage: Option[ByteVector] = None)(implicit system: ActorSystem): Unit = { // let's simulate a connection if (initializePeer) { - switchboard.send(peer, Peer.Init(channels, Map.empty)) + switchboard.send(peer, Peer.InitWithoutKeys(channels, Map.empty)) } val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures()) switchboard.send(peer, PeerConnection.ConnectionReady(peerConnection.ref, remoteNodeId, fakeIPAddress, outgoing = true, localInit, remoteInit)) @@ -149,7 +149,7 @@ class PeerSpec extends FixtureSpec { import f._ val probe = TestProbe() - probe.send(peer, Peer.Init(Set.empty, Map.empty)) + probe.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) probe.send(peer, Peer.Connect(remoteNodeId, address_opt = None, probe.ref, isPersistent = true)) probe.expectMsg(PeerConnection.ConnectionResult.NoAddressFound) } @@ -176,7 +176,7 @@ class PeerSpec extends FixtureSpec { val mockAddress_opt = NodeAddress.fromParts(serverAddress.getHostName, serverAddress.getPort).toOption val probe = TestProbe() - probe.send(peer, Peer.Init(Set.empty, Map.empty)) + probe.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) // we have auto-reconnect=false so we need to manually tell the peer to reconnect probe.send(peer, Peer.Connect(remoteNodeId, mockAddress_opt, probe.ref, isPersistent = true)) @@ -197,7 +197,7 @@ class PeerSpec extends FixtureSpec { assert(invalidDnsHostname_opt.get == DnsHostname("eclair.invalid", 9735)) val probe = TestProbe() - probe.send(peer, Peer.Init(Set.empty, Map.empty)) + probe.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) probe.send(peer, Peer.Connect(remoteNodeId, invalidDnsHostname_opt, probe.ref, isPersistent = true)) probe.expectMsgType[PeerConnection.ConnectionResult.ConnectionFailed] } @@ -216,7 +216,7 @@ class PeerSpec extends FixtureSpec { nodeParams.db.network.addNode(ann) val probe = TestProbe() - probe.send(peer, Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) + probe.send(peer, Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty)) // assert our mock server got an incoming connection (the client was spawned with the address from node_announcement) eventually { @@ -237,7 +237,7 @@ class PeerSpec extends FixtureSpec { val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures()) val remoteInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures().add(Features.WakeUpNotificationClient, FeatureSupport.Optional)) val mobileWalletChannel = ChannelCodecsSpec.normal.copy(commitments = ChannelCodecsSpec.normal.commitments.updateInitFeatures(localInit, remoteInit)) - probe.send(peer, Peer.Init(Set(mobileWalletChannel), Map.empty)) + probe.send(peer, Peer.InitWithoutKeys(Set(mobileWalletChannel), Map.empty)) // the reconnection task will stay in the idle state monitor.expectNoMessage(100 millis) } @@ -282,7 +282,7 @@ class PeerSpec extends FixtureSpec { import f._ val probe = TestProbe() - switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) + switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) eventually { probe.send(peer, Peer.GetPeerInfo(None)) @@ -339,7 +339,7 @@ class PeerSpec extends FixtureSpec { monitor.expectMsg(FSM.CurrentState(reconnectionTask, ReconnectionTask.IDLE)) val probe = TestProbe() - probe.send(peer, Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) + probe.send(peer, Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty)) // the reconnection task will wait a little... monitor.expectMsg(FSM.Transition(reconnectionTask, ReconnectionTask.IDLE, ReconnectionTask.WAITING)) @@ -735,7 +735,7 @@ class PeerSpec extends FixtureSpec { import f._ val probe = TestProbe() probe.watch(peer) - switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) + switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) eventually { probe.send(peer, Peer.GetPeerInfo(None)) assert(probe.expectMsgType[Peer.PeerInfo].state == Peer.DISCONNECTED) @@ -806,7 +806,7 @@ class PeerSpec extends FixtureSpec { import f._ // Peer storage is not loaded after initialization. - switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) + switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) assert(peer.stateData.peerStorage == PeerStorage.Uninitialized) // Store some data in the DB for this peer. @@ -860,7 +860,7 @@ class PeerSpec extends FixtureSpec { // When we receive an incoming connection, we don't store the peer details in our DB. assert(nodeParams.db.peers.getPeer(remoteNodeId).isEmpty) - switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) + switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures()) val remoteInit = protocol.Init(TestConstants.Bob.nodeParams.features.initFeatures()) switchboard.send(peer, PeerConnection.ConnectionReady(peerConnection.ref, remoteNodeId, fakeIPAddress, outgoing = false, localInit, remoteInit)) @@ -892,7 +892,7 @@ class PeerSpec extends FixtureSpec { nodeParams.db.peers.addOrUpdatePeerFeatures(remoteNodeId, nodeInfo.features) // We initialize ourselves after a restart, but our peer doesn't reconnect immediately to us. - switchboard.send(peer, Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) + switchboard.send(peer, Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty)) // When we request information about the peer, we will fetch it from the DB. val probe = TestProbe() probe.send(peer, Peer.GetPeerInfo(Some(probe.ref.toTyped))) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala index ab4bdca2ab..ac5d3d21ad 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala @@ -31,9 +31,9 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { // If we have a channel with that remote peer, we will automatically reconnect. val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(List(ChannelCodecsSpec.normal)) + switchboard ! Switchboard.InitWithoutKeys(List(ChannelCodecsSpec.normal)) probe.expectMsg(remoteNodeId) - peer.expectMsg(Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) + peer.expectMsg(Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty).init(nodeParams)) } test("on initialization create peers with pending on-the-fly funding proposals") { @@ -60,12 +60,12 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (probe, peer) = (TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(List(channel)) + switchboard ! Switchboard.InitWithoutKeys(List(channel)) probe.expectMsgAllOf(remoteNodeId1, remoteNodeId2) probe.expectNoMessage(100 millis) peer.expectMsgAllOf( - Peer.Init(Set(channel), Map(paymentHash1 -> pendingOnTheFly1)), - Peer.Init(Set.empty, Map(paymentHash2 -> pendingOnTheFly2)), + Peer.InitWithoutKeys(Set(channel), Map(paymentHash1 -> pendingOnTheFly1)).init(nodeParams), + Peer.InitWithoutKeys(Set.empty, Map(paymentHash2 -> pendingOnTheFly2)).init(nodeParams), ) peer.expectNoMessage(100 millis) } @@ -78,10 +78,10 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { nodeParams.db.network.addNode(NodeAnnouncement(ByteVector64.Zeroes, Features.empty, 0 unixsec, remoteNodeId, Color(0, 0, 0), "alias", remoteNodeAddress :: Nil)) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(Nil) + switchboard ! Switchboard.InitWithoutKeys(Nil) probe.send(switchboard, Peer.Connect(remoteNodeId, None, probe.ref, isPersistent = true)) probe.expectMsg(remoteNodeId) - peer.expectMsg(Peer.Init(Set.empty, Map.empty)) + peer.expectMsg(Peer.InitWithoutKeys(Set.empty, Map.empty).init(nodeParams)) val connect = peer.expectMsgType[Peer.Connect] assert(connect.nodeId == remoteNodeId) assert(connect.address_opt.isEmpty) @@ -92,10 +92,10 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (probe, peer) = (TestProbe(), TestProbe()) val remoteNodeId = randomKey().publicKey val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(Nil) + switchboard ! Switchboard.InitWithoutKeys(Nil) probe.send(switchboard, Peer.Connect(remoteNodeId, None, probe.ref, isPersistent = true)) probe.expectMsg(remoteNodeId) - peer.expectMsg(Peer.Init(Set.empty, Map.empty)) + peer.expectMsg(Peer.InitWithoutKeys(Set.empty, Map.empty).init(nodeParams)) peer.expectMsgType[Peer.Connect] val unknownNodeId = randomKey().publicKey @@ -108,7 +108,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { def sendFeatures(nodeParams: NodeParams, channels: Seq[PersistentChannelData], remoteNodeId: PublicKey, expectedFeatures: Features[InitFeature], expectedSync: Boolean): Unit = { val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(channels) + switchboard ! Switchboard.InitWithoutKeys(channels) switchboard ! PeerConnection.Authenticated(peerConnection.ref, remoteNodeId, outgoing = true) val initConnection = peerConnection.expectMsgType[PeerConnection.InitializeConnection] assert(initConnection.chainHash == nodeParams.chainHash) @@ -127,7 +127,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val remoteNodeId = ChannelCodecsSpec.normal.commitments.remoteNodeId val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(Nil) + switchboard ! Switchboard.InitWithoutKeys(Nil) // We have a channel with our peer, so we trigger a sync when connecting. switchboard ! ChannelIdAssigned(TestProbe().ref, remoteNodeId, randomBytes32(), randomBytes32()) @@ -177,7 +177,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val nodeParams = Alice.nodeParams.modify(_.routerConf.syncConf.whitelist).setTo(Set.empty).modify(_.routerConf.syncConf.peerLimit).setTo(2) val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(List( + switchboard ! Switchboard.InitWithoutKeys(List( dummyDataNormal(alice, Satoshi(500)), dummyDataNormal(alice, Satoshi(600)), dummyDataNormal(bob, Satoshi(1000)), @@ -202,7 +202,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val nodeParams = Alice.nodeParams.modify(_.routerConf.syncConf.whitelist).setTo(Set(dave)).modify(_.routerConf.syncConf.peerLimit).setTo(1) val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(List( + switchboard ! Switchboard.InitWithoutKeys(List( dummyDataNormal(alice, Satoshi(500)), dummyDataNormal(alice, Satoshi(600)), dummyDataNormal(bob, Satoshi(1000)), @@ -225,7 +225,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { test("get peer info") { val (probe, peer) = (TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(Alice.nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(Nil) + switchboard ! Switchboard.InitWithoutKeys(Nil) val knownPeerNodeId = randomKey().publicKey probe.send(switchboard, Peer.Connect(knownPeerNodeId, None, probe.ref, isPersistent = true)) probe.expectMsg(knownPeerNodeId) @@ -248,7 +248,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val unknownNodeId1 = randomKey().publicKey val unknownNodeId2 = randomKey().publicKey val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.Init(Nil) + switchboard ! Switchboard.InitWithoutKeys(Nil) // Do not track nodes we connect to. switchboard ! PeerConnection.Authenticated(peerConnection.ref, randomKey().publicKey, outgoing = true) @@ -291,7 +291,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (peer, probe) = (TestProbe(), TestProbe()) val remoteNodeId = ChannelCodecsSpec.normal.commitments.remoteNodeId val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(TestProbe(), peer))) - switchboard ! Switchboard.Init(Nil) + switchboard ! Switchboard.InitWithoutKeys(Nil) switchboard ! Peer.Connect(remoteNodeId, None, TestProbe().ref, isPersistent = true) peer.expectMsgType[Peer.Init] probe.send(switchboard, GetPeers) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala index 5c630d6e68..9062c005ad 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala @@ -116,7 +116,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. // channel 1 goes to NORMAL state: @@ -182,7 +182,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) // Upstream channels go back to the NORMAL state, but HTLCs are kept because the on-the-fly proposal was funded. system.eventStream.publish(ChannelStateChanged(channel.ref, channels.head.commitments.channelId, system.deadLetters, a, OFFLINE, NORMAL, Some(channels(0).commitments))) channel.expectNoMessage(100 millis) @@ -219,7 +219,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. // channel 1 goes to NORMAL state: @@ -267,7 +267,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupLocalPayments(nodeParams) val (relayer, _) = createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(List(testCase.channel)) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(List(testCase.channel)) register.expectNoMessage(100 millis) sender.send(relayer, testCase.fails(1)) @@ -297,7 +297,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupLocalPayments(nodeParams) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(List(testCase.channel)) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(List(testCase.channel)) register.expectNoMessage(100 millis) sender.send(relayer, testCase.fulfills(1)) @@ -335,7 +335,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val initialized = Promise[Done]() val postRestart = system.actorOf(PostRestartHtlcCleaner.props(nodeParams, register.ref, Some(initialized))) - postRestart ! PostRestartHtlcCleaner.Init(testCase.channels) + postRestart ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) awaitCond(initialized.isCompleted) register.expectNoMessage(100 millis) @@ -438,7 +438,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channels = stored(data_upstream_1, data_upstream_2, data_upstream_3, data_downstream) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. val (channel_upstream_1, channel_upstream_2, channel_upstream_3) = (TestProbe(), TestProbe(), TestProbe()) @@ -478,7 +478,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit nodeParams.db.pendingCommands.addSettlementCommand(channelId_ab_1, CMD_FAIL_HTLC(4, FailureReason.LocalFailure(PermanentChannelFailure()), None)) val (_, postRestart) = f.createRelayer(nodeParams) - postRestart ! PostRestartHtlcCleaner.Init(List(channelData)) + postRestart ! PostRestartHtlcCleaner.InitWithoutKeys(List(channelData)) sender.send(postRestart, PostRestartHtlcCleaner.GetBrokenHtlcs) val brokenHtlcs = sender.expectMsgType[PostRestartHtlcCleaner.BrokenHtlcs] assert(brokenHtlcs.relayedOut.isEmpty) @@ -520,7 +520,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit assert(Closing.isClosed(downstreamChannel, None).isEmpty) val (_, postRestart) = f.createRelayer(nodeParams) - postRestart ! PostRestartHtlcCleaner.Init(channels) + postRestart ! PostRestartHtlcCleaner.InitWithoutKeys(channels) sender.send(postRestart, PostRestartHtlcCleaner.GetBrokenHtlcs) val brokenHtlcs = sender.expectMsgType[PostRestartHtlcCleaner.BrokenHtlcs] assert(brokenHtlcs.relayedOut.isEmpty) @@ -532,7 +532,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupChannelRelayedPayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(testCase.channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFail(testCase.downstream, testCase.upstream)) @@ -554,7 +554,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channels = List(data_ab, data_bc) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFail(htlc_bc.add, upstream)) @@ -567,7 +567,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupChannelRelayedPayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(testCase.channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFulfill(testCase.downstream, testCase.upstream, preimage1)) @@ -584,7 +584,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(testCase.channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) register.expectNoMessage(100 millis) // This downstream HTLC has two upstream HTLCs. @@ -615,7 +615,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(testCase.channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) register.expectNoMessage(100 millis) // This downstream HTLC has two upstream HTLCs. @@ -645,7 +645,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(testCase.channels) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFail(testCase.downstream_2_1, testCase.upstream_2)) @@ -683,7 +683,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val data_bc = ChannelCodecsSpec.makeChannelDataNormal(htlc_bc, Map(1L -> Origin.Cold(upstreamChannel), 2L -> Origin.Cold(upstreamTrampoline))) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.Init(Seq(data_ab, data_bc)) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(Seq(data_ab, data_bc)) // HTLC failures are not relayed upstream, as we will retry until we reach the HTLC timeout. sender.send(relayer, buildForwardFail(htlc_bc(0).add, Upstream.Cold.Channel(htlc_ab(0).add, a))) @@ -729,7 +729,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams1) - relayer ! PostRestartHtlcCleaner.Init(List(c)) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(List(c)) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. // Standard channel goes to NORMAL state: @@ -758,7 +758,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit nodeParams1.db.pendingCommands.addSettlementCommand(channelId_ab_1, cmd1) nodeParams1.db.pendingCommands.addSettlementCommand(channelId_ab_1, cmd2) val (relayer, _) = f.createRelayer(nodeParams1) - relayer ! PostRestartHtlcCleaner.Init(Nil) + relayer ! PostRestartHtlcCleaner.InitWithoutKeys(Nil) register.expectNoMessage(100 millis) awaitCond(Seq(cmd1) == nodeParams1.db.pendingCommands.listSettlementCommands(channelId_ab_1)) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala index b523f57d5d..43ed9b5ba4 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala @@ -193,7 +193,7 @@ class OnTheFlyFundingSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { val rateLimiter = TestProbe() val probe = TestProbe() val peer = TestFSMRef(new Peer(nodeParams, remoteNodeId, new DummyOnChainWallet(), FakeChannelFactory(remoteNodeId, channel), TestProbe().ref, register.ref, TestProbe().ref, rateLimiter.ref)) - peer ! Peer.Init(Set.empty, Map.empty) + peer ! Peer.InitWithoutKeys(Set.empty, Map.empty) withFixture(test.toNoArgTest(FixtureParam(nodeParams, remoteNodeId, peer, peerConnection, channel, register, rateLimiter, probe))) } @@ -495,7 +495,7 @@ class OnTheFlyFundingSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { // When restarting, we watch for pending proposals. val peerAfterRestart = TestFSMRef(new Peer(nodeParams, remoteNodeId, new DummyOnChainWallet(), FakeChannelFactory(remoteNodeId, channel), TestProbe().ref, register.ref, TestProbe().ref, TestProbe().ref)) - peerAfterRestart ! Peer.Init(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) + peerAfterRestart ! Peer.InitWithoutKeys(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) probe.watch(peerAfterRestart.ref) // The last funding proposal reaches its CLTV expiry. @@ -984,7 +984,7 @@ class OnTheFlyFundingSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { // On restart, we don't attempt the payment again: it's already pending. val peerAfterRestart = TestFSMRef(new Peer(nodeParams, remoteNodeId, new DummyOnChainWallet(), FakeChannelFactory(remoteNodeId, channel), TestProbe().ref, register.ref, TestProbe().ref, TestProbe().ref)) - peerAfterRestart ! Peer.Init(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) + peerAfterRestart ! Peer.InitWithoutKeys(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) connect(peerAfterRestart) peerAfterRestart ! ChannelReadyForPayments(channel.ref, remoteNodeId, channelId, purchase.txId, fundingTxIndex = 1) val channelData2 = makeChannelData(localChanges = LocalChanges(Nil, htlc :: Nil, Nil)) From 7b17a4fc2a9aa8f0409340b50d4b7edd1fc66abd Mon Sep 17 00:00:00 2001 From: sstone Date: Mon, 4 May 2026 11:05:24 +0200 Subject: [PATCH 2/2] Keep channel keys as a transient property of channel data --- .../scala/fr/acinq/eclair/DBChecker.scala | 18 +++++----- .../main/scala/fr/acinq/eclair/Setup.scala | 4 +-- .../fr/acinq/eclair/channel/ChannelData.scala | 25 ++++++------- .../main/scala/fr/acinq/eclair/io/Peer.scala | 17 +++------ .../fr/acinq/eclair/io/Switchboard.scala | 20 +++++------ .../relay/PostRestartHtlcCleaner.scala | 21 +++++------ .../acinq/eclair/payment/relay/Relayer.scala | 1 - .../basic/fixtures/MinimalNodeFixture.scala | 2 +- .../scala/fr/acinq/eclair/io/PeerSpec.scala | 24 ++++++------- .../fr/acinq/eclair/io/SwitchboardSpec.scala | 32 ++++++++--------- .../payment/PostRestartHtlcCleanerSpec.scala | 36 +++++++++---------- .../payment/relay/OnTheFlyFundingSpec.scala | 6 ++-- 12 files changed, 96 insertions(+), 110 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala b/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala index 8b309131da..53603d453a 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/DBChecker.scala @@ -16,7 +16,7 @@ package fr.acinq.eclair -import fr.acinq.eclair.channel.{ChannelDataWithCommitments, ChannelDataWithoutCommitments, PersistentChannelData, PersistentChannelDataAndChannelKeys} +import fr.acinq.eclair.channel.{ChannelDataWithCommitments, PersistentChannelData} import fr.acinq.eclair.router.Router import fr.acinq.eclair.wire.protocol.NodeAnnouncement import grizzled.slf4j.Logging @@ -31,16 +31,16 @@ object DBChecker extends Logging { * - it is compatible with the current version of eclair * - channel keys can be re-generated from the channel seed */ - def checkChannelsDB(nodeParams: NodeParams): Seq[PersistentChannelDataAndChannelKeys] = { + def checkChannelsDB(nodeParams: NodeParams): Seq[PersistentChannelData] = { Try(nodeParams.db.channels.listLocalChannels()) match { case Success(channels) => - channels.map(channel => { - val channelWithKeys = channel.withChannelKeys(nodeParams) - if (!channelWithKeys.validateSeed()) { - throw InvalidChannelSeedException(channel.channelId) - } - channelWithKeys - }) + channels.collect { + case data: ChannelDataWithCommitments => + val channelKeys = nodeParams.channelKeyManager.channelKeys(data.channelParams.channelConfig, data.channelParams.localParams.fundingKeyPath) + data.setChannelKeys(channelKeys) + data + case data => data + } case Failure(t) => throw IncompatibleDBException(t) } } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala index b5440fc7ba..c21f718541 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/Setup.scala @@ -389,7 +389,7 @@ class Setup(val datadir: File, txPublisherFactory = Channel.SimpleTxPublisherFactory(nodeParams, bitcoinClient) channelFactory = Peer.SimpleChannelFactory(nodeParams, watcher, relayer, bitcoinClient, txPublisherFactory) - pendingChannelsRateLimiter = system.spawn(Behaviors.supervise(PendingChannelsRateLimiter(nodeParams, router.toTyped, channels.map(_.channelData))).onFailure(typed.SupervisorStrategy.resume), name = "pending-channels-rate-limiter") + pendingChannelsRateLimiter = system.spawn(Behaviors.supervise(PendingChannelsRateLimiter(nodeParams, router.toTyped, channels)).onFailure(typed.SupervisorStrategy.resume), name = "pending-channels-rate-limiter") peerFactory = Switchboard.SimplePeerFactory(nodeParams, bitcoinClient, channelFactory, pendingChannelsRateLimiter, register, router.toTyped) switchboard = system.actorOf(SimpleSupervisor.props(Switchboard.props(nodeParams, peerFactory), "switchboard", SupervisorStrategy.Resume)) @@ -403,7 +403,7 @@ class Setup(val datadir: File, balanceActor = system.spawn(BalanceActor(bitcoinClient, nodeParams.channelConf.minDepth, channelsListener, nodeParams.balanceCheckInterval), name = "balance-actor") postman = system.spawn(Behaviors.supervise(Postman(nodeParams, switchboard, router.toTyped, register, offerManager)).onFailure(typed.SupervisorStrategy.restart), name = "postman") peerScorer_opt = if (nodeParams.peerScoringConfig.enabled) { - val statsTracker = system.spawn(Behaviors.supervise(PeerStatsTracker(nodeParams.peerStatsTrackerConfig, nodeParams.db.audit, channels.map(_.channelData))).onFailure(typed.SupervisorStrategy.restart), name = "peer-stats-tracker") + val statsTracker = system.spawn(Behaviors.supervise(PeerStatsTracker(nodeParams.peerStatsTrackerConfig, nodeParams.db.audit, channels)).onFailure(typed.SupervisorStrategy.restart), name = "peer-stats-tracker") Some(system.spawn(Behaviors.supervise(PeerScorer(nodeParams, bitcoinClient, statsTracker, register)).onFailure(typed.SupervisorStrategy.restart), name = "peer-scorer")) } else None diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala index 5262ea93ae..6a3bf520ad 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/ChannelData.scala @@ -29,7 +29,7 @@ import fr.acinq.eclair.reputation.Reputation import fr.acinq.eclair.transactions.CommitmentSpec import fr.acinq.eclair.transactions.Transactions._ import fr.acinq.eclair.wire.protocol.{ChannelAnnouncement, ChannelReady, ChannelReestablish, ChannelUpdate, ClosingSigned, CommitSig, FailureReason, FundingCreated, FundingSigned, Init, LiquidityAds, OnionRoutingPacket, OpenChannel, OpenDualFundedChannel, Shutdown, SpliceInit, Stfu, TxInitRbf, TxSignatures, UpdateAddHtlc, UpdateFailHtlc, UpdateFailMalformedHtlc, UpdateFulfillHtlc} -import fr.acinq.eclair.{Alias, BlockHeight, CltvExpiry, CltvExpiryDelta, Features, InitFeature, MilliSatoshi, MilliSatoshiLong, NodeParams, RealShortChannelId, TimestampMilli, UInt64} +import fr.acinq.eclair.{Alias, BlockHeight, CltvExpiry, CltvExpiryDelta, Features, InitFeature, InvalidChannelSeedException, MilliSatoshi, MilliSatoshiLong, RealShortChannelId, TimestampMilli, UInt64} import scodec.bits.ByteVector import java.util.UUID @@ -550,29 +550,30 @@ case object Nothing extends TransientChannelData { sealed trait PersistentChannelData extends ChannelData { def remoteNodeId: PublicKey def channelParams: ChannelParams - def withChannelKeys(nodeParams: NodeParams): PersistentChannelDataAndChannelKeys = { - val channelKeys = nodeParams.channelKeyManager.channelKeys(channelParams.channelConfig, channelParams.localParams.fundingKeyPath) - PersistentChannelDataAndChannelKeys(this, channelKeys) - } -} - -case class PersistentChannelDataAndChannelKeys(channelData: PersistentChannelData, channelKeys: ChannelKeys) { - def validateSeed(): Boolean = channelData match { - case c: ChannelDataWithCommitments => c.commitments.validateSeed(channelKeys) - case _: DATA_WAIT_FOR_DUAL_FUNDING_SIGNED => true + // channel keys are never persisted or serialized + @transient private var channelKeys_opt: Option[ChannelKeys] = None + def validateChannelKeys(channelKeys: ChannelKeys): Boolean + def setChannelKeys(channelKeys: ChannelKeys): Unit = { + if (!validateChannelKeys(channelKeys)) { + throw InvalidChannelSeedException(channelId) + } + channelKeys_opt = Some(channelKeys) } + def getChannelKeys: Option[ChannelKeys] = channelKeys_opt } - sealed trait ChannelDataWithoutCommitments extends PersistentChannelData { val channelId: ByteVector32 = channelParams.channelId val remoteNodeId: PublicKey = channelParams.remoteNodeId def channelParams: ChannelParams + override def validateChannelKeys(channelKeys: ChannelKeys): Boolean = true } + sealed trait ChannelDataWithCommitments extends PersistentChannelData { val channelId: ByteVector32 = commitments.channelId val remoteNodeId: PublicKey = commitments.remoteNodeId val channelParams: ChannelParams = commitments.channelParams def commitments: Commitments + override def validateChannelKeys(channelKeys: ChannelKeys): Boolean = commitments.validateSeed(channelKeys) } sealed trait ClosedData extends ChannelData 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 5333415139..75cd98e99b 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 @@ -80,19 +80,16 @@ class Peer(val nodeParams: NodeParams, startWith(INSTANTIATING, Nothing) when(INSTANTIATING) { - case Event(init: InitWithoutKeys, _) => - self.forward(Init(init.storedChannels.map(_.withChannelKeys(nodeParams)), pendingOnTheFlyFunding)) - stay() case Event(init: Init, _) => pendingOnTheFlyFunding = init.pendingOnTheFlyFunding val channels = init.storedChannels.map { state => - val channelKeys = state.channelKeys + val channelKeys = state.getChannelKeys.getOrElse(nodeParams.channelKeyManager.channelKeys(state.channelParams.channelConfig, state.channelParams.localParams.fundingKeyPath)) val channel = spawnChannel(channelKeys) - channel ! INPUT_RESTORED(state.channelData) - FinalChannelId(state.channelData.channelId) -> channel + channel ! INPUT_RESTORED(state) + FinalChannelId(state.channelId) -> channel }.toMap // We only connect to nodes with whom we have a channel, which aren't mobile wallets. - val autoReconnect = init.storedChannels.exists(c => !c.channelData.channelParams.remoteParams.initFeatures.hasFeature(Features.WakeUpNotificationClient)) + val autoReconnect = init.storedChannels.exists(c => !c.channelParams.remoteParams.initFeatures.hasFeature(Features.WakeUpNotificationClient)) context.system.eventStream.publish(PeerCreated(self, remoteNodeId)) // When we restart, we will attempt to reconnect right away, but then we'll wait. // 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. @@ -1124,11 +1121,7 @@ object Peer { case object DISCONNECTED extends State case object CONNECTED extends State - case class Init(storedChannels: Set[PersistentChannelDataAndChannelKeys], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]) - // should be used in tests only as computing channels keys is expensive - case class InitWithoutKeys(storedChannels: Set[PersistentChannelData], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]) { - def init(nodeParams: NodeParams) = Init(storedChannels.map(_.withChannelKeys(nodeParams)), pendingOnTheFlyFunding) - } + case class Init(storedChannels: Set[PersistentChannelData], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]) case class Connect(nodeId: PublicKey, address_opt: Option[NodeAddress], replyTo: ActorRef, isPersistent: Boolean) { def uri: Option[NodeURI] = address_opt.map(NodeURI(nodeId, _)) } diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala b/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala index f9f3287b00..c455056c66 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/io/Switchboard.scala @@ -50,22 +50,20 @@ class Switchboard(nodeParams: NodeParams, peerFactory: Switchboard.PeerFactory) context.system.toTyped.receptionist ! Receptionist.Register(SwitchboardServiceKey, context.self.toTyped[GetPeerInfo]) def receive: Receive = { - case init: InitWithoutKeys => - self.forward(Init(init.channels.map(_.withChannelKeys(nodeParams)))) case init: Init => // Check if channels that are still in CLOSING state have actually been closed. This can happen when the app is stopped // just after a channel state has transitioned to CLOSED and before it has effectively been removed. // Closed channels will be removed, other channels will be restored. - val (channels, closedChannels) = init.channels.partition(c => Closing.isClosed(c.channelData, None).isEmpty) + val (channels, closedChannels) = init.channels.partition(c => Closing.isClosed(c, None).isEmpty) closedChannels.foreach(c => { - log.info("channel {} was closed before restarting, updating the DB", c.channelData.channelId) - val closingData_opt = (c.channelData, Closing.isClosed(c.channelData, None)) match { + log.info("channel {} was closed before restarting, updating the DB", c.channelId) + val closingData_opt = (c, Closing.isClosed(c, None)) match { case (c: DATA_CLOSING, Some(closingType)) => Some(DATA_CLOSED(c, closingType)) case _ => None } - nodeParams.db.channels.removeChannel(c.channelData.channelId, closingData_opt) + nodeParams.db.channels.removeChannel(c.channelId, closingData_opt) }) - val peersWithChannels = channels.groupBy(_.channelData.remoteNodeId) + val peersWithChannels = channels.groupBy(_.remoteNodeId) val peersWithOnTheFlyFunding = nodeParams.db.liquidity.listPendingOnTheFlyFunding() peersWithChannels.foreach { case (remoteNodeId, states) => createOrGetPeer(remoteNodeId, offlineChannels = states.toSet, peersWithOnTheFlyFunding.getOrElse(remoteNodeId, Map.empty)) } // We must re-create peers that have a funded on-the-fly payment, even if they don't have a channel yet. @@ -73,7 +71,7 @@ class Switchboard(nodeParams: NodeParams, peerFactory: Switchboard.PeerFactory) (peersWithOnTheFlyFunding -- peersWithChannels.keySet).foreach { case (remoteNodeId, pending) => createOrGetPeer(remoteNodeId, Set.empty, pending) } - val peerCapacities = channels.map(_.channelData).map { + val peerCapacities = channels.map { case channelData: ChannelDataWithoutCommitments => (channelData.remoteNodeId, 0L) case channelData: ChannelDataWithCommitments => (channelData.remoteNodeId, channelData.commitments.capacity.toLong) }.groupMapReduce[PublicKey, Long](_._1)(_._2)(_ + _) @@ -156,7 +154,7 @@ class Switchboard(nodeParams: NodeParams, peerFactory: Switchboard.PeerFactory) def createPeer(remoteNodeId: PublicKey): ActorRef = peerFactory.spawn(context, remoteNodeId) - def createOrGetPeer(remoteNodeId: PublicKey, offlineChannels: Set[PersistentChannelDataAndChannelKeys], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]): ActorRef = { + def createOrGetPeer(remoteNodeId: PublicKey, offlineChannels: Set[PersistentChannelData], pendingOnTheFlyFunding: Map[ByteVector32, OnTheFlyFunding.Pending]): ActorRef = { getPeer(remoteNodeId) match { case Some(peer) => peer case None => @@ -192,9 +190,7 @@ object Switchboard { def peerActorName(remoteNodeId: PublicKey): String = s"peer-$remoteNodeId" // @formatter:off - case class Init(channels: Seq[PersistentChannelDataAndChannelKeys]) - // should be used in tests only as computing channels keys is expensive - case class InitWithoutKeys(channels: Seq[PersistentChannelData]) + case class Init(channels: Seq[PersistentChannelData]) case object GetPeers case class GetPeerInfo(replyTo: typed.ActorRef[PeerInfoResponse], remoteNodeId: PublicKey) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala index df26ccccfb..895630c6d4 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/PostRestartHtlcCleaner.scala @@ -60,8 +60,6 @@ class PostRestartHtlcCleaner(nodeParams: NodeParams, register: ActorRef, initial context.system.eventStream.subscribe(self, classOf[ChannelStateChanged]) override def receive: Receive = { - case init: InitWithoutKeys => - self.forward(Init(init.channels.map(_.withChannelKeys(nodeParams)))) case init: Init => // If we do nothing after a restart, incoming HTLCs that were committed upstream but not relayed will eventually // expire and we won't lose money, but the channel will get closed, which is a major inconvenience. We want to detect @@ -73,7 +71,7 @@ class PostRestartHtlcCleaner(nodeParams: NodeParams, register: ActorRef, initial val channels = listLocalChannels(init.channels) val onTheFlyPayments = nodeParams.db.liquidity.listPendingOnTheFlyPayments().values.flatten.toSet val nonStandardIncomingHtlcs: Seq[IncomingHtlc] = nodeParams.pluginParams.collect { case p: CustomCommitmentsPlugin => p.getIncomingHtlcs(nodeParams, log) }.flatten - val htlcsIn: Seq[IncomingHtlc] = getIncomingHtlcs(channels.map(_.channelData), nodeParams.db.payments, nodeParams.privateKey, nodeParams.features) ++ nonStandardIncomingHtlcs + val htlcsIn: Seq[IncomingHtlc] = getIncomingHtlcs(channels, nodeParams.db.payments, nodeParams.privateKey, nodeParams.features) ++ nonStandardIncomingHtlcs val nonStandardRelayedOutHtlcs: Map[Origin.Cold, Set[(ByteVector32, Long)]] = nodeParams.pluginParams.collect { case p: CustomCommitmentsPlugin => p.getHtlcsRelayedOut(htlcsIn, nodeParams, log) }.flatten.toMap val relayedOut: Map[Origin.Cold, Set[(ByteVector32, Long)]] = getHtlcsRelayedOut(nodeParams, channels, htlcsIn) ++ nonStandardRelayedOutHtlcs @@ -312,10 +310,7 @@ object PostRestartHtlcCleaner { def props(nodeParams: NodeParams, register: ActorRef, initialized: Option[Promise[Done]] = None): Props = Props(new PostRestartHtlcCleaner(nodeParams, register, initialized)) - case class Init(channels: Seq[PersistentChannelDataAndChannelKeys]) - - // should be used in tests only, as computing channel keys is expensive - case class InitWithoutKeys(channels: Seq[PersistentChannelData]) + case class Init(channels: Seq[PersistentChannelData]) case object GetBrokenHtlcs @@ -410,9 +405,10 @@ object PostRestartHtlcCleaner { .toMap /** @return pending outgoing HTLCs, grouped by their upstream origin. */ - private def getHtlcsRelayedOut(nodeParams: NodeParams, channels: Seq[PersistentChannelDataAndChannelKeys], htlcsIn: Seq[IncomingHtlc])(implicit log: LoggingAdapter): Map[Origin.Cold, Set[(ByteVector32, Long)]] = { + private def getHtlcsRelayedOut(nodeParams: NodeParams, channels: Seq[PersistentChannelData], htlcsIn: Seq[IncomingHtlc])(implicit log: LoggingAdapter): Map[Origin.Cold, Set[(ByteVector32, Long)]] = { val htlcsOut = channels - .collect { case PersistentChannelDataAndChannelKeys(c: ChannelDataWithCommitments, channelKeys) => + .collect { case c: ChannelDataWithCommitments => c } + .flatMap { c => // Filter out HTLCs that will never reach the blockchain or have already been settled on-chain. val htlcsToIgnore: Set[Long] = c match { case d: DATA_CLOSING => @@ -433,6 +429,7 @@ object PostRestartHtlcCleaner { case Some(_: Closing.MutualClose) => Set.empty case None => Set.empty } + val channelKeys = d.getChannelKeys.getOrElse(nodeParams.channelKeyManager.channelKeys(d.commitments.channelParams.channelConfig, d.commitments.localChannelParams.fundingKeyPath)) val timedOutHtlcs: Set[Long] = (closingType_opt match { case Some(c: Closing.LocalClose) => confirmedTxs.flatMap(tx => Closing.trimmedOrTimedOutHtlcs(channelKeys, d.commitments.latest, c.localCommit, tx)) case Some(c: Closing.RemoteClose) => confirmedTxs.flatMap(tx => Closing.trimmedOrTimedOutHtlcs(channelKeys, d.commitments.latest, c.remoteCommit, tx)) @@ -447,7 +444,7 @@ object PostRestartHtlcCleaner { c.commitments.originChannels.collect { case (outgoingHtlcId, origin: Origin.Cold) if !htlcsToIgnore.contains(outgoingHtlcId) => (origin, c.channelId, outgoingHtlcId) } - }.flatten + } groupByOrigin(htlcsOut, htlcsIn) } @@ -457,8 +454,8 @@ object PostRestartHtlcCleaner { * and before it has effectively been removed. Such closed channels will automatically be removed once the channel is * restored. */ - private def listLocalChannels(channels: Seq[PersistentChannelDataAndChannelKeys]): Seq[PersistentChannelDataAndChannelKeys] = - channels.filterNot(c => Closing.isClosed(c.channelData, None).isDefined) + private def listLocalChannels(channels: Seq[PersistentChannelData]): Seq[PersistentChannelData] = + channels.filterNot(c => Closing.isClosed(c, None).isDefined) /** * We store [[CMD_FULFILL_HTLC]]/[[CMD_FAIL_HTLC]]/[[CMD_FAIL_MALFORMED_HTLC]] in a database diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala index e8f6a4bb68..06dec87653 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/payment/relay/Relayer.scala @@ -64,7 +64,6 @@ class Relayer(nodeParams: NodeParams, router: ActorRef, register: ActorRef, paym def receive: Receive = { case init: PostRestartHtlcCleaner.Init => postRestartCleaner forward init - case init: PostRestartHtlcCleaner.InitWithoutKeys => postRestartCleaner forward init case RelayForward(add, originNode, incomingChannelOccupancy) => log.debug(s"received forwarding request for htlc #${add.id} from channelId=${add.channelId}") IncomingPaymentPacket.decrypt(add, nodeParams.privateKey, nodeParams.features) match { diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala b/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala index 18c6242efb..5a93b17dbc 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/integration/basic/fixtures/MinimalNodeFixture.scala @@ -107,7 +107,7 @@ object MinimalNodeFixture extends Assertions with Eventually with IntegrationPat val switchboard = system.actorOf(Switchboard.props(nodeParams, peerFactory), "switchboard") val paymentFactory = PaymentInitiator.SimplePaymentFactory(nodeParams, router, register) val paymentInitiator = system.actorOf(PaymentInitiator.props(nodeParams, paymentFactory), "payment-initiator") - val channels = nodeParams.db.channels.listLocalChannels().map(_.withChannelKeys(nodeParams)) + val channels = nodeParams.db.channels.listLocalChannels() val postman = system.spawn(Behaviors.supervise(Postman(nodeParams, switchboard, router.toTyped, register, offerManager)).onFailure(typed.SupervisorStrategy.restart), name = "postman") switchboard ! Switchboard.Init(channels) relayer ! PostRestartHtlcCleaner.Init(channels) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala index 9c23ad34d1..ff3d2f2172 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/PeerSpec.scala @@ -116,7 +116,7 @@ class PeerSpec extends FixtureSpec { def connect(remoteNodeId: PublicKey, peer: TestFSMRef[Peer.State, Peer.Data, Peer], peerConnection: TestProbe, switchboard: TestProbe, channels: Set[PersistentChannelData] = Set.empty, remoteInit: protocol.Init = protocol.Init(Bob.nodeParams.features.initFeatures()), initializePeer: Boolean = true, peerStorage: Option[ByteVector] = None)(implicit system: ActorSystem): Unit = { // let's simulate a connection if (initializePeer) { - switchboard.send(peer, Peer.InitWithoutKeys(channels, Map.empty)) + switchboard.send(peer, Peer.Init(channels, Map.empty)) } val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures()) switchboard.send(peer, PeerConnection.ConnectionReady(peerConnection.ref, remoteNodeId, fakeIPAddress, outgoing = true, localInit, remoteInit)) @@ -149,7 +149,7 @@ class PeerSpec extends FixtureSpec { import f._ val probe = TestProbe() - probe.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) + probe.send(peer, Peer.Init(Set.empty, Map.empty)) probe.send(peer, Peer.Connect(remoteNodeId, address_opt = None, probe.ref, isPersistent = true)) probe.expectMsg(PeerConnection.ConnectionResult.NoAddressFound) } @@ -176,7 +176,7 @@ class PeerSpec extends FixtureSpec { val mockAddress_opt = NodeAddress.fromParts(serverAddress.getHostName, serverAddress.getPort).toOption val probe = TestProbe() - probe.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) + probe.send(peer, Peer.Init(Set.empty, Map.empty)) // we have auto-reconnect=false so we need to manually tell the peer to reconnect probe.send(peer, Peer.Connect(remoteNodeId, mockAddress_opt, probe.ref, isPersistent = true)) @@ -197,7 +197,7 @@ class PeerSpec extends FixtureSpec { assert(invalidDnsHostname_opt.get == DnsHostname("eclair.invalid", 9735)) val probe = TestProbe() - probe.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) + probe.send(peer, Peer.Init(Set.empty, Map.empty)) probe.send(peer, Peer.Connect(remoteNodeId, invalidDnsHostname_opt, probe.ref, isPersistent = true)) probe.expectMsgType[PeerConnection.ConnectionResult.ConnectionFailed] } @@ -216,7 +216,7 @@ class PeerSpec extends FixtureSpec { nodeParams.db.network.addNode(ann) val probe = TestProbe() - probe.send(peer, Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty)) + probe.send(peer, Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) // assert our mock server got an incoming connection (the client was spawned with the address from node_announcement) eventually { @@ -237,7 +237,7 @@ class PeerSpec extends FixtureSpec { val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures()) val remoteInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures().add(Features.WakeUpNotificationClient, FeatureSupport.Optional)) val mobileWalletChannel = ChannelCodecsSpec.normal.copy(commitments = ChannelCodecsSpec.normal.commitments.updateInitFeatures(localInit, remoteInit)) - probe.send(peer, Peer.InitWithoutKeys(Set(mobileWalletChannel), Map.empty)) + probe.send(peer, Peer.Init(Set(mobileWalletChannel), Map.empty)) // the reconnection task will stay in the idle state monitor.expectNoMessage(100 millis) } @@ -282,7 +282,7 @@ class PeerSpec extends FixtureSpec { import f._ val probe = TestProbe() - switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) + switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) eventually { probe.send(peer, Peer.GetPeerInfo(None)) @@ -339,7 +339,7 @@ class PeerSpec extends FixtureSpec { monitor.expectMsg(FSM.CurrentState(reconnectionTask, ReconnectionTask.IDLE)) val probe = TestProbe() - probe.send(peer, Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty)) + probe.send(peer, Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) // the reconnection task will wait a little... monitor.expectMsg(FSM.Transition(reconnectionTask, ReconnectionTask.IDLE, ReconnectionTask.WAITING)) @@ -735,7 +735,7 @@ class PeerSpec extends FixtureSpec { import f._ val probe = TestProbe() probe.watch(peer) - switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) + switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) eventually { probe.send(peer, Peer.GetPeerInfo(None)) assert(probe.expectMsgType[Peer.PeerInfo].state == Peer.DISCONNECTED) @@ -806,7 +806,7 @@ class PeerSpec extends FixtureSpec { import f._ // Peer storage is not loaded after initialization. - switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) + switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) assert(peer.stateData.peerStorage == PeerStorage.Uninitialized) // Store some data in the DB for this peer. @@ -860,7 +860,7 @@ class PeerSpec extends FixtureSpec { // When we receive an incoming connection, we don't store the peer details in our DB. assert(nodeParams.db.peers.getPeer(remoteNodeId).isEmpty) - switchboard.send(peer, Peer.InitWithoutKeys(Set.empty, Map.empty)) + switchboard.send(peer, Peer.Init(Set.empty, Map.empty)) val localInit = protocol.Init(peer.underlyingActor.nodeParams.features.initFeatures()) val remoteInit = protocol.Init(TestConstants.Bob.nodeParams.features.initFeatures()) switchboard.send(peer, PeerConnection.ConnectionReady(peerConnection.ref, remoteNodeId, fakeIPAddress, outgoing = false, localInit, remoteInit)) @@ -892,7 +892,7 @@ class PeerSpec extends FixtureSpec { nodeParams.db.peers.addOrUpdatePeerFeatures(remoteNodeId, nodeInfo.features) // We initialize ourselves after a restart, but our peer doesn't reconnect immediately to us. - switchboard.send(peer, Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty)) + switchboard.send(peer, Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) // When we request information about the peer, we will fetch it from the DB. val probe = TestProbe() probe.send(peer, Peer.GetPeerInfo(Some(probe.ref.toTyped))) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala index ac5d3d21ad..ab4bdca2ab 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/io/SwitchboardSpec.scala @@ -31,9 +31,9 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { // If we have a channel with that remote peer, we will automatically reconnect. val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(List(ChannelCodecsSpec.normal)) + switchboard ! Switchboard.Init(List(ChannelCodecsSpec.normal)) probe.expectMsg(remoteNodeId) - peer.expectMsg(Peer.InitWithoutKeys(Set(ChannelCodecsSpec.normal), Map.empty).init(nodeParams)) + peer.expectMsg(Peer.Init(Set(ChannelCodecsSpec.normal), Map.empty)) } test("on initialization create peers with pending on-the-fly funding proposals") { @@ -60,12 +60,12 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (probe, peer) = (TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(List(channel)) + switchboard ! Switchboard.Init(List(channel)) probe.expectMsgAllOf(remoteNodeId1, remoteNodeId2) probe.expectNoMessage(100 millis) peer.expectMsgAllOf( - Peer.InitWithoutKeys(Set(channel), Map(paymentHash1 -> pendingOnTheFly1)).init(nodeParams), - Peer.InitWithoutKeys(Set.empty, Map(paymentHash2 -> pendingOnTheFly2)).init(nodeParams), + Peer.Init(Set(channel), Map(paymentHash1 -> pendingOnTheFly1)), + Peer.Init(Set.empty, Map(paymentHash2 -> pendingOnTheFly2)), ) peer.expectNoMessage(100 millis) } @@ -78,10 +78,10 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { nodeParams.db.network.addNode(NodeAnnouncement(ByteVector64.Zeroes, Features.empty, 0 unixsec, remoteNodeId, Color(0, 0, 0), "alias", remoteNodeAddress :: Nil)) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(Nil) + switchboard ! Switchboard.Init(Nil) probe.send(switchboard, Peer.Connect(remoteNodeId, None, probe.ref, isPersistent = true)) probe.expectMsg(remoteNodeId) - peer.expectMsg(Peer.InitWithoutKeys(Set.empty, Map.empty).init(nodeParams)) + peer.expectMsg(Peer.Init(Set.empty, Map.empty)) val connect = peer.expectMsgType[Peer.Connect] assert(connect.nodeId == remoteNodeId) assert(connect.address_opt.isEmpty) @@ -92,10 +92,10 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (probe, peer) = (TestProbe(), TestProbe()) val remoteNodeId = randomKey().publicKey val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(Nil) + switchboard ! Switchboard.Init(Nil) probe.send(switchboard, Peer.Connect(remoteNodeId, None, probe.ref, isPersistent = true)) probe.expectMsg(remoteNodeId) - peer.expectMsg(Peer.InitWithoutKeys(Set.empty, Map.empty).init(nodeParams)) + peer.expectMsg(Peer.Init(Set.empty, Map.empty)) peer.expectMsgType[Peer.Connect] val unknownNodeId = randomKey().publicKey @@ -108,7 +108,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { def sendFeatures(nodeParams: NodeParams, channels: Seq[PersistentChannelData], remoteNodeId: PublicKey, expectedFeatures: Features[InitFeature], expectedSync: Boolean): Unit = { val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(channels) + switchboard ! Switchboard.Init(channels) switchboard ! PeerConnection.Authenticated(peerConnection.ref, remoteNodeId, outgoing = true) val initConnection = peerConnection.expectMsgType[PeerConnection.InitializeConnection] assert(initConnection.chainHash == nodeParams.chainHash) @@ -127,7 +127,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val remoteNodeId = ChannelCodecsSpec.normal.commitments.remoteNodeId val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(Nil) + switchboard ! Switchboard.Init(Nil) // We have a channel with our peer, so we trigger a sync when connecting. switchboard ! ChannelIdAssigned(TestProbe().ref, remoteNodeId, randomBytes32(), randomBytes32()) @@ -177,7 +177,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val nodeParams = Alice.nodeParams.modify(_.routerConf.syncConf.whitelist).setTo(Set.empty).modify(_.routerConf.syncConf.peerLimit).setTo(2) val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(List( + switchboard ! Switchboard.Init(List( dummyDataNormal(alice, Satoshi(500)), dummyDataNormal(alice, Satoshi(600)), dummyDataNormal(bob, Satoshi(1000)), @@ -202,7 +202,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val nodeParams = Alice.nodeParams.modify(_.routerConf.syncConf.whitelist).setTo(Set(dave)).modify(_.routerConf.syncConf.peerLimit).setTo(1) val (probe, peer, peerConnection) = (TestProbe(), TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(List( + switchboard ! Switchboard.Init(List( dummyDataNormal(alice, Satoshi(500)), dummyDataNormal(alice, Satoshi(600)), dummyDataNormal(bob, Satoshi(1000)), @@ -225,7 +225,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { test("get peer info") { val (probe, peer) = (TestProbe(), TestProbe()) val switchboard = TestActorRef(new Switchboard(Alice.nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(Nil) + switchboard ! Switchboard.Init(Nil) val knownPeerNodeId = randomKey().publicKey probe.send(switchboard, Peer.Connect(knownPeerNodeId, None, probe.ref, isPersistent = true)) probe.expectMsg(knownPeerNodeId) @@ -248,7 +248,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val unknownNodeId1 = randomKey().publicKey val unknownNodeId2 = randomKey().publicKey val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(probe, peer))) - switchboard ! Switchboard.InitWithoutKeys(Nil) + switchboard ! Switchboard.Init(Nil) // Do not track nodes we connect to. switchboard ! PeerConnection.Authenticated(peerConnection.ref, randomKey().publicKey, outgoing = true) @@ -291,7 +291,7 @@ class SwitchboardSpec extends TestKitBaseClass with AnyFunSuiteLike { val (peer, probe) = (TestProbe(), TestProbe()) val remoteNodeId = ChannelCodecsSpec.normal.commitments.remoteNodeId val switchboard = TestActorRef(new Switchboard(nodeParams, FakePeerFactory(TestProbe(), peer))) - switchboard ! Switchboard.InitWithoutKeys(Nil) + switchboard ! Switchboard.Init(Nil) switchboard ! Peer.Connect(remoteNodeId, None, TestProbe().ref, isPersistent = true) peer.expectMsgType[Peer.Init] probe.send(switchboard, GetPeers) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala index 9062c005ad..5c630d6e68 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/PostRestartHtlcCleanerSpec.scala @@ -116,7 +116,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) + relayer ! PostRestartHtlcCleaner.Init(channels) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. // channel 1 goes to NORMAL state: @@ -182,7 +182,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) + relayer ! PostRestartHtlcCleaner.Init(channels) // Upstream channels go back to the NORMAL state, but HTLCs are kept because the on-the-fly proposal was funded. system.eventStream.publish(ChannelStateChanged(channel.ref, channels.head.commitments.channelId, system.deadLetters, a, OFFLINE, NORMAL, Some(channels(0).commitments))) channel.expectNoMessage(100 millis) @@ -219,7 +219,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) + relayer ! PostRestartHtlcCleaner.Init(channels) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. // channel 1 goes to NORMAL state: @@ -267,7 +267,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupLocalPayments(nodeParams) val (relayer, _) = createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(List(testCase.channel)) + relayer ! PostRestartHtlcCleaner.Init(List(testCase.channel)) register.expectNoMessage(100 millis) sender.send(relayer, testCase.fails(1)) @@ -297,7 +297,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupLocalPayments(nodeParams) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(List(testCase.channel)) + relayer ! PostRestartHtlcCleaner.Init(List(testCase.channel)) register.expectNoMessage(100 millis) sender.send(relayer, testCase.fulfills(1)) @@ -335,7 +335,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val initialized = Promise[Done]() val postRestart = system.actorOf(PostRestartHtlcCleaner.props(nodeParams, register.ref, Some(initialized))) - postRestart ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) + postRestart ! PostRestartHtlcCleaner.Init(testCase.channels) awaitCond(initialized.isCompleted) register.expectNoMessage(100 millis) @@ -438,7 +438,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channels = stored(data_upstream_1, data_upstream_2, data_upstream_3, data_downstream) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) + relayer ! PostRestartHtlcCleaner.Init(channels) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. val (channel_upstream_1, channel_upstream_2, channel_upstream_3) = (TestProbe(), TestProbe(), TestProbe()) @@ -478,7 +478,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit nodeParams.db.pendingCommands.addSettlementCommand(channelId_ab_1, CMD_FAIL_HTLC(4, FailureReason.LocalFailure(PermanentChannelFailure()), None)) val (_, postRestart) = f.createRelayer(nodeParams) - postRestart ! PostRestartHtlcCleaner.InitWithoutKeys(List(channelData)) + postRestart ! PostRestartHtlcCleaner.Init(List(channelData)) sender.send(postRestart, PostRestartHtlcCleaner.GetBrokenHtlcs) val brokenHtlcs = sender.expectMsgType[PostRestartHtlcCleaner.BrokenHtlcs] assert(brokenHtlcs.relayedOut.isEmpty) @@ -520,7 +520,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit assert(Closing.isClosed(downstreamChannel, None).isEmpty) val (_, postRestart) = f.createRelayer(nodeParams) - postRestart ! PostRestartHtlcCleaner.InitWithoutKeys(channels) + postRestart ! PostRestartHtlcCleaner.Init(channels) sender.send(postRestart, PostRestartHtlcCleaner.GetBrokenHtlcs) val brokenHtlcs = sender.expectMsgType[PostRestartHtlcCleaner.BrokenHtlcs] assert(brokenHtlcs.relayedOut.isEmpty) @@ -532,7 +532,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupChannelRelayedPayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) + relayer ! PostRestartHtlcCleaner.Init(testCase.channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFail(testCase.downstream, testCase.upstream)) @@ -554,7 +554,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channels = List(data_ab, data_bc) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(channels) + relayer ! PostRestartHtlcCleaner.Init(channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFail(htlc_bc.add, upstream)) @@ -567,7 +567,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupChannelRelayedPayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) + relayer ! PostRestartHtlcCleaner.Init(testCase.channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFulfill(testCase.downstream, testCase.upstream, preimage1)) @@ -584,7 +584,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) + relayer ! PostRestartHtlcCleaner.Init(testCase.channels) register.expectNoMessage(100 millis) // This downstream HTLC has two upstream HTLCs. @@ -615,7 +615,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) + relayer ! PostRestartHtlcCleaner.Init(testCase.channels) register.expectNoMessage(100 millis) // This downstream HTLC has two upstream HTLCs. @@ -645,7 +645,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val testCase = setupTrampolinePayments() val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(testCase.channels) + relayer ! PostRestartHtlcCleaner.Init(testCase.channels) register.expectNoMessage(100 millis) sender.send(relayer, buildForwardFail(testCase.downstream_2_1, testCase.upstream_2)) @@ -683,7 +683,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val data_bc = ChannelCodecsSpec.makeChannelDataNormal(htlc_bc, Map(1L -> Origin.Cold(upstreamChannel), 2L -> Origin.Cold(upstreamTrampoline))) val (relayer, _) = f.createRelayer(nodeParams) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(Seq(data_ab, data_bc)) + relayer ! PostRestartHtlcCleaner.Init(Seq(data_ab, data_bc)) // HTLC failures are not relayed upstream, as we will retry until we reach the HTLC timeout. sender.send(relayer, buildForwardFail(htlc_bc(0).add, Upstream.Cold.Channel(htlc_ab(0).add, a))) @@ -729,7 +729,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit val channel = TestProbe() val (relayer, _) = f.createRelayer(nodeParams1) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(List(c)) + relayer ! PostRestartHtlcCleaner.Init(List(c)) register.expectNoMessage(100 millis) // nothing should happen while channels are still offline. // Standard channel goes to NORMAL state: @@ -758,7 +758,7 @@ class PostRestartHtlcCleanerSpec extends TestKitBaseClass with FixtureAnyFunSuit nodeParams1.db.pendingCommands.addSettlementCommand(channelId_ab_1, cmd1) nodeParams1.db.pendingCommands.addSettlementCommand(channelId_ab_1, cmd2) val (relayer, _) = f.createRelayer(nodeParams1) - relayer ! PostRestartHtlcCleaner.InitWithoutKeys(Nil) + relayer ! PostRestartHtlcCleaner.Init(Nil) register.expectNoMessage(100 millis) awaitCond(Seq(cmd1) == nodeParams1.db.pendingCommands.listSettlementCommands(channelId_ab_1)) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala index 43ed9b5ba4..b523f57d5d 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/payment/relay/OnTheFlyFundingSpec.scala @@ -193,7 +193,7 @@ class OnTheFlyFundingSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { val rateLimiter = TestProbe() val probe = TestProbe() val peer = TestFSMRef(new Peer(nodeParams, remoteNodeId, new DummyOnChainWallet(), FakeChannelFactory(remoteNodeId, channel), TestProbe().ref, register.ref, TestProbe().ref, rateLimiter.ref)) - peer ! Peer.InitWithoutKeys(Set.empty, Map.empty) + peer ! Peer.Init(Set.empty, Map.empty) withFixture(test.toNoArgTest(FixtureParam(nodeParams, remoteNodeId, peer, peerConnection, channel, register, rateLimiter, probe))) } @@ -495,7 +495,7 @@ class OnTheFlyFundingSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { // When restarting, we watch for pending proposals. val peerAfterRestart = TestFSMRef(new Peer(nodeParams, remoteNodeId, new DummyOnChainWallet(), FakeChannelFactory(remoteNodeId, channel), TestProbe().ref, register.ref, TestProbe().ref, TestProbe().ref)) - peerAfterRestart ! Peer.InitWithoutKeys(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) + peerAfterRestart ! Peer.Init(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) probe.watch(peerAfterRestart.ref) // The last funding proposal reaches its CLTV expiry. @@ -984,7 +984,7 @@ class OnTheFlyFundingSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike { // On restart, we don't attempt the payment again: it's already pending. val peerAfterRestart = TestFSMRef(new Peer(nodeParams, remoteNodeId, new DummyOnChainWallet(), FakeChannelFactory(remoteNodeId, channel), TestProbe().ref, register.ref, TestProbe().ref, TestProbe().ref)) - peerAfterRestart ! Peer.InitWithoutKeys(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) + peerAfterRestart ! Peer.Init(Set.empty, nodeParams.db.liquidity.listPendingOnTheFlyFunding(remoteNodeId)) connect(peerAfterRestart) peerAfterRestart ! ChannelReadyForPayments(channel.ref, remoteNodeId, channelId, purchase.txId, fundingTxIndex = 1) val channelData2 = makeChannelData(localChanges = LocalChanges(Nil, htlc :: Nil, Nil))