@@ -260,6 +260,27 @@ enum UpdateStatus {
260
260
DisabledStaged ,
261
261
}
262
262
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
+
263
284
// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
264
285
// has been completed, and then turn into a Channel to get compiler-time enforcement of things like
265
286
// 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> {
1718
1739
// number of pending HTLCs that are on track to be in our next commitment tx, plus an additional
1719
1740
// HTLC if `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs
1720
1741
// 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 {
1722
1743
assert ! ( self . is_outbound( ) ) ;
1723
1744
1724
1745
let real_dust_limit_success_sat = ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1725
1746
let real_dust_limit_timeout_sat = ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1726
1747
1727
1748
let mut addl_htlcs = 0 ;
1728
1749
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
+ }
1733
1761
}
1734
1762
1735
1763
let mut included_htlcs = 0 ;
@@ -1783,11 +1811,13 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
1783
1811
let commitment_tx_info = CommitmentTxInfoCached {
1784
1812
fee,
1785
1813
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 ,
1788
1817
} ,
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 ,
1791
1821
} ,
1792
1822
feerate : self . feerate_per_kw ,
1793
1823
} ;
@@ -1800,18 +1830,25 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
1800
1830
// pending HTLCs that are on track to be in their next commitment tx, plus an additional HTLC if
1801
1831
// `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs are
1802
1832
// 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 {
1804
1834
assert ! ( !self . is_outbound( ) ) ;
1805
1835
1806
1836
let real_dust_limit_success_sat = ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
1807
1837
let real_dust_limit_timeout_sat = ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
1808
1838
1809
1839
let mut addl_htlcs = 0 ;
1810
1840
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
+ }
1815
1852
}
1816
1853
1817
1854
// 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> {
1851
1888
let commitment_tx_info = CommitmentTxInfoCached {
1852
1889
fee,
1853
1890
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 ,
1856
1894
} ,
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 ,
1859
1898
} ,
1860
1899
feerate : self . feerate_per_kw ,
1861
1900
} ;
@@ -1929,7 +1968,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
1929
1968
// Check that the remote can afford to pay for this HTLC on-chain at the current
1930
1969
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
1931
1970
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
1933
1973
} ;
1934
1974
if pending_remote_value_msat - msg. amount_msat < remote_commit_tx_fee_msat {
1935
1975
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> {
1950
1990
// the extra htlc when calculating the next remote commitment transaction fee as we should
1951
1991
// still be able to afford adding this HTLC plus one more future HTLC, regardless of being
1952
1992
// 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 ( ( ) ) ) ;
1954
1995
if pending_remote_value_msat - msg. amount_msat - chan_reserve_msat < remote_fee_cost_incl_stuck_buffer_msat {
1955
1996
// Note that if the pending_forward_status is not updated here, then it's because we're already failing
1956
1997
// the HTLC, i.e. its status is already set to failing.
@@ -1959,7 +2000,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
1959
2000
}
1960
2001
} else {
1961
2002
// 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 ) ;
1963
2005
if self . value_to_self_msat < self . counterparty_selected_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat {
1964
2006
return Err ( ChannelError :: Close ( "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value" . to_owned ( ) ) ) ;
1965
2007
}
@@ -3863,7 +3905,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
3863
3905
// Check that we won't violate the remote channel reserve by adding this HTLC.
3864
3906
let counterparty_balance_msat = self . channel_value_satoshis * 1000 - self . value_to_self_msat ;
3865
3907
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 ) ;
3867
3910
if counterparty_balance_msat < holder_selected_chan_reserve_msat + counterparty_commit_tx_fee_msat {
3868
3911
return Err ( ChannelError :: Ignore ( "Cannot send value that would put counterparty balance under holder-announced channel reserve value" . to_owned ( ) ) ) ;
3869
3912
}
@@ -3876,7 +3919,8 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
3876
3919
3877
3920
// `2 *` and extra HTLC are for the fee spike buffer.
3878
3921
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 ( ( ) ) )
3880
3924
} else { 0 } ;
3881
3925
if pending_value_to_self_msat - amount_msat < commit_tx_fee_msat {
3882
3926
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 {
4648
4692
use bitcoin:: hashes:: hex:: FromHex ;
4649
4693
use hex;
4650
4694
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 } ;
4652
4696
use ln:: channel:: MAX_FUNDING_SATOSHIS ;
4653
4697
use ln:: features:: InitFeatures ;
4654
4698
use ln:: msgs:: { OptionalField , DataLossProtect , DecodeError } ;
@@ -4791,15 +4835,17 @@ mod tests {
4791
4835
4792
4836
// Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass
4793
4837
// 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 ) ;
4795
4840
let local_commit_fee_0_htlcs = node_a_chan. commit_tx_fee_msat ( 0 ) ;
4796
4841
assert_eq ! ( local_commit_tx_fee, local_commit_fee_0_htlcs) ;
4797
4842
4798
4843
// Finally, make sure that when Node A calculates the remote's commitment transaction fees, all
4799
4844
// of the HTLCs are seen to be above the dust limit.
4800
4845
node_a_chan. channel_transaction_parameters . is_outbound_from_holder = false ;
4801
4846
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 ) ;
4803
4849
assert_eq ! ( remote_commit_tx_fee, remote_commit_fee_3_htlcs) ;
4804
4850
}
4805
4851
@@ -4825,24 +4871,28 @@ mod tests {
4825
4871
// If HTLC_SUCCESS_TX_WEIGHT and HTLC_TIMEOUT_TX_WEIGHT were swapped: then this HTLC would be
4826
4872
// counted as dust when it shouldn't be.
4827
4873
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 ) ;
4829
4876
assert_eq ! ( commitment_tx_fee, commitment_tx_fee_1_htlc) ;
4830
4877
4831
4878
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
4832
4879
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 ) ;
4834
4882
assert_eq ! ( commitment_tx_fee, commitment_tx_fee_0_htlcs) ;
4835
4883
4836
4884
chan. channel_transaction_parameters . is_outbound_from_holder = false ;
4837
4885
4838
4886
// If swapped: this HTLC would be counted as non-dust when it shouldn't be.
4839
4887
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 ) ;
4841
4890
assert_eq ! ( commitment_tx_fee, commitment_tx_fee_0_htlcs) ;
4842
4891
4843
4892
// If swapped: this HTLC would be counted as dust when it shouldn't be.
4844
4893
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 ) ;
4846
4896
assert_eq ! ( commitment_tx_fee, commitment_tx_fee_1_htlc) ;
4847
4897
}
4848
4898
0 commit comments