Merge pull request #10325 from f321x/debug_not_enough_balance
lightning: fix race when doing concurrent payments
This commit is contained in:
@@ -914,6 +914,7 @@ class LNWallet(LNWorker):
|
|||||||
self._paysessions = dict() # type: Dict[bytes, PaySession]
|
self._paysessions = dict() # type: Dict[bytes, PaySession]
|
||||||
self.sent_htlcs_info = dict() # type: Dict[SentHtlcKey, SentHtlcInfo]
|
self.sent_htlcs_info = dict() # type: Dict[SentHtlcKey, SentHtlcInfo]
|
||||||
self.received_mpp_htlcs = self.db.get_dict('received_mpp_htlcs') # type: Dict[str, ReceivedMPPStatus] # payment_key -> ReceivedMPPStatus
|
self.received_mpp_htlcs = self.db.get_dict('received_mpp_htlcs') # type: Dict[str, ReceivedMPPStatus] # payment_key -> ReceivedMPPStatus
|
||||||
|
self._channel_sending_capacity_lock = asyncio.Lock()
|
||||||
|
|
||||||
# detect inflight payments
|
# detect inflight payments
|
||||||
self.inflight_payments = set() # (not persisted) keys of invoices that are in PR_INFLIGHT state
|
self.inflight_payments = set() # (not persisted) keys of invoices that are in PR_INFLIGHT state
|
||||||
@@ -1698,27 +1699,33 @@ class LNWallet(LNWorker):
|
|||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
if (amount_to_send := paysession.get_outstanding_amount_to_send()) > 0:
|
if (amount_to_send := paysession.get_outstanding_amount_to_send()) > 0:
|
||||||
# 1. create a set of routes for remaining amount.
|
|
||||||
# note: path-finding runs in a separate thread so that we don't block the asyncio loop
|
|
||||||
# graph updates might occur during the computation
|
|
||||||
remaining_fee_budget_msat = (budget.fee_msat * amount_to_send) // amount_to_pay
|
remaining_fee_budget_msat = (budget.fee_msat * amount_to_send) // amount_to_pay
|
||||||
routes = self.create_routes_for_payment(
|
# splitting the amount of the payment between our channels requires the correct
|
||||||
paysession=paysession,
|
# available channel balance. to prevent concurrent splitting attempts from
|
||||||
amount_msat=amount_to_send,
|
# using stale channel balances for the split calculation a lock needs to be
|
||||||
full_path=full_path,
|
# taken until the htlcs are added to the channel so the next splitting attempt
|
||||||
fwd_trampoline_onion=fwd_trampoline_onion,
|
# acts on a correct channel balance.
|
||||||
channels=channels,
|
async with self._channel_sending_capacity_lock:
|
||||||
budget=budget._replace(fee_msat=remaining_fee_budget_msat),
|
# 1. create a set of routes for remaining amount.
|
||||||
)
|
# note: path-finding runs in a separate thread so that we don't block the asyncio loop
|
||||||
# 2. send htlcs
|
# graph updates might occur during the computation
|
||||||
async for sent_htlc_info, cltv_delta, trampoline_onion in routes:
|
routes = self.create_routes_for_payment(
|
||||||
await self.pay_to_route(
|
|
||||||
paysession=paysession,
|
paysession=paysession,
|
||||||
sent_htlc_info=sent_htlc_info,
|
amount_msat=amount_to_send,
|
||||||
min_final_cltv_delta=cltv_delta,
|
full_path=full_path,
|
||||||
trampoline_onion=trampoline_onion,
|
fwd_trampoline_onion=fwd_trampoline_onion,
|
||||||
fw_payment_key=fw_payment_key,
|
channels=channels,
|
||||||
|
budget=budget._replace(fee_msat=remaining_fee_budget_msat),
|
||||||
)
|
)
|
||||||
|
# 2. send htlcs
|
||||||
|
async for sent_htlc_info, cltv_delta, trampoline_onion in routes:
|
||||||
|
await self.pay_to_route(
|
||||||
|
paysession=paysession,
|
||||||
|
sent_htlc_info=sent_htlc_info,
|
||||||
|
min_final_cltv_delta=cltv_delta,
|
||||||
|
trampoline_onion=trampoline_onion,
|
||||||
|
fw_payment_key=fw_payment_key,
|
||||||
|
)
|
||||||
# invoice_status is triggered in self.set_invoice_status when it actually changes.
|
# invoice_status is triggered in self.set_invoice_status when it actually changes.
|
||||||
# It is also triggered here to update progress for a lightning payment in the GUI
|
# It is also triggered here to update progress for a lightning payment in the GUI
|
||||||
# (e.g. attempt counter)
|
# (e.g. attempt counter)
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
|
|||||||
self._payment_bundles_pkey_to_canon = {} # type: Dict[bytes, bytes]
|
self._payment_bundles_pkey_to_canon = {} # type: Dict[bytes, bytes]
|
||||||
self._payment_bundles_canon_to_pkeylist = {} # type: Dict[bytes, Sequence[bytes]]
|
self._payment_bundles_canon_to_pkeylist = {} # type: Dict[bytes, Sequence[bytes]]
|
||||||
self.config.INITIAL_TRAMPOLINE_FEE_LEVEL = 0
|
self.config.INITIAL_TRAMPOLINE_FEE_LEVEL = 0
|
||||||
|
self._channel_sending_capacity_lock = asyncio.Lock()
|
||||||
|
|
||||||
self.logger.info(f"created LNWallet[{name}] with nodeID={local_keypair.pubkey.hex()}")
|
self.logger.info(f"created LNWallet[{name}] with nodeID={local_keypair.pubkey.hex()}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user