splice: Test for two channel splice
What changed, and why it matters
This commit only adds a new automated test to the project's test suite. It exercises a feature called 'cross-channel splicing'—combining updates to two payment channels into a single Bitcoin transaction. There is no change to production code, no bug fix, and no security-related content.
No security action needed. Treat as routine test-coverage addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff adds a single Python test, test_two_chan_splice_in, to tests/test_splicing.py. The test sets up a three-node line graph, initiates splices on two channels from the middle node using a shared PSBT, runs splice_init/splice_update/splice_signed RPCs, mines a block, and verifies channels return to normal state and payments still work. No implementation code is modified.
Changed components
tests/test_splicing.pyInspect captured patch +73 / −0
diff --git a/tests/test_splicing.py b/tests/test_splicing.py
index b1a8595f..19a62e90 100644
--- a/tests/test_splicing.py
+++ b/tests/test_splicing.py
@@ -47,6 +47,79 @@ def test_splice(node_factory, bitcoind):
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
+@pytest.mark.openchannel('v1')
+@pytest.mark.openchannel('v2')
+@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
+def test_two_chan_splice_in(node_factory, bitcoind):
+ l1, l2, l3 = node_factory.line_graph(3, fundamount=1000000, wait_for_announce=True, opts={'experimental-splicing': None})
+
+ # l2 will splice funds into the channels with l1 and l3 at the same time
+
+ chan_id1 = l2.get_channel_id(l1)
+ chan_id2 = l2.get_channel_id(l3)
+
+ # add extra sats to pay fee
+ funds_result = l2.rpc.fundpsbt("209000sat", "slow", 166, excess_as_change=True)
+
+ # Intiate splices to both channels
+ result = l2.rpc.splice_init(chan_id1, 100000, funds_result['psbt'])
+ result = l2.rpc.splice_init(chan_id2, 100000, result['psbt']) # start with psbt from first channel
+
+ done1 = False
+ done2 = False
+ sigs1 = False
+ sigs2 = False
+
+ while not done1 or not done2:
+ if not done1:
+ result = l2.rpc.splice_update(chan_id1, result['psbt'])
+ done1 = result['commitments_secured']
+ sigs1 = result['signatures_secured']
+ print("chan 1 " + result['psbt'])
+ if not done2:
+ result = l2.rpc.splice_update(chan_id2, result['psbt'])
+ done2 = result['commitments_secured']
+ sigs2 = result['signatures_secured']
+ print("chan 2 " + result['psbt'])
+
+ # Due to splice signing order, we may or may not have signatures
+ # from all peers, but we must have them from one.
+ print("Sigs1 " + str(sigs1) + ", Sigs2 " + str(sigs2))
+ assert(sigs1 or sigs2)
+
+ # Sign the inputs provided by `fundpsbt`
+ result = l2.rpc.signpsbt(result['psbt'])
+ result['psbt'] = result['signed_psbt']
+
+ if sigs2:
+ # If chan2 gave us sigs, start with chan1
+ result = l2.rpc.splice_signed(chan_id1, result['psbt'])
+ result = l2.rpc.splice_signed(chan_id2, result['psbt'])
+ else:
+ # If chan1 gave us sigs, start with chan2
+ result = l2.rpc.splice_signed(chan_id2, result['psbt'])
+ result = l2.rpc.splice_signed(chan_id1, result['psbt'])
+
+ l3.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
+ l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
+ l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
+
+ wait_for(lambda: len(list(bitcoind.rpc.getrawmempool(True).keys())) == 1)
+ assert result['txid'] in list(bitcoind.rpc.getrawmempool(True).keys())
+
+ bitcoind.generate_block(6, wait_for_mempool=1)
+
+ l3.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
+ l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
+ l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
+
+ inv = l2.rpc.invoice(10**2, '1', 'no_1')
+ l1.rpc.pay(inv['bolt11'])
+
+ inv = l3.rpc.invoice(10**2, '2', 'no_2')
+ l2.rpc.pay(inv['bolt11'])
+
+
@pytest.mark.openchannel('v1')
@pytest.mark.openchannel('v2')
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
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.