]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Implement channel closing for block_disconnected on ChainListener + test
authorAntoine Riard <ariard@student.42.fr>
Sat, 14 Jul 2018 02:08:14 +0000 (02:08 +0000)
committerAntoine Riard <ariard@student.42.fr>
Fri, 20 Jul 2018 02:37:05 +0000 (02:37 +0000)
src/ln/channel.rs
src/ln/channelmanager.rs

index 93e4a2cf3be5d12f0d903725b0ddcd32e6a2f1a7..6c04ff1df48e6a33fab482c4d20bc4ecee66ad31 100644 (file)
@@ -231,7 +231,7 @@ const BOTH_SIDES_SHUTDOWN_MASK: u32 = (ChannelState::LocalShutdownSent as u32 |
 
 // TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking
 // has been completed, and then turn into a Channel to get compiler-time enforcement of things like
-// calling get_channel_id() before we're set up or things like get_outbound_funding_signed on an
+// calling channel_id() before we're set up or things like get_outbound_funding_signed on an
 // inbound channel.
 pub struct Channel {
        user_id: u64,
index 13f38c431c1c92bfacbc27aa578c82f1a810a471..725f598c3337739625e1b74cd25d1fad5c8d4076 100644 (file)
@@ -1085,13 +1085,25 @@ impl ChainListener for ChannelManager {
                }
        }
 
+       /// We force-close the channel without letting our counterparty participate in the shutdown
        fn block_disconnected(&self, header: &BlockHeader) {
-               let mut channel_state = self.channel_state.lock().unwrap();
-               for channel in channel_state.by_id.values_mut() {
-                       if channel.block_disconnected(header) {
-                               //TODO Close channel here
+               let mut channel_lock = self.channel_state.lock().unwrap();
+               let channel_state = channel_lock.borrow_parts();
+               let short_to_id = channel_state.short_to_id;
+               channel_state.by_id.retain(|_,  v| {
+                       if v.block_disconnected(header) {
+                               let tx = v.force_shutdown();
+                               for broadcast_tx in tx {
+                                       self.tx_broadcaster.broadcast_transaction(&broadcast_tx);
+                               }
+                               if let Some(short_id) = v.get_short_channel_id() {
+                                       short_to_id.remove(&short_id);
+                               }
+                               false
+                       } else {
+                               true
                        }
-               }
+               });
        }
 }
 
@@ -1816,6 +1828,7 @@ impl ChannelMessageHandler for ChannelManager {
 mod tests {
        use chain::chaininterface;
        use chain::transaction::OutPoint;
+       use chain::chaininterface::ChainListener;
        use ln::channelmanager::{ChannelManager,OnionKeys};
        use ln::router::{Route, RouteHop, Router};
        use ln::msgs;
@@ -2919,4 +2932,30 @@ mod tests {
                        assert_eq!(node.chan_monitor.added_monitors.lock().unwrap().len(), 0);
                }
        }
+
+       #[test]
+       fn test_unconf_chan() {
+               // After creating a chan between nodes, we disconnect all blocks previously seen to force a channel close on nodes[0] side
+               let nodes = create_network(2);
+               create_announced_chan_between_nodes(&nodes, 0, 1);
+
+               let channel_state = nodes[0].node.channel_state.lock().unwrap();
+               assert_eq!(channel_state.by_id.len(), 1);
+               assert_eq!(channel_state.short_to_id.len(), 1);
+               mem::drop(channel_state);
+
+               let mut headers = Vec::new();
+               let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+               headers.push(header.clone());
+               for _i in 2..100 {
+                       header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+                       headers.push(header.clone());
+               }
+               while !headers.is_empty() {
+                       nodes[0].node.block_disconnected(&headers.pop().unwrap());
+               }
+               let channel_state = nodes[0].node.channel_state.lock().unwrap();
+               assert_eq!(channel_state.by_id.len(), 0);
+               assert_eq!(channel_state.short_to_id.len(), 0);
+       }
 }