pytest: test that we correctly mark a payment part failed if we cannot queue it to the channeld for the peer.
What changed, and why it matters
This commit only adds a new automated test to the project's test suite. It does not change any production code, so by itself it cannot introduce or fix a live security issue. The test checks that a specific payment function reports a failure correctly when a peer connection drops at a particular moment.
No immediate action required for this commit. Treat it as test coverage. If the xfail test is intended to guard against a real bug, monitor the follow-up commit that makes the test pass and review that change for security relevance.
Security signals we found
No production code changes
Test-only commit
xfail test indicating known behavior not yet fixed
Covers failure-handling path for payment queueing to channeld
Evidence from the diff
The diff adds a single pytest test, test_injectpaymentonion_peerfail, to tests/test_pay.py. The test uses injectpaymentonion, a developer/experimental RPC, and deliberately disconnects a peer during HTLC processing to verify that the payment part is marked failed (with WIRE_TEMPORARY_CHANNEL_FAILURE) and that no listsendpays entry is created when the failure occurs before queueing to channeld. It is marked @pytest.mark.xfail(strict=True), meaning the test is currently expected to fail until the underlying behavior is implemented or fixed. No application, library, or protocol code is modified.
Changed components
tests/test_pay.pyInspect captured patch +44 / −0
diff --git a/tests/test_pay.py b/tests/test_pay.py
index 1afddd56..9820a2bf 100644
--- a/tests/test_pay.py
+++ b/tests/test_pay.py
@@ -6755,6 +6755,50 @@ def test_injectpaymentonion_failures(node_factory, executor):
assert 'onionreply' in err.value.error['data']
+@pytest.mark.xfail(strict=True)
+def test_injectpaymentonion_peerfail(node_factory, executor):
+ l1, l2 = node_factory.line_graph(2,
+ opts=[{'may_reconnect': True,
+ 'dev-no-reconnect': None,
+ 'disconnect': ['=WIRE_UPDATE_ADD_HTLC', '-WIRE_COMMITMENT_SIGNED']},
+ {'may_reconnect': True,
+ 'dev-no-reconnect': None}])
+ blockheight = l1.rpc.getinfo()['blockheight']
+
+ inv1 = l2.rpc.invoice(1000, "test_injectpaymentonion_peerfail", "test_injectpaymentonion_peerfail")
+
+ # First hop for injectpaymentonion is self.
+ hops = [{'pubkey': l1.info['id'],
+ 'payload': serialize_payload_tlv(1000, 18 + 6, first_scid(l1, l2), blockheight).hex()},
+ {'pubkey': l2.info['id'],
+ 'payload': serialize_payload_final_tlv(1000, 18, 1000, blockheight, inv1['payment_secret']).hex()}]
+ onion = l1.rpc.createonion(hops=hops, assocdata=inv1['payment_hash'])
+
+ l1.rpc.disconnect(l2.info['id'], force=True)
+ with pytest.raises(RpcError, match='WIRE_TEMPORARY_CHANNEL_FAILURE'):
+ l1.rpc.injectpaymentonion(onion=onion['onion'],
+ payment_hash=inv1['payment_hash'],
+ amount_msat=1000,
+ cltv_expiry=blockheight + 18 + 6,
+ partid=1,
+ groupid=0)
+ # In fact, it won't create any sendpays entry, since it fails too early.
+ assert l1.rpc.listsendpays() == {'payments': []}
+
+ # This will hang, since we disconnect once committed. But provides another
+ # (legitimately) pending payment for our migration code to test.
+ l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
+ executor.submit(l1.rpc.injectpaymentonion,
+ onion=onion['onion'],
+ payment_hash=inv1['payment_hash'],
+ amount_msat=1000,
+ cltv_expiry=blockheight + 18 + 6,
+ partid=2,
+ groupid=0)
+ l1.daemon.wait_for_log("dev_disconnect: =WIRE_UPDATE_ADD_HTLC")
+ assert [p['status'] for p in l1.rpc.listsendpays()['payments']] == ['pending']
+
+
def test_parallel_channels_reserve(node_factory, bitcoind):
"""Tests wether we are able to pay through parallel channels concurrently.
To do that we need to enable strict-forwarding."""
Why this scored 12/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.