Skip to content

Commit 654ab36

Browse files
committed
pay: fix infinite loop when CLTV budget exceeded
When p->route is NULL (no route found), the adaptive splitter was still trying to split payments, causing thousands of attempts all failing for the same reason. Fixes: #8167 Changelog-Fixed: pay: no longer loops infinitely when CLTV budget exceeded
1 parent 10b10eb commit 654ab36

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

plugins/libplugin-pay.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3692,6 +3692,9 @@ static struct command_result *adaptive_splitter_cb(struct adaptive_split_mod_dat
36923692
fields, root->payment_secret,
36933693
root->final_amount.millisatoshis); /* Raw: onion payload */
36943694
} else if (p->step == PAYMENT_STEP_FAILED && !p->abort) {
3695+
/* No route means splitting won't help. */
3696+
if (p->route == NULL)
3697+
return payment_continue(p);
36953698
if (amount_msat_greater(p->our_amount, MPP_ADAPTIVE_LOWER_LIMIT)) {
36963699
struct payment *a, *b;
36973700
/* Random number in the range [90%, 110%] */

tests/test_pay.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,34 @@ def test_pay_limits(node_factory):
150150
assert status[0]['strategy'] == "Initial attempt"
151151

152152

153+
def test_pay_cltv_budget_exceeded_no_infinite_loop(node_factory):
154+
"""Test that CLTV budget exceeded doesn't cause infinite splitting loop.
155+
156+
Reproduces issue #8167: When CLTV delay exceeds budget, the adaptive
157+
splitter would keep splitting the payment into thousands of sub-payments,
158+
even though splitting cannot help (CLTV requirements don't depend on
159+
payment amount).
160+
"""
161+
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True)
162+
163+
# Use amount > 100k msat to enable MPP adaptive splitting
164+
amt = 1000000
165+
166+
inv = l3.rpc.invoice(amt, "test_cltv_no_infinite_loop", 'description')['bolt11']
167+
168+
# Set maxdelay very low to trigger CLTV budget exceeded
169+
PAY_STOPPED_RETRYING = 210
170+
failmsg = r'CLTV delay exceeds our CLTV budget'
171+
172+
with pytest.raises(RpcError, match=failmsg) as err:
173+
l1.rpc.call('pay', {'bolt11': inv, 'maxdelay': 5})
174+
175+
assert err.value.error['code'] == PAY_STOPPED_RETRYING
176+
177+
status = l1.rpc.call('paystatus', {'bolt11': inv})['pay'][0]['attempts']
178+
assert len(status) < 10, f"Too many attempts ({len(status)}), possible infinite loop bug"
179+
180+
153181
def test_pay_exclude_node(node_factory, bitcoind):
154182
"""Test excluding the node if there's the NODE-level error in the failure_code
155183
"""

0 commit comments

Comments
 (0)