Account for zero fee HTLC transaction within dust limit calculation
[rust-lightning] / lightning / src / ln / channel.rs
index 7e978d9407ca3207450b3926f0c3471b978ae419..6c17fd357a3092de3ad1321f62efb5bdfaf70573 100644 (file)
@@ -1466,7 +1466,12 @@ impl<Signer: Sign> Channel<Signer> {
                        ($htlc: expr, $outbound: expr, $source: expr, $state_name: expr) => {
                                if $outbound == local { // "offered HTLC output"
                                        let htlc_in_tx = get_htlc_in_commitment!($htlc, true);
-                                       if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + (feerate_per_kw as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) {
+                                       let htlc_tx_fee = if self.opt_anchors() {
+                                               0
+                                       } else {
+                                               feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000
+                                       };
+                                       if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
                                                log_trace!(logger, "   ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
                                                included_non_dust_htlcs.push((htlc_in_tx, $source));
                                        } else {
@@ -1475,7 +1480,12 @@ impl<Signer: Sign> Channel<Signer> {
                                        }
                                } else {
                                        let htlc_in_tx = get_htlc_in_commitment!($htlc, false);
-                                       if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + (feerate_per_kw as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) {
+                                       let htlc_tx_fee = if self.opt_anchors() {
+                                               0
+                                       } else {
+                                               feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000
+                                       };
+                                       if $htlc.amount_msat / 1000 >= broadcaster_dust_limit_satoshis + htlc_tx_fee {
                                                log_trace!(logger, "   ...including {} {} HTLC {} (hash {}) with value {}", if $outbound { "outbound" } else { "inbound" }, $state_name, $htlc.htlc_id, log_bytes!($htlc.payment_hash.0), $htlc.amount_msat);
                                                included_non_dust_htlcs.push((htlc_in_tx, $source));
                                        } else {
@@ -2396,8 +2406,15 @@ impl<Signer: Sign> Channel<Signer> {
                        on_holder_tx_holding_cell_htlcs_count: 0,
                };
 
-               let counterparty_dust_limit_timeout_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
-               let holder_dust_limit_success_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
+               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
+                       (0, 0)
+               } else {
+                       let dust_buffer_feerate = self.get_dust_buffer_feerate(outbound_feerate_update) as u64;
+                       (dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
+                               dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
+               };
+               let counterparty_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.counterparty_dust_limit_satoshis;
+               let holder_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
                for ref htlc in self.pending_inbound_htlcs.iter() {
                        stats.pending_htlcs_value_msat += htlc.amount_msat;
                        if htlc.amount_msat / 1000 < counterparty_dust_limit_timeout_sat {
@@ -2421,8 +2438,15 @@ impl<Signer: Sign> Channel<Signer> {
                        on_holder_tx_holding_cell_htlcs_count: 0,
                };
 
-               let counterparty_dust_limit_success_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
-               let holder_dust_limit_timeout_sat = (self.get_dust_buffer_feerate(outbound_feerate_update) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
+               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
+                       (0, 0)
+               } else {
+                       let dust_buffer_feerate = self.get_dust_buffer_feerate(outbound_feerate_update) as u64;
+                       (dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
+                               dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
+               };
+               let counterparty_dust_limit_success_sat = htlc_success_dust_limit + self.counterparty_dust_limit_satoshis;
+               let holder_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
                for ref htlc in self.pending_outbound_htlcs.iter() {
                        stats.pending_htlcs_value_msat += htlc.amount_msat;
                        if htlc.amount_msat / 1000 < counterparty_dust_limit_success_sat {
@@ -2512,8 +2536,14 @@ impl<Signer: Sign> Channel<Signer> {
        fn next_local_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
                assert!(self.is_outbound());
 
-               let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
-               let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
+               let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
+                       (0, 0)
+               } else {
+                       (self.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
+                               self.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
+               };
+               let real_dust_limit_success_sat = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
+               let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
 
                let mut addl_htlcs = 0;
                if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
@@ -2603,8 +2633,14 @@ impl<Signer: Sign> Channel<Signer> {
        fn next_remote_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 {
                assert!(!self.is_outbound());
 
-               let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
-               let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
+               let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
+                       (0, 0)
+               } else {
+                       (self.feerate_per_kw as u64 * htlc_success_tx_weight(false) / 1000,
+                               self.feerate_per_kw as u64 * htlc_timeout_tx_weight(false) / 1000)
+               };
+               let real_dust_limit_success_sat = htlc_success_dust_limit + self.counterparty_dust_limit_satoshis;
+               let real_dust_limit_timeout_sat = htlc_timeout_dust_limit + self.counterparty_dust_limit_satoshis;
 
                let mut addl_htlcs = 0;
                if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; }
@@ -2727,7 +2763,14 @@ impl<Signer: Sign> Channel<Signer> {
                        }
                }
 
-               let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
+               let (htlc_timeout_dust_limit, htlc_success_dust_limit) = if self.opt_anchors() {
+                       (0, 0)
+               } else {
+                       let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
+                       (dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000,
+                               dust_buffer_feerate * htlc_success_tx_weight(false) / 1000)
+               };
+               let exposure_dust_limit_timeout_sats = htlc_timeout_dust_limit + self.counterparty_dust_limit_satoshis;
                if msg.amount_msat / 1000 < exposure_dust_limit_timeout_sats {
                        let on_counterparty_tx_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat + msg.amount_msat;
                        if on_counterparty_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -2737,7 +2780,7 @@ impl<Signer: Sign> Channel<Signer> {
                        }
                }
 
-               let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
+               let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.holder_dust_limit_satoshis;
                if msg.amount_msat / 1000 < exposure_dust_limit_success_sats {
                        let on_holder_tx_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat + msg.amount_msat;
                        if on_holder_tx_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -4771,6 +4814,9 @@ impl<Signer: Sign> Channel<Signer> {
        }
 
        fn check_get_channel_ready(&mut self, height: u32) -> Option<msgs::ChannelReady> {
+               // Called:
+               //  * always when a new block/transactions are confirmed with the new height
+               //  * when funding is signed with a height of 0
                if self.funding_tx_confirmation_height == 0 && self.minimum_depth != Some(0) {
                        return None;
                }
@@ -4796,7 +4842,7 @@ impl<Signer: Sign> Channel<Signer> {
                        // We got a reorg but not enough to trigger a force close, just ignore.
                        false
                } else {
-                       if self.channel_state < ChannelState::ChannelFunded as u32 {
+                       if self.funding_tx_confirmation_height != 0 && self.channel_state < ChannelState::ChannelFunded as u32 {
                                // We should never see a funding transaction on-chain until we've received
                                // funding_signed (if we're an outbound channel), or seen funding_generated (if we're
                                // an inbound channel - before that we have no known funding TXID). The fuzzer,
@@ -5441,7 +5487,14 @@ impl<Signer: Sign> Channel<Signer> {
                        }
                }
 
-               let exposure_dust_limit_success_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_success_tx_weight(self.opt_anchors()) / 1000) + self.counterparty_dust_limit_satoshis;
+               let (htlc_success_dust_limit, htlc_timeout_dust_limit) = if self.opt_anchors() {
+                       (0, 0)
+               } else {
+                       let dust_buffer_feerate = self.get_dust_buffer_feerate(None) as u64;
+                       (dust_buffer_feerate * htlc_success_tx_weight(false) / 1000,
+                               dust_buffer_feerate * htlc_timeout_tx_weight(false) / 1000)
+               };
+               let exposure_dust_limit_success_sats = htlc_success_dust_limit + self.counterparty_dust_limit_satoshis;
                if amount_msat / 1000 < exposure_dust_limit_success_sats {
                        let on_counterparty_dust_htlc_exposure_msat = inbound_stats.on_counterparty_tx_dust_exposure_msat + outbound_stats.on_counterparty_tx_dust_exposure_msat + amount_msat;
                        if on_counterparty_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -5450,7 +5503,7 @@ impl<Signer: Sign> Channel<Signer> {
                        }
                }
 
-               let exposure_dust_limit_timeout_sats = (self.get_dust_buffer_feerate(None) as u64 * htlc_timeout_tx_weight(self.opt_anchors()) / 1000) + self.holder_dust_limit_satoshis;
+               let exposure_dust_limit_timeout_sats = htlc_timeout_dust_limit + self.holder_dust_limit_satoshis;
                if amount_msat / 1000 <  exposure_dust_limit_timeout_sats {
                        let on_holder_dust_htlc_exposure_msat = inbound_stats.on_holder_tx_dust_exposure_msat + outbound_stats.on_holder_tx_dust_exposure_msat + amount_msat;
                        if on_holder_dust_htlc_exposure_msat > self.get_max_dust_htlc_exposure_msat() {
@@ -6603,7 +6656,7 @@ mod tests {
        use util::errors::APIError;
        use util::test_utils;
        use util::test_utils::OnGetShutdownScriptpubkey;
-       use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
+       use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Scalar};
        use bitcoin::secp256k1::ffi::Signature as FFISignature;
        use bitcoin::secp256k1::{SecretKey,PublicKey};
        use bitcoin::secp256k1::ecdh::SharedSecret;
@@ -6648,7 +6701,7 @@ mod tests {
                type Signer = InMemorySigner;
 
                fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { panic!(); }
-               fn ecdh(&self, _recipient: Recipient, _other_key: &PublicKey, _tweak: Option<&[u8; 32]>) -> Result<SharedSecret, ()> { panic!(); }
+               fn ecdh(&self, _recipient: Recipient, _other_key: &PublicKey, _tweak: Option<&Scalar>) -> Result<SharedSecret, ()> { panic!(); }
                fn get_inbound_payment_key_material(&self) -> KeyMaterial { panic!(); }
                fn get_destination_script(&self) -> Script {
                        let secp_ctx = Secp256k1::signing_only();