@@ -2868,3 +2868,69 @@ def test_sendpsbt_crash(bitcoind, node_factory):
28682868 bitcoind .generate_block (1 , wait_for_mempool = 1 )
28692869
28702870 assert l1 .daemon .is_in_log ('Signed and sent psbt for waiting channel' )
2871+
2872+
2873+ @pytest .mark .parametrize ("stay_withheld" , [True , False ])
2874+ @pytest .mark .parametrize ("mutual_close" , [True , False ])
2875+ def test_zeroconf_withhold (node_factory , bitcoind , stay_withheld , mutual_close ):
2876+ plugin_path = Path (__file__ ).parent / "plugins" / "zeroconf-selective.py"
2877+
2878+ l1 , l2 = node_factory .get_nodes (2 , opts = [{'may_reconnect' : True },
2879+ {
2880+ 'plugin' : str (plugin_path ),
2881+ 'zeroconf_allow' : '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518' ,
2882+ 'may_reconnect' : True
2883+ }])
2884+ # Try to open a mindepth=0 channel
2885+ l1 .fundwallet (10 ** 7 )
2886+
2887+ l1 .connect (l2 )
2888+ amount = 1000000
2889+ funding_addr = l1 .rpc .fundchannel_start (l2 .info ['id' ], f"{ amount } sat" , mindepth = 0 )['funding_address' ]
2890+
2891+ # Create the funding transaction
2892+ psbt = l1 .rpc .fundpsbt (amount , "1000perkw" , 1000 , excess_as_change = True )['psbt' ]
2893+ psbt = l1 .rpc .addpsbtoutput (1000000 , psbt , destination = funding_addr )['psbt' ]
2894+
2895+ # Be sure fundchannel_complete is successful
2896+ assert l1 .rpc .fundchannel_complete (l2 .info ['id' ], psbt , withhold = True )['commitments_secured' ]
2897+
2898+ # It's withheld.
2899+ assert only_one (l1 .rpc .listpeerchannels ()['channels' ])['funding' ]['withheld' ] is True
2900+
2901+ # We can use the channel.
2902+ l1 .rpc .xpay (l2 .rpc .invoice (100 , "test_zeroconf_withhold" , "test_zeroconf_withhold" )['bolt11' ])
2903+
2904+ # But mempool is empty! No funding tx!
2905+ assert bitcoind .rpc .getrawmempool () == []
2906+
2907+ # Restarting doesn't make it transmit!
2908+ l1 .restart ()
2909+ assert bitcoind .rpc .getrawmempool () == []
2910+
2911+ if mutual_close :
2912+ l1 .connect (l2 )
2913+
2914+ if not stay_withheld :
2915+ # sendpsbt marks it as no longer withheld.
2916+ l1 .rpc .sendpsbt (l1 .rpc .signpsbt (psbt )['signed_psbt' ])
2917+ assert only_one (l1 .rpc .listpeerchannels ()['channels' ])['funding' ]['withheld' ] is False
2918+ assert l1 .daemon .is_in_log (r'Funding PSBT sent, and stored for rexmit \(was withheld\)' )
2919+ wait_for (lambda : len (bitcoind .rpc .getrawmempool ()) == 1 )
2920+
2921+ ret = l1 .rpc .close (l2 .info ['id' ], unilateraltimeout = 10 )
2922+ if stay_withheld :
2923+ assert ret ['txs' ] == []
2924+ assert ret ['txids' ] == []
2925+ assert bitcoind .rpc .getrawmempool () == []
2926+ else :
2927+ assert len (ret ['txs' ]) == 1
2928+ assert len (ret ['txids' ]) == 1
2929+ wait_for (lambda : len (bitcoind .rpc .getrawmempool ()) == 2 )
2930+
2931+ # If withheld, it's moved to closed immediately.
2932+ if stay_withheld :
2933+ assert l1 .rpc .listpeerchannels ()['channels' ] == []
2934+ assert only_one (l1 .rpc .listclosedchannels ()['closedchannels' ])['funding_withheld' ] is True
2935+ else :
2936+ wait_for (lambda : only_one (l1 .rpc .listpeerchannels ()['channels' ])['state' ] == 'CLOSINGD_COMPLETE' )
0 commit comments