pytest: test if we correctly route using old scids after splice
What changed, and why it matters
This commit adds a test showing that, after a Bitcoin 'splice' operation changes a Lightning channel's short channel ID (SCID), payments routed using the old SCID fail. The test is marked as expected-to-fail, meaning the bug is known but not yet fixed. It could cause legitimate post-splice payments to fail until the network learns the new routing identifier.
Treat as a known functional bug rather than an immediate security vulnerability. Developers should implement backward-compatible routing by old SCIDs after splice and remove the xfail marker once the underlying routing/gossip code is fixed. Users relying on experimental splicing should be aware that in-flight or cached routes may fail after a splice.
Security signals we found
Post-splice routing failure using legacy short channel IDs
Test marked xfail documenting a known, unfixed bug
Potential payment reliability / denial-of-service concern for spliced channels
Evidence from the diff
The patch adds test_route_by_old_scid in tests/test_splicing.py. It creates a three-node line graph with experimental splicing enabled, records a pre-splice route from l1 to l3 via l2, performs a splice on the l2-l3 channel, then attempts to pay using the previously cached route. The test is decorated with @pytest.mark.xfail(strict=True), so it documents a current failure: routing by the old SCID after splice does not work correctly.
Changed components
tests/test_splicing.pyLightning routing/gossip layer (implicated by test, not patched)Splicing feature (experimental)Inspect captured patch +28 / −0
diff --git a/tests/test_splicing.py b/tests/test_splicing.py
index 19a62e90..0a0747dd 100644
--- a/tests/test_splicing.py
+++ b/tests/test_splicing.py
@@ -497,3 +497,31 @@ def test_splice_stuck_htlc(node_factory, bitcoind, executor):
# Check that the splice doesn't generate a unilateral close transaction
time.sleep(5)
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
+
+
+@pytest.mark.xfail(strict=True)
+def test_route_by_old_scid(node_factory, bitcoind):
+ l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, opts={'experimental-splicing': None})
+
+ # Get pre-splice route.
+ inv = l3.rpc.invoice(10000000, 'test_route_by_old_scid', 'test_route_by_old_scid')
+ route = l1.rpc.getroute(l3.info['id'], 10000000, 1)['route']
+
+ # Do a splice
+ funds_result = l2.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
+ chan_id = l2.get_channel_id(l3)
+ result = l2.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
+ result = l2.rpc.splice_update(chan_id, result['psbt'])
+ assert(result['commitments_secured'] is False)
+ result = l2.rpc.splice_update(chan_id, result['psbt'])
+ assert(result['commitments_secured'] is True)
+ result = l2.rpc.signpsbt(result['psbt'])
+ result = l2.rpc.splice_signed(chan_id, result['signed_psbt'])
+
+ wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_AWAITING_SPLICE')
+ bitcoind.generate_block(6, wait_for_mempool=1)
+ wait_for(lambda: only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['state'] == 'CHANNELD_NORMAL')
+
+ # Now l1 tries to send using old scid: should work
+ l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
+ l1.rpc.waitsendpay(inv['payment_hash'])
Why this scored 44/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.