@@ -5077,6 +5077,99 @@ fn do_test_splice_rbf_tiebreak(
50775077 }
50785078}
50795079
5080+ #[ test]
5081+ fn test_splice_rbf_tiebreak_feerate_too_high_rejected ( ) {
5082+ // Node 0 (winner) proposes an RBF feerate far above node 1's (loser) max_feerate, and
5083+ // node 1's fair fee at that feerate exceeds its budget. This triggers
5084+ // FeeRateAdjustmentError::TooHigh in the queued contribution path, causing node 1 to
5085+ // reject with WarnAndDisconnect.
5086+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
5087+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
5088+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , None ] ) ;
5089+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
5090+
5091+ let node_id_0 = nodes[ 0 ] . node . get_our_node_id ( ) ;
5092+ let node_id_1 = nodes[ 1 ] . node . get_our_node_id ( ) ;
5093+
5094+ let initial_channel_value_sat = 100_000 ;
5095+ let ( _, _, channel_id, _) =
5096+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , initial_channel_value_sat, 0 ) ;
5097+
5098+ let added_value = Amount :: from_sat ( 50_000 ) ;
5099+ provide_utxo_reserves ( & nodes, 2 , added_value * 2 ) ;
5100+
5101+ // Complete an initial splice-in from node 0.
5102+ let funding_contribution = do_initiate_splice_in ( & nodes[ 0 ] , & nodes[ 1 ] , channel_id, added_value) ;
5103+ let ( _first_splice_tx, _new_funding_script) =
5104+ splice_channel ( & nodes[ 0 ] , & nodes[ 1 ] , channel_id, funding_contribution) ;
5105+
5106+ // Provide more UTXOs for both nodes' RBF attempts.
5107+ provide_utxo_reserves ( & nodes, 2 , added_value * 2 ) ;
5108+
5109+ // Node 0 uses an extremely high feerate (100,000 sat/kwu). Node 1 uses the minimum RBF
5110+ // feerate with a moderate splice-in (50,000 sats) and a low max_feerate (3,000 sat/kwu).
5111+ // The target (100k) far exceeds node 1's max (3k), and the fair fee at 100k exceeds
5112+ // node 1's budget, triggering TooHigh.
5113+ let high_feerate = FeeRate :: from_sat_per_kwu ( 100_000 ) ;
5114+ let min_rbf_feerate_sat_per_kwu = ( FEERATE_FLOOR_SATS_PER_KW as u64 * 25 + 23 ) / 24 ;
5115+ let min_rbf_feerate = FeeRate :: from_sat_per_kwu ( min_rbf_feerate_sat_per_kwu) ;
5116+ let node_1_max_feerate = FeeRate :: from_sat_per_kwu ( 3_000 ) ;
5117+
5118+ let funding_template_0 =
5119+ nodes[ 0 ] . node . rbf_channel ( & channel_id, & node_id_1, high_feerate, FeeRate :: MAX ) . unwrap ( ) ;
5120+ let wallet_0 = WalletSync :: new ( Arc :: clone ( & nodes[ 0 ] . wallet_source ) , nodes[ 0 ] . logger ) ;
5121+ let node_0_funding_contribution =
5122+ funding_template_0. splice_in_sync ( added_value, & wallet_0) . unwrap ( ) ;
5123+ nodes[ 0 ]
5124+ . node
5125+ . funding_contributed ( & channel_id, & node_id_1, node_0_funding_contribution. clone ( ) , None )
5126+ . unwrap ( ) ;
5127+
5128+ let funding_template_1 = nodes[ 1 ]
5129+ . node
5130+ . rbf_channel ( & channel_id, & node_id_0, min_rbf_feerate, node_1_max_feerate)
5131+ . unwrap ( ) ;
5132+ let wallet_1 = WalletSync :: new ( Arc :: clone ( & nodes[ 1 ] . wallet_source ) , nodes[ 1 ] . logger ) ;
5133+ let node_1_funding_contribution =
5134+ funding_template_1. splice_in_sync ( added_value, & wallet_1) . unwrap ( ) ;
5135+ nodes[ 1 ]
5136+ . node
5137+ . funding_contributed ( & channel_id, & node_id_0, node_1_funding_contribution. clone ( ) , None )
5138+ . unwrap ( ) ;
5139+
5140+ // Both sent STFU.
5141+ let stfu_0 = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendStfu , node_id_1) ;
5142+ let stfu_1 = get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendStfu , node_id_0) ;
5143+
5144+ // Tie-break: node 0 wins.
5145+ nodes[ 1 ] . node . handle_stfu ( node_id_0, & stfu_0) ;
5146+ assert ! ( nodes[ 1 ] . node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
5147+ nodes[ 0 ] . node . handle_stfu ( node_id_1, & stfu_1) ;
5148+
5149+ // Node 0 sends tx_init_rbf at 100,000 sat/kwu.
5150+ let tx_init_rbf = get_event_msg ! ( nodes[ 0 ] , MessageSendEvent :: SendTxInitRbf , node_id_1) ;
5151+ assert_eq ! ( tx_init_rbf. feerate_sat_per_1000_weight, high_feerate. to_sat_per_kwu( ) as u32 ) ;
5152+
5153+ // Node 1 handles tx_init_rbf — TooHigh: target (100k) >> max (3k) and fair fee > budget.
5154+ nodes[ 1 ] . node . handle_tx_init_rbf ( node_id_0, & tx_init_rbf) ;
5155+
5156+ let msg_events = nodes[ 1 ] . node . get_and_clear_pending_msg_events ( ) ;
5157+ assert_eq ! ( msg_events. len( ) , 1 , "{msg_events:?}" ) ;
5158+ match & msg_events[ 0 ] {
5159+ MessageSendEvent :: HandleError {
5160+ action : msgs:: ErrorAction :: DisconnectPeerWithWarning { msg } ,
5161+ ..
5162+ } => {
5163+ assert ! (
5164+ msg. data. contains( "Cannot accommodate initiator's feerate" ) ,
5165+ "Unexpected warning: {}" ,
5166+ msg. data
5167+ ) ;
5168+ } ,
5169+ other => panic ! ( "Expected HandleError/DisconnectPeerWithWarning, got {:?}" , other) ,
5170+ }
5171+ }
5172+
50805173#[ test]
50815174fn test_splice_rbf_acceptor_recontributes ( ) {
50825175 // When the counterparty RBFs a splice and we have no pending QuiescentAction,
0 commit comments