Force-close channels on reorg only if the funding is unconfirmed
[rust-lightning] / lightning / src / ln / channel.rs
index cf70749ad96b7bbc98fdca8917db77c0d03d601e..1eeb3d428096748c7e440c89671a63a19062aa04 100644 (file)
@@ -1103,14 +1103,13 @@ impl<Signer: Sign> Channel<Signer> {
                if msg.channel_reserve_satoshis > msg.funding_satoshis {
                        return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must be not greater than funding_satoshis: {}", msg.channel_reserve_satoshis, msg.funding_satoshis)));
                }
-               let funding_value = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
-               if msg.push_msat > funding_value {
-                       return Err(ChannelError::Close(format!("push_msat {} was larger than funding value {}", msg.push_msat, funding_value)));
+               let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
+               if msg.push_msat > full_channel_value_msat {
+                       return Err(ChannelError::Close(format!("push_msat {} was larger than channel amount minus reserve ({})", msg.push_msat, full_channel_value_msat)));
                }
                if msg.dust_limit_satoshis > msg.funding_satoshis {
                        return Err(ChannelError::Close(format!("dust_limit_satoshis {} was larger than funding_satoshis {}. Peer never wants payout outputs?", msg.dust_limit_satoshis, msg.funding_satoshis)));
                }
-               let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
                if msg.htlc_minimum_msat >= full_channel_value_msat {
                        return Err(ChannelError::Close(format!("Minimum htlc value ({}) was larger than full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat)));
                }
@@ -1164,6 +1163,9 @@ impl<Signer: Sign> Channel<Signer> {
                if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
                        return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
                }
+               if holder_selected_channel_reserve_satoshis * 1000 >= full_channel_value_msat {
+                       return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). Channel value is ({} - {}).", holder_selected_channel_reserve_satoshis, full_channel_value_msat, msg.push_msat)));
+               }
                if msg.channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
                        log_debug!(logger, "channel_reserve_satoshis ({}) is smaller than our dust limit ({}). We can broadcast stale states without any risk, implying this channel is very insecure for our counterparty.",
                                msg.channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS);
@@ -4718,10 +4720,14 @@ impl<Signer: Sign> Channel<Signer> {
                        }
 
                        // If we've sent funding_locked (or have both sent and received funding_locked), and
-                       // the funding transaction's confirmation count has dipped below minimum_depth / 2,
+                       // the funding transaction has become unconfirmed,
                        // close the channel and hope we can get the latest state on chain (because presumably
                        // the funding transaction is at least still in the mempool of most nodes).
-                       if funding_tx_confirmations < self.minimum_depth.unwrap() as i64 / 2 {
+                       //
+                       // Note that ideally we wouldn't force-close if we see *any* reorg on a 1-conf channel,
+                       // but not doing so may lead to the `ChannelManager::short_to_id` map being
+                       // inconsistent, so we currently have to.
+                       if funding_tx_confirmations == 0 && self.funding_tx_confirmed_in.is_some() {
                                let err_reason = format!("Funding transaction was un-confirmed. Locked at {} confs, now have {} confs.",
                                        self.minimum_depth.unwrap(), funding_tx_confirmations);
                                return Err(ClosureReason::ProcessingError { err: err_reason });