From a1fe22dcd0e6689fa5318ae482bb0e2b27da247b Mon Sep 17 00:00:00 2001 From: Bastien Teinturier <31281497+t-bast@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:21:34 +0200 Subject: [PATCH] Stay in Offline and Syncing on splice published (#539) When a splice transaction is published, it triggers a BITCOIN_FUNDING_SPENT event that we should ignore, because this isn't a force-close. If that event is received while we're in Offline or Syncing, we need to stay in those "wrapper" states. --- .../acinq/lightning/channel/states/Offline.kt | 9 ++++++++- .../acinq/lightning/channel/states/Syncing.kt | 9 ++++++++- .../channel/states/SpliceTestsCommon.kt | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Offline.kt b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Offline.kt index 9233f5b7b..99b1456f3 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Offline.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Offline.kt @@ -96,7 +96,14 @@ data class Offline(val state: PersistedChannelState) : ChannelState() { ) Pair(nextState, actions) } - else -> state.run { handlePotentialForceClose(watch) } + else -> { + val (nextState, actions) = state.run { handlePotentialForceClose(watch) } + when (nextState) { + is Closing -> Pair(nextState, actions) + is Closed -> Pair(nextState, actions) + else -> Pair(Offline(nextState), actions) + } + } } } is WaitForFundingSigned -> Pair(this@Offline, listOf()) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Syncing.kt b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Syncing.kt index 36b124858..a3bec493f 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Syncing.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/channel/states/Syncing.kt @@ -299,7 +299,14 @@ data class Syncing(val state: PersistedChannelState, val channelReestablishSent: ) Pair(nextState, actions) } - else -> state.run { handlePotentialForceClose(watch) } + else -> { + val (nextState, actions) = state.run { handlePotentialForceClose(watch) } + when (nextState) { + is Closing -> Pair(nextState, actions) + is Closed -> Pair(nextState, actions) + else -> Pair(Syncing(nextState, channelReestablishSent), actions) + } + } } is WatchEventConfirmed -> { if (watch.event is BITCOIN_FUNDING_DEPTHOK) { diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/SpliceTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/SpliceTestsCommon.kt index a431f4aa6..525d414ca 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/SpliceTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/SpliceTestsCommon.kt @@ -728,6 +728,25 @@ class SpliceTestsCommon : LightningTestSuite() { actionsBob6.contains(ChannelAction.Storage.SetLocked(spliceTx1.txid)) } + @Test + fun `disconnect -- splice tx published`() { + val (alice, bob) = reachNormalWithConfirmedFundingTx() + val (alice1, bob1) = spliceOut(alice, bob, 40_000.sat) + val spliceTx = alice1.commitments.latest.localFundingStatus.signedTx!! + + val (alice2, _) = alice1.process(ChannelCommand.Disconnected) + val (bob2, _) = bob1.process(ChannelCommand.Disconnected) + assertIs(alice2.state) + assertIs(bob2.state) + + val (alice3, actionsAlice3) = alice2.process(ChannelCommand.WatchReceived(WatchEventSpent(alice.channelId, BITCOIN_FUNDING_SPENT, spliceTx))) + assertIs(alice3.state) + assertTrue(actionsAlice3.isEmpty()) + val (bob3, actionsBob3) = bob2.process(ChannelCommand.WatchReceived(WatchEventSpent(bob.channelId, BITCOIN_FUNDING_SPENT, spliceTx))) + assertIs(bob3.state) + assertTrue(actionsBob3.isEmpty()) + } + @Test fun `force-close -- latest active commitment`() { val (alice, bob) = reachNormalWithConfirmedFundingTx()