Correctly handle sending announcement sigs on public 0conf channels
authorMatt Corallo <git@bluematt.me>
Fri, 1 Apr 2022 21:29:59 +0000 (21:29 +0000)
committerMatt Corallo <git@bluematt.me>
Fri, 27 May 2022 22:40:07 +0000 (22:40 +0000)
lightning/src/ln/channel.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/priv_short_conf_tests.rs

index 70a13f9ebe173ee9ce8ed7d941c356683cd6a59d..8362f6d73b771b1988f2d779192f057742d1b8af 100644 (file)
@@ -4657,12 +4657,11 @@ impl<Signer: Sign> Channel<Signer> {
        pub fn transactions_confirmed<L: Deref>(&mut self, block_hash: &BlockHash, height: u32,
                txdata: &TransactionData, genesis_block_hash: BlockHash, node_pk: PublicKey, logger: &L)
        -> Result<(Option<msgs::FundingLocked>, Option<msgs::AnnouncementSignatures>), 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() ||
index c0e33e5b93a51a4d620f6c19eec016a15468f70e..e54874b9e2e2f2c80382bd13c2a554ae13bba578 100644 (file)
@@ -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
index 48b4b07c7d76d1f73238b27eb851ac32e720adbb..53cadecb06315742a34489f105282bdcf6bec1dd 100644 (file)
@@ -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);
index c6525681749d75abea6c4e92fb701014be7ecfbe..3a8b9a4fbe3a5666075287715fc522b66ff94cac 100644 (file)
@@ -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<UserConfig>) -> 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"),
+       };
+}