@@ -1666,14 +1666,19 @@ impl InteractiveTxConstructor {
16661666/// Determine whether a change output should be added or not, and if so, of what size,
16671667/// considering our given inputs, outputs, and intended contribution.
16681668/// Computes and takes into account fees.
1669- /// Return value is the value computed for the change output (in satoshis),
1670- /// or None if a change is not needed/possible.
1669+ /// Three outcomes are possible:
1670+ /// - Inputs are sufficient for intended contribution, fees, and a larger-than-dust change:
1671+ /// Ok(Some(change_amount))
1672+ /// - Inputs are sufficient for intended contribution and fees, but not for a change:
1673+ /// Ok(None)
1674+ /// - Insputs are not sufficent to cover contribution and fees:
1675+ /// Err(AbortReason::InsufficientFees)
16711676#[ allow( dead_code) ] // TODO(dual_funding): Remove once begin_interactive_funding_tx_construction() is used
16721677pub ( super ) fn calculate_change_output_value (
16731678 is_initiator : bool , our_contribution : u64 , funding_inputs_prev_outputs : & Vec < & TxOut > ,
16741679 funding_outputs : & Vec < OutputOwned > , funding_feerate_sat_per_1000_weight : u32 ,
16751680 holder_dust_limit_satoshis : u64 ,
1676- ) -> Option < u64 > {
1681+ ) -> Result < Option < u64 > , AbortReason > {
16771682 let our_funding_inputs_weight =
16781683 funding_inputs_prev_outputs. iter ( ) . fold ( 0u64 , |weight, prev_output| {
16791684 weight. saturating_add ( estimate_input_weight ( prev_output) . to_wu ( ) )
@@ -1696,13 +1701,19 @@ pub(super) fn calculate_change_output_value(
16961701 funding_inputs_prev_outputs. iter ( ) . map ( |out| out. value . to_sat ( ) ) . sum ( ) ;
16971702
16981703 // Note: in case of additional outputs, they will have to be subtracted here
1699- let remaining_value =
1700- total_input_satoshis. saturating_sub ( our_contribution) . saturating_sub ( fees_sats) ;
17011704
1702- if remaining_value <= holder_dust_limit_satoshis {
1703- None
1705+ let min_contribution_and_fees = our_contribution. saturating_add ( fees_sats) ;
1706+ let min_contribution_and_fees_and_dust = min_contribution_and_fees. saturating_add ( holder_dust_limit_satoshis) ;
1707+ if total_input_satoshis < min_contribution_and_fees {
1708+ // Not enough to cover contribution plus fees
1709+ Err ( AbortReason :: InsufficientFees )
1710+ } else if total_input_satoshis < min_contribution_and_fees_and_dust {
1711+ // Enough to cover contribution plus fees, but leftover is below dust limit
1712+ Ok ( None )
17041713 } else {
1705- Some ( remaining_value)
1714+ // Enough to have over-dust change
1715+ let remaining_value = total_input_satoshis. saturating_sub ( min_contribution_and_fees) ;
1716+ Ok ( Some ( remaining_value) )
17061717 }
17071718}
17081719
@@ -2666,7 +2677,7 @@ mod tests {
26662677 funding_feerate_sat_per_1000_weight,
26672678 300 ,
26682679 ) ;
2669- assert_eq ! ( res. unwrap( ) , gross_change - fees - common_fees) ;
2680+ assert_eq ! ( res. unwrap( ) . unwrap ( ) , gross_change - fees - common_fees) ;
26702681 }
26712682 {
26722683 // There is leftover for change, without common fees
@@ -2678,7 +2689,7 @@ mod tests {
26782689 funding_feerate_sat_per_1000_weight,
26792690 300 ,
26802691 ) ;
2681- assert_eq ! ( res. unwrap( ) , gross_change - fees) ;
2692+ assert_eq ! ( res. unwrap( ) . unwrap ( ) , gross_change - fees) ;
26822693 }
26832694 {
26842695 // Larger fee, smaller change
@@ -2690,7 +2701,7 @@ mod tests {
26902701 9000 ,
26912702 300 ,
26922703 ) ;
2693- assert_eq ! ( res. unwrap( ) , 14384 ) ;
2704+ assert_eq ! ( res. unwrap( ) . unwrap ( ) , 14384 ) ;
26942705 }
26952706 {
26962707 // Insufficient inputs, no leftover
@@ -2702,7 +2713,7 @@ mod tests {
27022713 funding_feerate_sat_per_1000_weight,
27032714 300 ,
27042715 ) ;
2705- assert ! ( res. is_none ( ) ) ;
2716+ assert_eq ! ( res. err ( ) . unwrap ( ) , AbortReason :: InsufficientFees ) ;
27062717 }
27072718 {
27082719 // Very small leftover
@@ -2714,7 +2725,7 @@ mod tests {
27142725 funding_feerate_sat_per_1000_weight,
27152726 300 ,
27162727 ) ;
2717- assert ! ( res. is_none( ) ) ;
2728+ assert ! ( res. unwrap ( ) . is_none( ) ) ;
27182729 }
27192730 {
27202731 // Small leftover, but not dust
@@ -2726,7 +2737,7 @@ mod tests {
27262737 funding_feerate_sat_per_1000_weight,
27272738 100 ,
27282739 ) ;
2729- assert_eq ! ( res. unwrap( ) , 154 ) ;
2740+ assert_eq ! ( res. unwrap( ) . unwrap ( ) , 154 ) ;
27302741 }
27312742 }
27322743}
0 commit comments