]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Add `FundingTxBroadcastSafe` event
authorjbesraa <jbesraa@gmail.com>
Thu, 9 May 2024 13:37:33 +0000 (16:37 +0300)
committerjbesraa <jbesraa@gmail.com>
Mon, 8 Jul 2024 17:10:19 +0000 (20:10 +0300)
The `FundingTxBroadcastSafe` event indicates that we have received
`funding_signed` message from our counterparty and that you should
broadcast the funding transaction.

This event is only emitted if upon generating the funding transaction
you call `ChannelManager::unsafe_manual_funding_transaction_generated`
that will emit this event instead of `ChannelPending` event.

`ChannelManager::unsafe_manual_funding_transaction_generated` wont check
if the funding transaction is signed, those its unsafe. It is manual
because you are responsibile on broadcasting the transaction once the
event is received.

lightning/src/events/mod.rs
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs

index e59a3d1850139960d46cbd30031a2087bea79ee0..8246499b6f59808285f7cf1155134ae291125f95 100644 (file)
@@ -579,6 +579,32 @@ pub enum Event {
                /// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
                user_channel_id: u128,
        },
+       /// Used to indicate that the counterparty node has provided the signature(s) required to
+       /// recover our funds in case they go offline.
+       ///
+       /// It is safe (and your responsibility) to broadcast the funding transaction upon receiving this
+       /// event.
+       ///
+       /// This event is only emitted if you called
+       /// [`ChannelManager::unsafe_manual_funding_transaction_generated`] instead of
+       /// [`ChannelManager::funding_transaction_generated`].
+       ///
+       /// [`ChannelManager::unsafe_manual_funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::unsafe_manual_funding_transaction_generated
+       /// [`ChannelManager::funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::funding_transaction_generated
+       FundingTxBroadcastSafe {
+               /// The `channel_id` indicating which channel has reached this stage.
+               channel_id: ChannelId,
+               /// The `user_channel_id` value passed in to [`ChannelManager::create_channel`].
+               ///
+               /// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
+               user_channel_id: u128,
+               /// The outpoint of the channel's funding transaction.
+               funding_txo: OutPoint,
+               /// The `node_id` of the channel counterparty.
+               counterparty_node_id: PublicKey,
+               /// The `temporary_channel_id` this channel used to be known by during channel establishment.
+               former_temporary_channel_id: ChannelId,
+       },
        /// Indicates that we've been offered a payment and it needs to be claimed via calling
        /// [`ChannelManager::claim_funds`] with the preimage given in [`PaymentPurpose`].
        ///
@@ -1528,7 +1554,17 @@ impl Writeable for Event {
                                        (0, payment_id, required),
                                        (2, invoice, required),
                                        (4, responder, option),
-                               })
+                               });
+                       },
+                       &Event::FundingTxBroadcastSafe { ref channel_id, ref user_channel_id, ref funding_txo, ref counterparty_node_id, ref former_temporary_channel_id} => {
+                               43u8.write(writer)?;
+                               write_tlv_fields!(writer, {
+                                       (0, channel_id, required),
+                                       (2, user_channel_id, required),
+                                       (4, funding_txo, required),
+                                       (6, counterparty_node_id, required),
+                                       (8, former_temporary_channel_id, required),
+                               });
                        },
                        // Note that, going forward, all new events must only write data inside of
                        // `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
@@ -1981,6 +2017,27 @@ impl MaybeReadable for Event {
                                };
                                f()
                        },
+                       43u8 => {
+                               let mut channel_id = RequiredWrapper(None);
+                               let mut user_channel_id = RequiredWrapper(None);
+                               let mut funding_txo = RequiredWrapper(None);
+                               let mut counterparty_node_id = RequiredWrapper(None);
+                               let mut former_temporary_channel_id = RequiredWrapper(None);
+                               read_tlv_fields!(reader, {
+                                       (0, channel_id, required),
+                                       (2, user_channel_id, required),
+                                       (4, funding_txo, required),
+                                       (6, counterparty_node_id, required),
+                                       (8, former_temporary_channel_id, required)
+                               });
+                               Ok(Some(Event::FundingTxBroadcastSafe {
+                                       channel_id: channel_id.0.unwrap(),
+                                       user_channel_id: user_channel_id.0.unwrap(),
+                                       funding_txo: funding_txo.0.unwrap(),
+                                       counterparty_node_id: counterparty_node_id.0.unwrap(),
+                                       former_temporary_channel_id: former_temporary_channel_id.0.unwrap(),
+                               }))
+                       },
                        // Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
                        // Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
                        // reads.
index 4de551b72bfdf331d4ee64b15bbe8b69ee95d8bf..dcfa038c3dcb00849d20bc8107174037b0724253 100644 (file)
@@ -1331,7 +1331,12 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
        counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,
 
        pub(crate) channel_transaction_parameters: ChannelTransactionParameters,
+       /// The transaction which funds this channel. Note that for manually-funded channels (i.e.,
+       /// is_manual_broadcast is true) this will be a dummy empty transaction.
        funding_transaction: Option<Transaction>,
+       /// This flag indicates that it is the user's responsibility to validated and broadcast the
+       /// funding transaction.
+       is_manual_broadcast: bool,
        is_batch_funding: Option<()>,
 
        counterparty_cur_commitment_point: Option<PublicKey>,
@@ -1419,6 +1424,9 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
        // We track whether we already emitted a `ChannelPending` event.
        channel_pending_event_emitted: bool,
 
+       // We track whether we already emitted a `FundingTxBroadcastSafe` event.
+       funding_tx_broadcast_safe_event_emitted: bool,
+
        // We track whether we already emitted a `ChannelReady` event.
        channel_ready_event_emitted: bool,
 
@@ -1758,6 +1766,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        outbound_scid_alias: 0,
 
                        channel_pending_event_emitted: false,
+                       funding_tx_broadcast_safe_event_emitted: false,
                        channel_ready_event_emitted: false,
 
                        #[cfg(any(test, fuzzing))]
@@ -1769,6 +1778,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        local_initiated_shutdown: None,
 
                        blocked_monitor_updates: Vec::new(),
+
+                       is_manual_broadcast: false,
                };
 
                Ok(channel_context)
@@ -1982,6 +1993,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                        outbound_scid_alias,
 
                        channel_pending_event_emitted: false,
+                       funding_tx_broadcast_safe_event_emitted: false,
                        channel_ready_event_emitted: false,
 
                        #[cfg(any(test, fuzzing))]
@@ -1992,6 +2004,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
 
                        blocked_monitor_updates: Vec::new(),
                        local_initiated_shutdown: None,
+                       is_manual_broadcast: false,
                })
        }
 
@@ -2370,6 +2383,10 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                self.config.options.forwarding_fee_proportional_millionths
        }
 
+       pub fn is_manual_broadcast(&self) -> bool {
+               self.is_manual_broadcast
+       }
+
        pub fn get_cltv_expiry_delta(&self) -> u16 {
                cmp::max(self.config.options.cltv_expiry_delta, MIN_CLTV_EXPIRY_DELTA)
        }
@@ -2404,6 +2421,11 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                self.channel_pending_event_emitted
        }
 
+       // Returns whether we already emitted a `FundingTxBroadcastSafe` event.
+       pub(crate) fn funding_tx_broadcast_safe_event_emitted(&self) -> bool {
+               self.funding_tx_broadcast_safe_event_emitted
+       }
+
        // Remembers that we already emitted a `ChannelPending` event.
        pub(crate) fn set_channel_pending_event_emitted(&mut self) {
                self.channel_pending_event_emitted = true;
@@ -2419,6 +2441,11 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                self.channel_ready_event_emitted = true;
        }
 
+       // Remembers that we already emitted a `FundingTxBroadcastSafe` event.
+       pub(crate) fn set_funding_tx_broadcast_safe_event_emitted(&mut self) {
+               self.funding_tx_broadcast_safe_event_emitted = true;
+       }
+
        /// Tracks the number of ticks elapsed since the previous [`ChannelConfig`] was updated. Once
        /// [`EXPIRE_PREV_CONFIG_TICKS`] is reached, the previous config is considered expired and will
        /// no longer be considered when forwarding HTLCs.
@@ -2455,6 +2482,17 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider  {
                did_channel_update
        }
 
+       /// Marking the channel as manual broadcast is used in order to prevent LDK from automatically
+       /// broadcasting the funding transaction.
+       ///
+       /// This is useful if you wish to get hold of the funding transaction before it is broadcasted
+       /// via [`Event::FundingTxBroadcastSafe`] event.
+       ///
+       /// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
+       pub fn set_manual_broadcast(&mut self) {
+               self.is_manual_broadcast = true;
+       }
+
        /// Returns true if funding_signed was sent/received and the
        /// funding transaction has been broadcast if necessary.
        pub fn is_funding_broadcast(&self) -> bool {
@@ -8706,6 +8744,7 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
 
                let channel_pending_event_emitted = Some(self.context.channel_pending_event_emitted);
                let channel_ready_event_emitted = Some(self.context.channel_ready_event_emitted);
+               let funding_tx_broadcast_safe_event_emitted = Some(self.context.funding_tx_broadcast_safe_event_emitted);
 
                // `user_id` used to be a single u64 value. In order to remain backwards compatible with
                // versions prior to 0.0.113, the u128 is serialized as two separate u64 values. Therefore,
@@ -8718,6 +8757,7 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
                if !self.context.monitor_pending_update_adds.is_empty() {
                        monitor_pending_update_adds = Some(&self.context.monitor_pending_update_adds);
                }
+               let is_manual_broadcast = Some(self.context.is_manual_broadcast);
 
                // `current_point` will become optional when async signing is implemented.
                let cur_holder_commitment_point = Some(self.context.holder_commitment_point.current_point());
@@ -8762,6 +8802,8 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
                        (45, cur_holder_commitment_point, option),
                        (47, next_holder_commitment_point, option),
                        (49, self.context.local_initiated_shutdown, option), // Added in 0.0.122
+                       (51, is_manual_broadcast, option), // Added in 0.0.124
+                       (53, funding_tx_broadcast_safe_event_emitted, option) // Added in 0.0.124
                });
 
                Ok(())
@@ -9050,6 +9092,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                let mut outbound_scid_alias = None;
                let mut channel_pending_event_emitted = None;
                let mut channel_ready_event_emitted = None;
+               let mut funding_tx_broadcast_safe_event_emitted = None;
 
                let mut user_id_high_opt: Option<u64> = None;
                let mut channel_keys_id: Option<[u8; 32]> = None;
@@ -9073,6 +9116,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
 
                let mut cur_holder_commitment_point_opt: Option<PublicKey> = None;
                let mut next_holder_commitment_point_opt: Option<PublicKey> = None;
+               let mut is_manual_broadcast = None;
 
                read_tlv_fields!(reader, {
                        (0, announcement_sigs, option),
@@ -9107,6 +9151,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                        (45, cur_holder_commitment_point_opt, option),
                        (47, next_holder_commitment_point_opt, option),
                        (49, local_initiated_shutdown, option),
+                       (51, is_manual_broadcast, option),
+                       (53, funding_tx_broadcast_safe_event_emitted, option),
                });
 
                let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
@@ -9347,6 +9393,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                                // Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
                                outbound_scid_alias: outbound_scid_alias.unwrap_or(0),
 
+                               funding_tx_broadcast_safe_event_emitted: funding_tx_broadcast_safe_event_emitted.unwrap_or(false),
                                channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
                                channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),
 
@@ -9359,6 +9406,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
                                local_initiated_shutdown,
 
                                blocked_monitor_updates: blocked_monitor_updates.unwrap(),
+                               is_manual_broadcast: is_manual_broadcast.unwrap_or(false),
                        },
                        #[cfg(any(dual_funding, splicing))]
                        dual_funding_channel_context: None,
index 807dce7cd4536c2307f74df5190445549a0ebdac..804c574cfbd16c0b0084431ae6681a49fa08c8a6 100644 (file)
@@ -699,6 +699,51 @@ struct ClaimablePayment {
        htlcs: Vec<ClaimableHTLC>,
 }
 
+/// Represent the channel funding transaction type.
+enum FundingType {
+       /// This variant is useful when we want LDK to validate the funding transaction and
+       /// broadcast it automatically.
+       ///
+       /// This is the normal flow.
+       Checked(Transaction),
+       /// This variant is useful when we want to loosen the validation checks and allow to
+       /// manually broadcast the funding transaction, leaving the responsibility to the caller.
+       ///
+       /// This is useful in cases of constructing the funding transaction as part of another
+       /// flow and the caller wants to perform the validation and broadcasting. An example of such
+       /// scenario could be when constructing the funding transaction as part of a Payjoin
+       /// transaction.
+       Unchecked(OutPoint),
+}
+
+impl FundingType {
+       fn txid(&self) -> Txid {
+               match self {
+                       FundingType::Checked(tx) => tx.txid(),
+                       FundingType::Unchecked(outp) => outp.txid,
+               }
+       }
+
+       fn transaction_or_dummy(&self) -> Transaction {
+               match self {
+                       FundingType::Checked(tx) => tx.clone(),
+                       FundingType::Unchecked(_) => Transaction {
+                               version: bitcoin::transaction::Version::TWO,
+                               lock_time: bitcoin::absolute::LockTime::ZERO,
+                               input: Vec::new(),
+                               output: Vec::new(),
+                       },
+               }
+       }
+
+       fn is_manual_broadcast(&self) -> bool {
+               match self {
+                       FundingType::Checked(_) => false,
+                       FundingType::Unchecked(_) => true,
+               }
+       }
+}
+
 /// Information about claimable or being-claimed payments
 struct ClaimablePayments {
        /// Map from payment hash to the payment data and any HTLCs which are to us and can be
@@ -2568,6 +2613,21 @@ macro_rules! send_channel_ready {
                }
        }}
 }
+macro_rules! emit_funding_tx_broadcast_safe_event {
+       ($locked_events: expr, $channel: expr, $funding_txo: expr) => {
+               if !$channel.context.funding_tx_broadcast_safe_event_emitted() {
+                       $locked_events.push_back((events::Event::FundingTxBroadcastSafe {
+                               channel_id: $channel.context.channel_id(),
+                               user_channel_id: $channel.context.get_user_id(),
+                               funding_txo: $funding_txo,
+                               counterparty_node_id: $channel.context.get_counterparty_node_id(),
+                               former_temporary_channel_id: $channel.context.temporary_channel_id()
+                                       .expect("Unreachable: FundingTxBroadcastSafe event feature added to channel establishment process in LDK v0.0.124 where this should never be None."),
+                       }, None));
+                       $channel.context.set_funding_tx_broadcast_safe_event_emitted();
+               }
+       }
+}
 
 macro_rules! emit_channel_pending_event {
        ($locked_events: expr, $channel: expr) => {
@@ -4238,9 +4298,9 @@ where
 
        /// Handles the generation of a funding transaction, optionally (for tests) with a function
        /// which checks the correctness of the funding transaction given the associated channel.
-       fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, &'static str>>(
+       fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>) -> Result<OutPoint, &'static str>>(
                &self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, is_batch_funding: bool,
-               mut find_funding_output: FundingOutput,
+               mut find_funding_output: FundingOutput, is_manual_broadcast: bool,
        ) -> Result<(), APIError> {
                let per_peer_state = self.per_peer_state.read().unwrap();
                let peer_state_mutex = per_peer_state.get(counterparty_node_id)
@@ -4265,7 +4325,7 @@ where
                                        let _: Result<(), _> = handle_error!(self, Err(err), counterparty);
                                        Err($api_err)
                                } } }
-                               match find_funding_output(&chan, &funding_transaction) {
+                               match find_funding_output(&chan) {
                                        Ok(found_funding_txo) => funding_txo = found_funding_txo,
                                        Err(err) => {
                                                let chan_err = ChannelError::close(err.to_owned());
@@ -4304,6 +4364,9 @@ where
                                msg,
                        });
                }
+               if is_manual_broadcast {
+                       chan.context.set_manual_broadcast();
+               }
                match peer_state.channel_by_id.entry(chan.context.channel_id()) {
                        hash_map::Entry::Occupied(_) => {
                                panic!("Generated duplicate funding txid?");
@@ -4333,9 +4396,10 @@ where
 
        #[cfg(test)]
        pub(crate) fn funding_transaction_generated_unchecked(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, output_index: u16) -> Result<(), APIError> {
-               self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, false, |_, tx| {
-                       Ok(OutPoint { txid: tx.txid(), index: output_index })
-               })
+               let txid = funding_transaction.txid();
+               self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, false, |_| {
+                       Ok(OutPoint { txid, index: output_index })
+               }, false)
        }
 
        /// Call this upon creation of a funding transaction for the given channel.
@@ -4372,6 +4436,42 @@ where
                self.batch_funding_transaction_generated(&[(temporary_channel_id, counterparty_node_id)], funding_transaction)
        }
 
+
+       /// **Unsafe**: This method does not validate the spent output. It is the caller's
+       /// responsibility to ensure the spent outputs are SegWit, as well as making sure the funding
+       /// transaction has a final absolute locktime, i.e., its locktime is lower than the next block height.
+       ///
+       /// For a safer method, please refer to [`ChannelManager::funding_transaction_generated`].
+       ///
+       /// Call this in response to a [`Event::FundingGenerationReady`] event.
+       ///
+       /// Note that if this method is called successfully, the funding transaction won't be
+       /// broadcasted and you are expected to broadcast it manually when receiving the
+       /// [`Event::FundingTxBroadcastSafe`] event.
+       ///
+       /// Returns [`APIError::ChannelUnavailable`] if a funding transaction has already been provided
+       /// for the channel or if the channel has been closed as indicated by [`Event::ChannelClosed`].
+       ///
+       /// May panic if the funding output is duplicative with some other channel (note that this
+       /// should be trivially prevented by using unique funding transaction keys per-channel).
+       ///
+       /// Note to keep the miner incentives aligned in moving the blockchain forward, we recommend
+       /// the wallet software generating the funding transaction to apply anti-fee sniping as
+       /// implemented by Bitcoin Core wallet. See <https://bitcoinops.org/en/topics/fee-sniping/> for
+       /// more details.
+       ///
+       /// [`Event::FundingGenerationReady`]: crate::events::Event::FundingGenerationReady
+       /// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
+       /// [`Event::ChannelClosed`]: crate::events::Event::ChannelClosed
+       /// [`ChannelManager::funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::funding_transaction_generated
+       pub fn unsafe_manual_funding_transaction_generated(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding: OutPoint) -> Result<(), APIError> {
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+
+               let temporary_channels = &[(temporary_channel_id, counterparty_node_id)];
+               return self.batch_funding_transaction_generated_intern(temporary_channels, FundingType::Unchecked(funding));
+
+       }
+
        /// Call this upon creation of a batch funding transaction for the given channels.
        ///
        /// Return values are identical to [`Self::funding_transaction_generated`], respective to
@@ -4395,12 +4495,17 @@ where
                                }
                        }
                }
-               if funding_transaction.output.len() > u16::max_value() as usize {
-                       result = result.and(Err(APIError::APIMisuseError {
-                               err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
-                       }));
-               }
-               {
+               result.and(self.batch_funding_transaction_generated_intern(temporary_channels, FundingType::Checked(funding_transaction)))
+       }
+
+       fn batch_funding_transaction_generated_intern(&self, temporary_channels: &[(&ChannelId, &PublicKey)], funding: FundingType) -> Result<(), APIError> {
+               let mut result = Ok(());
+               if let FundingType::Checked(funding_transaction) = &funding {
+                       if funding_transaction.output.len() > u16::max_value() as usize {
+                               result = result.and(Err(APIError::APIMisuseError {
+                                       err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
+                               }));
+                       }
                        let height = self.best_block.read().unwrap().height;
                        // Transactions are evaluated as final by network mempools if their locktime is strictly
                        // lower than the next block height. However, the modules constituting our Lightning
@@ -4408,7 +4513,7 @@ where
                        // module is ahead of LDK, only allow one more block of headroom.
                        if !funding_transaction.input.iter().all(|input| input.sequence == Sequence::MAX) &&
                                funding_transaction.lock_time.is_block_height() &&
-                               funding_transaction.lock_time.to_consensus_u32() > height + 1
+                                       funding_transaction.lock_time.to_consensus_u32() > height + 1
                        {
                                result = result.and(Err(APIError::APIMisuseError {
                                        err: "Funding transaction absolute timelock is non-final".to_owned()
@@ -4416,7 +4521,7 @@ where
                        }
                }
 
-               let txid = funding_transaction.txid();
+               let txid = funding.txid();
                let is_batch_funding = temporary_channels.len() > 1;
                let mut funding_batch_states = if is_batch_funding {
                        Some(self.funding_batch_states.lock().unwrap())
@@ -4434,27 +4539,33 @@ where
                                btree_map::Entry::Vacant(vacant) => Some(vacant.insert(Vec::new())),
                        }
                });
+               let is_manual_broadcast = funding.is_manual_broadcast();
                for &(temporary_channel_id, counterparty_node_id) in temporary_channels {
                        result = result.and_then(|_| self.funding_transaction_generated_intern(
                                temporary_channel_id,
                                counterparty_node_id,
-                               funding_transaction.clone(),
+                               funding.transaction_or_dummy(),
                                is_batch_funding,
-                               |chan, tx| {
+                               |chan| {
                                        let mut output_index = None;
                                        let expected_spk = chan.context.get_funding_redeemscript().to_p2wsh();
-                                       for (idx, outp) in tx.output.iter().enumerate() {
-                                               if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
-                                                       if output_index.is_some() {
-                                                               return Err("Multiple outputs matched the expected script and value");
+                                       let outpoint = match &funding {
+                                               FundingType::Checked(tx) => {
+                                                       for (idx, outp) in tx.output.iter().enumerate() {
+                                                               if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
+                                                                       if output_index.is_some() {
+                                                                               return Err("Multiple outputs matched the expected script and value");
+                                                                       }
+                                                                       output_index = Some(idx as u16);
+                                                               }
                                                        }
-                                                       output_index = Some(idx as u16);
-                                               }
-                                       }
-                                       if output_index.is_none() {
-                                               return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
-                                       }
-                                       let outpoint = OutPoint { txid: tx.txid(), index: output_index.unwrap() };
+                                                       if output_index.is_none() {
+                                                               return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
+                                                       }
+                                                       OutPoint { txid, index: output_index.unwrap() }
+                                               },
+                                               FundingType::Unchecked(outpoint) => outpoint.clone(),
+                                       };
                                        if let Some(funding_batch_state) = funding_batch_state.as_mut() {
                                                // TODO(dual_funding): We only do batch funding for V1 channels at the moment, but we'll probably
                                                // need to fix this somehow to not rely on using the outpoint for the channel ID if we
@@ -4462,7 +4573,8 @@ where
                                                funding_batch_state.push((ChannelId::v1_from_funding_outpoint(outpoint), *counterparty_node_id, false));
                                        }
                                        Ok(outpoint)
-                               })
+                               },
+                               is_manual_broadcast)
                        );
                }
                if let Err(ref e) = result {
@@ -6650,8 +6762,22 @@ where
                }
 
                if let Some(tx) = funding_broadcastable {
-                       log_info!(logger, "Broadcasting funding transaction with txid {}", tx.txid());
-                       self.tx_broadcaster.broadcast_transactions(&[&tx]);
+                       if channel.context.is_manual_broadcast() {
+                               log_info!(logger, "Not broadcasting funding transaction with txid {} as it is manually managed", tx.txid());
+                               let mut pending_events = self.pending_events.lock().unwrap();
+                               match channel.context.get_funding_txo() {
+                                       Some(funding_txo) => {
+                                               emit_funding_tx_broadcast_safe_event!(pending_events, channel, funding_txo.into_bitcoin_outpoint())
+                                       },
+                                       None => {
+                                               debug_assert!(false, "Channel resumed without a funding txo, this should never happen!");
+                                               return (htlc_forwards, decode_update_add_htlcs);
+                                       }
+                               };
+                       } else {
+                               log_info!(logger, "Broadcasting funding transaction with txid {}", tx.txid());
+                               self.tx_broadcaster.broadcast_transactions(&[&tx]);
+                       }
                }
 
                {
index a01388f20158a65317027bce11f47b51c9108478..768cea0faa2f17d776d8258f51ba6deee984b96d 100644 (file)
@@ -11207,3 +11207,62 @@ fn test_accept_inbound_channel_errors_queued() {
        assert_eq!(get_err_msg(&nodes[1], &nodes[0].node.get_our_node_id()).channel_id,
                open_channel_msg.common_fields.temporary_channel_id);
 }
+
+#[test]
+fn test_funding_signed_event() {
+       let mut cfg = UserConfig::default();
+       cfg.channel_handshake_config.minimum_depth = 1;
+       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, &[Some(cfg), Some(cfg)]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       assert!(nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 42, None, None).is_ok());
+       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(), &open_channel);
+       let accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
+
+       nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel);
+       let (temporary_channel_id, tx, funding_outpoint) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100_000, 42);
+       nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), funding_outpoint).unwrap();
+       check_added_monitors!(nodes[0], 0);
+
+       let funding_created = 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);
+       check_added_monitors!(nodes[1], 1);
+       expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
+
+       let funding_signed = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
+       nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &funding_signed);
+       check_added_monitors!(nodes[0], 1);
+       let events = &nodes[0].node.get_and_clear_pending_events();
+       assert_eq!(events.len(), 2);
+       match &events[0] {
+               crate::events::Event::FundingTxBroadcastSafe { funding_txo, .. } => {
+                       assert_eq!(funding_txo.txid, funding_outpoint.txid);
+                       assert_eq!(funding_txo.vout, funding_outpoint.index.into());
+               },
+               _ => panic!("Unexpected event"),
+       };
+       match &events[1] {
+               crate::events::Event::ChannelPending { counterparty_node_id, .. } => {
+                       assert_eq!(*&nodes[1].node.get_our_node_id(), *counterparty_node_id);
+               },
+               _ => panic!("Unexpected event"),
+       };
+
+       mine_transaction(&nodes[0], &tx);
+       mine_transaction(&nodes[1], &tx);
+
+       let as_channel_ready = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReady, nodes[0].node.get_our_node_id());
+       nodes[1].node.handle_channel_ready(&nodes[0].node.get_our_node_id(), &as_channel_ready);
+       let as_channel_ready = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReady, nodes[1].node.get_our_node_id());
+       nodes[0].node.handle_channel_ready(&nodes[1].node.get_our_node_id(), &as_channel_ready);
+
+       expect_channel_ready_event(&nodes[0], &nodes[1].node.get_our_node_id());
+       expect_channel_ready_event(&nodes[1], &nodes[0].node.get_our_node_id());
+       nodes[0].node.get_and_clear_pending_msg_events();
+       nodes[1].node.get_and_clear_pending_msg_events();
+}
+