From b432a1406a68ee12c9d14a236e2f1d19bc5df55a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 20 May 2025 12:11:20 +0200 Subject: [PATCH] lnchannel: apply stricter max_htlc_value_in_flight rules for receiving Otherwise we create invoices that eclair cannot route to us --- electrum/lnchannel.py | 10 +++++++++- tests/test_lnchannel.py | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/electrum/lnchannel.py b/electrum/lnchannel.py index 276ed51a4..eb1908a04 100644 --- a/electrum/lnchannel.py +++ b/electrum/lnchannel.py @@ -1150,7 +1150,15 @@ class Channel(AbstractChannel): # check "max_htlc_value_in_flight_msat" ctn = self.get_next_ctn(htlc_receiver) current_htlc_sum = htlcsum(self.hm.htlcs_by_direction(htlc_receiver, direction=RECEIVED, ctn=ctn).values()) - return self.config[htlc_receiver].max_htlc_value_in_flight_msat - current_htlc_sum + max_inflight = self.config[htlc_receiver].max_htlc_value_in_flight_msat + if htlc_receiver == LOCAL: + # in order to send, eclair applies both local and remote max values + # https://github.com/ACINQ/eclair/blob/9b0c00a2a28d3ba6c7f3d01fbd2d8704ebbdc75d/eclair-core/src/main/scala/fr/acinq/eclair/channel/Commitments.scala#L503 + max_inflight = min( + self.config[LOCAL].max_htlc_value_in_flight_msat, + self.config[REMOTE].max_htlc_value_in_flight_msat + ) + return max_inflight - current_htlc_sum def can_pay(self, amount_msat: int, *, check_frozen=False) -> bool: """Returns whether we can add an HTLC of given value.""" diff --git a/tests/test_lnchannel.py b/tests/test_lnchannel.py index 9b281dbdc..39d8d935c 100644 --- a/tests/test_lnchannel.py +++ b/tests/test_lnchannel.py @@ -738,24 +738,31 @@ class TestAvailableToSpend(ElectrumTestCase): local_max_inflight=1000000000, remote_max_inflight=2000000000) + # alice can send 20 but bob can only receive 10, because of stricter receiving rules self.assertEqual(2000000000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(1000000000, bob_channel.available_to_spend(REMOTE)) + + # bob can send 10, alice can receive 10 self.assertEqual(1000000000, bob_channel.available_to_spend(LOCAL)) + self.assertEqual(1000000000, alice_channel.available_to_spend(REMOTE)) paymentPreimage1 = b"\x01" * 32 htlc = UpdateAddHtlc( payment_hash=bitcoin.sha256(paymentPreimage1), - amount_msat=1500000000, + amount_msat=1000000000, cltv_abs=5, timestamp=0, ) - - # put 15mBTC inflight a->b + # put 10mBTC inflight a->b alice_idx1 = alice_channel.add_htlc(htlc).htlc_id bob_idx1 = bob_channel.receive_htlc(htlc).htlc_id force_state_transition(alice_channel, bob_channel) - self.assertEqual(500000000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(1000000000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(0, bob_channel.available_to_spend(REMOTE)) + self.assertEqual(1000000000, bob_channel.available_to_spend(LOCAL)) + self.assertEqual(1000000000, alice_channel.available_to_spend(REMOTE)) paymentPreimage2 = b"\x02" * 32 htlc2 = UpdateAddHtlc( @@ -764,7 +771,6 @@ class TestAvailableToSpend(ElectrumTestCase): cltv_abs=5, timestamp=0, ) - # try to add another 15mBTC HTLC while 15mBTC already inflight with self.assertRaises(lnutil.PaymentFailure): alice_idx2 = alice_channel.add_htlc(htlc2).htlc_id @@ -775,7 +781,10 @@ class TestAvailableToSpend(ElectrumTestCase): force_state_transition(alice_channel, bob_channel) self.assertEqual(2000000000, alice_channel.available_to_spend(LOCAL)) + self.assertEqual(1000000000, alice_channel.available_to_spend(REMOTE)) + self.assertEqual(1000000000, bob_channel.available_to_spend(LOCAL)) + self.assertEqual(1000000000, alice_channel.available_to_spend(REMOTE)) class TestAvailableToSpendAnchors(TestAvailableToSpend):