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.
This commit is contained in:
Matt Corallo 2020-11-15 16:44:09 -05:00
parent 8f10a1d33d
commit 22de94afdd
3 changed files with 153 additions and 5 deletions

View 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

View 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 {

View 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);
}