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