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