pytest: add tests for what we want askrene to do.
What changed, and why it matters
This commit only adds a new pytest test file. It does not change any production code. The test describes desired future behavior for the askrene routing module's error messages when a payment source or destination lacks enough channel capacity. Because no actual code behavior is modified, this commit cannot introduce or fix a security vulnerability on its own.
No security action needed. Treat as a normal test-only commit. If reviewing a larger patch series, evaluate the implementation commit that will make this test pass.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds a single test, test_explain_source_dest_failures, to tests/test_askrene.py. The test is marked @pytest.mark.xfail(strict=True), meaning it is expected to fail until the corresponding implementation is written. It asserts that getroutes should return specific RPC errors (PAY_INSUFFICIENT_FUNDS, PAY_DESTINATION_INSUFFICIENT_CAPACITY) with human-readable messages about source/destination capacity. No library, daemon, or RPC implementation code is touched.
Changed components
tests/test_askrene.pyInspect captured patch +67 / −0
diff --git a/tests/test_askrene.py b/tests/test_askrene.py
index ba5af5c4..005a36f2 100644
--- a/tests/test_askrene.py
+++ b/tests/test_askrene.py
@@ -2741,6 +2741,73 @@ def test_bad_user_entries(node_factory):
)
+@pytest.mark.xfail(strict=True)
+def test_explain_source_dest_failures(node_factory, bitcoind):
+ """askrene should give intelligent failure reasons when source or destination don't have
+ capacity"""
+ # l1 --100k--> l2 --200k--> l3
+ # |
+ # 50k
+ # v
+ # l4
+ l1, l2, l3, l4 = node_factory.get_nodes(4)
+ node_factory.join_nodes([l1, l2], fundamount=100000)
+ node_factory.join_nodes([l2, l3], fundamount=200000)
+ node_factory.join_nodes([l2, l4], fundamount=50000)
+
+ # Make sure everyone knows everything
+ bitcoind.generate_block(5)
+ wait_for(lambda: all([len(n.rpc.listchannels()['channels']) == 6 for n in [l1, l2, l3, l4]]))
+
+ # We can't afford this
+ with pytest.raises(RpcError,
+ match=r"We could not find a usable set of paths. Total source capacity is only 100000000msat \(in 1 channels\)"):
+ l1.rpc.getroutes(source=l1.info['id'],
+ destination=l3.info['id'],
+ amount_msat='100001sat',
+ layers=['auto.localchans', 'auto.sourcefree'],
+ maxfee_msat=10000,
+ final_cltv=5)
+
+ # They can't afford this
+ with pytest.raises(RpcError,
+ match=r"We could not find a usable set of paths. Total destination capacity is only 50000000msat \(in 1 channels\)"):
+ l1.rpc.getroutes(source=l1.info['id'],
+ destination=l4.info['id'],
+ amount_msat='50001sat',
+ layers=['auto.localchans', 'auto.sourcefree'],
+ maxfee_msat=10000,
+ final_cltv=5)
+
+ # Add some information, and we should know that too.
+ l1.rpc.xpay(l4.rpc.invoice('30000sat', 'test_explain_simple_failures2', 'test_explain_simple_failures2')['bolt11'])
+
+ # This is actually just auto.localchans knowing the capacity!
+ with pytest.raises(RpcError,
+ match=r"We could not find a usable set of paths. We know from auto.localchans that source has maximum capacity [0-9]*msat \(in 1 channels\)") as err:
+ l1.rpc.getroutes(source=l1.info['id'],
+ destination=l3.info['id'],
+ amount_msat='80001sat',
+ layers=['auto.localchans', 'auto.sourcefree', 'xpay'],
+ maxfee_msat=10000,
+ final_cltv=5)
+ PAY_INSUFFICIENT_FUNDS = 215
+ assert err.value.error['code'] == PAY_INSUFFICIENT_FUNDS
+
+ # This is the impression in the xpay layer telling us 30,000sat is already gone (of 50,000).
+ with pytest.raises(RpcError,
+ match=r"We could not find a usable set of paths. We know from xpay that destination has maximum capacity [0-9]*msat \(in 1 channels\)") as err:
+ l1.rpc.getroutes(source=l1.info['id'],
+ destination=l4.info['id'],
+ amount_msat='20001sat',
+ layers=['auto.localchans', 'auto.sourcefree', 'xpay'],
+ maxfee_msat=10000,
+ final_cltv=5)
+
+ PAY_DESTINATION_INSUFFICIENT_CAPACITY = 220
+ assert err.value.error['code'] == PAY_DESTINATION_INSUFFICIENT_CAPACITY
+
+
def test_constraint_impression_ordering(node_factory):
"""Constraints and impressions must be applied in timestamp order.
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.