From 35e318e951f60777ffcd8efeddab8faab80bfa4e Mon Sep 17 00:00:00 2001 From: Pierre-Marie Padiou Date: Fri, 3 Nov 2023 18:47:32 +0100 Subject: [PATCH] Remove reserve check if splice contribution is positive (#2771) If remote has a positive contribution, we do not check their post-splice reserve level, because they are improving their situation, even if they stay below the requirement. Note that if local splices-in some funds in the same operation, remote post-splice reserve may actually be worse than before, but that's not their fault. --- .../fr/acinq/eclair/channel/fsm/Channel.scala | 2 +- .../channel/fund/InteractiveTxBuilder.scala | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) 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 69eca8106a..5942c8dbe7 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 @@ -2690,7 +2690,7 @@ class Channel(val nodeParams: NodeParams, val wallet: OnChainChannelFunder with spliceOut = cmd.spliceOutputs, targetFeerate = targetFeerate) val commitTxFees = Transactions.commitTxTotalCost(d.commitments.params.remoteParams.dustLimit, parentCommitment.remoteCommit.spec, d.commitments.params.commitmentFormat) - if (parentCommitment.localCommit.spec.toLocal + fundingContribution < parentCommitment.localChannelReserve(d.commitments.params).max(commitTxFees)) { + if (fundingContribution < 0.sat && parentCommitment.localCommit.spec.toLocal + fundingContribution < parentCommitment.localChannelReserve(d.commitments.params).max(commitTxFees)) { log.warning(s"cannot do splice: insufficient funds (commitTxFees=$commitTxFees reserve=${parentCommitment.localChannelReserve(d.commitments.params)})") Left(InvalidSpliceRequest(d.channelId)) } else if (cmd.spliceOut_opt.map(_.scriptPubKey).exists(!MutualClose.isValidFinalScriptPubkey(_, allowAnySegwit = true))) { diff --git a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala index 33507a76ae..f26c79051a 100644 --- a/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala +++ b/eclair-core/src/main/scala/fr/acinq/eclair/channel/fund/InteractiveTxBuilder.scala @@ -660,11 +660,17 @@ private class InteractiveTxBuilder(replyTo: ActorRef[InteractiveTxBuilder.Respon } val sharedInput_opt = fundingParams.sharedInput_opt.map(_ => { - val remoteReserve = channelParams.remoteChannelReserveForCapacity(fundingParams.fundingAmount, isSplice = true) - // We ignore the reserve requirement if we are splicing funds into the channel, which increases the size of the reserve. - if (sharedOutput.remoteAmount < remoteReserve && remoteOutputs.nonEmpty && localInputs.isEmpty) { - log.warn("invalid interactive tx: peer takes too much funds out and falls below the channel reserve ({} < {})", sharedOutput.remoteAmount, remoteReserve) - return Left(InvalidCompleteInteractiveTx(fundingParams.channelId)) + if (fundingParams.remoteContribution >= 0.sat) { + // If remote has a positive contribution, we do not check their post-splice reserve level, because they are improving + // their situation, even if they stay below the requirement. Note that if local splices-in some funds in the same + // operation, remote post-splice reserve may actually be worse than before, but that's not their fault. + } else { + // If remote removes funds from the channel, it must meet reserve requirements post-splice + val remoteReserve = channelParams.remoteChannelReserveForCapacity(fundingParams.fundingAmount, isSplice = true) + if (sharedOutput.remoteAmount < remoteReserve) { + log.warn("invalid interactive tx: peer takes too much funds out and falls below the channel reserve ({} < {})", sharedOutput.remoteAmount, remoteReserve) + return Left(InvalidCompleteInteractiveTx(fundingParams.channelId)) + } } if (sharedInputs.length > 1) { log.warn("invalid interactive tx: shared input included multiple times")