@@ -59,8 +59,8 @@ pub(super) enum FeeRateAdjustmentError {
5959 FeeBufferOverflow ,
6060 /// The re-estimated fee exceeds the available fee buffer regardless of `max_feerate`. The fee
6161 /// buffer is the maximum fee that can be accommodated:
62- /// - **splice-in **: the selected inputs' value minus the contributed amount
63- /// - **splice-out **: the channel balance minus the withdrawal outputs
62+ /// - **input-backed contributions **: the original fee plus any change output value
63+ /// - **input-less contributions **: the channel balance minus the withdrawal outputs
6464 FeeBufferInsufficient { source : & ' static str , available : Amount , required : Amount } ,
6565}
6666
@@ -288,7 +288,7 @@ macro_rules! build_funding_contribution {
288288 let max_feerate: FeeRate = $max_feerate;
289289 let force_coin_selection: bool = $force_coin_selection;
290290
291- let value_removed = validate_funding_contribution_params(
291+ let _value_removed = validate_funding_contribution_params(
292292 value_added,
293293 & outputs,
294294 min_rbf_feerate,
@@ -312,8 +312,6 @@ macro_rules! build_funding_contribution {
312312 . map( |shared_input| shared_input. previous_utxo. value)
313313 . unwrap_or( Amount :: ZERO )
314314 . checked_add( value_added)
315- . ok_or( FundingContributionError :: InvalidSpliceValue ) ?
316- . checked_sub( value_removed)
317315 . ok_or( FundingContributionError :: InvalidSpliceValue ) ?,
318316 script_pubkey: make_funding_redeemscript( & dummy_pubkey, & dummy_pubkey) . to_p2wsh( ) ,
319317 } ;
@@ -469,7 +467,8 @@ impl FundingTemplate {
469467 /// `value_added` and `outputs` are the complete parameters for this contribution, not
470468 /// increments on top of a prior contribution. When replacing a prior contribution via RBF,
471469 /// use [`FundingTemplate::prior_contribution`] to inspect the prior parameters and combine
472- /// them as needed.
470+ /// them as needed. The withdrawal `outputs` are funded by the selected wallet inputs and do
471+ /// not reduce the requested `value_added` to the channel.
473472 pub async fn splice_in_and_out < W : CoinSelectionSource + MaybeSend > (
474473 self , value_added : Amount , outputs : Vec < TxOut > , min_feerate : FeeRate , max_feerate : FeeRate ,
475474 wallet : W ,
@@ -528,9 +527,9 @@ impl FundingTemplate {
528527 /// the fee difference. For splice-out (no wallet inputs), the holder's channel balance
529528 /// covers the higher fees.
530529 /// - If adjustment fails, coin selection is re-run using the prior contribution's
531- /// parameters and the caller's `max_feerate`. For splice-out contributions, this changes
532- /// the fee source: wallet inputs are selected to cover fees instead of deducting them
533- /// from the channel balance.
530+ /// parameters and the caller's `max_feerate`. For prior contributions without inputs,
531+ /// this changes the funding source: wallet inputs are selected to cover the outputs and
532+ /// fees instead of deducting them from the channel balance.
534533 /// - If no prior contribution exists, coin selection is run for a fee-bump-only contribution
535534 /// (`value_added = 0`), covering fees for the common fields and shared input/output via
536535 /// a newly selected input. Check [`FundingTemplate::prior_contribution`] to see if this
@@ -712,8 +711,10 @@ pub struct FundingContribution {
712711 /// excess amount will be sent to a change output.
713712 inputs : Vec < FundingTxInput > ,
714713
715- /// The outputs to include in the funding transaction. The total value of all outputs plus fees
716- /// will be the amount that is removed.
714+ /// The outputs to include in the funding transaction.
715+ ///
716+ /// When no wallet inputs are contributed, these outputs are paid from the channel balance.
717+ /// Otherwise, they are paid by the contributed inputs.
717718 outputs : Vec < TxOut > ,
718719
719720 /// The output where any change will be sent.
@@ -912,82 +913,63 @@ impl FundingContribution {
912913 }
913914 }
914915
916+ let target_fee = estimate_transaction_fee (
917+ & self . inputs ,
918+ & self . outputs ,
919+ self . change_output . as_ref ( ) ,
920+ is_initiator,
921+ self . is_splice ,
922+ target_feerate,
923+ ) ;
924+
915925 if !self . inputs . is_empty ( ) {
916- if let Some ( ref change_output) = self . change_output {
917- let old_change_value = change_output. value ;
918- let dust_limit = change_output. script_pubkey . minimal_non_dust ( ) ;
926+ let fee_buffer = self
927+ . estimated_fee
928+ . checked_add (
929+ self . change_output . as_ref ( ) . map_or ( Amount :: ZERO , |output| output. value ) ,
930+ )
931+ . ok_or ( FeeRateAdjustmentError :: FeeBufferOverflow ) ?;
919932
920- // Target fee including the change output's weight.
921- let target_fee = estimate_transaction_fee (
922- & self . inputs ,
923- & self . outputs ,
924- self . change_output . as_ref ( ) ,
925- is_initiator,
926- self . is_splice ,
927- target_feerate,
928- ) ;
933+ if let Some ( change_output) = self . change_output . as_ref ( ) {
934+ let dust_limit = change_output. script_pubkey . minimal_non_dust ( ) ;
935+ if let Some ( new_change_value) = fee_buffer. checked_sub ( target_fee) {
936+ if new_change_value >= dust_limit {
937+ return Ok ( ( target_fee, Some ( new_change_value) ) ) ;
938+ }
929939
930- let fee_buffer = self
931- . estimated_fee
932- . checked_add ( old_change_value)
933- . ok_or ( FeeRateAdjustmentError :: FeeBufferOverflow ) ?;
934-
935- match fee_buffer. checked_sub ( target_fee) {
936- Some ( new_change_value) if new_change_value >= dust_limit => {
937- Ok ( ( target_fee, Some ( new_change_value) ) )
938- } ,
939- _ => {
940- // Change would be below dust or negative. Try without change.
941- let target_fee_no_change = estimate_transaction_fee (
942- & self . inputs ,
943- & self . outputs ,
944- None ,
945- is_initiator,
946- self . is_splice ,
947- target_feerate,
948- ) ;
949- if target_fee_no_change > fee_buffer {
950- Err ( FeeRateAdjustmentError :: FeeBufferInsufficient {
951- source : "estimated fee + change value" ,
952- available : fee_buffer,
953- required : target_fee_no_change,
954- } )
955- } else {
956- Ok ( ( target_fee_no_change, None ) )
957- }
958- } ,
940+ // Our remaining change was not enough to be a valid output, fallthrough to the
941+ // no remaining change case.
959942 }
960- } else {
961- // No change output.
962- let target_fee = estimate_transaction_fee (
943+
944+ let target_fee_no_change = estimate_transaction_fee (
963945 & self . inputs ,
964946 & self . outputs ,
965947 None ,
966948 is_initiator,
967949 self . is_splice ,
968950 target_feerate,
969951 ) ;
970- if target_fee > self . estimated_fee {
971- return Err ( FeeRateAdjustmentError :: FeeBufferInsufficient {
972- source : "estimated fee" ,
973- available : self . estimated_fee ,
974- required : target_fee,
975- } ) ;
952+ if target_fee_no_change > fee_buffer {
953+ Err ( FeeRateAdjustmentError :: FeeBufferInsufficient {
954+ source : "estimated fee + change value" ,
955+ available : fee_buffer,
956+ required : target_fee_no_change,
957+ } )
958+ } else {
959+ Ok ( ( target_fee_no_change, None ) )
976960 }
961+ } else if let Some ( _surplus) = fee_buffer. checked_sub ( target_fee) {
977962 Ok ( ( target_fee, None ) )
963+ } else {
964+ Err ( FeeRateAdjustmentError :: FeeBufferInsufficient {
965+ source : "estimated fee" ,
966+ available : fee_buffer,
967+ required : target_fee,
968+ } )
978969 }
979970 } else {
980- // No inputs (splice-out): fees paid from channel balance.
981- let target_fee = estimate_transaction_fee (
982- & [ ] ,
983- & self . outputs ,
984- None ,
985- is_initiator,
986- self . is_splice ,
987- target_feerate,
988- ) ;
989-
990- // Check that the channel balance can cover the withdrawal outputs plus fees.
971+ // Without coin-selected inputs, both the withdrawals and the fee come from the channel
972+ // balance.
991973 let value_removed: Amount = self . outputs . iter ( ) . map ( |o| o. value ) . sum ( ) ;
992974 let total_cost = target_fee
993975 . checked_add ( value_removed)
@@ -999,7 +981,6 @@ impl FundingContribution {
999981 required : target_fee,
1000982 } ) ;
1001983 }
1002- // Surplus goes back to the channel balance.
1003984 Ok ( ( target_fee, None ) )
1004985 }
1005986 }
0 commit comments