Handle invalid funding tx in channelmanager 2018-07-invalid-funding
authorMatt Corallo <git@bluematt.me>
Sun, 29 Jul 2018 05:59:42 +0000 (01:59 -0400)
committerMatt Corallo <git@bluematt.me>
Sun, 29 Jul 2018 17:30:04 +0000 (13:30 -0400)
src/ln/channel.rs
src/ln/channelmanager.rs

index 3a442091cb0919436d605d661ad8aae68810de44..acee06409c3f8479137b1561ac1b2d636cf6c9e2 100644 (file)
@@ -14,7 +14,7 @@ use crypto::digest::Digest;
 use crypto::hkdf::{hkdf_extract,hkdf_expand};
 
 use ln::msgs;
-use ln::msgs::{HandleError, MsgEncodable};
+use ln::msgs::{ErrorAction, HandleError, MsgEncodable};
 use ln::channelmonitor::ChannelMonitor;
 use ln::channelmanager::{PendingForwardHTLCInfo, HTLCFailReason};
 use ln::chan_utils::{TxCreationKeys,HTLCOutputInCommitment,HTLC_SUCCESS_TX_WEIGHT,HTLC_TIMEOUT_TX_WEIGHT};
@@ -1916,7 +1916,10 @@ impl Channel {
        /// Called by channelmanager based on chain blocks being connected.
        /// Note that we only need to use this to detect funding_signed, anything else is handled by
        /// the channel_monitor.
-       pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> Option<msgs::FundingLocked> {
+       /// In case of Err, the channel may have been closed, at which point the standard requirements
+       /// apply - no calls may be made except those explicitly stated to be allowed post-shutdown.
+       /// Only returns an ErrorAction of DisconnectPeer, if Err.
+       pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> Result<Option<msgs::FundingLocked>, HandleError> {
                let non_shutdown_state = self.channel_state & (!BOTH_SIDES_SHUTDOWN_MASK);
                if self.funding_tx_confirmations > 0 {
                        if header.bitcoin_hash() != self.last_block_connected {
@@ -1940,10 +1943,10 @@ impl Channel {
                                        //a protocol oversight, but I assume I'm just missing something.
                                        let next_per_commitment_secret = self.build_local_commitment_secret(self.cur_local_commitment_transaction_number);
                                        let next_per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &next_per_commitment_secret).unwrap();
-                                       return Some(msgs::FundingLocked {
+                                       return Ok(Some(msgs::FundingLocked {
                                                channel_id: self.channel_id,
                                                next_per_commitment_point: next_per_commitment_point,
-                                       });
+                                       }));
                                }
                        }
                }
@@ -1955,6 +1958,7 @@ impl Channel {
                                                tx.output[txo_idx].value != self.channel_value_satoshis {
                                                self.channel_state = ChannelState::ShutdownComplete as u32;
                                                self.channel_update_count += 1;
+                                               return Err(HandleError{err: "funding tx had wrong script/value", action: Some(ErrorAction::DisconnectPeer{msg: None})});
                                        } else {
                                                self.funding_tx_confirmations = 1;
                                                self.short_channel_id = Some(((height as u64)          << (5*8)) |
@@ -1964,7 +1968,7 @@ impl Channel {
                                }
                        }
                }
-               None
+               Ok(None)
        }
 
        /// Called by channelmanager based on chain blocks being disconnected.
index 6c349fd0ebe2c031c0cbddfb8b3c41354b56778d..f39d0c093ceac2055c7105821a8396c7be431fd8 100644 (file)
@@ -1141,7 +1141,8 @@ impl ChainListener for ChannelManager {
                        let channel_state = channel_lock.borrow_parts();
                        let short_to_id = channel_state.short_to_id;
                        channel_state.by_id.retain(|_, channel| {
-                               if let Some(funding_locked) = channel.block_connected(header, height, txn_matched, indexes_of_txn_matched) {
+                               let chan_res = channel.block_connected(header, height, txn_matched, indexes_of_txn_matched);
+                               if let Ok(Some(funding_locked)) = chan_res {
                                        let announcement_sigs = match self.get_announcement_sigs(channel) {
                                                Ok(res) => res,
                                                Err(_e) => {
@@ -1155,6 +1156,16 @@ impl ChainListener for ChannelManager {
                                                announcement_sigs: announcement_sigs
                                        });
                                        short_to_id.insert(channel.get_short_channel_id().unwrap(), channel.channel_id());
+                               } else if let Err(e) = chan_res {
+                                       if let Some(msgs::ErrorAction::DisconnectPeer { msg }) = e.action {
+                                               new_events.push(events::Event::DisconnectPeer {
+                                                       node_id: channel.get_their_node_id(),
+                                                       msg: msg
+                                               });
+                                       } else { unreachable!(); }
+                                       if channel.is_shutdown() {
+                                               return false;
+                                       }
                                }
                                if let Some(funding_txo) = channel.get_funding_txo() {
                                        for tx in txn_matched {