@@ -274,6 +274,14 @@ enum HTLCInitiator {
274
274
RemoteOffered ,
275
275
}
276
276
277
+ /// An enum gathering stats on pending HTLCs, either inbound or outbound side.
278
+ struct HTLCStats {
279
+ pending_htlcs : u32 ,
280
+ pending_htlcs_value_msat : u64 ,
281
+ on_counterparty_tx_dust_exposure_msat : u64 ,
282
+ on_holder_tx_dust_exposure_msat : u64 ,
283
+ }
284
+
277
285
/// Used when calculating whether we or the remote can afford an additional HTLC.
278
286
struct HTLCCandidate {
279
287
amount_msat : u64 ,
@@ -1816,32 +1824,63 @@ impl<Signer: Sign> Channel<Signer> {
1816
1824
Ok ( ( ) )
1817
1825
}
1818
1826
1819
- /// Returns (inbound_htlc_count, htlc_inbound_value_msat)
1820
- fn get_inbound_pending_htlc_stats ( & self ) -> ( u32 , u64 ) {
1821
- let mut htlc_inbound_value_msat = 0 ;
1827
+ /// Returns a HTLCStats about inbound pending htlcs
1828
+ fn get_inbound_pending_htlc_stats ( & self ) -> HTLCStats {
1829
+ let mut stats = HTLCStats {
1830
+ pending_htlcs : self . pending_inbound_htlcs . len ( ) as u32 ,
1831
+ pending_htlcs_value_msat : 0 ,
1832
+ on_counterparty_tx_dust_exposure_msat : 0 ,
1833
+ on_holder_tx_dust_exposure_msat : 0 ,
1834
+ } ;
1835
+
1836
+ let counterparty_dust_limit_timeout_sat = ( self . get_dust_buffer_feerate ( ) as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
1837
+ let holder_dust_limit_success_sat = ( self . get_dust_buffer_feerate ( ) as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1822
1838
for ref htlc in self . pending_inbound_htlcs . iter ( ) {
1823
- htlc_inbound_value_msat += htlc. amount_msat ;
1839
+ stats. pending_htlcs_value_msat += htlc. amount_msat ;
1840
+ if htlc. amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
1841
+ stats. on_counterparty_tx_dust_exposure_msat += htlc. amount_msat ;
1842
+ }
1843
+ if htlc. amount_msat / 1000 < holder_dust_limit_success_sat {
1844
+ stats. on_holder_tx_dust_exposure_msat += htlc. amount_msat ;
1845
+ }
1824
1846
}
1825
- ( self . pending_inbound_htlcs . len ( ) as u32 , htlc_inbound_value_msat )
1847
+ stats
1826
1848
}
1827
1849
1828
- /// Returns (outbound_htlc_count, htlc_outbound_value_msat) *including* pending adds in our
1829
- /// holding cell.
1830
- fn get_outbound_pending_htlc_stats ( & self ) -> ( u32 , u64 ) {
1831
- let mut htlc_outbound_value_msat = 0 ;
1850
+ /// Returns a HTLCStats about pending outbound htlcs, *including* pending adds in our holding cell.
1851
+ fn get_outbound_pending_htlc_stats ( & self ) -> HTLCStats {
1852
+ let mut stats = HTLCStats {
1853
+ pending_htlcs : self . pending_outbound_htlcs . len ( ) as u32 ,
1854
+ pending_htlcs_value_msat : 0 ,
1855
+ on_counterparty_tx_dust_exposure_msat : 0 ,
1856
+ on_holder_tx_dust_exposure_msat : 0 ,
1857
+ } ;
1858
+
1859
+ let counterparty_dust_limit_success_sat = ( self . get_dust_buffer_feerate ( ) as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
1860
+ let holder_dust_limit_timeout_sat = ( self . get_dust_buffer_feerate ( ) as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1832
1861
for ref htlc in self . pending_outbound_htlcs . iter ( ) {
1833
- htlc_outbound_value_msat += htlc. amount_msat ;
1862
+ stats. pending_htlcs_value_msat += htlc. amount_msat ;
1863
+ if htlc. amount_msat / 1000 < counterparty_dust_limit_success_sat {
1864
+ stats. on_counterparty_tx_dust_exposure_msat += htlc. amount_msat ;
1865
+ }
1866
+ if htlc. amount_msat / 1000 < holder_dust_limit_timeout_sat {
1867
+ stats. on_holder_tx_dust_exposure_msat += htlc. amount_msat ;
1868
+ }
1834
1869
}
1835
1870
1836
- let mut htlc_outbound_count = self . pending_outbound_htlcs . len ( ) ;
1837
1871
for update in self . holding_cell_htlc_updates . iter ( ) {
1838
1872
if let & HTLCUpdateAwaitingACK :: AddHTLC { ref amount_msat, .. } = update {
1839
- htlc_outbound_count += 1 ;
1840
- htlc_outbound_value_msat += amount_msat;
1873
+ stats. pending_htlcs += 1 ;
1874
+ stats. pending_htlcs_value_msat += amount_msat;
1875
+ if * amount_msat / 1000 < counterparty_dust_limit_success_sat {
1876
+ stats. on_counterparty_tx_dust_exposure_msat += amount_msat;
1877
+ }
1878
+ if * amount_msat / 1000 < holder_dust_limit_timeout_sat {
1879
+ stats. on_holder_tx_dust_exposure_msat += amount_msat;
1880
+ }
1841
1881
}
1842
1882
}
1843
-
1844
- ( htlc_outbound_count as u32 , htlc_outbound_value_msat)
1883
+ stats
1845
1884
}
1846
1885
1847
1886
/// Get the available (ie not including pending HTLCs) inbound and outbound balance in msat.
@@ -1853,11 +1892,11 @@ impl<Signer: Sign> Channel<Signer> {
1853
1892
(
1854
1893
cmp:: max ( self . channel_value_satoshis as i64 * 1000
1855
1894
- self . value_to_self_msat as i64
1856
- - self . get_inbound_pending_htlc_stats ( ) . 1 as i64
1895
+ - self . get_inbound_pending_htlc_stats ( ) . pending_htlcs_value_msat as i64
1857
1896
- Self :: get_holder_selected_channel_reserve_satoshis ( self . channel_value_satoshis ) as i64 * 1000 ,
1858
1897
0 ) as u64 ,
1859
1898
cmp:: max ( self . value_to_self_msat as i64
1860
- - self . get_outbound_pending_htlc_stats ( ) . 1 as i64
1899
+ - self . get_outbound_pending_htlc_stats ( ) . pending_htlcs_value_msat as i64
1861
1900
- self . counterparty_selected_channel_reserve_satoshis . unwrap_or ( 0 ) as i64 * 1000 ,
1862
1901
0 ) as u64
1863
1902
)
@@ -2069,12 +2108,13 @@ impl<Signer: Sign> Channel<Signer> {
2069
2108
return Err ( ChannelError :: Close ( format ! ( "Remote side tried to send less than our minimum HTLC value. Lower limit: ({}). Actual: ({})" , self . holder_htlc_minimum_msat, msg. amount_msat) ) ) ;
2070
2109
}
2071
2110
2072
- let ( inbound_htlc_count, htlc_inbound_value_msat) = self . get_inbound_pending_htlc_stats ( ) ;
2073
- if inbound_htlc_count + 1 > OUR_MAX_HTLCS as u32 {
2111
+ let inbound_stats = self . get_inbound_pending_htlc_stats ( ) ;
2112
+ let outbound_stats = self . get_outbound_pending_htlc_stats ( ) ;
2113
+ if inbound_stats. pending_htlcs + 1 > OUR_MAX_HTLCS as u32 {
2074
2114
return Err ( ChannelError :: Close ( format ! ( "Remote tried to push more than our max accepted HTLCs ({})" , OUR_MAX_HTLCS ) ) ) ;
2075
2115
}
2076
2116
let holder_max_htlc_value_in_flight_msat = Channel :: < Signer > :: get_holder_max_htlc_value_in_flight_msat ( self . channel_value_satoshis ) ;
2077
- if htlc_inbound_value_msat + msg. amount_msat > holder_max_htlc_value_in_flight_msat {
2117
+ if inbound_stats . pending_htlcs_value_msat + msg. amount_msat > holder_max_htlc_value_in_flight_msat {
2078
2118
return Err ( ChannelError :: Close ( format ! ( "Remote HTLC add would put them over our max HTLC value ({})" , holder_max_htlc_value_in_flight_msat) ) ) ;
2079
2119
}
2080
2120
// Check holder_selected_channel_reserve_satoshis (we're getting paid, so they have to at least meet
@@ -2099,7 +2139,7 @@ impl<Signer: Sign> Channel<Signer> {
2099
2139
}
2100
2140
2101
2141
let pending_value_to_self_msat =
2102
- self . value_to_self_msat + htlc_inbound_value_msat - removed_outbound_total_msat;
2142
+ self . value_to_self_msat + inbound_stats . pending_htlcs_value_msat - removed_outbound_total_msat;
2103
2143
let pending_remote_value_msat =
2104
2144
self . channel_value_satoshis * 1000 - pending_value_to_self_msat;
2105
2145
if pending_remote_value_msat < msg. amount_msat {
@@ -3502,11 +3542,24 @@ impl<Signer: Sign> Channel<Signer> {
3502
3542
cmp:: max ( self . config . cltv_expiry_delta , MIN_CLTV_EXPIRY_DELTA )
3503
3543
}
3504
3544
3545
+ pub fn get_max_dust_htlc_exposure_msat ( & self ) -> u64 {
3546
+ self . config . max_dust_htlc_exposure_msat
3547
+ }
3548
+
3505
3549
#[ cfg( test) ]
3506
3550
pub fn get_feerate ( & self ) -> u32 {
3507
3551
self . feerate_per_kw
3508
3552
}
3509
3553
3554
+ pub fn get_dust_buffer_feerate ( & self ) -> u32 {
3555
+ // When calculating our exposure to dust HTLCs, we assume that the channel feerate
3556
+ // may, at any point, increase by at least 10 sat/vB (i.e 2530 sat/kWU) or 25%,
3557
+ // whichever is higher. This ensures that we aren't suddenly exposed to significantly
3558
+ // more dust balance if the feerate increases when we have several HTLCs pending
3559
+ // which are near the dust limit.
3560
+ cmp:: max ( 2530 , self . feerate_per_kw * 1250 / 1000 )
3561
+ }
3562
+
3510
3563
pub fn get_cur_holder_commitment_transaction_number ( & self ) -> u64 {
3511
3564
self . cur_holder_commitment_transaction_number + 1
3512
3565
}
@@ -4145,12 +4198,13 @@ impl<Signer: Sign> Channel<Signer> {
4145
4198
return Err ( ChannelError :: Ignore ( "Cannot send an HTLC while disconnected from channel counterparty" . to_owned ( ) ) ) ;
4146
4199
}
4147
4200
4148
- let ( outbound_htlc_count, htlc_outbound_value_msat) = self . get_outbound_pending_htlc_stats ( ) ;
4149
- if outbound_htlc_count + 1 > self . counterparty_max_accepted_htlcs as u32 {
4201
+ let inbound_stats = self . get_inbound_pending_htlc_stats ( ) ;
4202
+ let outbound_stats = self . get_outbound_pending_htlc_stats ( ) ;
4203
+ if outbound_stats. pending_htlcs + 1 > self . counterparty_max_accepted_htlcs as u32 {
4150
4204
return Err ( ChannelError :: Ignore ( format ! ( "Cannot push more than their max accepted HTLCs ({})" , self . counterparty_max_accepted_htlcs) ) ) ;
4151
4205
}
4152
4206
// Check their_max_htlc_value_in_flight_msat
4153
- if htlc_outbound_value_msat + amount_msat > self . counterparty_max_htlc_value_in_flight_msat {
4207
+ if outbound_stats . pending_htlcs_value_msat + amount_msat > self . counterparty_max_htlc_value_in_flight_msat {
4154
4208
return Err ( ChannelError :: Ignore ( format ! ( "Cannot send value that would put us over the max HTLC value in flight our peer will accept ({})" , self . counterparty_max_htlc_value_in_flight_msat) ) ) ;
4155
4209
}
4156
4210
@@ -4165,7 +4219,7 @@ impl<Signer: Sign> Channel<Signer> {
4165
4219
}
4166
4220
}
4167
4221
4168
- let pending_value_to_self_msat = self . value_to_self_msat - htlc_outbound_value_msat ;
4222
+ let pending_value_to_self_msat = self . value_to_self_msat - outbound_stats . pending_htlcs_value_msat ;
4169
4223
if pending_value_to_self_msat < amount_msat {
4170
4224
return Err ( ChannelError :: Ignore ( format ! ( "Cannot send value that would overdraw remaining funds. Amount: {}, pending value to self {}" , amount_msat, pending_value_to_self_msat) ) ) ;
4171
4225
}
0 commit comments