Skip to content

Commit 7f40dbb

Browse files
committed
tests: check for incoming + outgoing dust limits
1 parent 719f875 commit 7f40dbb

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

tests/test_pay.py

+104
Original file line numberDiff line numberDiff line change
@@ -2436,6 +2436,110 @@ def test_lockup_drain(node_factory, bitcoind):
24362436
l2.pay(l1, total // 2)
24372437

24382438

2439+
@pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs")
2440+
def test_htlc_too_dusty_outgoing(node_factory, bitcoind):
2441+
""" Try to hit the 'too much dust' limit, should fail the HTLC """
2442+
feerate = 10000
2443+
max_dust_limit_sat = 50000
2444+
2445+
l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True,
2446+
'feerates': (feerate, feerate, feerate, feerate),
2447+
'max-dust-htlc-exposure-msat': '{}sat'.format(max_dust_limit_sat),
2448+
'allow_warning': True})
2449+
2450+
# l2 holds all of l1's htlcs hostage
2451+
l2.rpc.dev_ignore_htlcs(id=l1.info['id'], ignore=True)
2452+
2453+
# l2's max dust limit is set to 100k
2454+
htlc_val_sat = 5000
2455+
htlc_val_msat = htlc_val_sat * 1000
2456+
num_dusty_htlcs = max_dust_limit_sat // htlc_val_sat
2457+
2458+
# add a some non-dusty htlcs, these will fail when we raise the dust limit
2459+
route = l1.rpc.getroute(l2.info['id'], 10000 * 1000, 1)['route']
2460+
for i in range(0, 3):
2461+
inv = l2.rpc.invoice((10000 * 1000), str(i + 100), str(i + 100))
2462+
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2463+
l2.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs')
2464+
res = only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments'])
2465+
assert res['status'] == 'pending'
2466+
2467+
# add some dusty-htlcs
2468+
route = l1.rpc.getroute(l2.info['id'], htlc_val_msat, 1)['route']
2469+
for i in range(0, num_dusty_htlcs):
2470+
inv = l2.rpc.invoice(htlc_val_msat, str(i), str(i))
2471+
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2472+
l2.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs')
2473+
res = only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments'])
2474+
assert res['status'] == 'pending'
2475+
2476+
# one more should tip it over, and return a payment failure
2477+
inv = l2.rpc.invoice(htlc_val_msat, str(num_dusty_htlcs), str(num_dusty_htlcs))
2478+
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2479+
l1.daemon.wait_for_log('CHANNEL_ERR_DUST_FAILURE')
2480+
wait_for(lambda: only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments'])['status'] == 'failed')
2481+
2482+
# but we can still add a non dust htlc
2483+
route = l1.rpc.getroute(l2.info['id'], 10000 * 1000, 1)['route']
2484+
inv = l2.rpc.invoice((10000 * 1000), str(120), str(120))
2485+
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2486+
l2.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs')
2487+
res = only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments'])
2488+
assert res['status'] == 'pending'
2489+
2490+
# Ok, adjust our feerate upward, so the non-dust htlcs are now dust
2491+
# note that this is above the buffer we've been keeping, so the channel
2492+
# should automatically fail
2493+
l1.set_feerates([feerate * 2] * 4, False)
2494+
l1.restart()
2495+
2496+
# the channel should fail -- too much dust
2497+
inv = l2.rpc.invoice(htlc_val_msat, str(num_dusty_htlcs + 1), str(num_dusty_htlcs + 1))
2498+
with pytest.raises(RpcError, match=r'WIRE_UNKNOWN_NEXT_PEER'):
2499+
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2500+
2501+
2502+
@pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs")
2503+
def test_htlc_too_dusty_incoming(node_factory, bitcoind):
2504+
""" Try to hit the 'too much dust' limit, should fail the HTLC """
2505+
feerate = 30000
2506+
l1, l2, l3 = node_factory.line_graph(3, opts=[{'may_reconnect': True,
2507+
'feerates': (feerate, feerate, feerate, feerate),
2508+
'max-dust-htlc-exposure-msat': '200000sat'},
2509+
{'may_reconnect': True,
2510+
'feerates': (feerate, feerate, feerate, feerate),
2511+
'max-dust-htlc-exposure-msat': '100000sat',
2512+
'fee-base': 0,
2513+
'fee-per-satoshi': 0},
2514+
{}],
2515+
wait_for_announce=True)
2516+
2517+
# on the l2->l3, and l3 holds all the htlcs hostage
2518+
# have l3 hold onto all the htlcs and not fulfill them
2519+
l3.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True)
2520+
2521+
# l2's max dust limit is set to 100k
2522+
max_dust_limit_sat = 100000
2523+
htlc_val_sat = 10000
2524+
htlc_val_msat = htlc_val_sat * 1000
2525+
num_dusty_htlcs = max_dust_limit_sat // htlc_val_sat
2526+
route = l1.rpc.getroute(l3.info['id'], htlc_val_msat, 1)['route']
2527+
2528+
# l1 sends as much money as it can
2529+
for i in range(0, num_dusty_htlcs):
2530+
inv = l3.rpc.invoice(htlc_val_msat, str(i), str(i))
2531+
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2532+
l3.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs')
2533+
res = only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments'])
2534+
assert res['status'] == 'pending'
2535+
2536+
# one more should tip it over, and return a payment failure
2537+
inv = l3.rpc.invoice(htlc_val_msat, str(num_dusty_htlcs), str(num_dusty_htlcs))
2538+
l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
2539+
l2.daemon.wait_for_log('failing immediately, as requested')
2540+
wait_for(lambda: only_one(l1.rpc.listsendpays(payment_hash=inv['payment_hash'])['payments'])['status'] == 'failed')
2541+
2542+
24392543
def test_error_returns_blockheight(node_factory, bitcoind):
24402544
"""Test that incorrect_or_unknown_payment_details returns block height"""
24412545
l1, l2 = node_factory.line_graph(2)

0 commit comments

Comments
 (0)