Skip to content

Commit 8208b07

Browse files
committed
splice: add tests for funding_tx_index tracking
Add regression tests that each funding transaction carries a funding_tx_index (0 for the original funding, +1 per splice), that it is persisted and reloaded across restart, and that a pending splice inflight carries its index. Changelog-None
1 parent c4e8c4f commit 8208b07

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

tests/test_splicing_disconnect.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,62 @@ def test_splice_disconnect_sig(node_factory, bitcoind):
6666
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
6767

6868

69+
@pytest.mark.openchannel('v1')
70+
@pytest.mark.openchannel('v2')
71+
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
72+
@pytest.mark.xfail(strict=True, reason="funding_tx_index tracking not yet implemented")
73+
def test_splice_reconnect_after_lock_no_channel_ready(node_factory, bitcoind):
74+
# Once a splice locks, channel funding txid is updated to the splice txid.
75+
# On reconnect we must still recognise the peer's `my_current_funding_locked`
76+
# as a splice (funding_tx_index > 0) and NOT retransmit `channel_ready`.
77+
# channeld used to compare the txid against the (already-updated) channel
78+
# funding txid, which is wrong once a splice completes. This drives a full
79+
# splice + restart + reestablish to exercise the funding_tx_index path end
80+
# to end (DB columns, inflight wire, and the reestablish detection).
81+
l1 = node_factory.get_node(may_reconnect=True)
82+
l2 = node_factory.get_node(may_reconnect=True)
83+
l1.openchannel(l2, 1000000)
84+
85+
chan_id = l1.get_channel_id(l2)
86+
87+
# add extra sats to pay fee
88+
funds_result = l1.rpc.fundpsbt("107527sat", 0, 0, excess_as_change=True)
89+
90+
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
91+
result = l1.rpc.splice_update(chan_id, result['psbt'])
92+
assert(result['commitments_secured'] is False)
93+
result = l1.rpc.splice_update(chan_id, result['psbt'])
94+
assert(result['commitments_secured'] is True)
95+
result = l1.rpc.signpsbt(result['psbt'])
96+
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
97+
98+
# Confirm and lock the splice on both sides.
99+
bitcoind.generate_block(6, wait_for_mempool=1)
100+
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
101+
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
102+
103+
# Restart l1 so it reloads channel + inflight state from the DB (exercising
104+
# the new funding_tx_index columns) and reconnects/reestablishes.
105+
l1.restart()
106+
107+
l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
108+
l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
109+
l1.daemon.wait_for_log(r'billboard: Channel ready for use.')
110+
l2.daemon.wait_for_log(r'billboard: Channel ready for use.')
111+
112+
# The locked splice must have been persisted with funding_tx_index == 1...
113+
rows = l1.db_query("SELECT funding_tx_index FROM channels;")
114+
assert max(r['funding_tx_index'] for r in rows) == 1
115+
116+
# ...and recognised on reestablish, so channel_ready is NOT retransmitted.
117+
assert not l1.daemon.is_in_log(r'Retransmitting channel_ready')
118+
assert not l2.daemon.is_in_log(r'Retransmitting channel_ready')
119+
120+
# Channel still works after the splice + reconnect.
121+
inv = l2.rpc.invoice(10**2, 'lbl', 'desc')
122+
l1.rpc.xpay(inv['bolt11'])
123+
124+
69125
@pytest.mark.openchannel('v1')
70126
@pytest.mark.openchannel('v2')
71127
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
@@ -114,3 +170,81 @@ def test_splice_disconnect_commit(node_factory, bitcoind, executor):
114170
# Check that the splice doesn't generate a unilateral close transaction
115171
time.sleep(5)
116172
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
173+
174+
175+
@pytest.mark.openchannel('v1')
176+
@pytest.mark.openchannel('v2')
177+
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
178+
@pytest.mark.xfail(strict=True, reason="funding_tx_index tracking not yet implemented")
179+
def test_splice_funding_tx_index_increments(node_factory, bitcoind):
180+
# funding_tx_index is 0 for the original funding and increments by 1 per
181+
# splice. Two sequential splices must reach index 2 on the persisted
182+
# channel (exercises the parent + 1 assignment).
183+
l1 = node_factory.get_node(may_reconnect=True)
184+
l2 = node_factory.get_node(may_reconnect=True)
185+
l1.openchannel(l2, 1000000)
186+
chan_id = l1.get_channel_id(l2)
187+
188+
def do_splice(amount):
189+
funds = l1.rpc.fundpsbt('{}sat'.format(amount + 7527), 0, 0,
190+
excess_as_change=True)
191+
result = l1.rpc.splice_init(chan_id, amount, funds['psbt'])
192+
result = l1.rpc.splice_update(chan_id, result['psbt'])
193+
while result['commitments_secured'] is False:
194+
result = l1.rpc.splice_update(chan_id, result['psbt'])
195+
result = l1.rpc.signpsbt(result['psbt'])
196+
l1.rpc.splice_signed(chan_id, result['signed_psbt'])
197+
bitcoind.generate_block(6, wait_for_mempool=1)
198+
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
199+
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
200+
201+
def channel_funding_tx_index():
202+
rows = l1.db_query("SELECT funding_tx_index FROM channels;")
203+
return max(r['funding_tx_index'] for r in rows)
204+
205+
# First splice: 0 -> 1
206+
do_splice(100000)
207+
assert channel_funding_tx_index() == 1
208+
209+
# Second splice: 1 -> 2
210+
do_splice(50000)
211+
assert channel_funding_tx_index() == 2
212+
213+
214+
@pytest.mark.openchannel('v1')
215+
@pytest.mark.openchannel('v2')
216+
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
217+
@pytest.mark.xfail(strict=True, reason="funding_tx_index tracking not yet implemented")
218+
def test_splice_inflight_funding_tx_index(node_factory, bitcoind):
219+
# A pending (not-yet-locked) splice inflight carries funding_tx_index == 1
220+
# (the open was index 0), and the value must survive a restart so the
221+
# reestablish detection still has it.
222+
l1 = node_factory.get_node(may_reconnect=True)
223+
l2 = node_factory.get_node(may_reconnect=True)
224+
l1.openchannel(l2, 1000000)
225+
chan_id = l1.get_channel_id(l2)
226+
227+
funds = l1.rpc.fundpsbt("107527sat", 0, 0, excess_as_change=True)
228+
result = l1.rpc.splice_init(chan_id, 100000, funds['psbt'])
229+
result = l1.rpc.splice_update(chan_id, result['psbt'])
230+
while result['commitments_secured'] is False:
231+
result = l1.rpc.splice_update(chan_id, result['psbt'])
232+
result = l1.rpc.signpsbt(result['psbt'])
233+
l1.rpc.splice_signed(chan_id, result['signed_psbt'])
234+
235+
# The pending splice inflight is index 1.
236+
inflights = l1.db_query("SELECT funding_tx_index FROM"
237+
" channel_funding_inflights;")
238+
assert [r['funding_tx_index'] for r in inflights] == [1]
239+
240+
# Restart l1: the inflight (and its index) must reload from the DB.
241+
l1.restart()
242+
l1.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_REESTABLISH')
243+
inflights = l1.db_query("SELECT funding_tx_index FROM"
244+
" channel_funding_inflights;")
245+
assert [r['funding_tx_index'] for r in inflights] == [1]
246+
247+
# And the splice still completes after the restart.
248+
bitcoind.generate_block(6, wait_for_mempool=1)
249+
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
250+
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')

0 commit comments

Comments
 (0)