pytest: test withhold parameter to fundchannel_complete.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-11-18 22:24:33 +10:30
parent ead680eb7b
commit 2520bd5b90
2 changed files with 78 additions and 1 deletions

View File

@@ -2867,3 +2867,79 @@ def test_sendpsbt_crash(bitcoind, node_factory):
bitcoind.generate_block(1, wait_for_mempool=1)
assert l1.daemon.is_in_log('Signed and sent psbt for waiting channel')
@pytest.mark.parametrize("stay_withheld", [True, False])
@pytest.mark.parametrize("mutual_close", [True, False])
def test_zeroconf_withhold(node_factory, bitcoind, stay_withheld, mutual_close):
plugin_path = Path(__file__).parent / "plugins" / "zeroconf-selective.py"
l1, l2 = node_factory.get_nodes(2, opts=[{'may_reconnect': True,
'dev-no-reconnect': None,
},
{'plugin': str(plugin_path),
'zeroconf_allow': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518',
'may_reconnect': True,
'dev-no-reconnect': None,
}])
# Try to open a mindepth=0 channel
l1.fundwallet(10**7)
l1.connect(l2)
amount = 1000000
funding_addr = l1.rpc.fundchannel_start(l2.info['id'], f"{amount}sat", mindepth=0)['funding_address']
# Create the funding transaction
psbt = l1.rpc.fundpsbt(amount, "1000perkw", 1000, excess_as_change=True)['psbt']
psbt = l1.rpc.addpsbtoutput(1000000, psbt, destination=funding_addr)['psbt']
# Be sure fundchannel_complete is successful
assert l1.rpc.fundchannel_complete(l2.info['id'], psbt, withhold=True)['commitments_secured']
# It's withheld.
assert only_one(l1.rpc.listpeerchannels()['channels'])['funding']['withheld'] is True
# We can use the channel (once they send an update)
wait_for(lambda: 'remote' in only_one(l1.rpc.listpeerchannels()['channels'])['updates'])
l1.rpc.xpay(l2.rpc.invoice(100, "test_zeroconf_withhold", "test_zeroconf_withhold")['bolt11'])
# But mempool is empty! No funding tx!
assert bitcoind.rpc.getrawmempool() == []
# Restarting doesn't make it transmit!
l1.restart()
assert bitcoind.rpc.getrawmempool() == []
if mutual_close:
l1.connect(l2)
if not stay_withheld:
# sendpsbt marks it as no longer withheld.
l1.rpc.sendpsbt(l1.rpc.signpsbt(psbt)['signed_psbt'])
assert only_one(l1.rpc.listpeerchannels()['channels'])['funding']['withheld'] is False
assert l1.daemon.is_in_log(r'Funding PSBT sent, and stored for rexmit \(was withheld\)')
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 1)
if mutual_close:
ret = l1.rpc.close(l2.info['id'])
else:
ret = l1.rpc.close(l2.info['id'], unilateraltimeout=1)
if stay_withheld:
assert ret['txs'] == []
assert ret['txids'] == []
assert bitcoind.rpc.getrawmempool() == []
else:
assert len(ret['txs']) == 1
assert len(ret['txids']) == 1
wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 2)
# If withheld, it's moved to closed immediately.
if stay_withheld:
assert l1.rpc.listpeerchannels()['channels'] == []
assert only_one(l1.rpc.listclosedchannels()['closedchannels'])['funding_withheld'] is True
else:
if mutual_close:
wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'CLOSINGD_COMPLETE')
else:
wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['state'] == 'AWAITING_UNILATERAL')