From 4d8ef0c95d603c629bc07732032b5015c64fa270 Mon Sep 17 00:00:00 2001 From: t-bast Date: Tue, 10 Sep 2024 17:13:18 +0200 Subject: [PATCH] Remove `minInboundLiquidityTarget` We previously forced wallets to purchase additional inbound liquidity every time an on-chain transaction was created. We now allow wallets to disable automatic liquidity purchases: the LSP will need to add enough funds on their side to cover the commitment fees, which the wallet won't be paying for. But we still make a dummy purchase of 1 sat to ensure that the liquidity ads flow is used and the wallet refunds the mining fees paid by the LSP. --- .../kotlin/fr/acinq/lightning/io/Peer.kt | 6 ++++-- .../payment/IncomingPaymentHandler.kt | 4 ++-- .../lightning/payment/LiquidityPolicy.kt | 8 -------- .../fr/acinq/lightning/io/peer/PeerTest.kt | 2 +- .../IncomingPaymentHandlerTestsCommon.kt | 20 +++++++++---------- 5 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt b/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt index 6fde8aa75..9f686df35 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/io/Peer.kt @@ -1272,9 +1272,11 @@ class Peer( val currentFeerates = peerFeeratesFlow.filterNotNull().first() val requestRemoteFunding = run { // We need our peer to contribute, because they must have enough funds to pay the commitment fees. + // That means they will add at least one input to the funding transaction, and pay the corresponding mining fees. + // We always request a liquidity purchase, even for a dummy amount, which ensures that we refund their mining fees. val inboundLiquidityTarget = when (val policy = nodeParams.liquidityPolicy.first()) { - is LiquidityPolicy.Disable -> LiquidityPolicy.minInboundLiquidityTarget - is LiquidityPolicy.Auto -> policy.inboundLiquidityTarget ?: LiquidityPolicy.minInboundLiquidityTarget + is LiquidityPolicy.Disable -> 1.sat + is LiquidityPolicy.Auto -> policy.inboundLiquidityTarget ?: 1.sat } // We assume that the liquidity policy is correctly configured to match a funding lease offered by our peer. val fundingRate = walletParams.remoteFundingRates.findRate(inboundLiquidityTarget)!! diff --git a/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandler.kt b/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandler.kt index 55d70ac4f..d33a76dad 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandler.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandler.kt @@ -298,9 +298,9 @@ class IncomingPaymentHandler(val nodeParams: NodeParams, val db: PaymentsDb, pri return when (val liquidityPolicy = nodeParams.liquidityPolicy.value) { is LiquidityPolicy.Disable -> Either.Left(LiquidityEvents.Rejected(willAddHtlcAmount, 0.msat, LiquidityEvents.Source.OffChainPayment, LiquidityEvents.Rejected.Reason.PolicySetToDisabled)) is LiquidityPolicy.Auto -> { - // Whenever we receive on-the-fly funding, we take this opportunity to purchase inbound liquidity. + // Whenever we receive on-the-fly funding, we take this opportunity to purchase inbound liquidity, if configured. // This reduces the frequency of on-chain funding and thus the overall on-chain fees paid. - val additionalInboundLiquidity = liquidityPolicy.inboundLiquidityTarget ?: LiquidityPolicy.minInboundLiquidityTarget + val additionalInboundLiquidity = liquidityPolicy.inboundLiquidityTarget ?: 0.sat // We must round up to the nearest satoshi value instead of rounding down. val requestedAmount = (willAddHtlcAmount + 999.msat).truncateToSatoshi() + additionalInboundLiquidity when (val fundingRate = remoteFundingRates.findRate(requestedAmount)) { diff --git a/src/commonMain/kotlin/fr/acinq/lightning/payment/LiquidityPolicy.kt b/src/commonMain/kotlin/fr/acinq/lightning/payment/LiquidityPolicy.kt index 1d861ca13..2209b2523 100644 --- a/src/commonMain/kotlin/fr/acinq/lightning/payment/LiquidityPolicy.kt +++ b/src/commonMain/kotlin/fr/acinq/lightning/payment/LiquidityPolicy.kt @@ -38,12 +38,4 @@ sealed class LiquidityPolicy { } }?.let { reason -> LiquidityEvents.Rejected(amount, fee, source, reason) } } - - companion object { - /** - * We usually need our peer to contribute to channel funding, because they must have enough funds to pay the commitment fees. - * When we don't have an inbound liquidity target set, we use the following default amount. - */ - val minInboundLiquidityTarget: Satoshi = 100_000.sat - } } \ No newline at end of file diff --git a/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt b/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt index 9c5e448c4..56032a7c6 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/io/peer/PeerTest.kt @@ -222,7 +222,7 @@ class PeerTest : LightningTestSuite() { @Test fun `swap funds into a channel`() = runSuspendTest { val nodeParams = Pair(TestConstants.Alice.nodeParams, TestConstants.Bob.nodeParams) - nodeParams.second.liquidityPolicy.emit(LiquidityPolicy.Auto(inboundLiquidityTarget = null, maxAbsoluteFee = 20_000.sat, maxRelativeFeeBasisPoints = 1000, skipAbsoluteFeeCheck = false)) + nodeParams.second.liquidityPolicy.emit(LiquidityPolicy.Auto(inboundLiquidityTarget = 100_000.sat, maxAbsoluteFee = 20_000.sat, maxRelativeFeeBasisPoints = 1000, skipAbsoluteFeeCheck = false)) val walletParams = Pair(TestConstants.Alice.walletParams, TestConstants.Bob.walletParams) val (_, bob, _, bob2alice) = newPeers(this, nodeParams, walletParams, automateMessaging = false) diff --git a/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt b/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt index dedfb587e..6580c0ddf 100644 --- a/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt +++ b/src/commonTest/kotlin/fr/acinq/lightning/payment/IncomingPaymentHandlerTestsCommon.kt @@ -247,7 +247,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { assertIs(addLiquidity) assertEquals(incomingPayment.preimage, addLiquidity.preimage) assertEquals(defaultAmount, addLiquidity.paymentAmount) - assertTrue(defaultAmount < addLiquidity.requestedAmount) + assertEquals(defaultAmount, addLiquidity.requestedAmount.toMilliSatoshi()) assertEquals(TestConstants.fundingRates.fundingRates.first(), addLiquidity.fundingRate) assertEquals(listOf(willAddHtlc), addLiquidity.willAddHtlcs) // We don't update the payments DB: we're waiting to receive HTLCs after the open/splice. @@ -359,7 +359,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { assertIs(addLiquidity) assertEquals(incomingPayment.preimage, addLiquidity.preimage) assertEquals(defaultAmount, addLiquidity.paymentAmount) - assertTrue(defaultAmount < addLiquidity.requestedAmount) + assertEquals(defaultAmount, addLiquidity.requestedAmount.toMilliSatoshi()) assertEquals(TestConstants.fundingRates.fundingRates.first(), addLiquidity.fundingRate) assertNull(paymentHandler.db.getIncomingPayment(incomingPayment.paymentHash)?.received) } @@ -464,7 +464,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { @Test fun `receive multipart payment with a mix of HTLC and will_add_htlc`() = runSuspendTest { val channelId = randomBytes32() - val (amount1, amount2) = listOf(50_000_000.msat, 60_000_000.msat) + val (amount1, amount2) = listOf(50_000_000.msat, 100_000_000.msat) val totalAmount = amount1 + amount2 val (paymentHandler, incomingPayment, paymentSecret) = createFixture(totalAmount) checkDbPayment(incomingPayment, paymentHandler.db) @@ -489,7 +489,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { assertEquals(1, result.actions.size) val addLiquidity = result.actions.first() as AddLiquidityForIncomingPayment assertEquals(incomingPayment.preimage, addLiquidity.preimage) - assertEquals(amount2.truncateToSatoshi() + LiquidityPolicy.minInboundLiquidityTarget, addLiquidity.requestedAmount) + assertEquals(amount2.truncateToSatoshi(), addLiquidity.requestedAmount) assertEquals(totalAmount, addLiquidity.paymentAmount) assertNull(paymentHandler.db.getIncomingPayment(incomingPayment.paymentHash)?.received) } @@ -617,7 +617,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { @Test fun `receive multipart payment with funding fee`() = runSuspendTest { val channelId = randomBytes32() - val (amount1, amount2) = listOf(50_000_000.msat, 60_000_000.msat) + val (amount1, amount2) = listOf(50_000_000.msat, 100_000_000.msat) val totalAmount = amount1 + amount2 val (paymentHandler, incomingPayment, paymentSecret) = createFixture(totalAmount) checkDbPayment(incomingPayment, paymentHandler.db) @@ -676,7 +676,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { @Test fun `receive payment with funding fee -- from channel balance`() = runSuspendTest { val channelId = randomBytes32() - val amount = 50_000_000.msat + val amount = 100_000_000.msat val (paymentHandler, incomingPayment, paymentSecret) = createFixture(amount) checkDbPayment(incomingPayment, paymentHandler.db) @@ -743,7 +743,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { // We have a matching transaction in our DB. val purchase = LiquidityAds.Purchase.Standard( - defaultAmount.truncateToSatoshi() + LiquidityPolicy.minInboundLiquidityTarget, + defaultAmount.truncateToSatoshi(), LiquidityAds.Fees(2000.sat, 3000.sat), LiquidityAds.PaymentDetails.FromFutureHtlc(listOf(incomingPayment.paymentHash)), ) @@ -779,7 +779,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { // We have a matching transaction in our DB, but we paid the fees from our channel balance already. val purchase = LiquidityAds.Purchase.Standard( - defaultAmount.truncateToSatoshi() + LiquidityPolicy.minInboundLiquidityTarget, + defaultAmount.truncateToSatoshi(), LiquidityAds.Fees(2000.sat, 3000.sat), LiquidityAds.PaymentDetails.FromChannelBalanceForFutureHtlc(listOf(incomingPayment.paymentHash)), ) @@ -801,7 +801,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { // We have a matching transaction in our DB, but the fees must be paid with a different payment_hash. val purchase = LiquidityAds.Purchase.Standard( - defaultAmount.truncateToSatoshi() + LiquidityPolicy.minInboundLiquidityTarget, + defaultAmount.truncateToSatoshi(), LiquidityAds.Fees(2000.sat, 3000.sat), LiquidityAds.PaymentDetails.FromFutureHtlc(listOf(randomBytes32())), ) @@ -1436,7 +1436,7 @@ class IncomingPaymentHandlerTestsCommon : LightningTestSuite() { // We have a matching transaction in our DB, but the fees must be paid with a different payment_hash. val purchase = LiquidityAds.Purchase.Standard( - defaultAmount.truncateToSatoshi() + LiquidityPolicy.minInboundLiquidityTarget, + defaultAmount.truncateToSatoshi(), LiquidityAds.Fees(2000.sat, 3000.sat), LiquidityAds.PaymentDetails.FromFutureHtlcWithPreimage(listOf(preimage)), )