@@ -260,6 +260,27 @@ enum UpdateStatus {
260260 DisabledStaged ,
261261}
262262
263+ /// An enum indicating whether the local or remote side offered a given HTLC.
264+ enum HTLCInitiator {
265+ LocalOffered ,
266+ RemoteOffered ,
267+ }
268+
269+ /// Used when calculating whether we or the remote can afford an additional HTLC.
270+ struct HTLCCandidate {
271+ amount_msat : u64 ,
272+ origin : HTLCInitiator ,
273+ }
274+
275+ impl HTLCCandidate {
276+ fn new ( amount_msat : u64 , origin : HTLCInitiator ) -> Self {
277+ Self {
278+ amount_msat,
279+ origin,
280+ }
281+ }
282+ }
283+
263284// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
264285// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
265286// calling channel_id() before we're set up or things like get_outbound_funding_signed on an
@@ -1718,18 +1739,25 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17181739 // number of pending HTLCs that are on track to be in our next commitment tx, plus an additional
17191740 // HTLC if `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs
17201741 // are excluded.
1721- fn next_local_commit_tx_fee_msat ( & self , new_htlc_amount : u64 , locally_offered : bool , fee_spike_buffer_htlc : Option < ( ) > ) -> u64 {
1742+ fn next_local_commit_tx_fee_msat ( & self , htlc : HTLCCandidate , fee_spike_buffer_htlc : Option < ( ) > ) -> u64 {
17221743 assert ! ( self . is_outbound( ) ) ;
17231744
17241745 let real_dust_limit_success_sat = ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
17251746 let real_dust_limit_timeout_sat = ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
17261747
17271748 let mut addl_htlcs = 0 ;
17281749 if fee_spike_buffer_htlc. is_some ( ) { addl_htlcs += 1 ; }
1729- if locally_offered && new_htlc_amount / 1000 >= real_dust_limit_timeout_sat {
1730- addl_htlcs += 1 ;
1731- } else if !locally_offered && new_htlc_amount / 1000 >= real_dust_limit_success_sat {
1732- addl_htlcs += 1 ;
1750+ match htlc. origin {
1751+ HTLCInitiator :: LocalOffered => {
1752+ if htlc. amount_msat / 1000 >= real_dust_limit_timeout_sat {
1753+ addl_htlcs += 1 ;
1754+ }
1755+ } ,
1756+ HTLCInitiator :: RemoteOffered => {
1757+ if htlc. amount_msat / 1000 >= real_dust_limit_success_sat {
1758+ addl_htlcs += 1 ;
1759+ }
1760+ }
17331761 }
17341762
17351763 let mut included_htlcs = 0 ;
@@ -1783,11 +1811,13 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
17831811 let commitment_tx_info = CommitmentTxInfoCached {
17841812 fee,
17851813 total_pending_htlcs,
1786- next_holder_htlc_id : if locally_offered { self . next_holder_htlc_id + 1 } else {
1787- self . next_holder_htlc_id
1814+ next_holder_htlc_id : match htlc. origin {
1815+ HTLCInitiator :: LocalOffered => self . next_holder_htlc_id + 1 ,
1816+ HTLCInitiator :: RemoteOffered => self . next_holder_htlc_id ,
17881817 } ,
1789- next_counterparty_htlc_id : if locally_offered { self . next_counterparty_htlc_id } else {
1790- self . next_counterparty_htlc_id + 1
1818+ next_counterparty_htlc_id : match htlc. origin {
1819+ HTLCInitiator :: LocalOffered => self . next_counterparty_htlc_id ,
1820+ HTLCInitiator :: RemoteOffered => self . next_counterparty_htlc_id + 1 ,
17911821 } ,
17921822 feerate : self . feerate_per_kw ,
17931823 } ;
@@ -1800,18 +1830,25 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18001830 // pending HTLCs that are on track to be in their next commitment tx, plus an additional HTLC if
18011831 // `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs are
18021832 // excluded.
1803- fn next_remote_commit_tx_fee_msat ( & self , new_htlc_amount : u64 , locally_offered : bool , fee_spike_buffer_htlc : Option < ( ) > ) -> u64 {
1833+ fn next_remote_commit_tx_fee_msat ( & self , htlc : HTLCCandidate , fee_spike_buffer_htlc : Option < ( ) > ) -> u64 {
18041834 assert ! ( !self . is_outbound( ) ) ;
18051835
18061836 let real_dust_limit_success_sat = ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
18071837 let real_dust_limit_timeout_sat = ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
18081838
18091839 let mut addl_htlcs = 0 ;
18101840 if fee_spike_buffer_htlc. is_some ( ) { addl_htlcs += 1 ; }
1811- if locally_offered && new_htlc_amount / 1000 >= real_dust_limit_success_sat {
1812- addl_htlcs += 1 ;
1813- } else if !locally_offered && new_htlc_amount / 1000 >= real_dust_limit_timeout_sat {
1814- addl_htlcs += 1 ;
1841+ match htlc. origin {
1842+ HTLCInitiator :: LocalOffered => {
1843+ if htlc. amount_msat / 1000 >= real_dust_limit_success_sat {
1844+ addl_htlcs += 1 ;
1845+ }
1846+ } ,
1847+ HTLCInitiator :: RemoteOffered => {
1848+ if htlc. amount_msat / 1000 >= real_dust_limit_timeout_sat {
1849+ addl_htlcs += 1 ;
1850+ }
1851+ }
18151852 }
18161853
18171854 // When calculating the set of HTLCs which will be included in their next commitment_signed, all
@@ -1851,11 +1888,13 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
18511888 let commitment_tx_info = CommitmentTxInfoCached {
18521889 fee,
18531890 total_pending_htlcs,
1854- next_holder_htlc_id : if locally_offered { self . next_holder_htlc_id + 1 } else {
1855- self . next_holder_htlc_id
1891+ next_holder_htlc_id : match htlc. origin {
1892+ HTLCInitiator :: LocalOffered => self . next_holder_htlc_id + 1 ,
1893+ HTLCInitiator :: RemoteOffered => self . next_holder_htlc_id ,
18561894 } ,
1857- next_counterparty_htlc_id : if locally_offered { self . next_counterparty_htlc_id } else {
1858- self . next_counterparty_htlc_id + 1
1895+ next_counterparty_htlc_id : match htlc. origin {
1896+ HTLCInitiator :: LocalOffered => self . next_counterparty_htlc_id ,
1897+ HTLCInitiator :: RemoteOffered => self . next_counterparty_htlc_id + 1 ,
18591898 } ,
18601899 feerate : self . feerate_per_kw ,
18611900 } ;
@@ -1929,7 +1968,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
19291968 // Check that the remote can afford to pay for this HTLC on-chain at the current
19301969 // feerate_per_kw, while maintaining their channel reserve (as required by the spec).
19311970 let remote_commit_tx_fee_msat = if self . is_outbound ( ) { 0 } else {
1932- self . next_remote_commit_tx_fee_msat ( msg. amount_msat , false , None ) // Don't include the extra fee spike buffer HTLC in calculations
1971+ let htlc_candidate = HTLCCandidate :: new ( msg. amount_msat , HTLCInitiator :: RemoteOffered ) ;
1972+ self . next_remote_commit_tx_fee_msat ( htlc_candidate, None ) // Don't include the extra fee spike buffer HTLC in calculations
19331973 } ;
19341974 if pending_remote_value_msat - msg. amount_msat < remote_commit_tx_fee_msat {
19351975 return Err ( ChannelError :: Close ( "Remote HTLC add would not leave enough to pay for fees" . to_owned ( ) ) ) ;
@@ -1950,7 +1990,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
19501990 // the extra htlc when calculating the next remote commitment transaction fee as we should
19511991 // still be able to afford adding this HTLC plus one more future HTLC, regardless of being
19521992 // sensitive to fee spikes.
1953- let remote_fee_cost_incl_stuck_buffer_msat = 2 * self . next_remote_commit_tx_fee_msat ( msg. amount_msat , false , Some ( ( ) ) ) ;
1993+ let htlc_candidate = HTLCCandidate :: new ( msg. amount_msat , HTLCInitiator :: RemoteOffered ) ;
1994+ let remote_fee_cost_incl_stuck_buffer_msat = 2 * self . next_remote_commit_tx_fee_msat ( htlc_candidate, Some ( ( ) ) ) ;
19541995 if pending_remote_value_msat - msg. amount_msat - chan_reserve_msat < remote_fee_cost_incl_stuck_buffer_msat {
19551996 // Note that if the pending_forward_status is not updated here, then it's because we're already failing
19561997 // the HTLC, i.e. its status is already set to failing.
@@ -1959,7 +2000,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
19592000 }
19602001 } else {
19612002 // Check that they won't violate our local required channel reserve by adding this HTLC.
1962- let local_commit_tx_fee_msat = self . next_local_commit_tx_fee_msat ( msg. amount_msat , false , None ) ;
2003+ let htlc_candidate = HTLCCandidate :: new ( msg. amount_msat , HTLCInitiator :: RemoteOffered ) ;
2004+ let local_commit_tx_fee_msat = self . next_local_commit_tx_fee_msat ( htlc_candidate, None ) ;
19632005 if self . value_to_self_msat < self . counterparty_selected_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat {
19642006 return Err ( ChannelError :: Close ( "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value" . to_owned ( ) ) ) ;
19652007 }
@@ -3863,7 +3905,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
38633905 // Check that we won't violate the remote channel reserve by adding this HTLC.
38643906 let counterparty_balance_msat = self . channel_value_satoshis * 1000 - self . value_to_self_msat ;
38653907 let holder_selected_chan_reserve_msat = Channel :: < ChanSigner > :: get_holder_selected_channel_reserve_satoshis ( self . channel_value_satoshis ) ;
3866- let counterparty_commit_tx_fee_msat = self . next_remote_commit_tx_fee_msat ( amount_msat, true , None ) ;
3908+ let htlc_candidate = HTLCCandidate :: new ( amount_msat, HTLCInitiator :: LocalOffered ) ;
3909+ let counterparty_commit_tx_fee_msat = self . next_remote_commit_tx_fee_msat ( htlc_candidate, None ) ;
38673910 if counterparty_balance_msat < holder_selected_chan_reserve_msat + counterparty_commit_tx_fee_msat {
38683911 return Err ( ChannelError :: Ignore ( "Cannot send value that would put counterparty balance under holder-announced channel reserve value" . to_owned ( ) ) ) ;
38693912 }
@@ -3876,7 +3919,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
38763919
38773920 // `2 *` and extra HTLC are for the fee spike buffer.
38783921 let commit_tx_fee_msat = if self . is_outbound ( ) {
3879- 2 * self . next_local_commit_tx_fee_msat ( amount_msat, true , Some ( ( ) ) )
3922+ let htlc_candidate = HTLCCandidate :: new ( amount_msat, HTLCInitiator :: LocalOffered ) ;
3923+ 2 * self . next_local_commit_tx_fee_msat ( htlc_candidate, Some ( ( ) ) )
38803924 } else { 0 } ;
38813925 if pending_value_to_self_msat - amount_msat < commit_tx_fee_msat {
38823926 return Err ( ChannelError :: Ignore ( format ! ( "Cannot send value that would not leave enough to pay for fees. Pending value to self: {}. local_commit_tx_fee {}" , pending_value_to_self_msat, commit_tx_fee_msat) ) ) ;
@@ -4648,7 +4692,7 @@ mod tests {
46484692 use bitcoin:: hashes:: hex:: FromHex ;
46494693 use hex;
46504694 use ln:: channelmanager:: { HTLCSource , PaymentPreimage , PaymentHash } ;
4651- use ln:: channel:: { Channel , ChannelKeys , InboundHTLCOutput , OutboundHTLCOutput , InboundHTLCState , OutboundHTLCState , HTLCOutputInCommitment , TxCreationKeys } ;
4695+ use ln:: channel:: { Channel , ChannelKeys , InboundHTLCOutput , OutboundHTLCOutput , InboundHTLCState , OutboundHTLCState , HTLCOutputInCommitment , HTLCCandidate , HTLCInitiator , TxCreationKeys } ;
46524696 use ln:: channel:: MAX_FUNDING_SATOSHIS ;
46534697 use ln:: features:: InitFeatures ;
46544698 use ln:: msgs:: { OptionalField , DataLossProtect , DecodeError } ;
@@ -4791,15 +4835,17 @@ mod tests {
47914835
47924836 // Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
47934837 // the dust limit check.
4794- let local_commit_tx_fee = node_a_chan. next_local_commit_tx_fee_msat ( htlc_amount_msat, true , None ) ;
4838+ let htlc_candidate = HTLCCandidate :: new ( htlc_amount_msat, HTLCInitiator :: LocalOffered ) ;
4839+ let local_commit_tx_fee = node_a_chan. next_local_commit_tx_fee_msat ( htlc_candidate, None ) ;
47954840 let local_commit_fee_0_htlcs = node_a_chan. commit_tx_fee_msat ( 0 ) ;
47964841 assert_eq ! ( local_commit_tx_fee, local_commit_fee_0_htlcs) ;
47974842
47984843 // Finally, make sure that when Node A calculates the remote's commitment transaction fees, all
47994844 // of the HTLCs are seen to be above the dust limit.
48004845 node_a_chan. channel_transaction_parameters . is_outbound_from_holder = false ;
48014846 let remote_commit_fee_3_htlcs = node_a_chan. commit_tx_fee_msat ( 3 ) ;
4802- let remote_commit_tx_fee = node_a_chan. next_remote_commit_tx_fee_msat ( htlc_amount_msat, true , None ) ;
4847+ let htlc_candidate = HTLCCandidate :: new ( htlc_amount_msat, HTLCInitiator :: LocalOffered ) ;
4848+ let remote_commit_tx_fee = node_a_chan. next_remote_commit_tx_fee_msat ( htlc_candidate, None ) ;
48034849 assert_eq ! ( remote_commit_tx_fee, remote_commit_fee_3_htlcs) ;
48044850 }
48054851
@@ -4825,24 +4871,28 @@ mod tests {
48254871 // If HTLC_SUCCESS_TX_WEIGHT and HTLC_TIMEOUT_TX_WEIGHT were swapped: then this HTLC would be
48264872 // counted as dust when it shouldn't be.
48274873 let htlc_amt_above_timeout = ( ( 253 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + chan. holder_dust_limit_satoshis + 1 ) * 1000 ;
4828- let commitment_tx_fee = chan. next_local_commit_tx_fee_msat ( htlc_amt_above_timeout, true , None ) ;
4874+ let htlc_candidate = HTLCCandidate :: new ( htlc_amt_above_timeout, HTLCInitiator :: LocalOffered ) ;
4875+ let commitment_tx_fee = chan. next_local_commit_tx_fee_msat ( htlc_candidate, None ) ;
48294876 assert_eq ! ( commitment_tx_fee, commitment_tx_fee_1_htlc) ;
48304877
48314878 // If swapped: this HTLC would be counted as non-dust when it shouldn't be.
48324879 let dust_htlc_amt_below_success = ( ( 253 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + chan. holder_dust_limit_satoshis - 1 ) * 1000 ;
4833- let commitment_tx_fee = chan. next_local_commit_tx_fee_msat ( dust_htlc_amt_below_success, false , None ) ;
4880+ let htlc_candidate = HTLCCandidate :: new ( dust_htlc_amt_below_success, HTLCInitiator :: RemoteOffered ) ;
4881+ let commitment_tx_fee = chan. next_local_commit_tx_fee_msat ( htlc_candidate, None ) ;
48344882 assert_eq ! ( commitment_tx_fee, commitment_tx_fee_0_htlcs) ;
48354883
48364884 chan. channel_transaction_parameters . is_outbound_from_holder = false ;
48374885
48384886 // If swapped: this HTLC would be counted as non-dust when it shouldn't be.
48394887 let dust_htlc_amt_above_timeout = ( ( 253 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + chan. counterparty_dust_limit_satoshis + 1 ) * 1000 ;
4840- let commitment_tx_fee = chan. next_remote_commit_tx_fee_msat ( dust_htlc_amt_above_timeout, true , None ) ;
4888+ let htlc_candidate = HTLCCandidate :: new ( dust_htlc_amt_above_timeout, HTLCInitiator :: LocalOffered ) ;
4889+ let commitment_tx_fee = chan. next_remote_commit_tx_fee_msat ( htlc_candidate, None ) ;
48414890 assert_eq ! ( commitment_tx_fee, commitment_tx_fee_0_htlcs) ;
48424891
48434892 // If swapped: this HTLC would be counted as dust when it shouldn't be.
48444893 let htlc_amt_below_success = ( ( 253 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + chan. counterparty_dust_limit_satoshis - 1 ) * 1000 ;
4845- let commitment_tx_fee = chan. next_remote_commit_tx_fee_msat ( htlc_amt_below_success, false , None ) ;
4894+ let htlc_candidate = HTLCCandidate :: new ( htlc_amt_below_success, HTLCInitiator :: RemoteOffered ) ;
4895+ let commitment_tx_fee = chan. next_remote_commit_tx_fee_msat ( htlc_candidate, None ) ;
48464896 assert_eq ! ( commitment_tx_fee, commitment_tx_fee_1_htlc) ;
48474897 }
48484898
0 commit comments