From: Matt Corallo Date: Fri, 1 Apr 2022 21:29:59 +0000 (+0000) Subject: Correctly handle sending announcement sigs on public 0conf channels X-Git-Tag: v0.0.107~13^2~2 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=7ed7a7d22e6b7bb047db5a7fbbd1f19036b7e06f;p=rust-lightning Correctly handle sending announcement sigs on public 0conf channels --- diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 70a13f9e..8362f6d7 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -4657,12 +4657,11 @@ impl Channel { pub fn transactions_confirmed(&mut self, block_hash: &BlockHash, height: u32, txdata: &TransactionData, genesis_block_hash: BlockHash, node_pk: PublicKey, logger: &L) -> Result<(Option, Option), ClosureReason> where L::Target: Logger { - let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); if let Some(funding_txo) = self.get_funding_txo() { for &(index_in_block, tx) in txdata.iter() { - // If we haven't yet sent a funding_locked, but are in FundingSent (ignoring - // whether they've sent a funding_locked or not), check if we should send one. - if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 { + // Check if the transaction is the expected funding transaction, and if it is, + // check that it pays the right amount to the right script. + if self.funding_tx_confirmation_height == 0 { if tx.txid() == funding_txo.txid { let txo_idx = funding_txo.index as usize; if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() || diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index c0e33e5b..e54874b9 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -21,6 +21,7 @@ use ln::features::{InitFeatures, InvoiceFeatures}; use ln::msgs; use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler}; use util::enforcing_trait_impls::EnforcingSigner; +use util::scid_utils; use util::test_utils; use util::test_utils::{panicking, TestChainMonitor}; use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose}; @@ -48,9 +49,13 @@ pub const CHAN_CONFIRM_DEPTH: u32 = 10; /// Mine the given transaction in the next block and then mine CHAN_CONFIRM_DEPTH - 1 blocks on /// top, giving the given transaction CHAN_CONFIRM_DEPTH confirmations. -pub fn confirm_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) { - confirm_transaction_at(node, tx, node.best_block_info().1 + 1); +/// +/// Returns the SCID a channel confirmed in the given transaction will have, assuming the funding +/// output is the 1st output in the transaction. +pub fn confirm_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) -> u64 { + let scid = confirm_transaction_at(node, tx, node.best_block_info().1 + 1); connect_blocks(node, CHAN_CONFIRM_DEPTH - 1); + scid } /// Mine a signle block containing the given transaction pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction) { @@ -59,7 +64,10 @@ pub fn mine_transaction<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transac } /// Mine the given transaction at the given height, mining blocks as required to build to that /// height -pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) { +/// +/// Returns the SCID a channel confirmed in the given transaction will have, assuming the funding +/// output is the 1st output in the transaction. +pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &Transaction, conf_height: u32) -> u64 { let first_connect_height = node.best_block_info().1 + 1; assert!(first_connect_height <= conf_height); if conf_height > first_connect_height { @@ -74,6 +82,7 @@ pub fn confirm_transaction_at<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, tx: &T } block.txdata.push(tx.clone()); connect_block(node, &block); + scid_utils::scid_from_parts(conf_height as u64, block.txdata.len() as u64 - 1, 0).unwrap() } /// The possible ways we may notify a ChannelManager of a new block diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 48b4b07c..53cadecb 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -9222,7 +9222,11 @@ fn test_duplicate_chan_id() { 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(); + // Once we call `get_outbound_funding_created` the channel has a duplicate channel_id as + // another channel in the ChannelManager - an invalid state. Thus, we'd panic later when we + // try to create another channel. Instead, we drop the channel entirely here (leaving the + // channelmanager in a possibly nonsense state instead). + let mut as_chan = a_channel_lock.by_id.remove(&open_chan_2_msg.temporary_channel_id).unwrap(); let logger = test_utils::TestLogger::new(); as_chan.get_outbound_funding_created(tx.clone(), funding_outpoint, &&logger).unwrap() }; @@ -9260,7 +9264,7 @@ fn test_duplicate_chan_id() { let events_4 = nodes[0].node.get_and_clear_pending_events(); assert_eq!(events_4.len(), 0); assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); - assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[0].txid(), funding_output.txid); + assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[0], tx); 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); diff --git a/lightning/src/ln/priv_short_conf_tests.rs b/lightning/src/ln/priv_short_conf_tests.rs index c6525681..3a8b9a4f 100644 --- a/lightning/src/ln/priv_short_conf_tests.rs +++ b/lightning/src/ln/priv_short_conf_tests.rs @@ -565,80 +565,87 @@ fn test_scid_alias_returned() { .blamed_chan_closed(false).expected_htlc_error_data(0x1000|12, &err_data)); } -#[test] -fn test_simple_0conf_channel() { - // If our peer tells us they will accept our channel with 0 confs, and we funded the channel, - // we should trust the funding won't be double-spent (assuming `trust_own_funding_0conf` is - // set)! - // Further, if we `accept_inbound_channel_from_trusted_peer_0conf`, funding locked messages - // should fly immediately and the channel should be available for use as soon as they are - // received. - - 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; +// Receiver must have been initialized with manually_accept_inbound_channels set to true. +fn open_zero_conf_channel<'a, 'b, 'c, 'd>(initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>, initiator_config: Option) -> bitcoin::Transaction { + initiator.node.create_channel(receiver.node.get_our_node_id(), 100_000, 10_001, 42, initiator_config).unwrap(); + let open_channel = get_event_msg!(initiator, MessageSendEvent::SendOpenChannel, receiver.node.get_our_node_id()); - let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(chan_config)]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - - nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap(); - let open_channel = 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_channel); - let events = nodes[1].node.get_and_clear_pending_events(); + receiver.node.handle_open_channel(&initiator.node.get_our_node_id(), InitFeatures::known(), &open_channel); + let events = receiver.node.get_and_clear_pending_events(); assert_eq!(events.len(), 1); match events[0] { Event::OpenChannelRequest { temporary_channel_id, .. } => { - nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap(); + receiver.node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &initiator.node.get_our_node_id(), 0).unwrap(); }, _ => panic!("Unexpected event"), }; - let mut accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()); + let mut accept_channel = get_event_msg!(receiver, MessageSendEvent::SendAcceptChannel, initiator.node.get_our_node_id()); assert_eq!(accept_channel.minimum_depth, 0); - nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &accept_channel); + initiator.node.handle_accept_channel(&receiver.node.get_our_node_id(), InitFeatures::known(), &accept_channel); - let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100000, 42); - nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap(); - let funding_created = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id()); + let (temporary_channel_id, tx, _) = create_funding_transaction(&initiator, &receiver.node.get_our_node_id(), 100_000, 42); + initiator.node.funding_transaction_generated(&temporary_channel_id, &receiver.node.get_our_node_id(), tx.clone()).unwrap(); + let funding_created = get_event_msg!(initiator, MessageSendEvent::SendFundingCreated, receiver.node.get_our_node_id()); - nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created); - check_added_monitors!(nodes[1], 1); - let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events(); + receiver.node.handle_funding_created(&initiator.node.get_our_node_id(), &funding_created); + check_added_monitors!(receiver, 1); + let bs_signed_locked = receiver.node.get_and_clear_pending_msg_events(); assert_eq!(bs_signed_locked.len(), 2); let as_funding_locked; match &bs_signed_locked[0] { MessageSendEvent::SendFundingSigned { node_id, msg } => { - assert_eq!(*node_id, nodes[0].node.get_our_node_id()); - nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &msg); - check_added_monitors!(nodes[0], 1); + assert_eq!(*node_id, initiator.node.get_our_node_id()); + initiator.node.handle_funding_signed(&receiver.node.get_our_node_id(), &msg); + check_added_monitors!(initiator, 1); - assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); - assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0)[0], tx); + assert_eq!(initiator.tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1); + assert_eq!(initiator.tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0)[0], tx); - as_funding_locked = get_event_msg!(nodes[0], MessageSendEvent::SendFundingLocked, nodes[1].node.get_our_node_id()); + as_funding_locked = get_event_msg!(initiator, MessageSendEvent::SendFundingLocked, receiver.node.get_our_node_id()); } _ => panic!("Unexpected event"), } match &bs_signed_locked[1] { MessageSendEvent::SendFundingLocked { node_id, msg } => { - assert_eq!(*node_id, nodes[0].node.get_our_node_id()); - nodes[0].node.handle_funding_locked(&nodes[1].node.get_our_node_id(), &msg); + assert_eq!(*node_id, initiator.node.get_our_node_id()); + initiator.node.handle_funding_locked(&receiver.node.get_our_node_id(), &msg); } _ => panic!("Unexpected event"), } - nodes[1].node.handle_funding_locked(&nodes[0].node.get_our_node_id(), &as_funding_locked); + receiver.node.handle_funding_locked(&initiator.node.get_our_node_id(), &as_funding_locked); - let as_channel_update = get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id()); - let bs_channel_update = get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[0].node.get_our_node_id()); + let as_channel_update = get_event_msg!(initiator, MessageSendEvent::SendChannelUpdate, receiver.node.get_our_node_id()); + let bs_channel_update = get_event_msg!(receiver, MessageSendEvent::SendChannelUpdate, initiator.node.get_our_node_id()); - nodes[0].node.handle_channel_update(&nodes[1].node.get_our_node_id(), &bs_channel_update); - nodes[1].node.handle_channel_update(&nodes[0].node.get_our_node_id(), &as_channel_update); + initiator.node.handle_channel_update(&receiver.node.get_our_node_id(), &bs_channel_update); + receiver.node.handle_channel_update(&initiator.node.get_our_node_id(), &as_channel_update); - assert_eq!(nodes[0].node.list_usable_channels().len(), 1); - assert_eq!(nodes[1].node.list_usable_channels().len(), 1); + assert_eq!(initiator.node.list_usable_channels().len(), 1); + assert_eq!(receiver.node.list_usable_channels().len(), 1); + + tx +} + +#[test] +fn test_simple_0conf_channel() { + // If our peer tells us they will accept our channel with 0 confs, and we funded the channel, + // we should trust the funding won't be double-spent (assuming `trust_own_funding_0conf` is + // set)! + // Further, if we `accept_inbound_channel_from_trusted_peer_0conf`, funding locked messages + // should fly immediately and the channel should be available for use as soon as they are + // received. + + 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); + + open_zero_conf_channel(&nodes[0], &nodes[1], None); send_payment(&nodes[0], &[&nodes[1]], 100_000); } @@ -790,3 +797,79 @@ fn test_0conf_channel_with_async_monitor() { send_payment(&nodes[0], &[&nodes[1]], 100_000); } + +#[test] +fn test_0conf_close_no_early_chan_update() { + // Tests that even with a public channel 0conf channel, we don't generate a channel_update on + // closing. + 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_options.announced_channel = true; + open_zero_conf_channel(&nodes[0], &nodes[1], Some(chan_config)); + + // We can use the channel immediately, but won't generate a channel_update until we get confs + send_payment(&nodes[0], &[&nodes[1]], 100_000); + + nodes[0].node.force_close_all_channels(); + check_added_monitors!(nodes[0], 1); + check_closed_event!(&nodes[0], 1, ClosureReason::HolderForceClosed); + let _ = get_err_msg!(nodes[0], nodes[1].node.get_our_node_id()); +} + +#[test] +fn test_public_0conf_channel() { + // Tests that we will announce a public channel (after confirmation) even if its 0conf. + 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_options.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()); + 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[1].node.handle_announcement_signatures(&nodes[0].node.get_our_node_id(), &as_announcement_sigs); + nodes[0].node.handle_announcement_signatures(&nodes[1].node.get_our_node_id(), &bs_announcement_sigs); + + let bs_announcement = nodes[1].node.get_and_clear_pending_msg_events(); + assert_eq!(bs_announcement.len(), 1); + let announcement; + let bs_update; + match bs_announcement[0] { + MessageSendEvent::BroadcastChannelAnnouncement { ref msg, ref update_msg } => { + announcement = msg.clone(); + bs_update = update_msg.clone(); + }, + _ => panic!("Unexpected event"), + }; + + let as_announcement = nodes[0].node.get_and_clear_pending_msg_events(); + assert_eq!(as_announcement.len(), 1); + match as_announcement[0] { + MessageSendEvent::BroadcastChannelAnnouncement { ref msg, ref update_msg } => { + assert!(announcement == *msg); + assert_eq!(update_msg.contents.short_channel_id, scid); + assert_eq!(update_msg.contents.short_channel_id, announcement.contents.short_channel_id); + assert_eq!(update_msg.contents.short_channel_id, bs_update.contents.short_channel_id); + }, + _ => panic!("Unexpected event"), + }; +}