From e4e74e3ea0093e524a48cbe3fa9ca541b16a6bd0 Mon Sep 17 00:00:00 2001 From: t-bast Date: Mon, 22 Jul 2024 16:36:29 +0200 Subject: [PATCH] Remove support for splicing without quiescence We initially supported splicing with a poor man's quiescence, where we allowed splice messages if the commitments were already quiescent. We've shipped support for quiescence since then, which means that new even nodes relying on experimental splicing should support quiescence. We can thus remove support for the non-quiescent version. --- .../fr/acinq/eclair/channel/Commitments.scala | 5 +- .../fr/acinq/eclair/channel/fsm/Channel.scala | 96 +++---- .../scala/fr/acinq/eclair/TestConstants.scala | 8 +- .../ChannelStateTestsHelperMethods.scala | 10 +- .../states/e/NormalQuiescentStateSpec.scala | 2 +- .../states/e/NormalSplicesStateSpec.scala | 272 +++++------------- 6 files changed, 120 insertions(+), 273 deletions(-) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala index 5aceab5422..d6687fb5e6 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala @@ -133,9 +133,6 @@ case class ChannelParams(channelId: ByteVector32, else Right(remoteScriptPubKey) } - /** If both peers support quiescence, we have to exchange stfu when splicing. */ - def useQuiescence: Boolean = Features.canUseFeature(localParams.initFeatures, remoteParams.initFeatures, Features.Quiescence) - } object ChannelParams { @@ -824,7 +821,7 @@ case class Commitments(params: ChannelParams, def localIsQuiescent: Boolean = changes.localChanges.all.isEmpty def remoteIsQuiescent: Boolean = changes.remoteChanges.all.isEmpty // HTLCs and pending changes are the same for all active commitments, so we don't need to loop through all of them. - def isQuiescent: Boolean = (params.useQuiescence || active.head.hasNoPendingHtlcs) && localIsQuiescent && remoteIsQuiescent + def isQuiescent: Boolean = localIsQuiescent && remoteIsQuiescent def hasNoPendingHtlcsOrFeeUpdate: Boolean = active.head.hasNoPendingHtlcsOrFeeUpdate(changes) def hasPendingOrProposedHtlcs: Boolean = active.head.hasPendingOrProposedHtlcs(changes) def timedOutOutgoingHtlcs(currentHeight: BlockHeight): Set[UpdateAddHtlc] = active.head.timedOutOutgoingHtlcs(currentHeight) diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala index d254cff03e..f600579f31 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fsm/Channel.scala @@ -853,21 +853,13 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with case Event(cmd: CMD_SPLICE, d: DATA_NORMAL) => if (d.commitments.params.remoteParams.initFeatures.hasFeature(Features.Splicing)) { d.spliceStatus match { - case SpliceStatus.NoSplice if d.commitments.params.useQuiescence => + case SpliceStatus.NoSplice => startSingleTimer(QuiescenceTimeout.toString, QuiescenceTimeout(peer), nodeParams.channelConf.quiescenceTimeout) if (d.commitments.localIsQuiescent) { stay() using d.copy(spliceStatus = SpliceStatus.InitiatorQuiescent(cmd)) sending Stfu(d.channelId, initiator = true) } else { stay() using d.copy(spliceStatus = SpliceStatus.QuiescenceRequested(cmd)) } - case SpliceStatus.NoSplice if !d.commitments.params.useQuiescence => - initiateSplice(cmd, d) match { - case Left(f) => - cmd.replyTo ! RES_FAILURE(cmd, f) - stay() - case Right(spliceInit) => - stay() using d.copy(spliceStatus = SpliceStatus.SpliceRequested(cmd, spliceInit)) sending spliceInit - } case _ => log.warning("cannot initiate splice, another one is already in progress") cmd.replyTo ! RES_FAILURE(cmd, InvalidSpliceAlreadyInProgress(d.channelId)) @@ -885,62 +877,53 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with stay() case Event(msg: Stfu, d: DATA_NORMAL) => - if (d.commitments.params.useQuiescence) { - if (d.commitments.remoteIsQuiescent) { - d.spliceStatus match { - case SpliceStatus.NoSplice => - startSingleTimer(QuiescenceTimeout.toString, QuiescenceTimeout(peer), nodeParams.channelConf.quiescenceTimeout) - if (d.commitments.localIsQuiescent) { - stay() using d.copy(spliceStatus = SpliceStatus.NonInitiatorQuiescent) sending Stfu(d.channelId, initiator = false) - } else { - stay() using d.copy(spliceStatus = SpliceStatus.ReceivedStfu(msg)) - } - case SpliceStatus.QuiescenceRequested(cmd) => - // We could keep track of our splice attempt and merge it with the remote splice instead of cancelling it. - // But this is an edge case that should rarely occur, so it's probably not worth the additional complexity. - log.warning("our peer initiated quiescence before us, cancelling our splice attempt") - cmd.replyTo ! RES_FAILURE(cmd, ConcurrentRemoteSplice(d.channelId)) + if (d.commitments.remoteIsQuiescent) { + d.spliceStatus match { + case SpliceStatus.NoSplice => + startSingleTimer(QuiescenceTimeout.toString, QuiescenceTimeout(peer), nodeParams.channelConf.quiescenceTimeout) + if (d.commitments.localIsQuiescent) { + stay() using d.copy(spliceStatus = SpliceStatus.NonInitiatorQuiescent) sending Stfu(d.channelId, initiator = false) + } else { stay() using d.copy(spliceStatus = SpliceStatus.ReceivedStfu(msg)) - case SpliceStatus.InitiatorQuiescent(cmd) => - // if both sides send stfu at the same time, the quiescence initiator is the channel opener - if (!msg.initiator || d.commitments.params.localParams.isChannelOpener) { - initiateSplice(cmd, d) match { - case Left(f) => - cmd.replyTo ! RES_FAILURE(cmd, f) - context.system.scheduler.scheduleOnce(2 second, peer, Peer.Disconnect(remoteNodeId)) - stay() using d.copy(spliceStatus = SpliceStatus.NoSplice) sending Warning(d.channelId, f.getMessage) - case Right(spliceInit) => - stay() using d.copy(spliceStatus = SpliceStatus.SpliceRequested(cmd, spliceInit)) sending spliceInit - } - } else { - log.warning("concurrent stfu received and our peer is the channel initiator, cancelling our splice attempt") - cmd.replyTo ! RES_FAILURE(cmd, ConcurrentRemoteSplice(d.channelId)) - stay() using d.copy(spliceStatus = SpliceStatus.NonInitiatorQuiescent) + } + case SpliceStatus.QuiescenceRequested(cmd) => + // We could keep track of our splice attempt and merge it with the remote splice instead of cancelling it. + // But this is an edge case that should rarely occur, so it's probably not worth the additional complexity. + log.warning("our peer initiated quiescence before us, cancelling our splice attempt") + cmd.replyTo ! RES_FAILURE(cmd, ConcurrentRemoteSplice(d.channelId)) + stay() using d.copy(spliceStatus = SpliceStatus.ReceivedStfu(msg)) + case SpliceStatus.InitiatorQuiescent(cmd) => + // if both sides send stfu at the same time, the quiescence initiator is the channel opener + if (!msg.initiator || d.commitments.params.localParams.isChannelOpener) { + initiateSplice(cmd, d) match { + case Left(f) => + cmd.replyTo ! RES_FAILURE(cmd, f) + context.system.scheduler.scheduleOnce(2 second, peer, Peer.Disconnect(remoteNodeId)) + stay() using d.copy(spliceStatus = SpliceStatus.NoSplice) sending Warning(d.channelId, f.getMessage) + case Right(spliceInit) => + stay() using d.copy(spliceStatus = SpliceStatus.SpliceRequested(cmd, spliceInit)) sending spliceInit } - case _ => - log.warning("ignoring duplicate stfu") - stay() - } - } else { - log.warning("our peer sent stfu but is not quiescent") - // NB: we use a small delay to ensure we've sent our warning before disconnecting. - context.system.scheduler.scheduleOnce(2 second, peer, Peer.Disconnect(remoteNodeId)) - stay() using d.copy(spliceStatus = SpliceStatus.NoSplice) sending Warning(d.channelId, InvalidSpliceNotQuiescent(d.channelId).getMessage) + } else { + log.warning("concurrent stfu received and our peer is the channel initiator, cancelling our splice attempt") + cmd.replyTo ! RES_FAILURE(cmd, ConcurrentRemoteSplice(d.channelId)) + stay() using d.copy(spliceStatus = SpliceStatus.NonInitiatorQuiescent) + } + case _ => + log.warning("ignoring duplicate stfu") + stay() } } else { - log.warning("ignoring stfu because both peers do not advertise quiescence") - stay() + log.warning("our peer sent stfu but is not quiescent") + // NB: we use a small delay to ensure we've sent our warning before disconnecting. + context.system.scheduler.scheduleOnce(2 second, peer, Peer.Disconnect(remoteNodeId)) + stay() using d.copy(spliceStatus = SpliceStatus.NoSplice) sending Warning(d.channelId, InvalidSpliceNotQuiescent(d.channelId).getMessage) } case Event(_: QuiescenceTimeout, d: DATA_NORMAL) => handleQuiescenceTimeout(d) - case Event(_: SpliceInit, d: DATA_NORMAL) if d.spliceStatus == SpliceStatus.NoSplice && d.commitments.params.useQuiescence => - log.info("rejecting splice attempt: quiescence not negotiated") - stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceNotQuiescent(d.channelId).getMessage) - case Event(msg: SpliceInit, d: DATA_NORMAL) => d.spliceStatus match { - case SpliceStatus.NoSplice | SpliceStatus.NonInitiatorQuiescent => + case SpliceStatus.NonInitiatorQuiescent => if (!d.commitments.isQuiescent) { log.info("rejecting splice request: channel not quiescent") stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceNotQuiescent(d.channelId).getMessage) @@ -981,6 +964,9 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with txBuilder ! InteractiveTxBuilder.Start(self) stay() using d.copy(spliceStatus = SpliceStatus.SpliceInProgress(cmd_opt = None, sessionId, txBuilder, remoteCommitSig = None)) sending spliceAck } + case SpliceStatus.NoSplice => + log.info("rejecting splice attempt: quiescence not negotiated") + stay() using d.copy(spliceStatus = SpliceStatus.SpliceAborted) sending TxAbort(d.channelId, InvalidSpliceNotQuiescent(d.channelId).getMessage) case SpliceStatus.SpliceAborted => log.info("rejecting splice attempt: our previous tx_abort was not acked") stay() sending Warning(d.channelId, InvalidSpliceTxAbortNotAcked(d.channelId).getMessage) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala index 0b0483b437..6f520c1fb6 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/TestConstants.scala @@ -102,7 +102,9 @@ object TestConstants { Wumbo -> Optional, PaymentMetadata -> Optional, RouteBlinding -> Optional, - StaticRemoteKey -> Mandatory + StaticRemoteKey -> Mandatory, + Quiescence -> Optional, + Splicing -> Optional, ), unknown = Set(UnknownFeature(TestFeature.optional)) ), @@ -274,7 +276,9 @@ object TestConstants { PaymentMetadata -> Optional, RouteBlinding -> Optional, StaticRemoteKey -> Mandatory, - AnchorOutputsZeroFeeHtlcTx -> Optional + AnchorOutputsZeroFeeHtlcTx -> Optional, + Quiescence -> Optional, + Splicing -> Optional, ), pluginParams = Nil, overrideInitFeatures = Map.empty, diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala index d32838a674..97afafb6a0 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/ChannelStateTestsHelperMethods.scala @@ -51,8 +51,6 @@ object ChannelStateTestsTags { val DisableWumbo = "disable_wumbo" /** If set, channels will use option_dual_fund. */ val DualFunding = "dual_funding" - /** If set, peers will support splicing. */ - val Splicing = "splicing" /** If set, channels will use option_static_remotekey. */ val StaticRemoteKey = "static_remotekey" /** If set, channels will use option_anchor_outputs. */ @@ -91,8 +89,6 @@ object ChannelStateTestsTags { val RejectRbfAttempts = "reject_rbf_attempts" /** If set, the non-initiator will require a 1-block delay between RBF attempts. */ val DelayRbfAttempts = "delay_rbf_attempts" - /** If set, peers will support the quiesce protocol. */ - val Quiescence = "quiescence" /** If set, channels will adapt their max HTLC amount to the available balance */ val AdaptMaxHtlcAmount = "adapt-max-htlc-amount" } @@ -163,7 +159,7 @@ trait ChannelStateTestsBase extends Assertions with Eventually { .modify(_.channelConf.balanceThresholds).setToIf(tags.contains(ChannelStateTestsTags.AdaptMaxHtlcAmount))(Seq(Channel.BalanceThreshold(1_000 sat, 0 sat), Channel.BalanceThreshold(5_000 sat, 1_000 sat), Channel.BalanceThreshold(10_000 sat, 5_000 sat))) val wallet = wallet_opt match { case Some(wallet) => wallet - case None => if (tags.contains(ChannelStateTestsTags.DualFunding) || tags.contains(ChannelStateTestsTags.Splicing)) new SingleKeyOnChainWallet() else new DummyOnChainWallet() + case None => if (tags.contains(ChannelStateTestsTags.DualFunding)) new SingleKeyOnChainWallet() else new DummyOnChainWallet() } val alice: TestFSMRef[ChannelState, ChannelData, Channel] = { implicit val system: ActorSystem = systemA @@ -190,8 +186,6 @@ trait ChannelStateTestsBase extends Assertions with Eventually { .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ZeroConf))(_.updated(Features.ZeroConf, FeatureSupport.Optional)) .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ScidAlias))(_.updated(Features.ScidAlias, FeatureSupport.Optional)) .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.DualFunding))(_.updated(Features.DualFunding, FeatureSupport.Optional)) - .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.Splicing))(_.updated(Features.Splicing, FeatureSupport.Optional)) - .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.Quiescence))(_.updated(Features.Quiescence, FeatureSupport.Optional)) .initFeatures() val bobInitFeatures = Bob.nodeParams.features .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.DisableWumbo))(_.removed(Features.Wumbo)) @@ -204,8 +198,6 @@ trait ChannelStateTestsBase extends Assertions with Eventually { .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ZeroConf))(_.updated(Features.ZeroConf, FeatureSupport.Optional)) .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.ScidAlias))(_.updated(Features.ScidAlias, FeatureSupport.Optional)) .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.DualFunding))(_.updated(Features.DualFunding, FeatureSupport.Optional)) - .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.Splicing))(_.updated(Features.Splicing, FeatureSupport.Optional)) - .modify(_.activated).usingIf(tags.contains(ChannelStateTestsTags.Quiescence))(_.updated(Features.Quiescence, FeatureSupport.Optional)) .initFeatures() val channelType = ChannelTypes.defaultFromFeatures(aliceInitFeatures, bobInitFeatures, announceChannel = channelFlags.announceChannel) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalQuiescentStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalQuiescentStateSpec.scala index 8cc310007a..037e11784a 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalQuiescentStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalQuiescentStateSpec.scala @@ -45,7 +45,7 @@ class NormalQuiescentStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteL implicit val log: akka.event.LoggingAdapter = akka.event.NoLogging override def withFixture(test: OneArgTest): Outcome = { - val tags = test.tags + ChannelStateTestsTags.DualFunding + ChannelStateTestsTags.Splicing + ChannelStateTestsTags.Quiescence + val tags = test.tags + ChannelStateTestsTags.DualFunding val setup = init(tags = tags) import setup._ reachNormal(setup, tags) diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala index f690f8e1eb..ec7ad8f719 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/channel/states/e/NormalSplicesStateSpec.scala @@ -58,7 +58,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik implicit val log: akka.event.LoggingAdapter = akka.event.NoLogging override def withFixture(test: OneArgTest): Outcome = { - val tags = test.tags + DualFunding + Splicing + val tags = test.tags + DualFunding val setup = init(tags = tags) import setup._ reachNormal(setup, tags) @@ -71,17 +71,11 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik private val defaultSpliceOutScriptPubKey = hex"0020aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - private def useQuiescence(s: TestFSMRef[ChannelState, ChannelData, Channel]): Boolean = s.stateData.asInstanceOf[ChannelDataWithCommitments].commitments.params.useQuiescence - - private def useQuiescence(f: FixtureParam): Boolean = useQuiescence(f.alice) - private def initiateSpliceWithoutSigs(s: TestFSMRef[ChannelState, ChannelData, Channel], r: TestFSMRef[ChannelState, ChannelData, Channel], s2r: TestProbe, r2s: TestProbe, spliceIn_opt: Option[SpliceIn], spliceOut_opt: Option[SpliceOut]): TestProbe = { val sender = TestProbe() val cmd = CMD_SPLICE(sender.ref, spliceIn_opt, spliceOut_opt) s ! cmd - if (useQuiescence(s)) { - exchangeStfu(s, r, s2r, r2s) - } + exchangeStfu(s, r, s2r, r2s) s2r.expectMsgType[SpliceInit] s2r.forward(r) r2s.expectMsgType[SpliceAck] @@ -173,37 +167,29 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik private def setupHtlcs(f: FixtureParam): TestHtlcs = { import f._ - if (useQuiescence(f)) { - // add htlcs in both directions - val htlcsAliceToBob = Seq( - addHtlc(15_000_000 msat, alice, bob, alice2bob, bob2alice), - addHtlc(15_000_000 msat, alice, bob, alice2bob, bob2alice) - ) - crossSign(alice, bob, alice2bob, bob2alice) - val htlcsBobToAlice = Seq( - addHtlc(20_000_000 msat, bob, alice, bob2alice, alice2bob), - addHtlc(15_000_000 msat, bob, alice, bob2alice, alice2bob) - ) - crossSign(bob, alice, bob2alice, alice2bob) - - val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - assert(initialState.commitments.latest.capacity == 1_500_000.sat) - assert(initialState.commitments.latest.localCommit.spec.toLocal == 770_000_000.msat) - assert(initialState.commitments.latest.localCommit.spec.toRemote == 665_000_000.msat) - - alice2relayer.expectMsgType[Relayer.RelayForward] - alice2relayer.expectMsgType[Relayer.RelayForward] - bob2relayer.expectMsgType[Relayer.RelayForward] - bob2relayer.expectMsgType[Relayer.RelayForward] - - TestHtlcs(htlcsAliceToBob, htlcsBobToAlice) - } else { - val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] - assert(initialState.commitments.latest.capacity == 1_500_000.sat) - assert(initialState.commitments.latest.localCommit.spec.toLocal == 800_000_000.msat) - assert(initialState.commitments.latest.localCommit.spec.toRemote == 700_000_000.msat) - TestHtlcs(Seq.empty, Seq.empty) - } + // add htlcs in both directions + val htlcsAliceToBob = Seq( + addHtlc(15_000_000 msat, alice, bob, alice2bob, bob2alice), + addHtlc(15_000_000 msat, alice, bob, alice2bob, bob2alice) + ) + crossSign(alice, bob, alice2bob, bob2alice) + val htlcsBobToAlice = Seq( + addHtlc(20_000_000 msat, bob, alice, bob2alice, alice2bob), + addHtlc(15_000_000 msat, bob, alice, bob2alice, alice2bob) + ) + crossSign(bob, alice, bob2alice, alice2bob) + + val initialState = alice.stateData.asInstanceOf[DATA_NORMAL] + assert(initialState.commitments.latest.capacity == 1_500_000.sat) + assert(initialState.commitments.latest.localCommit.spec.toLocal == 770_000_000.msat) + assert(initialState.commitments.latest.localCommit.spec.toRemote == 665_000_000.msat) + + alice2relayer.expectMsgType[Relayer.RelayForward] + alice2relayer.expectMsgType[Relayer.RelayForward] + bob2relayer.expectMsgType[Relayer.RelayForward] + bob2relayer.expectMsgType[Relayer.RelayForward] + + TestHtlcs(htlcsAliceToBob, htlcsBobToAlice) } def spliceOutFee(f: FixtureParam, capacity: Satoshi): Satoshi = { @@ -223,7 +209,8 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik import f._ // if the swap includes a splice-in, swap-out fees will be paid from bitcoind so final capacity is predictable - val (outgoingHtlcs, incomingHtlcs) = if (useQuiescence(f)) (30_000_000.msat, 35_000_000.msat) else (0.msat, 0.msat) + val outgoingHtlcs = 30_000_000.msat + val incomingHtlcs = 35_000_000.msat val postSpliceState = alice.stateData.asInstanceOf[ChannelDataWithCommitments] assert(postSpliceState.commitments.latest.capacity == 1_900_000.sat - spliceOutFee) assert(postSpliceState.commitments.latest.localCommit.spec.toLocal == 1_200_000_000.msat - spliceOutFee - outgoingHtlcs) @@ -237,23 +224,21 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik checkPostSpliceState(f, spliceOutFee) - if (useQuiescence(f)) { - // resolve pre-splice HTLCs after splice - val Seq((preimage1a, htlc1a), (preimage2a, htlc2a)) = htlcs.aliceToBob - val Seq((preimage1b, htlc1b), (preimage2b, htlc2b)) = htlcs.bobToAlice - fulfillHtlc(htlc1a.id, preimage1a, bob, alice, bob2alice, alice2bob) - fulfillHtlc(htlc2a.id, preimage2a, bob, alice, bob2alice, alice2bob) - crossSign(bob, alice, bob2alice, alice2bob) - fulfillHtlc(htlc1b.id, preimage1b, alice, bob, alice2bob, bob2alice) - fulfillHtlc(htlc2b.id, preimage2b, alice, bob, alice2bob, bob2alice) - crossSign(alice, bob, alice2bob, bob2alice) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.localCommit.spec.htlcs.collect(outgoing).isEmpty) - assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.remoteCommit.spec.htlcs.collect(outgoing).isEmpty) - assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.localCommit.spec.htlcs.collect(outgoing).isEmpty) - assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.remoteCommit.spec.htlcs.collect(outgoing).isEmpty) - } + // resolve pre-splice HTLCs after splice + val Seq((preimage1a, htlc1a), (preimage2a, htlc2a)) = htlcs.aliceToBob + val Seq((preimage1b, htlc1b), (preimage2b, htlc2b)) = htlcs.bobToAlice + fulfillHtlc(htlc1a.id, preimage1a, bob, alice, bob2alice, alice2bob) + fulfillHtlc(htlc2a.id, preimage2a, bob, alice, bob2alice, alice2bob) + crossSign(bob, alice, bob2alice, alice2bob) + fulfillHtlc(htlc1b.id, preimage1b, alice, bob, alice2bob, bob2alice) + fulfillHtlc(htlc2b.id, preimage2b, alice, bob, alice2bob, bob2alice) + crossSign(alice, bob, alice2bob, bob2alice) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.localCommit.spec.htlcs.collect(outgoing).isEmpty) + assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.remoteCommit.spec.htlcs.collect(outgoing).isEmpty) + assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.localCommit.spec.htlcs.collect(outgoing).isEmpty) + assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.active.head.remoteCommit.spec.htlcs.collect(outgoing).isEmpty) - val settledHtlcs = if (useQuiescence(f)) 5_000_000.msat else 0.msat + val settledHtlcs = 5_000_000.msat val finalState = alice.stateData.asInstanceOf[DATA_NORMAL] assert(finalState.commitments.latest.capacity == 1_900_000.sat - spliceOutFee) assert(finalState.commitments.latest.localCommit.spec.toLocal == 1_200_000_000.msat - spliceOutFee + settledHtlcs) @@ -276,10 +261,10 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik } test("recv CMD_SPLICE (splice-in, non dual-funded channel)") { () => - val f = init(tags = Set(DualFunding, Splicing)) + val f = init(tags = Set(DualFunding)) import f._ - reachNormal(f, tags = Set(Splicing)) // we open a non dual-funded channel + reachNormal(f, tags = Set.empty) // we open a non dual-funded channel alice2bob.ignoreMsg { case _: ChannelUpdate => true } bob2alice.ignoreMsg { case _: ChannelUpdate => true } awaitCond(alice.stateName == NORMAL && bob.stateName == NORMAL) @@ -304,7 +289,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(postSpliceState.commitments.latest.remoteChannelReserve == 15_000.sat) } - test("recv CMD_SPLICE (splice-in, local and remote commit index mismatch)", Tag(Quiescence)) { f => + test("recv CMD_SPLICE (splice-in, local and remote commit index mismatch)") { f => import f._ // Alice and Bob asynchronously exchange HTLCs, which makes their commit indices diverge. @@ -368,18 +353,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(alice.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.spec.toRemote == 700_000_000.msat) } - test("recv CMD_SPLICE (splice-out, would go below reserve)") { f => - import f._ - - setupHtlcs(f) - - val sender = TestProbe() - val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = None, Some(SpliceOut(780_000.sat, defaultSpliceOutScriptPubKey))) - alice ! cmd - sender.expectMsgType[RES_FAILURE[_, _]] - } - - test("recv CMD_SPLICE (splice-out, would go below reserve, quiescent)", Tag(Quiescence), Tag(NoMaxHtlcValueInFlight)) { f => + test("recv CMD_SPLICE (splice-out, would go below reserve)", Tag(NoMaxHtlcValueInFlight)) { f => import f._ setupHtlcs(f) @@ -397,6 +371,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik val sender = TestProbe() val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(500_000 sat)), spliceOut_opt = None) alice ! cmd + exchangeStfu(f) // we tweak the feerate val spliceInit = alice2bob.expectMsgType[SpliceInit].copy(feerate = FeeratePerKw(100.sat)) bob.setFeerates(alice.nodeParams.currentFeerates.copy(minimum = FeeratePerKw(101.sat))) @@ -416,6 +391,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik val sender = TestProbe() val bobBalance = bob.stateData.asInstanceOf[DATA_NORMAL].commitments.latest.localCommit.spec.toLocal alice ! CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(100_000 sat)), spliceOut_opt = None) + exchangeStfu(f) val spliceInit = alice2bob.expectMsgType[SpliceInit] alice2bob.forward(bob, spliceInit) val spliceAck = bob2alice.expectMsgType[SpliceAck] @@ -460,27 +436,18 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik crossSign(alice, bob, alice2bob, bob2alice) } - def testSpliceInAndOutCmd(f: FixtureParam): Unit = { + test("recv CMD_SPLICE (splice-in + splice-out)") { f => val htlcs = setupHtlcs(f) - initiateSplice(f, spliceIn_opt = Some(SpliceIn(500_000 sat)), spliceOut_opt = Some(SpliceOut(100_000 sat, defaultSpliceOutScriptPubKey))) - resolveHtlcs(f, htlcs, spliceOutFee = 0.sat) } - test("recv CMD_SPLICE (splice-in + splice-out)") { f => - testSpliceInAndOutCmd(f) - } - - test("recv CMD_SPLICE (splice-in + splice-out, quiescence)", Tag(Quiescence)) { f => - testSpliceInAndOutCmd(f) - } - test("recv TxAbort (before TxComplete)") { f => import f._ val sender = TestProbe() alice ! CMD_SPLICE(sender.ref, spliceIn_opt = None, spliceOut_opt = Some(SpliceOut(50_000 sat, defaultSpliceOutScriptPubKey))) + exchangeStfu(f) alice2bob.expectMsgType[SpliceInit] alice2bob.forward(bob) bob2alice.expectMsgType[SpliceAck] @@ -505,6 +472,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik val sender = TestProbe() alice ! CMD_SPLICE(sender.ref, spliceIn_opt = None, spliceOut_opt = Some(SpliceOut(50_000 sat, defaultSpliceOutScriptPubKey))) + exchangeStfu(f) alice2bob.expectMsgType[SpliceInit] alice2bob.forward(bob) bob2alice.expectMsgType[SpliceAck] @@ -540,6 +508,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik val sender = TestProbe() alice ! CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(50_000 sat)), spliceOut_opt = None) + exchangeStfu(f) alice2bob.expectMsgType[SpliceInit] alice2bob.forward(bob) bob2alice.expectMsgType[SpliceAck] @@ -772,7 +741,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(bob.stateData.asInstanceOf[DATA_NORMAL].commitments.inactive.map(_.fundingTxIndex) == Seq.empty) } - test("emit post-splice events", Tag(NoMaxHtlcValueInFlight), Tag(Quiescence)) { f => + test("emit post-splice events", Tag(NoMaxHtlcValueInFlight)) { f => import f._ // Alice and Bob asynchronously exchange HTLCs, which makes their commit indices diverge. @@ -902,6 +871,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik val sender = TestProbe() val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(500_000 sat, pushAmount = 0 msat)), spliceOut_opt = None) alice ! cmd + exchangeStfu(f) alice2bob.expectMsgType[SpliceInit] alice ! CMD_ADD_HTLC(sender.ref, 500000 msat, randomBytes32(), CltvExpiryDelta(144).toCltvExpiry(currentBlockHeight), TestConstants.emptyOnionPacket, None, 1.0, localOrigin(sender.ref)) sender.expectMsgType[RES_ADD_FAILED[_]] @@ -913,6 +883,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik val sender = TestProbe() val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(500_000 sat, pushAmount = 0 msat)), spliceOut_opt = None) alice ! cmd + exchangeStfu(f) alice2bob.expectMsgType[SpliceInit] alice2bob.forward(bob) bob2alice.expectMsgType[SpliceAck] @@ -923,36 +894,12 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik alice2bob.expectNoMessage(100 millis) } - test("recv UpdateAddHtlc while a splice is requested") { f => - import f._ - val sender = TestProbe() - val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(500_000 sat, pushAmount = 0 msat)), spliceOut_opt = None) - alice ! cmd - alice2bob.expectMsgType[SpliceInit] - // we're holding the splice_init to create a race - - val (_, cmdAdd: CMD_ADD_HTLC) = makeCmdAdd(5_000_000 msat, bob.underlyingActor.remoteNodeId, bob.underlyingActor.nodeParams.currentBlockHeight) - bob ! cmdAdd - bob2alice.expectMsgType[UpdateAddHtlc] - bob2alice.forward(alice) - // now we forward the splice_init - alice2bob.forward(bob) - // bob rejects the SpliceInit because they have a pending htlc - bob2alice.expectMsgType[TxAbort] - bob2alice.forward(alice) - // alice returns a warning and schedules a disconnect after receiving UpdateAddHtlc - alice2bob.expectMsg(Warning(channelId(alice), ForbiddenDuringSplice(channelId(alice), "UpdateAddHtlc").getMessage)) - // alice confirms the splice abort - alice2bob.expectMsgType[TxAbort] - // the htlc is not added - assert(!alice.stateData.asInstanceOf[DATA_NORMAL].commitments.hasPendingOrProposedHtlcs) - } - test("recv UpdateAddHtlc while a splice is in progress") { f => import f._ val sender = TestProbe() val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(500_000 sat, pushAmount = 0 msat)), spliceOut_opt = None) alice ! cmd + exchangeStfu(f) alice2bob.expectMsgType[SpliceInit] alice2bob.forward(bob) bob2alice.expectMsgType[SpliceAck] @@ -1091,6 +1038,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik val sender = TestProbe() val cmd = CMD_SPLICE(sender.ref, spliceIn_opt = Some(SpliceIn(500_000 sat, pushAmount = 0 msat)), spliceOut_opt = None) alice ! cmd + exchangeStfu(f) alice2bob.expectMsgType[SpliceInit] alice2bob.forward(bob) bob2alice.expectMsgType[SpliceAck] @@ -1102,7 +1050,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(alice.stateData.asInstanceOf[DATA_NORMAL].spliceStatus == SpliceStatus.NoSplice) } - def testDisconnectCommitSigNotReceived(f: FixtureParam): Unit = { + test("disconnect (commit_sig not received)") { f => import f._ val htlcs = setupHtlcs(f) @@ -1133,15 +1081,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, 0.sat) } - test("disconnect (commit_sig not received)") { f => - testDisconnectCommitSigNotReceived(f) - } - - test("disconnect (commit_sig not received, quiescence)", Tag(Quiescence)) { f => - testDisconnectCommitSigNotReceived(f) - } - - def testDisconnectCommitSigReceivedByAlice(f: FixtureParam): Unit = { + test("disconnect (commit_sig received by alice)") { f => import f._ val htlcs = setupHtlcs(f) @@ -1173,15 +1113,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, spliceOutFee = 0.sat) } - test("disconnect (commit_sig received by alice)") { f => - testDisconnectCommitSigReceivedByAlice(f) - } - - test("disconnect (commit_sig received by alice, quiescence)", Tag(Quiescence)) { f => - testDisconnectCommitSigReceivedByAlice(f) - } - - def testDisconnectTxSignaturesSentByBob(f: FixtureParam): Unit = { + test("disconnect (tx_signatures sent by bob)") { f => import f._ val htlcs = setupHtlcs(f) @@ -1215,15 +1147,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, spliceOutFee = 0.sat) } - test("disconnect (tx_signatures sent by bob)") { f => - testDisconnectTxSignaturesSentByBob(f) - } - - test("disconnect (tx_signatures sent by bob, quiescence)", Tag(Quiescence)) { f => - testDisconnectTxSignaturesSentByBob(f) - } - - def testDisconnectTxSignaturesReceivedByAlice(f: FixtureParam): Unit = { + test("disconnect (tx_signatures received by alice)") { f => import f._ val htlcs = setupHtlcs(f) @@ -1264,15 +1188,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, spliceOutFee = 0.sat) } - test("disconnect (tx_signatures received by alice)") { f => - testDisconnectTxSignaturesReceivedByAlice(f) - } - - test("disconnect (tx_signatures received by alice, quiescence)", Tag(Quiescence)) { f => - testDisconnectTxSignaturesReceivedByAlice(f) - } - - def testDisconnectTxSignaturesReceivedByAliceZeroConf(f: FixtureParam): Unit = { + test("disconnect (tx_signatures received by alice, zero-conf)", Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => import f._ val htlcs = setupHtlcs(f) @@ -1314,14 +1230,6 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, spliceOutFee = 0.sat) } - test("disconnect (tx_signatures received by alice, zero-conf)", Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => - testDisconnectTxSignaturesReceivedByAliceZeroConf(f) - } - - test("disconnect (tx_signatures received by alice, zero-conf, quiescence)", Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs), Tag(Quiescence)) { f => - testDisconnectTxSignaturesReceivedByAliceZeroConf(f) - } - test("disconnect (tx_signatures sent by alice, splice confirms while bob is offline)") { f => import f._ @@ -1475,7 +1383,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik } } - def testForceCloseWithMultipleSplicesSimple(f: FixtureParam): Unit = { + test("force-close with multiple splices (simple)") { f => import f._ val htlcs = setupHtlcs(f) @@ -1547,15 +1455,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(Helpers.Closing.isClosed(alice.stateData.asInstanceOf[DATA_CLOSING], None).exists(_.isInstanceOf[LocalClose])) } - test("force-close with multiple splices (simple)") { f => - testForceCloseWithMultipleSplicesSimple(f) - } - - test("force-close with multiple splices (simple, quiescence)", Tag(Quiescence)) { f => - testForceCloseWithMultipleSplicesSimple(f) - } - - def testForceCloseWithMultipleSplicesPreviousActiveRemote(f: FixtureParam): Unit = { + test("force-close with multiple splices (previous active remote)", Tag(ChannelStateTestsTags.StaticRemoteKey), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f => import f._ val htlcs = setupHtlcs(f) @@ -1633,15 +1533,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(Helpers.Closing.isClosed(alice.stateData.asInstanceOf[DATA_CLOSING], None).exists(_.isInstanceOf[RemoteClose])) } - test("force-close with multiple splices (previous active remote)", Tag(ChannelStateTestsTags.StaticRemoteKey), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesPreviousActiveRemote(f) - } - - test("force-close with multiple splices (previous active remote, quiescence)", Tag(Quiescence), Tag(ChannelStateTestsTags.StaticRemoteKey), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesPreviousActiveRemote(f) - } - - def testForceCloseWithMultipleSplicesPreviousActiveRevoked(f: FixtureParam): Unit = { + test("force-close with multiple splices (previous active revoked)", Tag(ChannelStateTestsTags.StaticRemoteKey), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f => import f._ val htlcs = setupHtlcs(f) @@ -1713,15 +1605,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(Helpers.Closing.isClosed(alice.stateData.asInstanceOf[DATA_CLOSING], None).exists(_.isInstanceOf[RevokedClose])) } - test("force-close with multiple splices (previous active revoked)", Tag(ChannelStateTestsTags.StaticRemoteKey), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesPreviousActiveRevoked(f) - } - - test("force-close with multiple splices (previous active revoked, quiescent)", Tag(Quiescence), Tag(ChannelStateTestsTags.StaticRemoteKey), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesPreviousActiveRevoked(f) - } - - def testForceCloseWithMultipleSplicesInactiveRemote(f: FixtureParam): Unit = { + test("force-close with multiple splices (inactive remote)", Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => import f._ val htlcs = setupHtlcs(f) @@ -1828,15 +1712,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(Helpers.Closing.isClosed(alice.stateData.asInstanceOf[DATA_CLOSING], None).exists(_.isInstanceOf[RemoteClose])) } - test("force-close with multiple splices (inactive remote)", Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesInactiveRemote(f) - } - - test("force-close with multiple splices (inactive remote, quiescence)", Tag(Quiescence), Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesInactiveRemote(f) - } - - def testForceCloseWithMultipleSplicesInactiveRevoked(f: FixtureParam): Unit = { + test("force-close with multiple splices (inactive revoked)", Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => import f._ val htlcs = setupHtlcs(f) @@ -1947,14 +1823,6 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik assert(Helpers.Closing.isClosed(alice.stateData.asInstanceOf[DATA_CLOSING], None).exists(_.isInstanceOf[RevokedClose])) } - test("force-close with multiple splices (inactive revoked)", Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesInactiveRevoked(f) - } - - test("force-close with multiple splices (inactive revoked, quiescence)", Tag(Quiescence), Tag(ZeroConf), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => - testForceCloseWithMultipleSplicesInactiveRevoked(f) - } - test("put back watches after restart") { f => import f._ @@ -2049,7 +1917,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik bob2blockchain.expectNoMessage(100 millis) } - test("recv CMD_SPLICE (splice-in + splice-out) with pre and post splice htlcs", Tag(Quiescence)) { f => + test("recv CMD_SPLICE (splice-in + splice-out) with pre and post splice htlcs") { f => import f._ val htlcs = setupHtlcs(f) @@ -2086,7 +1954,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, spliceOutFee = 0.sat) } - test("recv CMD_SPLICE (splice-in + splice-out) with pending htlcs, resolved after splice locked", Tag(Quiescence), Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => + test("recv CMD_SPLICE (splice-in + splice-out) with pending htlcs, resolved after splice locked", Tag(AnchorOutputsZeroFeeHtlcTxs)) { f => import f._ val htlcs = setupHtlcs(f) @@ -2105,7 +1973,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, spliceOutFee = 0.sat) } - test("recv multiple CMD_SPLICE (splice-in, splice-out, quiescence)", Tag(Quiescence)) { f => + test("recv multiple CMD_SPLICE (splice-in, splice-out)") { f => val htlcs = setupHtlcs(f) initiateSplice(f, spliceIn_opt = Some(SpliceIn(500_000 sat))) @@ -2114,7 +1982,7 @@ class NormalSplicesStateSpec extends TestKitBaseClass with FixtureAnyFunSuiteLik resolveHtlcs(f, htlcs, spliceOutFee = spliceOutFee(f, capacity = 1_900_000.sat)) } - test("recv invalid htlc signatures during splice-in", Tag(Quiescence)) { f => + test("recv invalid htlc signatures during splice-in") { f => import f._ val htlcs = setupHtlcs(f)