diff --git a/src/commonMain/kotlin/fr/acinq/lightning/channel/Commitments.kt b/src/commonMain/kotlin/fr/acinq/lightning/channel/Commitments.kt index 7868688d5..41c88f08b 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/channel/Commitments.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/channel/Commitments.kt @@ -146,12 +146,12 @@ data class Commitment( val fundingAmount: Satoshi = commitInput.txOut.amount fun localChannelReserve(params: ChannelParams): Satoshi = when { - params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) && !params.localParams.isInitiator -> 0.sat + params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) -> 0.sat else -> (fundingAmount / 100).max(params.remoteParams.dustLimit) } fun remoteChannelReserve(params: ChannelParams): Satoshi = when { - params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) && params.localParams.isInitiator -> 0.sat + params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) -> 0.sat else -> (fundingAmount / 100).max(params.localParams.dustLimit) } @@ -390,14 +390,7 @@ data class Commitment( // and it would be tricky to check if the conditions are met at signing // (it also means that we need to check the fee of the initial commitment tx somewhere) val fees = commitTxFee(params.localParams.dustLimit, reduced) - // TODO: - // When migrating to the dual-funded model, we removed the explicit channel reserve from LocalParams. - // For channels that were created before the splicing update, this can result in a mismatch where we think - // the channel reserve is bigger than what is actually is, and incorrectly reject the remote update_fee. - // We temporarily ignore the channel reserve to avoid unnecessary force-close. - // We should restore the correct calculation that takes the reserve into account once all users have migrated. - // val missing = reduced.toRemote.truncateToSatoshi() - remoteChannelReserve(params) - fees - val missing = reduced.toRemote.truncateToSatoshi() - fees + val missing = reduced.toRemote.truncateToSatoshi() - remoteChannelReserve(params) - fees return if (missing < 0.sat) { Either.Left(CannotAffordFees(params.channelId, -missing, remoteChannelReserve(params), fees)) } else { @@ -515,11 +508,11 @@ data class FullCommitment( val fundingTxId: ByteVector32 = commitInput.outPoint.txid val fundingAmount = commitInput.txOut.amount val localChannelReserve = when { - params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) && !params.localParams.isInitiator -> 0.sat + params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) -> 0.sat else -> (fundingAmount / 100).max(params.remoteParams.dustLimit) } val remoteChannelReserve = when { - params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) && params.localParams.isInitiator -> 0.sat + params.channelFeatures.hasFeature(Feature.ZeroReserveChannels) -> 0.sat else -> (fundingAmount / 100).max(params.localParams.dustLimit) } } diff --git a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NormalTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NormalTestsCommon.kt index 0494f27c2..297e9bbd1 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NormalTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/channel/states/NormalTestsCommon.kt @@ -429,6 +429,21 @@ class NormalTestsCommon : LightningTestSuite() { assertEquals(alice1.commitments.changes.remoteChanges.proposed, listOf(add)) } + @Test + fun `recv UpdateAddHtlc -- zero-conf -- zero-reserve -- initiator`() { + val (alice0, bob0) = reachNormal(ChannelType.SupportedChannelType.AnchorOutputsZeroReserve, aliceFundingAmount = 10_000_000.sat, bobFundingAmount = 5_000_000.sat, alicePushAmount = 9_800_000_000.msat, zeroConf = true) + assertEquals(alice0.commitments.latest.fundingAmount, 15_000_000.sat) + assertEquals(alice0.commitments.latest.localCommit.spec.toLocal, 200_000_000.msat) + // If we applied the default 1% reserve, Alice's balance couldn't go below 150 000 sats. + // Alice still needs to pay the commit tx fees, so she cannot spend her entire balance. + val (nodes1, r, htlc) = addHtlc(160_000_000.msat, alice0, bob0) + val (alice2, bob2) = crossSign(nodes1.first, nodes1.second) + val (alice3, bob3) = fulfillHtlc(htlc.id, r, alice2, bob2) + val (bob4, alice4) = crossSign(bob3, alice3) + assertEquals(alice4.commitments.latest.localCommit.spec.toLocal, 40_000_000.msat) + assertEquals(bob4.commitments.latest.localCommit.spec.toRemote, 40_000_000.msat) + } + @Test fun `recv UpdateAddHtlc -- unexpected id`() { val (_, bob0) = reachNormal() @@ -1419,8 +1434,7 @@ class NormalTestsCommon : LightningTestSuite() { assertEquals(bob.commitments.copy(changes = bob.commitments.changes.copy(remoteChanges = bob.commitments.changes.remoteChanges.copy(proposed = bob.commitments.changes.remoteChanges.proposed + fee2))), bob2.commitments) } - // TODO: restore this test once we take the reserve into account (see canReceiveFee) - @Ignore + @Test fun `recv UpdateFee -- sender cannot afford it`() { val (alice, bob) = reachNormal() // We put all the balance on Bob's side, so that Alice cannot afford a feerate increase.