From: Matt Corallo Date: Sun, 15 Nov 2020 21:44:09 +0000 (-0500) Subject: Do not generate a channel-closed mon update for never-signed chans X-Git-Tag: v0.0.12~3^2~1 X-Git-Url: http://git.bitcoin.ninja/?a=commitdiff_plain;h=22de94afdde4ee6f960eedff3d6a7d01a76445ff;p=rust-lightning Do not generate a channel-closed mon update for never-signed chans 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. --- diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index a884b5f72..a09f63e8f 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -2362,7 +2362,12 @@ impl // 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 diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index bc8351e4d..8ff3d4eac 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -487,7 +487,11 @@ pub fn create_announced_chan_between_nodes<'a, 'b, 'c, 'd>(nodes: &'a Vec(nodes: &'a Vec>, 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>, 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 { diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index b3e5697c6..3916a07de 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -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); +}