Avoid panic when 0conf channel's ann. sigs race on-chain confirmation

A channel's `short_channel_id` is currently only set when the funding
transaction is confirmed via `transactions_confirmed`, which might be
well after the channel initally becomes usable, e.g., in the 0conf case.

Previously we would panic due to a reachable `unwrap` when receiving a
counterparty's `announcement_signatures` message for a 0conf channel
pending confirmation on-chain.

Here we fix this bug by avoiding unsafe `unwrap`s and just erroring out
and ignoring the announcement_signatures message if the `short_channel_id`
hasn't been set yet.
This commit is contained in:
Elias Rohrer 2023-06-17 13:16:52 +02:00
parent ae9e96e277
commit 82fdf0f62d
No known key found for this signature in database
GPG key ID: 36153082BDF676FD
2 changed files with 41 additions and 4 deletions

View file

@ -948,9 +948,9 @@ impl<Signer: ChannelSigner> ChannelContext<Signer> {
&self.channel_type &self.channel_type
} }
/// Guaranteed to be Some after both ChannelReady messages have been exchanged (and, thus, /// Gets the channel's `short_channel_id`.
/// is_usable() returns true). ///
/// Allowed in any state (including after shutdown) /// Will return `None` if the channel hasn't been confirmed yet.
pub fn get_short_channel_id(&self) -> Option<u64> { pub fn get_short_channel_id(&self) -> Option<u64> {
self.short_channel_id self.short_channel_id
} }
@ -4832,6 +4832,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel is not currently usable".to_owned())); return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel is not currently usable".to_owned()));
} }
let short_channel_id = self.context.get_short_channel_id()
.ok_or(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel has not been confirmed yet".to_owned()))?;
let node_id = NodeId::from_pubkey(&node_signer.get_node_id(Recipient::Node) let node_id = NodeId::from_pubkey(&node_signer.get_node_id(Recipient::Node)
.map_err(|_| ChannelError::Ignore("Failed to retrieve own public key".to_owned()))?); .map_err(|_| ChannelError::Ignore("Failed to retrieve own public key".to_owned()))?);
let counterparty_node_id = NodeId::from_pubkey(&self.context.get_counterparty_node_id()); let counterparty_node_id = NodeId::from_pubkey(&self.context.get_counterparty_node_id());
@ -4840,7 +4842,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
let msg = msgs::UnsignedChannelAnnouncement { let msg = msgs::UnsignedChannelAnnouncement {
features: channelmanager::provided_channel_features(&user_config), features: channelmanager::provided_channel_features(&user_config),
chain_hash, chain_hash,
short_channel_id: self.context.get_short_channel_id().unwrap(), short_channel_id,
node_id_1: if were_node_one { node_id } else { counterparty_node_id }, node_id_1: if were_node_one { node_id } else { counterparty_node_id },
node_id_2: if were_node_one { counterparty_node_id } else { node_id }, node_id_2: if were_node_one { counterparty_node_id } else { node_id },
bitcoin_key_1: NodeId::from_pubkey(if were_node_one { &self.context.get_holder_pubkeys().funding_pubkey } else { self.context.counterparty_funding_pubkey() }), bitcoin_key_1: NodeId::from_pubkey(if were_node_one { &self.context.get_holder_pubkeys().funding_pubkey } else { self.context.counterparty_funding_pubkey() }),

View file

@ -1007,3 +1007,38 @@ fn test_connect_before_funding() {
connect_blocks(&nodes[0], 1); connect_blocks(&nodes[0], 1);
connect_blocks(&nodes[1], 1); connect_blocks(&nodes[1], 1);
} }
#[test]
fn test_0conf_ann_sigs_racing_conf() {
// Previously we had a bug where we'd panic when receiving a counterparty's
// announcement_signatures message for a 0conf channel pending confirmation on-chain. Here we
// check that we just error out, ignore the announcement_signatures message, and proceed
// instead.
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let mut chan_config = test_default_channel_config();
chan_config.manually_accept_inbound_channels = true;
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(chan_config)]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
// This is the default but we force it on anyway
chan_config.channel_handshake_config.announced_channel = true;
let (tx, ..) = open_zero_conf_channel(&nodes[0], &nodes[1], Some(chan_config));
// We can use the channel immediately, but we can't announce it until we get 6+ confirmations
send_payment(&nodes[0], &[&nodes[1]], 100_000);
let scid = confirm_transaction(&nodes[0], &tx);
let as_announcement_sigs = get_event_msg!(nodes[0], MessageSendEvent::SendAnnouncementSignatures, nodes[1].node.get_our_node_id());
// Handling the announcement_signatures prior to the first confirmation would panic before.
nodes[1].node.handle_announcement_signatures(&nodes[0].node.get_our_node_id(), &as_announcement_sigs);
assert_eq!(confirm_transaction(&nodes[1], &tx), scid);
let bs_announcement_sigs = get_event_msg!(nodes[1], MessageSendEvent::SendAnnouncementSignatures, nodes[0].node.get_our_node_id());
nodes[0].node.handle_announcement_signatures(&nodes[1].node.get_our_node_id(), &bs_announcement_sigs);
let as_announcement = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(as_announcement.len(), 1);
}