Error handling in decoding onion
[rust-lightning] / src / ln / channelmanager.rs
index 9cca16a4278e0b66a2abf22bec64c46da656f237..ed86b511bd64d0b772b820a194dcf5e0457647f4 100644 (file)
@@ -23,7 +23,7 @@ use secp256k1;
 use chain::chaininterface::{BroadcasterInterface,ChainListener,ChainWatchInterface,FeeEstimator};
 use chain::transaction::OutPoint;
 use ln::channel::{Channel, ChannelError, ChannelKeys};
-use ln::channelmonitor::ManyChannelMonitor;
+use ln::channelmonitor::{ManyChannelMonitor, CLTV_CLAIM_BUFFER, HTLC_FAIL_TIMEOUT_BLOCKS};
 use ln::router::{Route,RouteHop};
 use ln::msgs;
 use ln::msgs::{HandleError,ChannelMessageHandler};
@@ -290,7 +290,27 @@ pub struct ChannelManager {
        logger: Arc<Logger>,
 }
 
+/// The minimum number of blocks between an inbound HTLC's CLTV and the corresponding outbound
+/// HTLC's CLTV. This should always be a few blocks greater than channelmonitor::CLTV_CLAIM_BUFFER,
+/// ie the node we forwarded the payment on to should always have enough room to reliably time out
+/// the HTLC via a full update_fail_htlc/commitment_signed dance before we hit the
+/// CLTV_CLAIM_BUFFER point (we static assert that its at least 3 blocks more).
 const CLTV_EXPIRY_DELTA: u16 = 6 * 24 * 2; //TODO?
+const CLTV_FAR_FAR_AWAY: u32 = 6 * 24 * 7; //TODO?
+
+// Check that our CLTV_EXPIRY is at least CLTV_CLAIM_BUFFER + 2*HTLC_FAIL_TIMEOUT_BLOCKS, ie that
+// if the next-hop peer fails the HTLC within HTLC_FAIL_TIMEOUT_BLOCKS then we'll still have
+// HTLC_FAIL_TIMEOUT_BLOCKS left to fail it backwards ourselves before hitting the
+// CLTV_CLAIM_BUFFER point and failing the channel on-chain to time out the HTLC.
+#[deny(const_err)]
+#[allow(dead_code)]
+const CHECK_CLTV_EXPIRY_SANITY: u32 = CLTV_EXPIRY_DELTA as u32 - 2*HTLC_FAIL_TIMEOUT_BLOCKS - CLTV_CLAIM_BUFFER;
+
+// Check for ability of an attacker to make us fail on-chain by delaying inbound claim. See
+// ChannelMontior::would_broadcast_at_height for a description of why this is needed.
+#[deny(const_err)]
+#[allow(dead_code)]
+const CHECK_CLTV_EXPIRY_SANITY_2: u32 = CLTV_EXPIRY_DELTA as u32 - HTLC_FAIL_TIMEOUT_BLOCKS - 2*CLTV_CLAIM_BUFFER;
 
 macro_rules! secp_call {
        ( $res: expr, $err: expr ) => {
@@ -449,7 +469,10 @@ impl ChannelManager {
                let channel_state = self.channel_state.lock().unwrap();
                let mut res = Vec::with_capacity(channel_state.by_id.len());
                for (channel_id, channel) in channel_state.by_id.iter() {
-                       if channel.is_usable() {
+                       // Note we use is_live here instead of usable which leads to somewhat confused
+                       // internal/external nomenclature, but that's ok cause that's probably what the user
+                       // really wanted anyway.
+                       if channel.is_live() {
                                res.push(ChannelDetails {
                                        channel_id: (*channel_id).clone(),
                                        short_channel_id: channel.get_short_channel_id(),
@@ -874,13 +897,17 @@ impl ChannelManager {
                        }
                };
 
-               //TODO: Check that msg.cltv_expiry is within acceptable bounds!
-
                let pending_forward_info = if next_hop_data.hmac == [0; 32] {
                                // OUR PAYMENT!
-                               if next_hop_data.data.amt_to_forward != msg.amount_msat {
+                               // final_expiry_too_soon
+                               if (msg.cltv_expiry as u64) < self.latest_block_height.load(Ordering::Acquire) as u64 + (CLTV_CLAIM_BUFFER + HTLC_FAIL_TIMEOUT_BLOCKS) as u64 {
+                                       return_err!("The final CLTV expiry is too soon to handle", 17, &[0;0]);
+                               }
+                               // final_incorrect_htlc_amount
+                               if next_hop_data.data.amt_to_forward > msg.amount_msat {
                                        return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
                                }
+                               // final_incorrect_cltv_expiry
                                if next_hop_data.data.outgoing_cltv_value != msg.cltv_expiry {
                                        return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
                                }
@@ -945,29 +972,49 @@ impl ChannelManager {
                        if onion_packet.is_some() { // If short_channel_id is 0 here, we'll reject them in the body here
                                let id_option = channel_state.as_ref().unwrap().short_to_id.get(&short_channel_id).cloned();
                                let forwarding_id = match id_option {
-                                       None => {
+                                       None => { // unknown_next_peer
                                                return_err!("Don't have available channel for forwarding as requested.", 0x4000 | 10, &[0;0]);
                                        },
                                        Some(id) => id.clone(),
                                };
-                               if let Some((err, code, chan_update)) = {
+                               if let Some((err, code, chan_update)) = loop {
                                        let chan = channel_state.as_mut().unwrap().by_id.get_mut(&forwarding_id).unwrap();
-                                       if !chan.is_live() {
-                                               Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, self.get_channel_update(chan).unwrap()))
-                                       } else {
-                                               let fee = amt_to_forward.checked_mul(self.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan.get_our_fee_base_msat(&*self.fee_estimator) as u64) });
-                                               if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward {
-                                                       Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, self.get_channel_update(chan).unwrap()))
-                                               } else {
-                                                       if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + CLTV_EXPIRY_DELTA as u64 {
-                                                               Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, self.get_channel_update(chan).unwrap()))
-                                                       } else {
-                                                               None
-                                                       }
-                                               }
+
+                                       if !chan.is_live() { // channel_disabled
+                                               break Some(("Forwarding channel is not in a ready state.", 0x1000 | 20, Some(self.get_channel_update(chan).unwrap())));
+                                       }
+                                       if *amt_to_forward < chan.get_their_htlc_minimum_msat() { // amount_below_minimum
+                                               break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, Some(self.get_channel_update(chan).unwrap())));
+                                       }
+                                       let fee = amt_to_forward.checked_mul(self.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan.get_our_fee_base_msat(&*self.fee_estimator) as u64) });
+                                       if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { // fee_insufficient
+                                               break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, Some(self.get_channel_update(chan).unwrap())));
                                        }
-                               } {
-                                       return_err!(err, code, &chan_update.encode_with_len()[..]);
+                                       if (msg.cltv_expiry as u64) < (*outgoing_cltv_value) as u64 + CLTV_EXPIRY_DELTA as u64 { // incorrect_cltv_expiry
+                                               break Some(("Forwarding node has tampered with the intended HTLC values or origin node has an obsolete cltv_expiry_delta", 0x1000 | 13, Some(self.get_channel_update(chan).unwrap())));
+                                       }
+                                       let cur_height = self.latest_block_height.load(Ordering::Acquire) as u32 + 1;
+                                       // We want to have at least HTLC_FAIL_TIMEOUT_BLOCKS to fail prior to going on chain CLAIM_BUFFER blocks before expiration
+                                       if msg.cltv_expiry <= cur_height + CLTV_CLAIM_BUFFER + HTLC_FAIL_TIMEOUT_BLOCKS as u32 { // expiry_too_soon
+                                               break Some(("CLTV expiry is too close", 0x1000 | 14, Some(self.get_channel_update(chan).unwrap())));
+                                       }
+                                       if msg.cltv_expiry > cur_height + CLTV_FAR_FAR_AWAY as u32 { // expiry_too_far
+                                               break Some(("CLTV expiry is too far in the future", 21, None));
+                                       }
+                                       break None;
+                               }
+                               {
+                                       let mut res = Vec::with_capacity(8 + 128);
+                                       if code == 0x1000 | 11 || code == 0x1000 | 12 {
+                                               res.extend_from_slice(&byte_utils::be64_to_array(msg.amount_msat));
+                                       }
+                                       else if code == 0x1000 | 13 {
+                                               res.extend_from_slice(&byte_utils::be32_to_array(msg.cltv_expiry));
+                                       }
+                                       if let Some(chan_update) = chan_update {
+                                               res.extend_from_slice(&chan_update.encode_with_len()[..]);
+                                       }
+                                       return_err!(err, code, &res[..]);
                                }
                        }
                }
@@ -997,7 +1044,7 @@ impl ChannelManager {
                };
 
                let msg_hash = Sha256dHash::from_data(&unsigned.encode()[..]);
-               let sig = self.secp_ctx.sign(&Message::from_slice(&msg_hash[..]).unwrap(), &self.our_network_key); //TODO Can we unwrap here?
+               let sig = self.secp_ctx.sign(&Message::from_slice(&msg_hash[..]).unwrap(), &self.our_network_key);
 
                Ok(msgs::ChannelUpdate {
                        signature: sig,
@@ -1050,7 +1097,7 @@ impl ChannelManager {
                        let channel_state = channel_state_lock.borrow_parts();
 
                        let id = match channel_state.short_to_id.get(&route.hops.first().unwrap().short_channel_id) {
-                               None => return Err(APIError::RouteError{err: "No channel available with first hop!"}),
+                               None => return Err(APIError::ChannelUnavailable{err: "No channel available with first hop!"}),
                                Some(id) => id.clone(),
                        };
 
@@ -1060,12 +1107,12 @@ impl ChannelManager {
                                        return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
                                }
                                if !chan.is_live() {
-                                       return Err(APIError::RouteError{err: "Peer for first hop currently disconnected!"});
+                                       return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected!"});
                                }
                                chan.send_htlc_and_commit(htlc_msat, payment_hash.clone(), htlc_cltv, HTLCSource::OutboundRoute {
                                        route: route.clone(),
                                        session_priv: session_priv.clone(),
-                               }, onion_packet).map_err(|he| APIError::RouteError{err: he.err})?
+                               }, onion_packet).map_err(|he| APIError::ChannelUnavailable{err: he.err})?
                        };
 
                        let first_hop_node_id = route.hops.first().unwrap().pubkey;
@@ -1102,7 +1149,6 @@ impl ChannelManager {
        /// May panic if the funding_txo is duplicative with some other channel (note that this should
        /// be trivially prevented by using unique funding transaction keys per-channel).
        pub fn funding_transaction_generated(&self, temporary_channel_id: &[u8; 32], funding_txo: OutPoint) {
-
                macro_rules! add_pending_event {
                        ($event: expr) => {
                                {
@@ -1307,6 +1353,8 @@ impl ChannelManager {
 
        /// Indicates that the preimage for payment_hash is unknown after a PaymentReceived event.
        pub fn fail_htlc_backwards(&self, payment_hash: &[u8; 32]) -> bool {
+               // TODO: Add ability to return 0x4000|16 (incorrect_payment_amount) if the amount we
+               // received is < expected or > 2*expected
                let mut channel_state = Some(self.channel_state.lock().unwrap());
                let removed_source = channel_state.as_mut().unwrap().claimable_htlcs.remove(payment_hash);
                if let Some(mut sources) = removed_source {
@@ -1804,7 +1852,8 @@ impl ChannelManager {
                                                                                // No such next-hop. We know this came from the
                                                                                // current node as the HMAC validated.
                                                                                res = Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
-                                                                                       short_channel_id: route_hop.short_channel_id
+                                                                                       short_channel_id: route_hop.short_channel_id,
+                                                                                       is_permanent: true,
                                                                                });
                                                                        },
                                                                        _ => {}, //TODO: Enumerate all of these!
@@ -1998,12 +2047,12 @@ impl ChannelManager {
                match channel_state.by_id.get_mut(&channel_id) {
                        None => return Err(APIError::APIMisuseError{err: "Failed to find corresponding channel"}),
                        Some(chan) => {
-                               if !chan.is_usable() {
-                                       return Err(APIError::APIMisuseError{err: "Channel is not in usuable state"});
-                               }
                                if !chan.is_outbound() {
                                        return Err(APIError::APIMisuseError{err: "update_fee cannot be sent for an inbound channel"});
                                }
+                               if !chan.is_live() {
+                                       return Err(APIError::ChannelUnavailable{err: "Channel is either not yet fully established or peer is currently disconnected"});
+                               }
                                if let Some((update_fee, commitment_signed, chan_monitor)) = chan.send_update_fee_and_commit(feerate_per_kw).map_err(|e| APIError::APIMisuseError{err: e.err})? {
                                        if let Err(_e) = self.monitor.add_update_monitor(chan_monitor.get_funding_txo().unwrap(), chan_monitor) {
                                                unimplemented!();
@@ -2350,6 +2399,7 @@ mod tests {
        use chain::transaction::OutPoint;
        use chain::chaininterface::ChainListener;
        use ln::channelmanager::{ChannelManager,OnionKeys};
+       use ln::channelmonitor::{CLTV_CLAIM_BUFFER, HTLC_FAIL_TIMEOUT_BLOCKS};
        use ln::router::{Route, RouteHop, Router};
        use ln::msgs;
        use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler};
@@ -2382,6 +2432,7 @@ mod tests {
        use std::default::Default;
        use std::rc::Rc;
        use std::sync::{Arc, Mutex};
+       use std::sync::atomic::Ordering;
        use std::time::Instant;
        use std::mem;
 
@@ -2574,7 +2625,7 @@ mod tests {
                (announcement, as_update, bs_update, channel_id, tx)
        }
 
-       fn create_chan_between_nodes_with_value_a(node_a: &Node, node_b: &Node, channel_value: u64, push_msat: u64) -> ((msgs::FundingLocked, msgs::AnnouncementSignatures), [u8; 32], Transaction) {
+       fn create_chan_between_nodes_with_value_init(node_a: &Node, node_b: &Node, channel_value: u64, push_msat: u64) -> Transaction {
                node_a.node.create_channel(node_b.node.get_our_node_id(), channel_value, push_msat, 42).unwrap();
 
                let events_1 = node_a.node.get_and_clear_pending_events();
@@ -2647,7 +2698,11 @@ mod tests {
                        _ => panic!("Unexpected event"),
                };
 
-               confirm_transaction(&node_b.chain_monitor, &tx, chan_id);
+               tx
+       }
+
+       fn create_chan_between_nodes_with_value_confirm(node_a: &Node, node_b: &Node, tx: &Transaction) -> ((msgs::FundingLocked, msgs::AnnouncementSignatures), [u8; 32]) {
+               confirm_transaction(&node_b.chain_monitor, &tx, tx.version);
                let events_5 = node_b.node.get_and_clear_pending_events();
                assert_eq!(events_5.len(), 1);
                match events_5[0] {
@@ -2661,7 +2716,7 @@ mod tests {
 
                let channel_id;
 
-               confirm_transaction(&node_a.chain_monitor, &tx, chan_id);
+               confirm_transaction(&node_a.chain_monitor, &tx, tx.version);
                let events_6 = node_a.node.get_and_clear_pending_events();
                assert_eq!(events_6.len(), 1);
                (match events_6[0] {
@@ -2671,7 +2726,13 @@ mod tests {
                                (msg.clone(), announcement_sigs.clone().unwrap())
                        },
                        _ => panic!("Unexpected event"),
-               }, channel_id, tx)
+               }, channel_id)
+       }
+
+       fn create_chan_between_nodes_with_value_a(node_a: &Node, node_b: &Node, channel_value: u64, push_msat: u64) -> ((msgs::FundingLocked, msgs::AnnouncementSignatures), [u8; 32], Transaction) {
+               let tx = create_chan_between_nodes_with_value_init(node_a, node_b, channel_value, push_msat);
+               let (msgs, chan_id) = create_chan_between_nodes_with_value_confirm(node_a, node_b, &tx);
+               (msgs, chan_id, tx)
        }
 
        fn create_chan_between_nodes_with_value_b(node_a: &Node, node_b: &Node, as_funding_msgs: &(msgs::FundingLocked, msgs::AnnouncementSignatures)) -> (msgs::ChannelAnnouncement, msgs::ChannelUpdate, msgs::ChannelUpdate) {
@@ -3015,7 +3076,7 @@ mod tests {
 
                let err = origin_node.node.send_payment(route, our_payment_hash).err().unwrap();
                match err {
-                       APIError::RouteError{err} => assert_eq!(err, "Cannot send value that would put us over our max HTLC value in flight"),
+                       APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us over our max HTLC value in flight"),
                        _ => panic!("Unknown error variants"),
                };
        }
@@ -3979,7 +4040,7 @@ mod tests {
                        assert!(route.hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
                        let err = nodes[0].node.send_payment(route, our_payment_hash).err().unwrap();
                        match err {
-                               APIError::RouteError{err} => assert_eq!(err, "Cannot send value that would put us over our max HTLC value in flight"),
+                               APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us over our max HTLC value in flight"),
                                _ => panic!("Unknown error variants"),
                        }
                }
@@ -4015,7 +4076,7 @@ mod tests {
                        let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value + 1);
                        let err = nodes[0].node.send_payment(route.clone(), our_payment_hash).err().unwrap();
                        match err {
-                               APIError::RouteError{err} => assert_eq!(err, "Cannot send value that would put us over our reserve value"),
+                               APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us over our reserve value"),
                                _ => panic!("Unknown error variants"),
                        }
                }
@@ -4040,7 +4101,7 @@ mod tests {
                {
                        let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_2 + 1);
                        match nodes[0].node.send_payment(route, our_payment_hash).err().unwrap() {
-                               APIError::RouteError{err} => assert_eq!(err, "Cannot send value that would put us over our reserve value"),
+                               APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us over our reserve value"),
                                _ => panic!("Unknown error variants"),
                        }
                }
@@ -4096,7 +4157,7 @@ mod tests {
                {
                        let (route, our_payment_hash, _) = get_route_and_payment_hash!(recv_value_22+1);
                        match nodes[0].node.send_payment(route, our_payment_hash).err().unwrap() {
-                               APIError::RouteError{err} => assert_eq!(err, "Cannot send value that would put us over our reserve value"),
+                               APIError::ChannelUnavailable{err} => assert_eq!(err, "Cannot send value that would put us over our reserve value"),
                                _ => panic!("Unknown error variants"),
                        }
                }
@@ -4257,13 +4318,22 @@ mod tests {
                assert_eq!(nodes[2].node.list_channels().len(), 0);
                assert_eq!(nodes[3].node.list_channels().len(), 1);
 
+               { // Cheat and reset nodes[4]'s height to 1
+                       let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+                       nodes[4].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![] }, 1);
+               }
+
+               assert_eq!(nodes[3].node.latest_block_height.load(Ordering::Acquire), 1);
+               assert_eq!(nodes[4].node.latest_block_height.load(Ordering::Acquire), 1);
                // One pending HTLC to time out:
                let payment_preimage_2 = route_payment(&nodes[3], &vec!(&nodes[4])[..], 3000000).0;
+               // CLTV expires at TEST_FINAL_CLTV + 1 (current height) + 1 (added in send_payment for
+               // buffer space).
 
                {
                        let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
-                       nodes[3].chain_monitor.block_connected_checked(&header, 1, &Vec::new()[..], &[0; 0]);
-                       for i in 2..TEST_FINAL_CLTV - 3 {
+                       nodes[3].chain_monitor.block_connected_checked(&header, 2, &Vec::new()[..], &[0; 0]);
+                       for i in 3..TEST_FINAL_CLTV + 2 + HTLC_FAIL_TIMEOUT_BLOCKS + 1 {
                                header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
                                nodes[3].chain_monitor.block_connected_checked(&header, i, &Vec::new()[..], &[0; 0]);
                        }
@@ -4274,8 +4344,8 @@ mod tests {
                        claim_funds!(nodes[4], nodes[3], payment_preimage_2);
 
                        header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
-                       nodes[4].chain_monitor.block_connected_checked(&header, 1, &Vec::new()[..], &[0; 0]);
-                       for i in 2..TEST_FINAL_CLTV - 3 {
+                       nodes[4].chain_monitor.block_connected_checked(&header, 2, &Vec::new()[..], &[0; 0]);
+                       for i in 3..TEST_FINAL_CLTV + 2 - CLTV_CLAIM_BUFFER + 1 {
                                header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
                                nodes[4].chain_monitor.block_connected_checked(&header, i, &Vec::new()[..], &[0; 0]);
                        }
@@ -4925,6 +4995,10 @@ mod tests {
                        _ => panic!("Unexpected event"),
                };
 
+               nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+               nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
+               reconnect_nodes(&nodes[0], &nodes[1], false, (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+
                nodes[1].node.channel_state.lock().unwrap().next_forward = Instant::now();
                nodes[1].node.process_pending_htlc_forwards();
 
@@ -5019,6 +5093,10 @@ mod tests {
                        reconnect_nodes(&nodes[0], &nodes[1], false, (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
                }
 
+               nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+               nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
+               reconnect_nodes(&nodes[0], &nodes[1], false, (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+
                // Channel should still work fine...
                let payment_preimage_2 = send_along_route(&nodes[0], route, &[&nodes[1]], 1000000).0;
                claim_payment(&nodes[0], &[&nodes[1]], payment_preimage_2);
@@ -5038,6 +5116,50 @@ mod tests {
                do_test_drop_messages_peer_disconnect(5);
        }
 
+       #[test]
+       fn test_funding_peer_disconnect() {
+               // Test that we can lock in our funding tx while disconnected
+               let nodes = create_network(2);
+               let tx = create_chan_between_nodes_with_value_init(&nodes[0], &nodes[1], 100000, 10001);
+
+               nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+               nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
+
+               confirm_transaction(&nodes[0].chain_monitor, &tx, tx.version);
+               let events_1 = nodes[0].node.get_and_clear_pending_events();
+               assert_eq!(events_1.len(), 1);
+               match events_1[0] {
+                       Event::SendFundingLocked { ref node_id, msg: _, ref announcement_sigs } => {
+                               assert_eq!(*node_id, nodes[1].node.get_our_node_id());
+                               assert!(announcement_sigs.is_none());
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+
+               confirm_transaction(&nodes[1].chain_monitor, &tx, tx.version);
+               let events_2 = nodes[1].node.get_and_clear_pending_events();
+               assert_eq!(events_2.len(), 1);
+               match events_2[0] {
+                       Event::SendFundingLocked { ref node_id, msg: _, ref announcement_sigs } => {
+                               assert_eq!(*node_id, nodes[0].node.get_our_node_id());
+                               assert!(announcement_sigs.is_none());
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+
+               reconnect_nodes(&nodes[0], &nodes[1], true, (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+               nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
+               nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
+               reconnect_nodes(&nodes[0], &nodes[1], true, (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
+
+               // TODO: We shouldn't need to manually pass list_usable_chanels here once we support
+               // rebroadcasting announcement_signatures upon reconnect.
+
+               let route = nodes[0].router.get_route(&nodes[1].node.get_our_node_id(), Some(&nodes[0].node.list_usable_channels()), &Vec::new(), 1000000, TEST_FINAL_CLTV).unwrap();
+               let (payment_preimage, _) = send_along_route(&nodes[0], route, &[&nodes[1]], 1000000);
+               claim_payment(&nodes[0], &[&nodes[1]], payment_preimage);
+       }
+
        #[test]
        fn test_invalid_channel_announcement() {
                //Test BOLT 7 channel_announcement msg requirement for final node, gather data to build customed channel_announcement msgs
@@ -5051,7 +5173,7 @@ mod tests {
                let as_chan = a_channel_lock.by_id.get(&chan_announcement.3).unwrap();
                let bs_chan = b_channel_lock.by_id.get(&chan_announcement.3).unwrap();
 
-               let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap() } );
+               let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap(), is_permanent: false } );
 
                let as_bitcoin_key = PublicKey::from_secret_key(&secp_ctx, &as_chan.get_local_keys().funding_key);
                let bs_bitcoin_key = PublicKey::from_secret_key(&secp_ctx, &bs_chan.get_local_keys().funding_key);
@@ -5098,7 +5220,7 @@ mod tests {
                let unsigned_msg = dummy_unsigned_msg!();
                sign_msg!(unsigned_msg);
                assert_eq!(nodes[0].router.handle_channel_announcement(&chan_announcement).unwrap(), true);
-               let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap() } );
+               let _ = nodes[0].router.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap(), is_permanent: false } );
 
                // Configured with Network::Testnet
                let mut unsigned_msg = dummy_unsigned_msg!();