]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Do not generate a channel-closed mon update for never-signed chans
authorMatt Corallo <git@bluematt.me>
Sun, 15 Nov 2020 21:44:09 +0000 (16:44 -0500)
committerMatt Corallo <git@bluematt.me>
Mon, 23 Nov 2020 22:00:07 +0000 (17:00 -0500)
The full_stack_target managed to find a bug where, if we receive
a funding_created message which has a channel_id identical to an
existing channel, we'll end up
 (a) having the monitor update for the new channel fail (due to
     duplicate outpoint),
 (b) creating a monitor update for the new channel as we
     force-close it,
 (c) panicing due to the force-close monitor update is applied to
     the original channel and is considered out-of-order.

Obviously we shouldn't be creating a force-close monitor update for
a channel which can never appear on chain, so we do that here and
add a test which previously failed and checks a few
duplicate-channel-id cases.

lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs

index a884b5f72d870a173f2d27986522ce2394f136ad..a09f63e8fc894ba3b319f08194d88f8795a0a3e0 100644 (file)
@@ -2362,7 +2362,12 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                                        // channel, not the temporary_channel_id. This is compatible with ourselves, but the
                                        // spec is somewhat ambiguous here. Not a huge deal since we'll send error messages for
                                        // any messages referencing a previously-closed channel anyway.
-                                       return Err(MsgHandleErrInternal::from_finish_shutdown("ChannelMonitor storage failure".to_owned(), funding_msg.channel_id, chan.force_shutdown(true), None));
+                                       // We do not do a force-close here as that would generate a monitor update for
+                                       // a monitor that we didn't manage to store (and that we don't care about - we
+                                       // don't respond with the funding_signed so the channel can never go on chain).
+                                       let (_funding_txo_option, _monitor_update, failed_htlcs) = chan.force_shutdown(true);
+                                       assert!(failed_htlcs.is_empty());
+                                       return Err(MsgHandleErrInternal::send_err_msg_no_close("ChannelMonitor storage failure".to_owned(), funding_msg.channel_id));
                                },
                                ChannelMonitorUpdateErr::TemporaryFailure => {
                                        // There's no problem signing a counterparty's funding transaction if our monitor
index bc8351e4da09e333b5bcc481d8a94e079187753e..8ff3d4eacf85c3972d7dd322747570b9c66779fc 100644 (file)
@@ -487,7 +487,11 @@ pub fn create_announced_chan_between_nodes<'a, 'b, 'c, 'd>(nodes: &'a Vec<Node<'
 
 pub fn create_announced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a Vec<Node<'b, 'c, 'd>>, a: usize, b: usize, channel_value: u64, push_msat: u64, a_flags: InitFeatures, b_flags: InitFeatures) -> (msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction) {
        let chan_announcement = create_chan_between_nodes_with_value(&nodes[a], &nodes[b], channel_value, push_msat, a_flags, b_flags);
+       update_nodes_with_chan_announce(nodes, a, b, &chan_announcement.0, &chan_announcement.1, &chan_announcement.2);
+       (chan_announcement.1, chan_announcement.2, chan_announcement.3, chan_announcement.4)
+}
 
+pub fn update_nodes_with_chan_announce<'a, 'b, 'c, 'd>(nodes: &'a Vec<Node<'b, 'c, 'd>>, a: usize, b: usize, ann: &msgs::ChannelAnnouncement, upd_1: &msgs::ChannelUpdate, upd_2: &msgs::ChannelUpdate) {
        nodes[a].node.broadcast_node_announcement([0, 0, 0], [0; 32], Vec::new());
        let a_events = nodes[a].node.get_and_clear_pending_msg_events();
        assert_eq!(a_events.len(), 1);
@@ -509,13 +513,12 @@ pub fn create_announced_chan_between_nodes_with_value<'a, 'b, 'c, 'd>(nodes: &'a
        };
 
        for node in nodes {
-               assert!(node.net_graph_msg_handler.handle_channel_announcement(&chan_announcement.0).unwrap());
-               node.net_graph_msg_handler.handle_channel_update(&chan_announcement.1).unwrap();
-               node.net_graph_msg_handler.handle_channel_update(&chan_announcement.2).unwrap();
+               assert!(node.net_graph_msg_handler.handle_channel_announcement(ann).unwrap());
+               node.net_graph_msg_handler.handle_channel_update(upd_1).unwrap();
+               node.net_graph_msg_handler.handle_channel_update(upd_2).unwrap();
                node.net_graph_msg_handler.handle_node_announcement(&a_node_announcement).unwrap();
                node.net_graph_msg_handler.handle_node_announcement(&b_node_announcement).unwrap();
        }
-       (chan_announcement.1, chan_announcement.2, chan_announcement.3, chan_announcement.4)
 }
 
 macro_rules! check_spends {
index b3e5697c66834c268b12cc1da97a7653f58c3e51..3916a07decbdae74036da49b6e763bbbfd891a0a 100644 (file)
@@ -8687,3 +8687,143 @@ fn test_onchain_htlc_settlement_after_close() {
        do_test_onchain_htlc_settlement_after_close(true, false);
        do_test_onchain_htlc_settlement_after_close(false, false);
 }
+
+#[test]
+fn test_duplicate_chan_id() {
+       // Test that if a given peer tries to open a channel with the same channel_id as one that is
+       // already open we reject it and keep the old channel.
+       //
+       // Previously, full_stack_target managed to figure out that if you tried to open two channels
+       // with the same funding output (ie post-funding channel_id), we'd create a monitor update for
+       // the existing channel when we detect the duplicate new channel, screwing up our monitor
+       // updating logic for the existing channel.
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       // Create an initial channel
+       nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
+       let mut open_chan_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_chan_msg);
+       nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()));
+
+       // Try to create a second channel with the same temporary_channel_id as the first and check
+       // that it is rejected.
+       nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_chan_msg);
+       {
+               let events = nodes[1].node.get_and_clear_pending_msg_events();
+               assert_eq!(events.len(), 1);
+               match events[0] {
+                       MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg }, node_id } => {
+                               // Technically, at this point, nodes[1] would be justified in thinking both the
+                               // first (valid) and second (invalid) channels are closed, given they both have
+                               // the same non-temporary channel_id. However, currently we do not, so we just
+                               // move forward with it.
+                               assert_eq!(msg.channel_id, open_chan_msg.temporary_channel_id);
+                               assert_eq!(node_id, nodes[0].node.get_our_node_id());
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+       }
+
+       // Move the first channel through the funding flow...
+       let (temporary_channel_id, tx, funding_output) = create_funding_transaction(&nodes[0], 100000, 42);
+
+       nodes[0].node.funding_transaction_generated(&temporary_channel_id, funding_output);
+       check_added_monitors!(nodes[0], 0);
+
+       let mut funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
+       {
+               let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
+               assert_eq!(added_monitors.len(), 1);
+               assert_eq!(added_monitors[0].0, funding_output);
+               added_monitors.clear();
+       }
+       let funding_signed_msg = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
+
+       let funding_outpoint = ::chain::transaction::OutPoint { txid: funding_created_msg.funding_txid, index: funding_created_msg.funding_output_index };
+       let channel_id = funding_outpoint.to_channel_id();
+
+       // Now we have the first channel past funding_created (ie it has a txid-based channel_id, not a
+       // temporary one).
+
+       // First try to open a second channel with a temporary channel id equal to the txid-based one.
+       // Technically this is allowed by the spec, but we don't support it and there's little reason
+       // to. Still, it shouldn't cause any other issues.
+       open_chan_msg.temporary_channel_id = channel_id;
+       nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_chan_msg);
+       {
+               let events = nodes[1].node.get_and_clear_pending_msg_events();
+               assert_eq!(events.len(), 1);
+               match events[0] {
+                       MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg }, node_id } => {
+                               // Technically, at this point, nodes[1] would be justified in thinking both
+                               // channels are closed, but currently we do not, so we just move forward with it.
+                               assert_eq!(msg.channel_id, open_chan_msg.temporary_channel_id);
+                               assert_eq!(node_id, nodes[0].node.get_our_node_id());
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+       }
+
+       // Now try to create a second channel which has a duplicate funding output.
+       nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
+       let open_chan_2_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_chan_2_msg);
+       nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()));
+       create_funding_transaction(&nodes[0], 100000, 42); // Get and check the FundingGenerationReady event
+
+       let funding_created = {
+               let mut a_channel_lock = nodes[0].node.channel_state.lock().unwrap();
+               let mut as_chan = a_channel_lock.by_id.get_mut(&open_chan_2_msg.temporary_channel_id).unwrap();
+               let logger = test_utils::TestLogger::new();
+               as_chan.get_outbound_funding_created(funding_outpoint, &&logger).unwrap()
+       };
+       check_added_monitors!(nodes[0], 0);
+       nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created);
+       // At this point we'll try to add a duplicate channel monitor, which will be rejected, but
+       // still needs to be cleared here.
+       check_added_monitors!(nodes[1], 1);
+
+       // ...still, nodes[1] will reject the duplicate channel.
+       {
+               let events = nodes[1].node.get_and_clear_pending_msg_events();
+               assert_eq!(events.len(), 1);
+               match events[0] {
+                       MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg }, node_id } => {
+                               // Technically, at this point, nodes[1] would be justified in thinking both
+                               // channels are closed, but currently we do not, so we just move forward with it.
+                               assert_eq!(msg.channel_id, channel_id);
+                               assert_eq!(node_id, nodes[0].node.get_our_node_id());
+                       },
+                       _ => panic!("Unexpected event"),
+               }
+       }
+
+       // finally, finish creating the original channel and send a payment over it to make sure
+       // everything is functional.
+       nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &funding_signed_msg);
+       {
+               let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap();
+               assert_eq!(added_monitors.len(), 1);
+               assert_eq!(added_monitors[0].0, funding_output);
+               added_monitors.clear();
+       }
+
+       let events_4 = nodes[0].node.get_and_clear_pending_events();
+       assert_eq!(events_4.len(), 1);
+       match events_4[0] {
+               Event::FundingBroadcastSafe { ref funding_txo, user_channel_id } => {
+                       assert_eq!(user_channel_id, 42);
+                       assert_eq!(*funding_txo, funding_output);
+               },
+               _ => panic!("Unexpected event"),
+       };
+
+       let (funding_locked, _) = create_chan_between_nodes_with_value_confirm(&nodes[0], &nodes[1], &tx);
+       let (announcement, as_update, bs_update) = create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &funding_locked);
+       update_nodes_with_chan_announce(&nodes, 0, 1, &announcement, &as_update, &bs_update);
+       send_payment(&nodes[0], &[&nodes[1]], 8000000, 8_000_000);
+}