diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/balance/CheckBalance.scala b/eclair-core/src/main/scala/fr/acinq/eclair/balance/CheckBalance.scala index 24ea89d04f..b67b14e0e7 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/balance/CheckBalance.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/balance/CheckBalance.scala @@ -200,6 +200,10 @@ object CheckBalance { this.copy(closing = this.closing.copy(toLocal = this.closing.toLocal + localBalance)) case None => this } + // If we have a fully signed mutual close transaction and a closing transaction is in our mempool or recently + // confirmed, the channel will most likely end up being mutual-closed (since the feerate is higher than any + // force-close transaction). We thus ignore this channel in our off-chain balance to avoid counting it twice. + case None if d.mutualClosePublished.nonEmpty && recentlySpentInputs.contains(d.commitments.latest.fundingInput) => this // We don't know yet which type of closing will confirm on-chain, so we use our default off-chain balance. case None => this.copy(closing = this.closing.addChannelBalance(d.commitments)) } diff --git a/eclair-core/src/test/scala/fr/acinq/eclair/balance/CheckBalanceSpec.scala b/eclair-core/src/test/scala/fr/acinq/eclair/balance/CheckBalanceSpec.scala index e232f42c66..d208ad21f5 100644 --- a/eclair-core/src/test/scala/fr/acinq/eclair/balance/CheckBalanceSpec.scala +++ b/eclair-core/src/test/scala/fr/acinq/eclair/balance/CheckBalanceSpec.scala @@ -98,6 +98,16 @@ class CheckBalanceSpec extends TestKitBaseClass with FixtureAnyFunSuiteLike with assert(CheckBalance.computeOffChainBalance(Seq(alice.stateData.asInstanceOf[DATA_NEGOTIATING_SIMPLE]), recentlySpentInputs = Set(closingTxInput)).negotiating == expected) } + test("channel closing with published closing tx (without option_simple_close)") { f => + import f._ + + mutualClose(alice, bob, alice2bob, bob2alice, alice2blockchain, bob2blockchain) + assert(alice.stateData.asInstanceOf[DATA_CLOSING].mutualClosePublished.nonEmpty) + val closingTxInput = alice.stateData.asInstanceOf[DATA_CLOSING].commitments.latest.fundingInput + val expected = MainAndHtlcBalance(toLocal = 0 sat, htlcs = 0 sat) + assert(CheckBalance.computeOffChainBalance(Seq(alice.stateData.asInstanceOf[DATA_CLOSING]), recentlySpentInputs = Set(closingTxInput)).closing == expected) + } + test("channel closed with remote commit tx", Tag(ChannelStateTestsTags.StaticRemoteKey), Tag(ChannelStateTestsTags.AnchorOutputsZeroFeeHtlcTxs)) { f => import f._