@@ -1757,13 +1757,13 @@ impl<Signer: Sign> Channel<Signer> {
1757
1757
let mut holder_dusted_htlc_msat = 0 ;
1758
1758
1759
1759
let counterparty_dust_limit_timeout_sat = ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
1760
- let holder_dust_limit_timeout_sat = ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1760
+ let holder_dust_limit_success_sat = ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1761
1761
for ref htlc in self . pending_inbound_htlcs . iter ( ) {
1762
1762
htlc_inbound_value_msat += htlc. amount_msat ;
1763
1763
if htlc. amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
1764
1764
counterparty_dusted_htlc_msat += htlc. amount_msat + ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) * 1000 ;
1765
1765
}
1766
- if htlc. amount_msat / 1000 < holder_dust_limit_timeout_sat {
1766
+ if htlc. amount_msat / 1000 < holder_dust_limit_success_sat {
1767
1767
holder_dusted_htlc_msat += htlc. amount_msat + ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) * 1000 ;
1768
1768
}
1769
1769
}
@@ -1778,14 +1778,14 @@ impl<Signer: Sign> Channel<Signer> {
1778
1778
let mut holder_dusted_htlc_msat = 0 ;
1779
1779
1780
1780
let counterparty_dust_limit_success_sat = ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . counterparty_dust_limit_satoshis ;
1781
- let holder_dust_limit_success_sat = ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1781
+ let holder_dust_limit_timeout_sat = ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis ;
1782
1782
for ref htlc in self . pending_outbound_htlcs . iter ( ) {
1783
1783
htlc_outbound_value_msat += htlc. amount_msat ;
1784
1784
if htlc. amount_msat / 1000 < counterparty_dust_limit_success_sat {
1785
1785
counterparty_dusted_htlc_msat += htlc. amount_msat + ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) * 1000 ;
1786
1786
}
1787
- if htlc. amount_msat / 1000 < holder_dust_limit_success_sat {
1788
- holder_dusted_htlc_msat += htlc. amount_msat + ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) * 1000 ;
1787
+ if htlc. amount_msat / 1000 < holder_dust_limit_timeout_sat {
1788
+ holder_dusted_htlc_msat += htlc. amount_msat + ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) * 1000 ;
1789
1789
}
1790
1790
}
1791
1791
@@ -1797,7 +1797,7 @@ impl<Signer: Sign> Channel<Signer> {
1797
1797
if * amount_msat / 1000 < counterparty_dust_limit_success_sat {
1798
1798
counterparty_dusted_htlc_msat += amount_msat + ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) * 1000 ;
1799
1799
}
1800
- if * amount_msat / 1000 < holder_dust_limit_success_sat {
1800
+ if * amount_msat / 1000 < holder_dust_limit_timeout_sat {
1801
1801
holder_dusted_htlc_msat += amount_msat + ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) * 1000 ;
1802
1802
}
1803
1803
}
@@ -2032,7 +2032,7 @@ impl<Signer: Sign> Channel<Signer> {
2032
2032
}
2033
2033
2034
2034
let ( inbound_htlc_count, htlc_inbound_value_msat, on_counterparty_tx_dust_inbound, on_holder_tx_dust_inbound) = self . get_inbound_pending_htlc_stats ( ) ;
2035
- let ( _ , _, on_counterparty_tx_dust_outbound, on_holder_tx_dust_outbound) = self . get_outbound_pending_htlc_stats ( ) ;
2035
+ let ( outbound_htlc_count , _, on_counterparty_tx_dust_outbound, on_holder_tx_dust_outbound) = self . get_outbound_pending_htlc_stats ( ) ;
2036
2036
if inbound_htlc_count + 1 > OUR_MAX_HTLCS as u32 {
2037
2037
return Err ( ChannelError :: Close ( format ! ( "Remote tried to push more than our max accepted HTLCs ({})" , OUR_MAX_HTLCS ) ) ) ;
2038
2038
}
@@ -2061,20 +2061,35 @@ impl<Signer: Sign> Channel<Signer> {
2061
2061
}
2062
2062
}
2063
2063
2064
+ #[ cfg( any( test, feature = "fuzztarget" ) ) ]
2065
+ {
2066
+ // Holder dust balance on holder commitment transaction share the same safety
2067
+ // risks though it's implictly bounded by the verification of the dust balance on
2068
+ // the counterparty commitment transaction, with a delta due to the asymmetry
2069
+ // of HTLC claim transaction weights.
2070
+ //
2071
+ // Assuming worst-case scenario of maximum number of inbound dust HTLCs,
2072
+ // this delta is defined by := (N * `htlc_minimum_msat`) + (`feerate_per_kw` * HTLC_SUCCESS_TX_WEIGHT / 1000 * 1000)
2073
+ // - (N * `htlc_minimum_msat`) + (`feerate_per_kw` * HTLC_TIMEOUT_TX_WEIGHT / 1000 * 1000)
2074
+ //
2075
+ // Where N is the number of pending inbound HTLCs. The HTLCs amounts are picked
2076
+ // up to be equal to the `htlc_minimum_msat` as this is maxima of "trimmed-to-dust"
2077
+ // weight difference between holder and counterparty transactions.
2078
+ //
2079
+ // With a default `max_balance_dust_htlc_msat`=5_000_000, N is at most 29.
2080
+ //
2081
+ // Assuming `feerate_per_kw`=253, solving the equation we find 344_000 millisatoshis.
2082
+ assert_eq ! ( on_holder_tx_dust_inbound - inbound_htlc_count as u64 * ( self . feerate_per_kw as u64 * ( HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT ) / 1000 * 1000 ) , on_counterparty_tx_dust_inbound) ;
2083
+ assert_eq ! ( on_holder_tx_dust_outbound + outbound_htlc_count as u64 * ( self . feerate_per_kw as u64 * ( HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT ) / 1000 * 1000 ) , on_counterparty_tx_dust_outbound) ;
2084
+ }
2085
+
2064
2086
if msg. amount_msat <= ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) * 1000 + self . counterparty_dust_limit_satoshis {
2065
2087
if on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound + msg. amount_msat + ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) * 1000 > self . get_max_balance_dust_htlc_msat ( ) {
2066
2088
log_info ! ( logger, "Cannot accept value that would put holder dusted balance {} on counterparty commitment over limit {}" , on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound, self . get_max_balance_dust_htlc_msat( ) ) ;
2067
2089
pending_forward_status = create_pending_htlc_status ( self , pending_forward_status, 0x1000 |7 ) ;
2068
2090
}
2069
2091
}
2070
2092
2071
- if msg. amount_msat <= ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) + self . holder_dust_limit_satoshis {
2072
- if on_holder_tx_dust_inbound + on_holder_tx_dust_outbound + msg. amount_msat + ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) > self . get_max_balance_dust_htlc_msat ( ) {
2073
- log_info ! ( logger, "Cannot accept value that would put holder dusted balance on holder commitment over limit {}" , self . get_max_balance_dust_htlc_msat( ) ) ;
2074
- pending_forward_status = create_pending_htlc_status ( self , pending_forward_status, 0x1000 |7 ) ;
2075
- }
2076
- }
2077
-
2078
2093
let pending_value_to_self_msat =
2079
2094
self . value_to_self_msat + htlc_inbound_value_msat - removed_outbound_total_msat;
2080
2095
let pending_remote_value_msat =
@@ -4143,18 +4158,19 @@ impl<Signer: Sign> Channel<Signer> {
4143
4158
}
4144
4159
}
4145
4160
4161
+ #[ cfg( any( test, feature = "fuzztarget" ) ) ]
4162
+ {
4163
+ // See comment in `update_add_htlc()` for rational.
4164
+ assert_eq ! ( on_holder_tx_dust_inbound - inbound_htlc_count as u64 * ( self . feerate_per_kw as u64 * ( HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT ) / 1000 * 1000 ) , on_counterparty_tx_dust_inbound) ;
4165
+ assert_eq ! ( on_holder_tx_dust_outbound + outbound_htlc_count as u64 * ( self . feerate_per_kw as u64 * ( HTLC_SUCCESS_TX_WEIGHT - HTLC_TIMEOUT_TX_WEIGHT ) / 1000 * 1000 ) , on_counterparty_tx_dust_outbound) ;
4166
+ }
4167
+
4146
4168
if amount_msat <= ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) * 1000 + self . counterparty_dust_limit_satoshis {
4147
4169
if on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound + amount_msat + ( self . feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000 ) * 1000 > self . get_max_balance_dust_htlc_msat ( ) {
4148
4170
return Err ( ChannelError :: Ignore ( format ! ( "Cannot send value that would put holder dusted balance {} on counterparty commitment over limit {}" , on_counterparty_tx_dust_inbound + on_counterparty_tx_dust_outbound, self . get_max_balance_dust_htlc_msat( ) ) ) ) ;
4149
4171
}
4150
4172
}
4151
4173
4152
- if amount_msat <= ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) * 1000 + self . holder_dust_limit_satoshis {
4153
- if on_holder_tx_dust_inbound + on_holder_tx_dust_outbound + amount_msat + ( self . feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000 ) * 1000 > self . get_max_balance_dust_htlc_msat ( ) {
4154
- return Err ( ChannelError :: Ignore ( format ! ( "Cannot send value that would put holder dusted balance on holder commitment over limit {}" , self . get_max_balance_dust_htlc_msat( ) ) ) ) ;
4155
- }
4156
- }
4157
-
4158
4174
let pending_value_to_self_msat = self . value_to_self_msat - htlc_outbound_value_msat;
4159
4175
if pending_value_to_self_msat < amount_msat {
4160
4176
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) ) ) ;
0 commit comments