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