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